3 Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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.
21 #include "mapsector.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/mathconstants.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
44 #include "database-dummy.h"
45 #include "database-sqlite3.h"
48 #include "database-leveldb.h"
51 #include "database-redis.h"
54 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
57 SQLite format specification:
58 - Initially only replaces sectors/ and sectors2/
60 If map.sqlite does not exist in the save dir
61 or the block was not found in the database
62 the map will try to load from sectors folder.
63 In either case, map.sqlite will be created
64 and all future saves will save there.
66 Structure of map.sqlite:
77 Map::Map(std::ostream &dout, IGameDef *gamedef):
81 m_transforming_liquid_loop_count_multiplier(1.0f),
82 m_unprocessed_count(0),
83 m_inc_trending_up_start_time(0),
84 m_queue_size_timer_started(false)
93 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
94 i != m_sectors.end(); ++i)
100 void Map::addEventReceiver(MapEventReceiver *event_receiver)
102 m_event_receivers.insert(event_receiver);
105 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
107 m_event_receivers.erase(event_receiver);
110 void Map::dispatchEvent(MapEditEvent *event)
112 for(std::set<MapEventReceiver*>::iterator
113 i = m_event_receivers.begin();
114 i != m_event_receivers.end(); ++i)
116 (*i)->onMapEditEvent(event);
120 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
122 if(m_sector_cache != NULL && p == m_sector_cache_p){
123 MapSector * sector = m_sector_cache;
127 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
129 if(n == m_sectors.end())
132 MapSector *sector = n->second;
134 // Cache the last result
135 m_sector_cache_p = p;
136 m_sector_cache = sector;
141 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
143 return getSectorNoGenerateNoExNoLock(p);
146 MapSector * Map::getSectorNoGenerate(v2s16 p)
148 MapSector *sector = getSectorNoGenerateNoEx(p);
150 throw InvalidPositionException();
155 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
157 v2s16 p2d(p3d.X, p3d.Z);
158 MapSector * sector = getSectorNoGenerateNoEx(p2d);
161 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
165 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
167 MapBlock *block = getBlockNoCreateNoEx(p3d);
169 throw InvalidPositionException();
173 bool Map::isNodeUnderground(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
177 MapBlock * block = getBlockNoCreate(blockpos);
178 return block->getIsUnderground();
180 catch(InvalidPositionException &e)
186 bool Map::isValidPosition(v3s16 p)
188 v3s16 blockpos = getNodeBlockPos(p);
189 MapBlock *block = getBlockNoCreate(blockpos);
190 return (block != NULL);
193 // Returns a CONTENT_IGNORE node if not found
194 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
196 v3s16 blockpos = getNodeBlockPos(p);
197 MapBlock *block = getBlockNoCreateNoEx(blockpos);
199 if (is_valid_position != NULL)
200 *is_valid_position = false;
201 return MapNode(CONTENT_IGNORE);
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
207 if (is_valid_position != NULL)
208 *is_valid_position = is_valid_p;
214 // throws InvalidPositionException if not found
215 // TODO: Now this is deprecated, getNodeNoEx should be renamed
216 MapNode Map::getNode(v3s16 p)
218 v3s16 blockpos = getNodeBlockPos(p);
219 MapBlock *block = getBlockNoCreateNoEx(blockpos);
221 throw InvalidPositionException();
222 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
223 bool is_valid_position;
224 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
225 if (!is_valid_position)
226 throw InvalidPositionException();
231 // throws InvalidPositionException if not found
232 void Map::setNode(v3s16 p, MapNode & n)
234 v3s16 blockpos = getNodeBlockPos(p);
235 MapBlock *block = getBlockNoCreate(blockpos);
236 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
237 // Never allow placing CONTENT_IGNORE, it fucks up stuff
238 if(n.getContent() == CONTENT_IGNORE){
240 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
241 <<" while trying to replace \""
242 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
243 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
244 debug_stacks_print_to(infostream);
247 block->setNodeNoCheck(relpos, n);
252 Goes recursively through the neighbours of the node.
254 Alters only transparent nodes.
256 If the lighting of the neighbour is lower than the lighting of
257 the node was (before changing it to 0 at the step before), the
258 lighting of the neighbour is set to 0 and then the same stuff
259 repeats for the neighbour.
261 The ending nodes of the routine are stored in light_sources.
262 This is useful when a light is removed. In such case, this
263 routine can be called for the light node and then again for
264 light_sources to re-light the area without the removed light.
266 values of from_nodes are lighting values.
268 void Map::unspreadLight(enum LightBank bank,
269 std::map<v3s16, u8> & from_nodes,
270 std::set<v3s16> & light_sources,
271 std::map<v3s16, MapBlock*> & modified_blocks)
273 INodeDefManager *nodemgr = m_gamedef->ndef();
276 v3s16(0,0,1), // back
278 v3s16(1,0,0), // right
279 v3s16(0,0,-1), // front
280 v3s16(0,-1,0), // bottom
281 v3s16(-1,0,0), // left
284 if(from_nodes.empty())
287 u32 blockchangecount = 0;
289 std::map<v3s16, u8> unlighted_nodes;
292 Initialize block cache
295 MapBlock *block = NULL;
296 // Cache this a bit, too
297 bool block_checked_in_modified = false;
299 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
300 j != from_nodes.end(); ++j)
302 v3s16 pos = j->first;
303 v3s16 blockpos = getNodeBlockPos(pos);
305 // Only fetch a new block if the block position has changed
307 if(block == NULL || blockpos != blockpos_last){
308 block = getBlockNoCreate(blockpos);
309 blockpos_last = blockpos;
311 block_checked_in_modified = false;
315 catch(InvalidPositionException &e)
323 // Calculate relative position in block
324 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
326 // Get node straight from the block
327 //MapNode n = block->getNode(relpos);
329 u8 oldlight = j->second;
331 // Loop through 6 neighbors
332 for(u16 i=0; i<6; i++)
334 // Get the position of the neighbor node
335 v3s16 n2pos = pos + dirs[i];
337 // Get the block where the node is located
338 v3s16 blockpos, relpos;
339 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
341 // Only fetch a new block if the block position has changed
343 if(block == NULL || blockpos != blockpos_last){
344 block = getBlockNoCreate(blockpos);
345 blockpos_last = blockpos;
347 block_checked_in_modified = false;
351 catch(InvalidPositionException &e) {
355 // Get node straight from the block
356 bool is_valid_position;
357 MapNode n2 = block->getNode(relpos, &is_valid_position);
358 if (!is_valid_position)
361 bool changed = false;
363 //TODO: Optimize output by optimizing light_sources?
366 If the neighbor is dimmer than what was specified
367 as oldlight (the light of the previous node)
369 if(n2.getLight(bank, nodemgr) < oldlight)
372 And the neighbor is transparent and it has some light
374 if(nodemgr->get(n2).light_propagates
375 && n2.getLight(bank, nodemgr) != 0)
378 Set light to 0 and add to queue
381 u8 current_light = n2.getLight(bank, nodemgr);
382 n2.setLight(bank, 0, nodemgr);
383 block->setNode(relpos, n2);
385 unlighted_nodes[n2pos] = current_light;
389 Remove from light_sources if it is there
390 NOTE: This doesn't happen nearly at all
392 /*if(light_sources.find(n2pos))
394 infostream<<"Removed from light_sources"<<std::endl;
395 light_sources.remove(n2pos);
400 if(light_sources.find(n2pos) != NULL)
401 light_sources.remove(n2pos);*/
404 light_sources.insert(n2pos);
407 // Add to modified_blocks
408 if(changed == true && block_checked_in_modified == false)
410 // If the block is not found in modified_blocks, add.
411 if(modified_blocks.find(blockpos) == modified_blocks.end())
413 modified_blocks[blockpos] = block;
415 block_checked_in_modified = true;
420 /*infostream<<"unspreadLight(): Changed block "
421 <<blockchangecount<<" times"
422 <<" for "<<from_nodes.size()<<" nodes"
425 if(!unlighted_nodes.empty())
426 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
430 A single-node wrapper of the above
432 void Map::unLightNeighbors(enum LightBank bank,
433 v3s16 pos, u8 lightwas,
434 std::set<v3s16> & light_sources,
435 std::map<v3s16, MapBlock*> & modified_blocks)
437 std::map<v3s16, u8> from_nodes;
438 from_nodes[pos] = lightwas;
440 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
444 Lights neighbors of from_nodes, collects all them and then
447 void Map::spreadLight(enum LightBank bank,
448 std::set<v3s16> & from_nodes,
449 std::map<v3s16, MapBlock*> & modified_blocks)
451 INodeDefManager *nodemgr = m_gamedef->ndef();
453 const v3s16 dirs[6] = {
454 v3s16(0,0,1), // back
456 v3s16(1,0,0), // right
457 v3s16(0,0,-1), // front
458 v3s16(0,-1,0), // bottom
459 v3s16(-1,0,0), // left
462 if(from_nodes.empty())
465 u32 blockchangecount = 0;
467 std::set<v3s16> lighted_nodes;
470 Initialize block cache
473 MapBlock *block = NULL;
474 // Cache this a bit, too
475 bool block_checked_in_modified = false;
477 for(std::set<v3s16>::iterator j = from_nodes.begin();
478 j != from_nodes.end(); ++j)
481 v3s16 blockpos, relpos;
483 getNodeBlockPosWithOffset(pos, blockpos, relpos);
485 // Only fetch a new block if the block position has changed
487 if(block == NULL || blockpos != blockpos_last){
488 block = getBlockNoCreate(blockpos);
489 blockpos_last = blockpos;
491 block_checked_in_modified = false;
495 catch(InvalidPositionException &e) {
502 // Get node straight from the block
503 bool is_valid_position;
504 MapNode n = block->getNode(relpos, &is_valid_position);
506 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
507 u8 newlight = diminish_light(oldlight);
509 // Loop through 6 neighbors
510 for(u16 i=0; i<6; i++){
511 // Get the position of the neighbor node
512 v3s16 n2pos = pos + dirs[i];
514 // Get the block where the node is located
515 v3s16 blockpos, relpos;
516 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
518 // Only fetch a new block if the block position has changed
520 if(block == NULL || blockpos != blockpos_last){
521 block = getBlockNoCreate(blockpos);
522 blockpos_last = blockpos;
524 block_checked_in_modified = false;
528 catch(InvalidPositionException &e) {
532 // Get node straight from the block
533 MapNode n2 = block->getNode(relpos, &is_valid_position);
534 if (!is_valid_position)
537 bool changed = false;
539 If the neighbor is brighter than the current node,
540 add to list (it will light up this node on its turn)
542 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
544 lighted_nodes.insert(n2pos);
548 If the neighbor is dimmer than how much light this node
549 would spread on it, add to list
551 if(n2.getLight(bank, nodemgr) < newlight)
553 if(nodemgr->get(n2).light_propagates)
555 n2.setLight(bank, newlight, nodemgr);
556 block->setNode(relpos, n2);
557 lighted_nodes.insert(n2pos);
562 // Add to modified_blocks
563 if(changed == true && block_checked_in_modified == false)
565 // If the block is not found in modified_blocks, add.
566 if(modified_blocks.find(blockpos) == modified_blocks.end())
568 modified_blocks[blockpos] = block;
570 block_checked_in_modified = true;
575 /*infostream<<"spreadLight(): Changed block "
576 <<blockchangecount<<" times"
577 <<" for "<<from_nodes.size()<<" nodes"
580 if(!lighted_nodes.empty())
581 spreadLight(bank, lighted_nodes, modified_blocks);
585 A single-node source variation of the above.
587 void Map::lightNeighbors(enum LightBank bank,
589 std::map<v3s16, MapBlock*> & modified_blocks)
591 std::set<v3s16> from_nodes;
592 from_nodes.insert(pos);
593 spreadLight(bank, from_nodes, modified_blocks);
596 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
598 INodeDefManager *nodemgr = m_gamedef->ndef();
601 v3s16(0,0,1), // back
603 v3s16(1,0,0), // right
604 v3s16(0,0,-1), // front
605 v3s16(0,-1,0), // bottom
606 v3s16(-1,0,0), // left
609 u8 brightest_light = 0;
610 v3s16 brightest_pos(0,0,0);
611 bool found_something = false;
613 // Loop through 6 neighbors
614 for(u16 i=0; i<6; i++){
615 // Get the position of the neighbor node
616 v3s16 n2pos = p + dirs[i];
618 bool is_valid_position;
619 n2 = getNodeNoEx(n2pos, &is_valid_position);
620 if (!is_valid_position)
623 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
624 brightest_light = n2.getLight(bank, nodemgr);
625 brightest_pos = n2pos;
626 found_something = true;
630 if(found_something == false)
631 throw InvalidPositionException();
633 return brightest_pos;
637 Propagates sunlight down from a node.
638 Starting point gets sunlight.
640 Returns the lowest y value of where the sunlight went.
642 Mud is turned into grass in where the sunlight stops.
644 s16 Map::propagateSunlight(v3s16 start,
645 std::map<v3s16, MapBlock*> & modified_blocks)
647 INodeDefManager *nodemgr = m_gamedef->ndef();
652 v3s16 pos(start.X, y, start.Z);
654 v3s16 blockpos = getNodeBlockPos(pos);
657 block = getBlockNoCreate(blockpos);
659 catch(InvalidPositionException &e)
664 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
665 bool is_valid_position;
666 MapNode n = block->getNode(relpos, &is_valid_position);
667 if (!is_valid_position)
670 if(nodemgr->get(n).sunlight_propagates)
672 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
673 block->setNode(relpos, n);
675 modified_blocks[blockpos] = block;
679 // Sunlight goes no further
686 void Map::updateLighting(enum LightBank bank,
687 std::map<v3s16, MapBlock*> & a_blocks,
688 std::map<v3s16, MapBlock*> & modified_blocks)
690 INodeDefManager *nodemgr = m_gamedef->ndef();
692 /*m_dout<<DTIME<<"Map::updateLighting(): "
693 <<a_blocks.size()<<" blocks."<<std::endl;*/
695 //TimeTaker timer("updateLighting");
699 //u32 count_was = modified_blocks.size();
701 //std::map<v3s16, MapBlock*> blocks_to_update;
703 std::set<v3s16> light_sources;
705 std::map<v3s16, u8> unlight_from;
707 int num_bottom_invalid = 0;
710 //TimeTaker t("first stuff");
712 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
713 i != a_blocks.end(); ++i)
715 MapBlock *block = i->second;
719 // Don't bother with dummy blocks.
723 v3s16 pos = block->getPos();
724 v3s16 posnodes = block->getPosRelative();
725 modified_blocks[pos] = block;
726 //blocks_to_update[pos] = block;
729 Clear all light from block
731 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
732 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
733 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
736 bool is_valid_position;
737 MapNode n = block->getNode(p, &is_valid_position);
738 if (!is_valid_position) {
739 /* This would happen when dealing with a
742 infostream<<"updateLighting(): InvalidPositionException"
746 u8 oldlight = n.getLight(bank, nodemgr);
747 n.setLight(bank, 0, nodemgr);
748 block->setNode(p, n);
750 // If node sources light, add to list
751 u8 source = nodemgr->get(n).light_source;
753 light_sources.insert(p + posnodes);
755 // Collect borders for unlighting
756 if((x==0 || x == MAP_BLOCKSIZE-1
757 || y==0 || y == MAP_BLOCKSIZE-1
758 || z==0 || z == MAP_BLOCKSIZE-1)
761 v3s16 p_map = p + posnodes;
762 unlight_from[p_map] = oldlight;
768 if(bank == LIGHTBANK_DAY)
770 bool bottom_valid = block->propagateSunlight(light_sources);
773 num_bottom_invalid++;
775 // If bottom is valid, we're done.
779 else if(bank == LIGHTBANK_NIGHT)
781 // For night lighting, sunlight is not propagated
786 // Invalid lighting bank
790 /*infostream<<"Bottom for sunlight-propagated block ("
791 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
794 // Bottom sunlight is not valid; get the block and loop to it
798 block = getBlockNoCreate(pos);
800 catch(InvalidPositionException &e)
811 Enable this to disable proper lighting for speeding up map
812 generation for testing or whatever
815 //if(g_settings->get(""))
817 core::map<v3s16, MapBlock*>::Iterator i;
818 i = blocks_to_update.getIterator();
819 for(; i.atEnd() == false; i++)
821 MapBlock *block = i.getNode()->getValue();
822 v3s16 p = block->getPos();
823 block->setLightingExpired(false);
831 //TimeTaker timer("unspreadLight");
832 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
837 u32 diff = modified_blocks.size() - count_was;
838 count_was = modified_blocks.size();
839 infostream<<"unspreadLight modified "<<diff<<std::endl;
843 //TimeTaker timer("spreadLight");
844 spreadLight(bank, light_sources, modified_blocks);
849 u32 diff = modified_blocks.size() - count_was;
850 count_was = modified_blocks.size();
851 infostream<<"spreadLight modified "<<diff<<std::endl;
857 //MapVoxelManipulator vmanip(this);
859 // Make a manual voxel manipulator and load all the blocks
860 // that touch the requested blocks
861 ManualMapVoxelManipulator vmanip(this);
864 //TimeTaker timer("initialEmerge");
866 core::map<v3s16, MapBlock*>::Iterator i;
867 i = blocks_to_update.getIterator();
868 for(; i.atEnd() == false; i++)
870 MapBlock *block = i.getNode()->getValue();
871 v3s16 p = block->getPos();
873 // Add all surrounding blocks
874 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
877 Add all surrounding blocks that have up-to-date lighting
878 NOTE: This doesn't quite do the job (not everything
879 appropriate is lighted)
881 /*for(s16 z=-1; z<=1; z++)
882 for(s16 y=-1; y<=1; y++)
883 for(s16 x=-1; x<=1; x++)
885 v3s16 p2 = p + v3s16(x,y,z);
886 MapBlock *block = getBlockNoCreateNoEx(p2);
891 if(block->getLightingExpired())
893 vmanip.initialEmerge(p2, p2);
896 // Lighting of block will be updated completely
897 block->setLightingExpired(false);
902 //TimeTaker timer("unSpreadLight");
903 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
906 //TimeTaker timer("spreadLight");
907 vmanip.spreadLight(bank, light_sources, nodemgr);
910 //TimeTaker timer("blitBack");
911 vmanip.blitBack(modified_blocks);
913 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
918 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
921 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
922 std::map<v3s16, MapBlock*> & modified_blocks)
924 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
925 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
928 Update information about whether day and night light differ
930 for(std::map<v3s16, MapBlock*>::iterator
931 i = modified_blocks.begin();
932 i != modified_blocks.end(); ++i)
934 MapBlock *block = i->second;
935 block->expireDayNightDiff();
941 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
942 std::map<v3s16, MapBlock*> &modified_blocks,
943 bool remove_metadata)
945 INodeDefManager *ndef = m_gamedef->ndef();
948 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
949 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
952 From this node to nodes underneath:
953 If lighting is sunlight (1.0), unlight neighbours and
958 v3s16 toppos = p + v3s16(0,1,0);
959 //v3s16 bottompos = p + v3s16(0,-1,0);
961 bool node_under_sunlight = true;
962 std::set<v3s16> light_sources;
965 Collect old node for rollback
967 RollbackNode rollback_oldnode(this, p, m_gamedef);
970 If there is a node at top and it doesn't have sunlight,
971 there has not been any sunlight going down.
973 Otherwise there probably is.
976 bool is_valid_position;
977 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
979 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
980 node_under_sunlight = false;
983 Remove all light that has come out of this node
986 enum LightBank banks[] =
991 for(s32 i=0; i<2; i++)
993 enum LightBank bank = banks[i];
995 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
997 // Add the block of the added node to modified_blocks
998 v3s16 blockpos = getNodeBlockPos(p);
999 MapBlock * block = getBlockNoCreate(blockpos);
1000 assert(block != NULL);
1001 modified_blocks[blockpos] = block;
1003 assert(isValidPosition(p));
1005 // Unlight neighbours of node.
1006 // This means setting light of all consequent dimmer nodes
1008 // This also collects the nodes at the border which will spread
1009 // light again into this.
1010 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1012 n.setLight(bank, 0, ndef);
1016 If node lets sunlight through and is under sunlight, it has
1019 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1021 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1025 Remove node metadata
1027 if (remove_metadata) {
1028 removeNodeMetadata(p);
1032 Set the node on the map
1038 If node is under sunlight and doesn't let sunlight through,
1039 take all sunlighted nodes under it and clear light from them
1040 and from where the light has been spread.
1041 TODO: This could be optimized by mass-unlighting instead
1044 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1048 //m_dout<<DTIME<<"y="<<y<<std::endl;
1049 v3s16 n2pos(p.X, y, p.Z);
1053 n2 = getNodeNoEx(n2pos, &is_valid_position);
1054 if (!is_valid_position)
1057 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1059 unLightNeighbors(LIGHTBANK_DAY,
1060 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1061 light_sources, modified_blocks);
1062 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1070 for(s32 i=0; i<2; i++)
1072 enum LightBank bank = banks[i];
1075 Spread light from all nodes that might be capable of doing so
1077 spreadLight(bank, light_sources, modified_blocks);
1081 Update information about whether day and night light differ
1083 for(std::map<v3s16, MapBlock*>::iterator
1084 i = modified_blocks.begin();
1085 i != modified_blocks.end(); ++i)
1087 i->second->expireDayNightDiff();
1093 if(m_gamedef->rollback())
1095 RollbackNode rollback_newnode(this, p, m_gamedef);
1096 RollbackAction action;
1097 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1098 m_gamedef->rollback()->reportAction(action);
1102 Add neighboring liquid nodes and the node itself if it is
1103 liquid (=water node was added) to transform queue.
1106 v3s16(0,0,0), // self
1107 v3s16(0,0,1), // back
1108 v3s16(0,1,0), // top
1109 v3s16(1,0,0), // right
1110 v3s16(0,0,-1), // front
1111 v3s16(0,-1,0), // bottom
1112 v3s16(-1,0,0), // left
1114 for(u16 i=0; i<7; i++)
1116 v3s16 p2 = p + dirs[i];
1118 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1119 if(is_valid_position
1120 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1122 m_transforming_liquid.push_back(p2);
1129 void Map::removeNodeAndUpdate(v3s16 p,
1130 std::map<v3s16, MapBlock*> &modified_blocks)
1132 INodeDefManager *ndef = m_gamedef->ndef();
1134 /*PrintInfo(m_dout);
1135 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1136 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1138 bool node_under_sunlight = true;
1140 v3s16 toppos = p + v3s16(0,1,0);
1142 // Node will be replaced with this
1143 content_t replace_material = CONTENT_AIR;
1146 Collect old node for rollback
1148 RollbackNode rollback_oldnode(this, p, m_gamedef);
1151 If there is a node at top and it doesn't have sunlight,
1152 there will be no sunlight going down.
1154 bool is_valid_position;
1155 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1157 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1158 node_under_sunlight = false;
1160 std::set<v3s16> light_sources;
1162 enum LightBank banks[] =
1167 for(s32 i=0; i<2; i++)
1169 enum LightBank bank = banks[i];
1172 Unlight neighbors (in case the node is a light source)
1174 unLightNeighbors(bank, p,
1175 getNodeNoEx(p).getLight(bank, ndef),
1176 light_sources, modified_blocks);
1180 Remove node metadata
1183 removeNodeMetadata(p);
1187 This also clears the lighting.
1190 MapNode n(replace_material);
1193 for(s32 i=0; i<2; i++)
1195 enum LightBank bank = banks[i];
1198 Recalculate lighting
1200 spreadLight(bank, light_sources, modified_blocks);
1203 // Add the block of the removed node to modified_blocks
1204 v3s16 blockpos = getNodeBlockPos(p);
1205 MapBlock * block = getBlockNoCreate(blockpos);
1206 assert(block != NULL);
1207 modified_blocks[blockpos] = block;
1210 If the removed node was under sunlight, propagate the
1211 sunlight down from it and then light all neighbors
1212 of the propagated blocks.
1214 if(node_under_sunlight)
1216 s16 ybottom = propagateSunlight(p, modified_blocks);
1217 /*m_dout<<DTIME<<"Node was under sunlight. "
1218 "Propagating sunlight";
1219 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1221 for(; y >= ybottom; y--)
1223 v3s16 p2(p.X, y, p.Z);
1224 /*m_dout<<DTIME<<"lighting neighbors of node ("
1225 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1227 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1232 // Set the lighting of this node to 0
1233 // TODO: Is this needed? Lighting is cleared up there already.
1234 MapNode n = getNodeNoEx(p, &is_valid_position);
1235 if (is_valid_position) {
1236 n.setLight(LIGHTBANK_DAY, 0, ndef);
1243 for(s32 i=0; i<2; i++)
1245 enum LightBank bank = banks[i];
1247 // Get the brightest neighbour node and propagate light from it
1248 v3s16 n2p = getBrightestNeighbour(bank, p);
1250 //MapNode n2 = getNode(n2p);
1251 lightNeighbors(bank, n2p, modified_blocks);
1253 catch(InvalidPositionException &e)
1259 Update information about whether day and night light differ
1261 for(std::map<v3s16, MapBlock*>::iterator
1262 i = modified_blocks.begin();
1263 i != modified_blocks.end(); ++i)
1265 i->second->expireDayNightDiff();
1271 if(m_gamedef->rollback())
1273 RollbackNode rollback_newnode(this, p, m_gamedef);
1274 RollbackAction action;
1275 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1276 m_gamedef->rollback()->reportAction(action);
1280 Add neighboring liquid nodes and this node to transform queue.
1281 (it's vital for the node itself to get updated last.)
1284 v3s16(0,0,1), // back
1285 v3s16(0,1,0), // top
1286 v3s16(1,0,0), // right
1287 v3s16(0,0,-1), // front
1288 v3s16(0,-1,0), // bottom
1289 v3s16(-1,0,0), // left
1290 v3s16(0,0,0), // self
1292 for(u16 i=0; i<7; i++)
1294 v3s16 p2 = p + dirs[i];
1296 bool is_position_valid;
1297 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1298 if (is_position_valid
1299 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1301 m_transforming_liquid.push_back(p2);
1306 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1309 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1313 bool succeeded = true;
1315 std::map<v3s16, MapBlock*> modified_blocks;
1316 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1318 // Copy modified_blocks to event
1319 for(std::map<v3s16, MapBlock*>::iterator
1320 i = modified_blocks.begin();
1321 i != modified_blocks.end(); ++i)
1323 event.modified_blocks.insert(i->first);
1326 catch(InvalidPositionException &e){
1330 dispatchEvent(&event);
1335 bool Map::removeNodeWithEvent(v3s16 p)
1338 event.type = MEET_REMOVENODE;
1341 bool succeeded = true;
1343 std::map<v3s16, MapBlock*> modified_blocks;
1344 removeNodeAndUpdate(p, modified_blocks);
1346 // Copy modified_blocks to event
1347 for(std::map<v3s16, MapBlock*>::iterator
1348 i = modified_blocks.begin();
1349 i != modified_blocks.end(); ++i)
1351 event.modified_blocks.insert(i->first);
1354 catch(InvalidPositionException &e){
1358 dispatchEvent(&event);
1363 bool Map::getDayNightDiff(v3s16 blockpos)
1366 v3s16 p = blockpos + v3s16(0,0,0);
1367 MapBlock *b = getBlockNoCreate(p);
1368 if(b->getDayNightDiff())
1371 catch(InvalidPositionException &e){}
1374 v3s16 p = blockpos + v3s16(-1,0,0);
1375 MapBlock *b = getBlockNoCreate(p);
1376 if(b->getDayNightDiff())
1379 catch(InvalidPositionException &e){}
1381 v3s16 p = blockpos + v3s16(0,-1,0);
1382 MapBlock *b = getBlockNoCreate(p);
1383 if(b->getDayNightDiff())
1386 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(0,0,-1);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->getDayNightDiff())
1393 catch(InvalidPositionException &e){}
1396 v3s16 p = blockpos + v3s16(1,0,0);
1397 MapBlock *b = getBlockNoCreate(p);
1398 if(b->getDayNightDiff())
1401 catch(InvalidPositionException &e){}
1403 v3s16 p = blockpos + v3s16(0,1,0);
1404 MapBlock *b = getBlockNoCreate(p);
1405 if(b->getDayNightDiff())
1408 catch(InvalidPositionException &e){}
1410 v3s16 p = blockpos + v3s16(0,0,1);
1411 MapBlock *b = getBlockNoCreate(p);
1412 if(b->getDayNightDiff())
1415 catch(InvalidPositionException &e){}
1421 Updates usage timers
1423 void Map::timerUpdate(float dtime, float unload_timeout,
1424 std::vector<v3s16> *unloaded_blocks)
1426 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1428 // Profile modified reasons
1429 Profiler modprofiler;
1431 std::vector<v2s16> sector_deletion_queue;
1432 u32 deleted_blocks_count = 0;
1433 u32 saved_blocks_count = 0;
1434 u32 block_count_all = 0;
1437 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1438 si != m_sectors.end(); ++si) {
1439 MapSector *sector = si->second;
1441 bool all_blocks_deleted = true;
1443 MapBlockVect blocks;
1444 sector->getBlocks(blocks);
1446 for(MapBlockVect::iterator i = blocks.begin();
1447 i != blocks.end(); ++i) {
1448 MapBlock *block = (*i);
1450 block->incrementUsageTimer(dtime);
1452 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) {
1453 v3s16 p = block->getPos();
1456 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1457 modprofiler.add(block->getModifiedReason(), 1);
1458 if (!saveBlock(block))
1460 saved_blocks_count++;
1463 // Delete from memory
1464 sector->deleteBlock(block);
1467 unloaded_blocks->push_back(p);
1469 deleted_blocks_count++;
1472 all_blocks_deleted = false;
1477 if(all_blocks_deleted) {
1478 sector_deletion_queue.push_back(si->first);
1483 // Finally delete the empty sectors
1484 deleteSectors(sector_deletion_queue);
1486 if(deleted_blocks_count != 0)
1488 PrintInfo(infostream); // ServerMap/ClientMap:
1489 infostream<<"Unloaded "<<deleted_blocks_count
1490 <<" blocks from memory";
1491 if(save_before_unloading)
1492 infostream<<", of which "<<saved_blocks_count<<" were written";
1493 infostream<<", "<<block_count_all<<" blocks in memory";
1494 infostream<<"."<<std::endl;
1495 if(saved_blocks_count != 0){
1496 PrintInfo(infostream); // ServerMap/ClientMap:
1497 infostream<<"Blocks modified by: "<<std::endl;
1498 modprofiler.print(infostream);
1503 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1505 timerUpdate(0.0, -1.0, unloaded_blocks);
1508 void Map::deleteSectors(std::vector<v2s16> §orList)
1510 for(std::vector<v2s16>::iterator j = sectorList.begin();
1511 j != sectorList.end(); ++j) {
1512 MapSector *sector = m_sectors[*j];
1513 // If sector is in sector cache, remove it from there
1514 if(m_sector_cache == sector)
1515 m_sector_cache = NULL;
1516 // Remove from map and delete
1517 m_sectors.erase(*j);
1522 void Map::PrintInfo(std::ostream &out)
1527 #define WATER_DROP_BOOST 4
1531 NEIGHBOR_SAME_LEVEL,
1534 struct NodeNeighbor {
1538 bool l; //can liquid
1544 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1551 void Map::transforming_liquid_add(v3s16 p) {
1552 m_transforming_liquid.push_back(p);
1555 s32 Map::transforming_liquid_size() {
1556 return m_transforming_liquid.size();
1559 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1562 INodeDefManager *nodemgr = m_gamedef->ndef();
1564 DSTACK(__FUNCTION_NAME);
1565 //TimeTaker timer("transformLiquids()");
1568 u32 initial_size = m_transforming_liquid.size();
1570 /*if(initial_size != 0)
1571 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1573 // list of nodes that due to viscosity have not reached their max level height
1574 std::deque<v3s16> must_reflow;
1576 // List of MapBlocks that will require a lighting update (due to lava)
1577 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1579 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1580 u32 loop_max = liquid_loop_max;
1584 /* If liquid_loop_max is not keeping up with the queue size increase
1585 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1587 if (m_transforming_liquid.size() > loop_max * 2) {
1589 float server_step = g_settings->getFloat("dedicated_server_step");
1590 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1591 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1593 m_transforming_liquid_loop_count_multiplier = 1.0;
1596 loop_max *= m_transforming_liquid_loop_count_multiplier;
1599 while(m_transforming_liquid.size() != 0)
1601 // This should be done here so that it is done when continue is used
1602 if(loopcount >= initial_size || loopcount >= loop_max)
1607 Get a queued transforming liquid node
1609 v3s16 p0 = m_transforming_liquid.front();
1610 m_transforming_liquid.pop_front();
1612 MapNode n0 = getNodeNoEx(p0);
1615 Collect information about current node
1617 s8 liquid_level = -1;
1618 content_t liquid_kind = CONTENT_IGNORE;
1619 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1620 switch (liquid_type) {
1622 liquid_level = LIQUID_LEVEL_SOURCE;
1623 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1625 case LIQUID_FLOWING:
1626 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1627 liquid_kind = n0.getContent();
1630 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1631 // continue with the next node.
1632 if (n0.getContent() != CONTENT_AIR)
1634 liquid_kind = CONTENT_AIR;
1639 Collect information about the environment
1641 const v3s16 *dirs = g_6dirs;
1642 NodeNeighbor sources[6]; // surrounding sources
1643 int num_sources = 0;
1644 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1646 NodeNeighbor airs[6]; // surrounding air
1648 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1649 int num_neutrals = 0;
1650 bool flowing_down = false;
1651 for (u16 i = 0; i < 6; i++) {
1652 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1655 nt = NEIGHBOR_UPPER;
1658 nt = NEIGHBOR_LOWER;
1661 v3s16 npos = p0 + dirs[i];
1662 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1663 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1665 if (nb.n.getContent() == CONTENT_AIR) {
1666 airs[num_airs++] = nb;
1667 // if the current node is a water source the neighbor
1668 // should be enqueded for transformation regardless of whether the
1669 // current node changes or not.
1670 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1671 m_transforming_liquid.push_back(npos);
1672 // if the current node happens to be a flowing node, it will start to flow down here.
1673 if (nb.t == NEIGHBOR_LOWER) {
1674 flowing_down = true;
1677 neutrals[num_neutrals++] = nb;
1681 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1682 if (liquid_kind == CONTENT_AIR)
1683 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1684 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1685 neutrals[num_neutrals++] = nb;
1687 // Do not count bottom source, it will screw things up
1689 sources[num_sources++] = nb;
1692 case LIQUID_FLOWING:
1693 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1694 if (liquid_kind == CONTENT_AIR)
1695 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1696 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1697 neutrals[num_neutrals++] = nb;
1699 flows[num_flows++] = nb;
1700 if (nb.t == NEIGHBOR_LOWER)
1701 flowing_down = true;
1708 decide on the type (and possibly level) of the current node
1710 content_t new_node_content;
1711 s8 new_node_level = -1;
1712 s8 max_node_level = -1;
1714 u8 range = nodemgr->get(liquid_kind).liquid_range;
1715 if (range > LIQUID_LEVEL_MAX+1)
1716 range = LIQUID_LEVEL_MAX+1;
1718 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1719 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1720 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1721 // it's perfectly safe to use liquid_kind here to determine the new node content.
1722 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1723 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1724 // liquid_kind is set properly, see above
1725 new_node_content = liquid_kind;
1726 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1727 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1728 new_node_content = CONTENT_AIR;
1730 // no surrounding sources, so get the maximum level that can flow into this node
1731 for (u16 i = 0; i < num_flows; i++) {
1732 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1733 switch (flows[i].t) {
1734 case NEIGHBOR_UPPER:
1735 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1736 max_node_level = LIQUID_LEVEL_MAX;
1737 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1738 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1739 } else if (nb_liquid_level > max_node_level)
1740 max_node_level = nb_liquid_level;
1742 case NEIGHBOR_LOWER:
1744 case NEIGHBOR_SAME_LEVEL:
1745 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1746 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1747 max_node_level = nb_liquid_level - 1;
1753 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1754 if (viscosity > 1 && max_node_level != liquid_level) {
1755 // amount to gain, limited by viscosity
1756 // must be at least 1 in absolute value
1757 s8 level_inc = max_node_level - liquid_level;
1758 if (level_inc < -viscosity || level_inc > viscosity)
1759 new_node_level = liquid_level + level_inc/viscosity;
1760 else if (level_inc < 0)
1761 new_node_level = liquid_level - 1;
1762 else if (level_inc > 0)
1763 new_node_level = liquid_level + 1;
1764 if (new_node_level != max_node_level)
1765 must_reflow.push_back(p0);
1767 new_node_level = max_node_level;
1769 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1770 new_node_content = liquid_kind;
1772 new_node_content = CONTENT_AIR;
1777 check if anything has changed. if not, just continue with the next node.
1779 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1780 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1781 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1787 update the current node
1790 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1791 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1792 // set level to last 3 bits, flowing down bit to 4th bit
1793 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1795 // set the liquid level and flow bit to 0
1796 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1798 n0.setContent(new_node_content);
1800 // Find out whether there is a suspect for this action
1801 std::string suspect;
1802 if(m_gamedef->rollback()) {
1803 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1806 if(m_gamedef->rollback() && !suspect.empty()){
1808 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1809 // Get old node for rollback
1810 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1814 RollbackNode rollback_newnode(this, p0, m_gamedef);
1815 RollbackAction action;
1816 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1817 m_gamedef->rollback()->reportAction(action);
1823 v3s16 blockpos = getNodeBlockPos(p0);
1824 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1826 modified_blocks[blockpos] = block;
1827 // If new or old node emits light, MapBlock requires lighting update
1828 if(nodemgr->get(n0).light_source != 0 ||
1829 nodemgr->get(n00).light_source != 0)
1830 lighting_modified_blocks[block->getPos()] = block;
1834 enqueue neighbors for update if neccessary
1836 switch (nodemgr->get(n0.getContent()).liquid_type) {
1838 case LIQUID_FLOWING:
1839 // make sure source flows into all neighboring nodes
1840 for (u16 i = 0; i < num_flows; i++)
1841 if (flows[i].t != NEIGHBOR_UPPER)
1842 m_transforming_liquid.push_back(flows[i].p);
1843 for (u16 i = 0; i < num_airs; i++)
1844 if (airs[i].t != NEIGHBOR_UPPER)
1845 m_transforming_liquid.push_back(airs[i].p);
1848 // this flow has turned to air; neighboring flows might need to do the same
1849 for (u16 i = 0; i < num_flows; i++)
1850 m_transforming_liquid.push_back(flows[i].p);
1854 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1856 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1857 m_transforming_liquid.push_back(*iter);
1859 updateLighting(lighting_modified_blocks, modified_blocks);
1862 /* ----------------------------------------------------------------------
1863 * Manage the queue so that it does not grow indefinately
1865 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1867 if (time_until_purge == 0)
1868 return; // Feature disabled
1870 time_until_purge *= 1000; // seconds -> milliseconds
1872 u32 curr_time = getTime(PRECISION_MILLI);
1873 u32 prev_unprocessed = m_unprocessed_count;
1874 m_unprocessed_count = m_transforming_liquid.size();
1876 // if unprocessed block count is decreasing or stable
1877 if (m_unprocessed_count <= prev_unprocessed) {
1878 m_queue_size_timer_started = false;
1880 if (!m_queue_size_timer_started)
1881 m_inc_trending_up_start_time = curr_time;
1882 m_queue_size_timer_started = true;
1885 // Account for curr_time overflowing
1886 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1887 m_queue_size_timer_started = false;
1889 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1890 * and the number of unprocessed blocks is still > liquid_loop_max then we
1891 * cannot keep up; dump the oldest blocks from the queue so that the queue
1892 * has liquid_loop_max items in it
1894 if (m_queue_size_timer_started
1895 && curr_time - m_inc_trending_up_start_time > time_until_purge
1896 && m_unprocessed_count > liquid_loop_max) {
1898 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1900 infostream << "transformLiquids(): DUMPING " << dump_qty
1901 << " blocks from the queue" << std::endl;
1904 m_transforming_liquid.pop_front();
1906 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1907 m_unprocessed_count = m_transforming_liquid.size();
1911 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1913 v3s16 blockpos = getNodeBlockPos(p);
1914 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1915 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1917 infostream<<"Map::getNodeMetadata(): Need to emerge "
1918 <<PP(blockpos)<<std::endl;
1919 block = emergeBlock(blockpos, false);
1922 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1926 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1930 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1932 v3s16 blockpos = getNodeBlockPos(p);
1933 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1934 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1936 infostream<<"Map::setNodeMetadata(): Need to emerge "
1937 <<PP(blockpos)<<std::endl;
1938 block = emergeBlock(blockpos, false);
1941 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1945 block->m_node_metadata.set(p_rel, meta);
1949 void Map::removeNodeMetadata(v3s16 p)
1951 v3s16 blockpos = getNodeBlockPos(p);
1952 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1953 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1956 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1960 block->m_node_metadata.remove(p_rel);
1963 NodeTimer Map::getNodeTimer(v3s16 p)
1965 v3s16 blockpos = getNodeBlockPos(p);
1966 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1967 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1969 infostream<<"Map::getNodeTimer(): Need to emerge "
1970 <<PP(blockpos)<<std::endl;
1971 block = emergeBlock(blockpos, false);
1974 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1978 NodeTimer t = block->m_node_timers.get(p_rel);
1982 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1984 v3s16 blockpos = getNodeBlockPos(p);
1985 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1986 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1988 infostream<<"Map::setNodeTimer(): Need to emerge "
1989 <<PP(blockpos)<<std::endl;
1990 block = emergeBlock(blockpos, false);
1993 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1997 block->m_node_timers.set(p_rel, t);
2000 void Map::removeNodeTimer(v3s16 p)
2002 v3s16 blockpos = getNodeBlockPos(p);
2003 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2004 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2007 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2011 block->m_node_timers.remove(p_rel);
2017 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2018 Map(dout_server, gamedef),
2020 m_map_metadata_changed(true)
2022 verbosestream<<__FUNCTION_NAME<<std::endl;
2025 Try to load map; if not found, create a new one.
2028 // Determine which database backend to use
2029 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2031 bool succeeded = conf.readConfigFile(conf_path.c_str());
2032 if (!succeeded || !conf.exists("backend")) {
2033 // fall back to sqlite3
2034 dbase = new Database_SQLite3(this, savedir);
2035 conf.set("backend", "sqlite3");
2037 std::string backend = conf.get("backend");
2038 if (backend == "dummy")
2039 dbase = new Database_Dummy(this);
2040 else if (backend == "sqlite3")
2041 dbase = new Database_SQLite3(this, savedir);
2043 else if (backend == "leveldb")
2044 dbase = new Database_LevelDB(this, savedir);
2047 else if (backend == "redis")
2048 dbase = new Database_Redis(this, savedir);
2051 throw BaseException("Unknown map backend");
2054 m_savedir = savedir;
2055 m_map_saving_enabled = false;
2059 // If directory exists, check contents and load if possible
2060 if(fs::PathExists(m_savedir))
2062 // If directory is empty, it is safe to save into it.
2063 if(fs::GetDirListing(m_savedir).size() == 0)
2065 infostream<<"ServerMap: Empty save directory is valid."
2067 m_map_saving_enabled = true;
2072 // Load map metadata (seed, chunksize)
2075 catch(SettingNotFoundException &e){
2076 infostream<<"ServerMap: Some metadata not found."
2077 <<" Using default settings."<<std::endl;
2079 catch(FileNotGoodException &e){
2080 infostream<<"WARNING: Could not load map metadata"
2081 //<<" Disabling chunk-based generator."
2086 infostream<<"ServerMap: Successfully loaded map "
2087 <<"metadata from "<<savedir
2088 <<", assuming valid save directory."
2089 <<" seed="<< m_emerge->params.seed <<"."
2092 m_map_saving_enabled = true;
2093 // Map loaded, not creating new one
2097 // If directory doesn't exist, it is safe to save to it
2099 m_map_saving_enabled = true;
2102 catch(std::exception &e)
2104 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2105 <<", exception: "<<e.what()<<std::endl;
2106 infostream<<"Please remove the map or fix it."<<std::endl;
2107 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2110 infostream<<"Initializing new map."<<std::endl;
2112 // Create zero sector
2113 emergeSector(v2s16(0,0));
2115 // Initially write whole map
2116 save(MOD_STATE_CLEAN);
2119 ServerMap::~ServerMap()
2121 verbosestream<<__FUNCTION_NAME<<std::endl;
2125 if(m_map_saving_enabled)
2127 // Save only changed parts
2128 save(MOD_STATE_WRITE_AT_UNLOAD);
2129 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2133 infostream<<"ServerMap: Map not saved"<<std::endl;
2136 catch(std::exception &e)
2138 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2139 <<", exception: "<<e.what()<<std::endl;
2143 Close database if it was opened
2151 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2152 for(; i.atEnd() == false; i++)
2154 MapChunk *chunk = i.getNode()->getValue();
2160 u64 ServerMap::getSeed()
2162 return m_emerge->params.seed;
2165 s16 ServerMap::getWaterLevel()
2167 return m_emerge->params.water_level;
2170 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2172 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2173 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2175 s16 chunksize = m_emerge->params.chunksize;
2176 s16 coffset = -chunksize / 2;
2177 v3s16 chunk_offset(coffset, coffset, coffset);
2178 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2179 v3s16 blockpos_min = blockpos_div * chunksize;
2180 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2181 blockpos_min += chunk_offset;
2182 blockpos_max += chunk_offset;
2184 v3s16 extra_borders(1,1,1);
2186 // Do nothing if not inside limits (+-1 because of neighbors)
2187 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2188 blockpos_over_limit(blockpos_max + extra_borders))
2191 data->seed = m_emerge->params.seed;
2192 data->blockpos_min = blockpos_min;
2193 data->blockpos_max = blockpos_max;
2194 data->blockpos_requested = blockpos;
2195 data->nodedef = m_gamedef->ndef();
2198 Create the whole area of this and the neighboring blocks
2201 //TimeTaker timer("initBlockMake() create area");
2203 for(s16 x=blockpos_min.X-extra_borders.X;
2204 x<=blockpos_max.X+extra_borders.X; x++)
2205 for(s16 z=blockpos_min.Z-extra_borders.Z;
2206 z<=blockpos_max.Z+extra_borders.Z; z++)
2208 v2s16 sectorpos(x, z);
2209 // Sector metadata is loaded from disk if not already loaded.
2210 ServerMapSector *sector = createSector(sectorpos);
2214 for(s16 y=blockpos_min.Y-extra_borders.Y;
2215 y<=blockpos_max.Y+extra_borders.Y; y++)
2218 //MapBlock *block = createBlock(p);
2219 // 1) get from memory, 2) load from disk
2220 MapBlock *block = emergeBlock(p, false);
2221 // 3) create a blank one
2224 block = createBlock(p);
2227 Block gets sunlight if this is true.
2229 Refer to the map generator heuristics.
2231 bool ug = m_emerge->isBlockUnderground(p);
2232 block->setIsUnderground(ug);
2235 // Lighting will not be valid after make_chunk is called
2236 block->setLightingExpired(true);
2237 // Lighting will be calculated
2238 //block->setLightingExpired(false);
2244 Now we have a big empty area.
2246 Make a ManualMapVoxelManipulator that contains this and the
2250 // The area that contains this block and it's neighbors
2251 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2252 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2254 data->vmanip = new MMVManip(this);
2255 //data->vmanip->setMap(this);
2259 //TimeTaker timer("initBlockMake() initialEmerge");
2260 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2263 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2264 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2265 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2266 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2267 core::map<v3s16, u8>::Node *n;
2268 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2271 u8 flags = n->getValue();
2272 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2278 // Data is ready now.
2282 void ServerMap::finishBlockMake(BlockMakeData *data,
2283 std::map<v3s16, MapBlock*> &changed_blocks)
2285 v3s16 blockpos_min = data->blockpos_min;
2286 v3s16 blockpos_max = data->blockpos_max;
2287 v3s16 blockpos_requested = data->blockpos_requested;
2288 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2289 <<blockpos_requested.Y<<","
2290 <<blockpos_requested.Z<<")"<<std::endl;*/
2292 v3s16 extra_borders(1,1,1);
2294 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2296 /*infostream<<"Resulting vmanip:"<<std::endl;
2297 data->vmanip.print(infostream);*/
2299 // Make sure affected blocks are loaded
2300 for(s16 x=blockpos_min.X-extra_borders.X;
2301 x<=blockpos_max.X+extra_borders.X; x++)
2302 for(s16 z=blockpos_min.Z-extra_borders.Z;
2303 z<=blockpos_max.Z+extra_borders.Z; z++)
2304 for(s16 y=blockpos_min.Y-extra_borders.Y;
2305 y<=blockpos_max.Y+extra_borders.Y; y++)
2308 // Load from disk if not already in memory
2309 emergeBlock(p, false);
2313 Blit generated stuff to map
2314 NOTE: blitBackAll adds nearly everything to changed_blocks
2318 //TimeTaker timer("finishBlockMake() blitBackAll");
2319 data->vmanip->blitBackAll(&changed_blocks);
2322 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2325 Copy transforming liquid information
2327 while(data->transforming_liquid.size() > 0)
2329 m_transforming_liquid.push_back(data->transforming_liquid.front());
2330 data->transforming_liquid.pop_front();
2334 Do stuff in central blocks
2342 TimeTaker t("finishBlockMake lighting update");
2344 core::map<v3s16, MapBlock*> lighting_update_blocks;
2347 for(s16 x=blockpos_min.X-extra_borders.X;
2348 x<=blockpos_max.X+extra_borders.X; x++)
2349 for(s16 z=blockpos_min.Z-extra_borders.Z;
2350 z<=blockpos_max.Z+extra_borders.Z; z++)
2351 for(s16 y=blockpos_min.Y-extra_borders.Y;
2352 y<=blockpos_max.Y+extra_borders.Y; y++)
2355 MapBlock *block = getBlockNoCreateNoEx(p);
2357 lighting_update_blocks.insert(block->getPos(), block);
2360 updateLighting(lighting_update_blocks, changed_blocks);
2364 Set lighting to non-expired state in all of them.
2365 This is cheating, but it is not fast enough if all of them
2366 would actually be updated.
2368 for(s16 x=blockpos_min.X-extra_borders.X;
2369 x<=blockpos_max.X+extra_borders.X; x++)
2370 for(s16 z=blockpos_min.Z-extra_borders.Z;
2371 z<=blockpos_max.Z+extra_borders.Z; z++)
2372 for(s16 y=blockpos_min.Y-extra_borders.Y;
2373 y<=blockpos_max.Y+extra_borders.Y; y++)
2376 MapBlock * block = getBlockNoCreateNoEx(p);
2378 block->setLightingExpired(false);
2382 if(enable_mapgen_debug_info == false)
2383 t.stop(true); // Hide output
2388 Go through changed blocks
2390 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2391 i != changed_blocks.end(); ++i)
2393 MapBlock *block = i->second;
2397 Update day/night difference cache of the MapBlocks
2399 block->expireDayNightDiff();
2401 Set block as modified
2403 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2404 "finishBlockMake expireDayNightDiff");
2408 Set central blocks as generated
2410 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2411 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2412 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2415 MapBlock *block = getBlockNoCreateNoEx(p);
2418 block->setGenerated(true);
2422 Save changed parts of map
2423 NOTE: Will be saved later.
2425 //save(MOD_STATE_WRITE_AT_UNLOAD);
2427 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2428 <<","<<blockpos_requested.Y<<","
2429 <<blockpos_requested.Z<<")"<<std::endl;*/
2433 if(enable_mapgen_debug_info)
2436 Analyze resulting blocks
2438 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2439 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2440 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2441 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2442 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2443 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2445 v3s16 p = v3s16(x,y,z);
2446 MapBlock *block = getBlockNoCreateNoEx(p);
2448 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2449 infostream<<"Generated "<<spos<<": "
2450 <<analyze_block(block)<<std::endl;
2455 getBlockNoCreateNoEx(blockpos_requested);
2458 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2460 DSTACKF("%s: p2d=(%d,%d)",
2465 Check if it exists already in memory
2467 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2472 Try to load it from disk (with blocks)
2474 //if(loadSectorFull(p2d) == true)
2477 Try to load metadata from disk
2480 if(loadSectorMeta(p2d) == true)
2482 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2485 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2486 throw InvalidPositionException("");
2492 Do not create over-limit
2494 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2495 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2496 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2497 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2498 throw InvalidPositionException("createSector(): pos. over limit");
2501 Generate blank sector
2504 sector = new ServerMapSector(this, p2d, m_gamedef);
2506 // Sector position on map in nodes
2507 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2512 m_sectors[p2d] = sector;
2519 This is a quick-hand function for calling makeBlock().
2521 MapBlock * ServerMap::generateBlock(
2523 std::map<v3s16, MapBlock*> &modified_blocks
2526 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2528 /*infostream<<"generateBlock(): "
2529 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2532 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2534 TimeTaker timer("generateBlock");
2536 //MapBlock *block = original_dummy;
2538 v2s16 p2d(p.X, p.Z);
2539 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2542 Do not generate over-limit
2544 if(blockpos_over_limit(p))
2546 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2547 throw InvalidPositionException("generateBlock(): pos. over limit");
2551 Create block make data
2554 initBlockMake(&data, p);
2560 TimeTaker t("mapgen::make_block()");
2561 mapgen->makeChunk(&data);
2562 //mapgen::make_block(&data);
2564 if(enable_mapgen_debug_info == false)
2565 t.stop(true); // Hide output
2569 Blit data back on map, update lighting, add mobs and whatever this does
2571 finishBlockMake(&data, modified_blocks);
2576 MapBlock *block = getBlockNoCreateNoEx(p);
2584 bool erroneus_content = false;
2585 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2586 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2587 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2590 MapNode n = block->getNode(p);
2591 if(n.getContent() == CONTENT_IGNORE)
2593 infostream<<"CONTENT_IGNORE at "
2594 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2596 erroneus_content = true;
2600 if(erroneus_content)
2609 Generate a completely empty block
2613 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2614 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2616 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2619 n.setContent(CONTENT_AIR);
2620 block->setNode(v3s16(x0,y0,z0), n);
2626 if(enable_mapgen_debug_info == false)
2627 timer.stop(true); // Hide output
2633 MapBlock * ServerMap::createBlock(v3s16 p)
2635 DSTACKF("%s: p=(%d,%d,%d)",
2636 __FUNCTION_NAME, p.X, p.Y, p.Z);
2639 Do not create over-limit
2641 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2642 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2643 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2644 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2645 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2646 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2647 throw InvalidPositionException("createBlock(): pos. over limit");
2649 v2s16 p2d(p.X, p.Z);
2652 This will create or load a sector if not found in memory.
2653 If block exists on disk, it will be loaded.
2655 NOTE: On old save formats, this will be slow, as it generates
2656 lighting on blocks for them.
2658 ServerMapSector *sector;
2660 sector = (ServerMapSector*)createSector(p2d);
2661 assert(sector->getId() == MAPSECTOR_SERVER);
2663 catch(InvalidPositionException &e)
2665 infostream<<"createBlock: createSector() failed"<<std::endl;
2669 NOTE: This should not be done, or at least the exception
2670 should not be passed on as std::exception, because it
2671 won't be catched at all.
2673 /*catch(std::exception &e)
2675 infostream<<"createBlock: createSector() failed: "
2676 <<e.what()<<std::endl;
2681 Try to get a block from the sector
2684 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2687 if(block->isDummy())
2692 block = sector->createBlankBlock(block_y);
2697 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2699 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2701 p.X, p.Y, p.Z, create_blank);
2704 MapBlock *block = getBlockNoCreateNoEx(p);
2705 if(block && block->isDummy() == false)
2710 MapBlock *block = loadBlock(p);
2716 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2717 MapBlock *block = sector->createBlankBlock(p.Y);
2725 std::map<v3s16, MapBlock*> modified_blocks;
2726 MapBlock *block = generateBlock(p, modified_blocks);
2730 event.type = MEET_OTHER;
2733 // Copy modified_blocks to event
2734 for(std::map<v3s16, MapBlock*>::iterator
2735 i = modified_blocks.begin();
2736 i != modified_blocks.end(); ++i)
2738 event.modified_blocks.insert(i->first);
2742 dispatchEvent(&event);
2752 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2754 MapBlock *block = getBlockNoCreateNoEx(p3d);
2756 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2761 void ServerMap::prepareBlock(MapBlock *block) {
2764 // N.B. This requires no synchronization, since data will not be modified unless
2765 // the VoxelManipulator being updated belongs to the same thread.
2766 void ServerMap::updateVManip(v3s16 pos)
2768 Mapgen *mg = m_emerge->getCurrentMapgen();
2772 MMVManip *vm = mg->vm;
2776 if (!vm->m_area.contains(pos))
2779 s32 idx = vm->m_area.index(pos);
2780 vm->m_data[idx] = getNodeNoEx(pos);
2781 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2783 vm->m_is_dirty = true;
2786 s16 ServerMap::findGroundLevel(v2s16 p2d)
2790 Uh, just do something random...
2792 // Find existing map from top to down
2795 v3s16 p(p2d.X, max, p2d.Y);
2796 for(; p.Y>min; p.Y--)
2798 MapNode n = getNodeNoEx(p);
2799 if(n.getContent() != CONTENT_IGNORE)
2804 // If this node is not air, go to plan b
2805 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2807 // Search existing walkable and return it
2808 for(; p.Y>min; p.Y--)
2810 MapNode n = getNodeNoEx(p);
2811 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2820 Determine from map generator noise functions
2823 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2826 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2827 //return (s16)level;
2830 bool ServerMap::loadFromFolders() {
2831 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2836 void ServerMap::createDirs(std::string path)
2838 if(fs::CreateAllDirs(path) == false)
2840 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2841 <<"\""<<path<<"\""<<std::endl;
2842 throw BaseException("ServerMap failed to create directory");
2846 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2852 snprintf(cc, 9, "%.4x%.4x",
2853 (unsigned int)pos.X&0xffff,
2854 (unsigned int)pos.Y&0xffff);
2856 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2858 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2859 (unsigned int)pos.X&0xfff,
2860 (unsigned int)pos.Y&0xfff);
2862 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2869 v2s16 ServerMap::getSectorPos(std::string dirname)
2871 unsigned int x = 0, y = 0;
2873 std::string component;
2874 fs::RemoveLastPathComponent(dirname, &component, 1);
2875 if(component.size() == 8)
2878 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2880 else if(component.size() == 3)
2883 fs::RemoveLastPathComponent(dirname, &component, 2);
2884 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2885 // Sign-extend the 12 bit values up to 16 bits...
2886 if(x&0x800) x|=0xF000;
2887 if(y&0x800) y|=0xF000;
2894 v2s16 pos((s16)x, (s16)y);
2898 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2900 v2s16 p2d = getSectorPos(sectordir);
2902 if(blockfile.size() != 4){
2903 throw InvalidFilenameException("Invalid block filename");
2906 int r = sscanf(blockfile.c_str(), "%4x", &y);
2908 throw InvalidFilenameException("Invalid block filename");
2909 return v3s16(p2d.X, y, p2d.Y);
2912 std::string ServerMap::getBlockFilename(v3s16 p)
2915 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2919 void ServerMap::save(ModifiedState save_level)
2921 DSTACK(__FUNCTION_NAME);
2922 if(m_map_saving_enabled == false) {
2923 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2927 if(save_level == MOD_STATE_CLEAN)
2928 infostream<<"ServerMap: Saving whole map, this can take time."
2931 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2935 // Profile modified reasons
2936 Profiler modprofiler;
2938 u32 sector_meta_count = 0;
2939 u32 block_count = 0;
2940 u32 block_count_all = 0; // Number of blocks in memory
2942 // Don't do anything with sqlite unless something is really saved
2943 bool save_started = false;
2945 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2946 i != m_sectors.end(); ++i) {
2947 ServerMapSector *sector = (ServerMapSector*)i->second;
2948 assert(sector->getId() == MAPSECTOR_SERVER);
2950 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2951 saveSectorMeta(sector);
2952 sector_meta_count++;
2955 MapBlockVect blocks;
2956 sector->getBlocks(blocks);
2958 for(MapBlockVect::iterator j = blocks.begin();
2959 j != blocks.end(); ++j) {
2960 MapBlock *block = *j;
2964 if(block->getModified() >= (u32)save_level) {
2968 save_started = true;
2971 modprofiler.add(block->getModifiedReason(), 1);
2976 /*infostream<<"ServerMap: Written block ("
2977 <<block->getPos().X<<","
2978 <<block->getPos().Y<<","
2979 <<block->getPos().Z<<")"
2989 Only print if something happened or saved whole map
2991 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2992 || block_count != 0) {
2993 infostream<<"ServerMap: Written: "
2994 <<sector_meta_count<<" sector metadata files, "
2995 <<block_count<<" block files"
2996 <<", "<<block_count_all<<" blocks in memory."
2998 PrintInfo(infostream); // ServerMap/ClientMap:
2999 infostream<<"Blocks modified by: "<<std::endl;
3000 modprofiler.print(infostream);
3004 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
3006 if(loadFromFolders()){
3007 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3008 <<"all blocks that are stored in flat files"<<std::endl;
3010 dbase->listAllLoadableBlocks(dst);
3013 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
3015 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3016 si != m_sectors.end(); ++si)
3018 MapSector *sector = si->second;
3020 MapBlockVect blocks;
3021 sector->getBlocks(blocks);
3023 for(MapBlockVect::iterator i = blocks.begin();
3024 i != blocks.end(); ++i) {
3025 v3s16 p = (*i)->getPos();
3031 void ServerMap::saveMapMeta()
3033 DSTACK(__FUNCTION_NAME);
3035 /*infostream<<"ServerMap::saveMapMeta(): "
3039 createDirs(m_savedir);
3041 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3042 std::ostringstream ss(std::ios_base::binary);
3046 m_emerge->saveParamsToSettings(¶ms);
3047 params.writeLines(ss);
3049 ss<<"[end_of_params]\n";
3051 if(!fs::safeWriteToFile(fullpath, ss.str()))
3053 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3054 <<"could not write "<<fullpath<<std::endl;
3055 throw FileNotGoodException("Cannot save chunk metadata");
3058 m_map_metadata_changed = false;
3061 void ServerMap::loadMapMeta()
3063 DSTACK(__FUNCTION_NAME);
3066 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3068 if (fs::PathExists(fullpath)) {
3069 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3071 errorstream << "ServerMap::loadMapMeta(): "
3072 "could not open " << fullpath << std::endl;
3073 throw FileNotGoodException("Cannot open map metadata");
3076 if (!params.parseConfigLines(is, "[end_of_params]")) {
3077 throw SerializationError("ServerMap::loadMapMeta(): "
3078 "[end_of_params] not found!");
3082 m_emerge->loadParamsFromSettings(¶ms);
3084 verbosestream << "ServerMap::loadMapMeta(): seed="
3085 << m_emerge->params.seed << std::endl;
3088 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3090 DSTACK(__FUNCTION_NAME);
3091 // Format used for writing
3092 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3094 v2s16 pos = sector->getPos();
3095 std::string dir = getSectorDir(pos);
3098 std::string fullpath = dir + DIR_DELIM + "meta";
3099 std::ostringstream ss(std::ios_base::binary);
3101 sector->serialize(ss, version);
3103 if(!fs::safeWriteToFile(fullpath, ss.str()))
3104 throw FileNotGoodException("Cannot write sector metafile");
3106 sector->differs_from_disk = false;
3109 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3111 DSTACK(__FUNCTION_NAME);
3113 v2s16 p2d = getSectorPos(sectordir);
3115 ServerMapSector *sector = NULL;
3117 std::string fullpath = sectordir + DIR_DELIM + "meta";
3118 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3119 if(is.good() == false)
3121 // If the directory exists anyway, it probably is in some old
3122 // format. Just go ahead and create the sector.
3123 if(fs::PathExists(sectordir))
3125 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3126 <<fullpath<<" doesn't exist but directory does."
3127 <<" Continuing with a sector with no metadata."
3129 sector = new ServerMapSector(this, p2d, m_gamedef);
3130 m_sectors[p2d] = sector;
3134 throw FileNotGoodException("Cannot open sector metafile");
3139 sector = ServerMapSector::deSerialize
3140 (is, this, p2d, m_sectors, m_gamedef);
3142 saveSectorMeta(sector);
3145 sector->differs_from_disk = false;
3150 bool ServerMap::loadSectorMeta(v2s16 p2d)
3152 DSTACK(__FUNCTION_NAME);
3154 // The directory layout we're going to load from.
3155 // 1 - original sectors/xxxxzzzz/
3156 // 2 - new sectors2/xxx/zzz/
3157 // If we load from anything but the latest structure, we will
3158 // immediately save to the new one, and remove the old.
3160 std::string sectordir1 = getSectorDir(p2d, 1);
3161 std::string sectordir;
3162 if(fs::PathExists(sectordir1))
3164 sectordir = sectordir1;
3169 sectordir = getSectorDir(p2d, 2);
3173 loadSectorMeta(sectordir, loadlayout != 2);
3175 catch(InvalidFilenameException &e)
3179 catch(FileNotGoodException &e)
3183 catch(std::exception &e)
3192 bool ServerMap::loadSectorFull(v2s16 p2d)
3194 DSTACK(__FUNCTION_NAME);
3196 MapSector *sector = NULL;
3198 // The directory layout we're going to load from.
3199 // 1 - original sectors/xxxxzzzz/
3200 // 2 - new sectors2/xxx/zzz/
3201 // If we load from anything but the latest structure, we will
3202 // immediately save to the new one, and remove the old.
3204 std::string sectordir1 = getSectorDir(p2d, 1);
3205 std::string sectordir;
3206 if(fs::PathExists(sectordir1))
3208 sectordir = sectordir1;
3213 sectordir = getSectorDir(p2d, 2);
3217 sector = loadSectorMeta(sectordir, loadlayout != 2);
3219 catch(InvalidFilenameException &e)
3223 catch(FileNotGoodException &e)
3227 catch(std::exception &e)
3235 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3237 std::vector<fs::DirListNode>::iterator i2;
3238 for(i2=list2.begin(); i2!=list2.end(); i2++)
3244 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3246 catch(InvalidFilenameException &e)
3248 // This catches unknown crap in directory
3254 infostream<<"Sector converted to new layout - deleting "<<
3255 sectordir1<<std::endl;
3256 fs::RecursiveDelete(sectordir1);
3263 void ServerMap::beginSave()
3268 void ServerMap::endSave()
3273 bool ServerMap::saveBlock(MapBlock *block)
3275 return saveBlock(block, dbase);
3278 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3280 v3s16 p3d = block->getPos();
3282 // Dummy blocks are not written
3283 if (block->isDummy()) {
3284 errorstream << "WARNING: saveBlock: Not writing dummy block "
3285 << PP(p3d) << std::endl;
3289 // Format used for writing
3290 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3293 [0] u8 serialization version
3296 std::ostringstream o(std::ios_base::binary);
3297 o.write((char*) &version, 1);
3298 block->serialize(o, version, true);
3300 std::string data = o.str();
3301 bool ret = db->saveBlock(p3d, data);
3303 // We just wrote it to the disk so clear modified flag
3304 block->resetModified();
3309 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3310 MapSector *sector, bool save_after_load)
3312 DSTACK(__FUNCTION_NAME);
3314 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3317 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3318 if(is.good() == false)
3319 throw FileNotGoodException("Cannot open block file");
3321 v3s16 p3d = getBlockPos(sectordir, blockfile);
3322 v2s16 p2d(p3d.X, p3d.Z);
3324 assert(sector->getPos() == p2d);
3326 u8 version = SER_FMT_VER_INVALID;
3327 is.read((char*)&version, 1);
3330 throw SerializationError("ServerMap::loadBlock(): Failed"
3331 " to read MapBlock version");
3333 /*u32 block_size = MapBlock::serializedLength(version);
3334 SharedBuffer<u8> data(block_size);
3335 is.read((char*)*data, block_size);*/
3337 // This will always return a sector because we're the server
3338 //MapSector *sector = emergeSector(p2d);
3340 MapBlock *block = NULL;
3341 bool created_new = false;
3342 block = sector->getBlockNoCreateNoEx(p3d.Y);
3345 block = sector->createBlankBlockNoInsert(p3d.Y);
3350 block->deSerialize(is, version, true);
3352 // If it's a new block, insert it to the map
3354 sector->insertBlock(block);
3357 Save blocks loaded in old format in new format
3360 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3364 // Should be in database now, so delete the old file
3365 fs::RecursiveDelete(fullpath);
3368 // We just loaded it from the disk, so it's up-to-date.
3369 block->resetModified();
3372 catch(SerializationError &e)
3374 infostream<<"WARNING: Invalid block data on disk "
3375 <<"fullpath="<<fullpath
3376 <<" (SerializationError). "
3377 <<"what()="<<e.what()
3379 // Ignoring. A new one will be generated.
3382 // TODO: Backup file; name is in fullpath.
3386 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3388 DSTACK(__FUNCTION_NAME);
3391 std::istringstream is(*blob, std::ios_base::binary);
3393 u8 version = SER_FMT_VER_INVALID;
3394 is.read((char*)&version, 1);
3397 throw SerializationError("ServerMap::loadBlock(): Failed"
3398 " to read MapBlock version");
3400 /*u32 block_size = MapBlock::serializedLength(version);
3401 SharedBuffer<u8> data(block_size);
3402 is.read((char*)*data, block_size);*/
3404 // This will always return a sector because we're the server
3405 //MapSector *sector = emergeSector(p2d);
3407 MapBlock *block = NULL;
3408 bool created_new = false;
3409 block = sector->getBlockNoCreateNoEx(p3d.Y);
3412 block = sector->createBlankBlockNoInsert(p3d.Y);
3417 block->deSerialize(is, version, true);
3419 // If it's a new block, insert it to the map
3421 sector->insertBlock(block);
3424 Save blocks loaded in old format in new format
3427 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3428 // Only save if asked to; no need to update version
3432 // We just loaded it from, so it's up-to-date.
3433 block->resetModified();
3436 catch(SerializationError &e)
3438 errorstream<<"Invalid block data in database"
3439 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3440 <<" (SerializationError): "<<e.what()<<std::endl;
3442 // TODO: Block should be marked as invalid in memory so that it is
3443 // not touched but the game can run
3445 if(g_settings->getBool("ignore_world_load_errors")){
3446 errorstream<<"Ignoring block load error. Duck and cover! "
3447 <<"(ignore_world_load_errors)"<<std::endl;
3449 throw SerializationError("Invalid block data in database");
3455 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3457 DSTACK(__FUNCTION_NAME);
3459 v2s16 p2d(blockpos.X, blockpos.Z);
3463 ret = dbase->loadBlock(blockpos);
3465 loadBlock(&ret, blockpos, createSector(p2d), false);
3466 return getBlockNoCreateNoEx(blockpos);
3468 // Not found in database, try the files
3470 // The directory layout we're going to load from.
3471 // 1 - original sectors/xxxxzzzz/
3472 // 2 - new sectors2/xxx/zzz/
3473 // If we load from anything but the latest structure, we will
3474 // immediately save to the new one, and remove the old.
3476 std::string sectordir1 = getSectorDir(p2d, 1);
3477 std::string sectordir;
3478 if(fs::PathExists(sectordir1))
3480 sectordir = sectordir1;
3485 sectordir = getSectorDir(p2d, 2);
3489 Make sure sector is loaded
3491 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3495 sector = loadSectorMeta(sectordir, loadlayout != 2);
3497 catch(InvalidFilenameException &e)
3501 catch(FileNotGoodException &e)
3505 catch(std::exception &e)
3512 Make sure file exists
3515 std::string blockfilename = getBlockFilename(blockpos);
3516 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3520 Load block and save it to the database
3522 loadBlock(sectordir, blockfilename, sector, true);
3523 return getBlockNoCreateNoEx(blockpos);
3526 bool ServerMap::deleteBlock(v3s16 blockpos)
3528 if (!dbase->deleteBlock(blockpos))
3531 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3533 v2s16 p2d(blockpos.X, blockpos.Z);
3534 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3537 sector->deleteBlock(block);
3543 void ServerMap::PrintInfo(std::ostream &out)
3548 MMVManip::MMVManip(Map *map):
3551 m_create_area(false),
3556 MMVManip::~MMVManip()
3560 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3561 bool load_if_inexistent)
3563 TimeTaker timer1("initialEmerge", &emerge_time);
3565 // Units of these are MapBlocks
3566 v3s16 p_min = blockpos_min;
3567 v3s16 p_max = blockpos_max;
3569 VoxelArea block_area_nodes
3570 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3572 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3575 infostream<<"initialEmerge: area: ";
3576 block_area_nodes.print(infostream);
3577 infostream<<" ("<<size_MB<<"MB)";
3578 infostream<<std::endl;
3581 addArea(block_area_nodes);
3583 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3584 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3585 for(s32 x=p_min.X; x<=p_max.X; x++)
3590 std::map<v3s16, u8>::iterator n;
3591 n = m_loaded_blocks.find(p);
3592 if(n != m_loaded_blocks.end())
3595 bool block_data_inexistent = false;
3598 TimeTaker timer1("emerge load", &emerge_load_time);
3600 block = m_map->getBlockNoCreate(p);
3601 if(block->isDummy())
3602 block_data_inexistent = true;
3604 block->copyTo(*this);
3606 catch(InvalidPositionException &e)
3608 block_data_inexistent = true;
3611 if(block_data_inexistent)
3614 if (load_if_inexistent) {
3615 ServerMap *svrmap = (ServerMap *)m_map;
3616 block = svrmap->emergeBlock(p, false);
3618 block = svrmap->createBlock(p);
3619 block->copyTo(*this);
3621 flags |= VMANIP_BLOCK_DATA_INEXIST;
3624 Mark area inexistent
3626 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3627 // Fill with VOXELFLAG_NO_DATA
3628 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3629 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3631 s32 i = m_area.index(a.MinEdge.X,y,z);
3632 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3636 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3638 // Mark that block was loaded as blank
3639 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3642 m_loaded_blocks[p] = flags;
3648 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3649 bool overwrite_generated)
3651 if(m_area.getExtent() == v3s16(0,0,0))
3655 Copy data of all blocks
3657 for(std::map<v3s16, u8>::iterator
3658 i = m_loaded_blocks.begin();
3659 i != m_loaded_blocks.end(); ++i)
3662 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3663 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3664 if ((existed == false) || (block == NULL) ||
3665 (overwrite_generated == false && block->isGenerated() == true))
3668 block->copyFrom(*this);
3671 (*modified_blocks)[p] = block;