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"
26 #include "serialization.h"
27 #include "nodemetadata.h"
33 #include "util/directiontables.h"
34 #include "util/mathconstants.h"
35 #include "rollback_interface.h"
36 #include "environment.h"
38 #include "mapgen_v6.h"
43 #include "database-dummy.h"
44 #include "database-sqlite3.h"
48 #include "database-leveldb.h"
51 #include "database-redis.h"
54 #include "database-postgresql.h"
57 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
64 Map::Map(std::ostream &dout, IGameDef *gamedef):
68 m_transforming_liquid_loop_count_multiplier(1.0f),
69 m_unprocessed_count(0),
70 m_inc_trending_up_start_time(0),
71 m_queue_size_timer_started(false)
80 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
81 i != m_sectors.end(); ++i)
87 void Map::addEventReceiver(MapEventReceiver *event_receiver)
89 m_event_receivers.insert(event_receiver);
92 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
94 m_event_receivers.erase(event_receiver);
97 void Map::dispatchEvent(MapEditEvent *event)
99 for(std::set<MapEventReceiver*>::iterator
100 i = m_event_receivers.begin();
101 i != m_event_receivers.end(); ++i)
103 (*i)->onMapEditEvent(event);
107 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
109 if(m_sector_cache != NULL && p == m_sector_cache_p){
110 MapSector * sector = m_sector_cache;
114 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
116 if(n == m_sectors.end())
119 MapSector *sector = n->second;
121 // Cache the last result
122 m_sector_cache_p = p;
123 m_sector_cache = sector;
128 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
130 return getSectorNoGenerateNoExNoLock(p);
133 MapSector * Map::getSectorNoGenerate(v2s16 p)
135 MapSector *sector = getSectorNoGenerateNoEx(p);
137 throw InvalidPositionException();
142 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
144 v2s16 p2d(p3d.X, p3d.Z);
145 MapSector * sector = getSectorNoGenerateNoEx(p2d);
148 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
152 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
154 MapBlock *block = getBlockNoCreateNoEx(p3d);
156 throw InvalidPositionException();
160 bool Map::isNodeUnderground(v3s16 p)
162 v3s16 blockpos = getNodeBlockPos(p);
164 MapBlock * block = getBlockNoCreate(blockpos);
165 return block->getIsUnderground();
167 catch(InvalidPositionException &e)
173 bool Map::isValidPosition(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock *block = getBlockNoCreate(blockpos);
177 return (block != NULL);
180 // Returns a CONTENT_IGNORE node if not found
181 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
183 v3s16 blockpos = getNodeBlockPos(p);
184 MapBlock *block = getBlockNoCreateNoEx(blockpos);
186 if (is_valid_position != NULL)
187 *is_valid_position = false;
188 return MapNode(CONTENT_IGNORE);
191 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
193 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
194 if (is_valid_position != NULL)
195 *is_valid_position = is_valid_p;
201 // throws InvalidPositionException if not found
202 // TODO: Now this is deprecated, getNodeNoEx should be renamed
203 MapNode Map::getNode(v3s16 p)
205 v3s16 blockpos = getNodeBlockPos(p);
206 MapBlock *block = getBlockNoCreateNoEx(blockpos);
208 throw InvalidPositionException();
209 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
210 bool is_valid_position;
211 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
212 if (!is_valid_position)
213 throw InvalidPositionException();
218 // throws InvalidPositionException if not found
219 void Map::setNode(v3s16 p, MapNode & n)
221 v3s16 blockpos = getNodeBlockPos(p);
222 MapBlock *block = getBlockNoCreate(blockpos);
223 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
224 // Never allow placing CONTENT_IGNORE, it fucks up stuff
225 if(n.getContent() == CONTENT_IGNORE){
227 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
228 <<" while trying to replace \""
229 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
230 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
231 debug_stacks_print_to(infostream);
234 block->setNodeNoCheck(relpos, n);
239 Goes recursively through the neighbours of the node.
241 Alters only transparent nodes.
243 If the lighting of the neighbour is lower than the lighting of
244 the node was (before changing it to 0 at the step before), the
245 lighting of the neighbour is set to 0 and then the same stuff
246 repeats for the neighbour.
248 The ending nodes of the routine are stored in light_sources.
249 This is useful when a light is removed. In such case, this
250 routine can be called for the light node and then again for
251 light_sources to re-light the area without the removed light.
253 values of from_nodes are lighting values.
255 void Map::unspreadLight(enum LightBank bank,
256 std::map<v3s16, u8> & from_nodes,
257 std::set<v3s16> & light_sources,
258 std::map<v3s16, MapBlock*> & modified_blocks)
260 INodeDefManager *nodemgr = m_gamedef->ndef();
263 v3s16(0,0,1), // back
265 v3s16(1,0,0), // right
266 v3s16(0,0,-1), // front
267 v3s16(0,-1,0), // bottom
268 v3s16(-1,0,0), // left
271 if(from_nodes.empty())
274 u32 blockchangecount = 0;
276 std::map<v3s16, u8> unlighted_nodes;
279 Initialize block cache
282 MapBlock *block = NULL;
283 // Cache this a bit, too
284 bool block_checked_in_modified = false;
286 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
287 j != from_nodes.end(); ++j)
289 v3s16 pos = j->first;
290 v3s16 blockpos = getNodeBlockPos(pos);
292 // Only fetch a new block if the block position has changed
294 if(block == NULL || blockpos != blockpos_last){
295 block = getBlockNoCreate(blockpos);
296 blockpos_last = blockpos;
298 block_checked_in_modified = false;
302 catch(InvalidPositionException &e)
310 // Calculate relative position in block
311 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
313 // Get node straight from the block
314 //MapNode n = block->getNode(relpos);
316 u8 oldlight = j->second;
318 // Loop through 6 neighbors
319 for(u16 i=0; i<6; i++)
321 // Get the position of the neighbor node
322 v3s16 n2pos = pos + dirs[i];
324 // Get the block where the node is located
325 v3s16 blockpos, relpos;
326 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
328 // Only fetch a new block if the block position has changed
330 if(block == NULL || blockpos != blockpos_last){
331 block = getBlockNoCreate(blockpos);
332 blockpos_last = blockpos;
334 block_checked_in_modified = false;
338 catch(InvalidPositionException &e) {
342 // Get node straight from the block
343 bool is_valid_position;
344 MapNode n2 = block->getNode(relpos, &is_valid_position);
345 if (!is_valid_position)
348 bool changed = false;
350 //TODO: Optimize output by optimizing light_sources?
353 If the neighbor is dimmer than what was specified
354 as oldlight (the light of the previous node)
356 if(n2.getLight(bank, nodemgr) < oldlight)
359 And the neighbor is transparent and it has some light
361 if(nodemgr->get(n2).light_propagates
362 && n2.getLight(bank, nodemgr) != 0)
365 Set light to 0 and add to queue
368 u8 current_light = n2.getLight(bank, nodemgr);
369 n2.setLight(bank, 0, nodemgr);
370 block->setNode(relpos, n2);
372 unlighted_nodes[n2pos] = current_light;
376 Remove from light_sources if it is there
377 NOTE: This doesn't happen nearly at all
379 /*if(light_sources.find(n2pos))
381 infostream<<"Removed from light_sources"<<std::endl;
382 light_sources.remove(n2pos);
387 if(light_sources.find(n2pos) != NULL)
388 light_sources.remove(n2pos);*/
391 light_sources.insert(n2pos);
394 // Add to modified_blocks
395 if(changed == true && block_checked_in_modified == false)
397 // If the block is not found in modified_blocks, add.
398 if(modified_blocks.find(blockpos) == modified_blocks.end())
400 modified_blocks[blockpos] = block;
402 block_checked_in_modified = true;
407 /*infostream<<"unspreadLight(): Changed block "
408 <<blockchangecount<<" times"
409 <<" for "<<from_nodes.size()<<" nodes"
412 if(!unlighted_nodes.empty())
413 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
417 A single-node wrapper of the above
419 void Map::unLightNeighbors(enum LightBank bank,
420 v3s16 pos, u8 lightwas,
421 std::set<v3s16> & light_sources,
422 std::map<v3s16, MapBlock*> & modified_blocks)
424 std::map<v3s16, u8> from_nodes;
425 from_nodes[pos] = lightwas;
427 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
431 Lights neighbors of from_nodes, collects all them and then
434 void Map::spreadLight(enum LightBank bank,
435 std::set<v3s16> & from_nodes,
436 std::map<v3s16, MapBlock*> & modified_blocks)
438 INodeDefManager *nodemgr = m_gamedef->ndef();
440 const v3s16 dirs[6] = {
441 v3s16(0,0,1), // back
443 v3s16(1,0,0), // right
444 v3s16(0,0,-1), // front
445 v3s16(0,-1,0), // bottom
446 v3s16(-1,0,0), // left
449 if(from_nodes.empty())
452 u32 blockchangecount = 0;
454 std::set<v3s16> lighted_nodes;
457 Initialize block cache
460 MapBlock *block = NULL;
461 // Cache this a bit, too
462 bool block_checked_in_modified = false;
464 for(std::set<v3s16>::iterator j = from_nodes.begin();
465 j != from_nodes.end(); ++j)
468 v3s16 blockpos, relpos;
470 getNodeBlockPosWithOffset(pos, blockpos, relpos);
472 // Only fetch a new block if the block position has changed
474 if(block == NULL || blockpos != blockpos_last){
475 block = getBlockNoCreate(blockpos);
476 blockpos_last = blockpos;
478 block_checked_in_modified = false;
482 catch(InvalidPositionException &e) {
489 // Get node straight from the block
490 bool is_valid_position;
491 MapNode n = block->getNode(relpos, &is_valid_position);
493 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
494 u8 newlight = diminish_light(oldlight);
496 // Loop through 6 neighbors
497 for(u16 i=0; i<6; i++){
498 // Get the position of the neighbor node
499 v3s16 n2pos = pos + dirs[i];
501 // Get the block where the node is located
502 v3s16 blockpos, relpos;
503 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
505 // Only fetch a new block if the block position has changed
507 if(block == NULL || blockpos != blockpos_last){
508 block = getBlockNoCreate(blockpos);
509 blockpos_last = blockpos;
511 block_checked_in_modified = false;
515 catch(InvalidPositionException &e) {
519 // Get node straight from the block
520 MapNode n2 = block->getNode(relpos, &is_valid_position);
521 if (!is_valid_position)
524 bool changed = false;
526 If the neighbor is brighter than the current node,
527 add to list (it will light up this node on its turn)
529 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
531 lighted_nodes.insert(n2pos);
535 If the neighbor is dimmer than how much light this node
536 would spread on it, add to list
538 if(n2.getLight(bank, nodemgr) < newlight)
540 if(nodemgr->get(n2).light_propagates)
542 n2.setLight(bank, newlight, nodemgr);
543 block->setNode(relpos, n2);
544 lighted_nodes.insert(n2pos);
549 // Add to modified_blocks
550 if(changed == true && block_checked_in_modified == false)
552 // If the block is not found in modified_blocks, add.
553 if(modified_blocks.find(blockpos) == modified_blocks.end())
555 modified_blocks[blockpos] = block;
557 block_checked_in_modified = true;
562 /*infostream<<"spreadLight(): Changed block "
563 <<blockchangecount<<" times"
564 <<" for "<<from_nodes.size()<<" nodes"
567 if(!lighted_nodes.empty())
568 spreadLight(bank, lighted_nodes, modified_blocks);
572 A single-node source variation of the above.
574 void Map::lightNeighbors(enum LightBank bank,
576 std::map<v3s16, MapBlock*> & modified_blocks)
578 std::set<v3s16> from_nodes;
579 from_nodes.insert(pos);
580 spreadLight(bank, from_nodes, modified_blocks);
583 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
585 INodeDefManager *nodemgr = m_gamedef->ndef();
588 v3s16(0,0,1), // back
590 v3s16(1,0,0), // right
591 v3s16(0,0,-1), // front
592 v3s16(0,-1,0), // bottom
593 v3s16(-1,0,0), // left
596 u8 brightest_light = 0;
597 v3s16 brightest_pos(0,0,0);
598 bool found_something = false;
600 // Loop through 6 neighbors
601 for(u16 i=0; i<6; i++){
602 // Get the position of the neighbor node
603 v3s16 n2pos = p + dirs[i];
605 bool is_valid_position;
606 n2 = getNodeNoEx(n2pos, &is_valid_position);
607 if (!is_valid_position)
610 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
611 brightest_light = n2.getLight(bank, nodemgr);
612 brightest_pos = n2pos;
613 found_something = true;
617 if(found_something == false)
618 throw InvalidPositionException();
620 return brightest_pos;
624 Propagates sunlight down from a node.
625 Starting point gets sunlight.
627 Returns the lowest y value of where the sunlight went.
629 Mud is turned into grass in where the sunlight stops.
631 s16 Map::propagateSunlight(v3s16 start,
632 std::map<v3s16, MapBlock*> & modified_blocks)
634 INodeDefManager *nodemgr = m_gamedef->ndef();
639 v3s16 pos(start.X, y, start.Z);
641 v3s16 blockpos = getNodeBlockPos(pos);
644 block = getBlockNoCreate(blockpos);
646 catch(InvalidPositionException &e)
651 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
652 bool is_valid_position;
653 MapNode n = block->getNode(relpos, &is_valid_position);
654 if (!is_valid_position)
657 if(nodemgr->get(n).sunlight_propagates)
659 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
660 block->setNode(relpos, n);
662 modified_blocks[blockpos] = block;
666 // Sunlight goes no further
673 void Map::updateLighting(enum LightBank bank,
674 std::map<v3s16, MapBlock*> & a_blocks,
675 std::map<v3s16, MapBlock*> & modified_blocks)
677 INodeDefManager *nodemgr = m_gamedef->ndef();
679 /*m_dout<<"Map::updateLighting(): "
680 <<a_blocks.size()<<" blocks."<<std::endl;*/
682 //TimeTaker timer("updateLighting");
686 //u32 count_was = modified_blocks.size();
688 //std::map<v3s16, MapBlock*> blocks_to_update;
690 std::set<v3s16> light_sources;
692 std::map<v3s16, u8> unlight_from;
694 int num_bottom_invalid = 0;
697 //TimeTaker t("first stuff");
699 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
700 i != a_blocks.end(); ++i)
702 MapBlock *block = i->second;
706 // Don't bother with dummy blocks.
710 v3s16 pos = block->getPos();
711 v3s16 posnodes = block->getPosRelative();
712 modified_blocks[pos] = block;
713 //blocks_to_update[pos] = block;
716 Clear all light from block
718 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
719 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
720 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
723 bool is_valid_position;
724 MapNode n = block->getNode(p, &is_valid_position);
725 if (!is_valid_position) {
726 /* This would happen when dealing with a
729 infostream<<"updateLighting(): InvalidPositionException"
733 u8 oldlight = n.getLight(bank, nodemgr);
734 n.setLight(bank, 0, nodemgr);
735 block->setNode(p, n);
737 // If node sources light, add to list
738 u8 source = nodemgr->get(n).light_source;
740 light_sources.insert(p + posnodes);
742 // Collect borders for unlighting
743 if((x==0 || x == MAP_BLOCKSIZE-1
744 || y==0 || y == MAP_BLOCKSIZE-1
745 || z==0 || z == MAP_BLOCKSIZE-1)
748 v3s16 p_map = p + posnodes;
749 unlight_from[p_map] = oldlight;
755 if(bank == LIGHTBANK_DAY)
757 bool bottom_valid = block->propagateSunlight(light_sources);
760 num_bottom_invalid++;
762 // If bottom is valid, we're done.
766 else if(bank == LIGHTBANK_NIGHT)
768 // For night lighting, sunlight is not propagated
773 assert("Invalid lighting bank" == NULL);
776 /*infostream<<"Bottom for sunlight-propagated block ("
777 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
780 // Bottom sunlight is not valid; get the block and loop to it
784 block = getBlockNoCreate(pos);
786 catch(InvalidPositionException &e)
788 FATAL_ERROR("Invalid position");
797 Enable this to disable proper lighting for speeding up map
798 generation for testing or whatever
801 //if(g_settings->get(""))
803 core::map<v3s16, MapBlock*>::Iterator i;
804 i = blocks_to_update.getIterator();
805 for(; i.atEnd() == false; i++)
807 MapBlock *block = i.getNode()->getValue();
808 v3s16 p = block->getPos();
809 block->setLightingExpired(false);
817 //TimeTaker timer("unspreadLight");
818 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
823 u32 diff = modified_blocks.size() - count_was;
824 count_was = modified_blocks.size();
825 infostream<<"unspreadLight modified "<<diff<<std::endl;
829 //TimeTaker timer("spreadLight");
830 spreadLight(bank, light_sources, modified_blocks);
835 u32 diff = modified_blocks.size() - count_was;
836 count_was = modified_blocks.size();
837 infostream<<"spreadLight modified "<<diff<<std::endl;
843 //MapVoxelManipulator vmanip(this);
845 // Make a manual voxel manipulator and load all the blocks
846 // that touch the requested blocks
847 ManualMapVoxelManipulator vmanip(this);
850 //TimeTaker timer("initialEmerge");
852 core::map<v3s16, MapBlock*>::Iterator i;
853 i = blocks_to_update.getIterator();
854 for(; i.atEnd() == false; i++)
856 MapBlock *block = i.getNode()->getValue();
857 v3s16 p = block->getPos();
859 // Add all surrounding blocks
860 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
863 Add all surrounding blocks that have up-to-date lighting
864 NOTE: This doesn't quite do the job (not everything
865 appropriate is lighted)
867 /*for(s16 z=-1; z<=1; z++)
868 for(s16 y=-1; y<=1; y++)
869 for(s16 x=-1; x<=1; x++)
871 v3s16 p2 = p + v3s16(x,y,z);
872 MapBlock *block = getBlockNoCreateNoEx(p2);
877 if(block->getLightingExpired())
879 vmanip.initialEmerge(p2, p2);
882 // Lighting of block will be updated completely
883 block->setLightingExpired(false);
888 //TimeTaker timer("unSpreadLight");
889 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
892 //TimeTaker timer("spreadLight");
893 vmanip.spreadLight(bank, light_sources, nodemgr);
896 //TimeTaker timer("blitBack");
897 vmanip.blitBack(modified_blocks);
899 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
904 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
907 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
908 std::map<v3s16, MapBlock*> & modified_blocks)
910 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
911 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
914 Update information about whether day and night light differ
916 for(std::map<v3s16, MapBlock*>::iterator
917 i = modified_blocks.begin();
918 i != modified_blocks.end(); ++i)
920 MapBlock *block = i->second;
921 block->expireDayNightDiff();
927 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
928 std::map<v3s16, MapBlock*> &modified_blocks,
929 bool remove_metadata)
931 INodeDefManager *ndef = m_gamedef->ndef();
934 m_dout<<"Map::addNodeAndUpdate(): p=("
935 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
938 From this node to nodes underneath:
939 If lighting is sunlight (1.0), unlight neighbours and
944 v3s16 toppos = p + v3s16(0,1,0);
945 //v3s16 bottompos = p + v3s16(0,-1,0);
947 bool node_under_sunlight = true;
948 std::set<v3s16> light_sources;
951 Collect old node for rollback
953 RollbackNode rollback_oldnode(this, p, m_gamedef);
956 If there is a node at top and it doesn't have sunlight,
957 there has not been any sunlight going down.
959 Otherwise there probably is.
962 bool is_valid_position;
963 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
965 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
966 node_under_sunlight = false;
969 Remove all light that has come out of this node
972 enum LightBank banks[] =
977 for(s32 i=0; i<2; i++)
979 enum LightBank bank = banks[i];
981 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
983 // Add the block of the added node to modified_blocks
984 v3s16 blockpos = getNodeBlockPos(p);
985 MapBlock * block = getBlockNoCreate(blockpos);
986 assert(block != NULL);
987 modified_blocks[blockpos] = block;
989 assert(isValidPosition(p));
991 // Unlight neighbours of node.
992 // This means setting light of all consequent dimmer nodes
994 // This also collects the nodes at the border which will spread
995 // light again into this.
996 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
998 n.setLight(bank, 0, ndef);
1002 If node lets sunlight through and is under sunlight, it has
1005 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1007 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1011 Remove node metadata
1013 if (remove_metadata) {
1014 removeNodeMetadata(p);
1018 Set the node on the map
1024 If node is under sunlight and doesn't let sunlight through,
1025 take all sunlighted nodes under it and clear light from them
1026 and from where the light has been spread.
1027 TODO: This could be optimized by mass-unlighting instead
1030 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1034 //m_dout<<"y="<<y<<std::endl;
1035 v3s16 n2pos(p.X, y, p.Z);
1039 n2 = getNodeNoEx(n2pos, &is_valid_position);
1040 if (!is_valid_position)
1043 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1045 unLightNeighbors(LIGHTBANK_DAY,
1046 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1047 light_sources, modified_blocks);
1048 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1056 for(s32 i=0; i<2; i++)
1058 enum LightBank bank = banks[i];
1061 Spread light from all nodes that might be capable of doing so
1063 spreadLight(bank, light_sources, modified_blocks);
1067 Update information about whether day and night light differ
1069 for(std::map<v3s16, MapBlock*>::iterator
1070 i = modified_blocks.begin();
1071 i != modified_blocks.end(); ++i)
1073 i->second->expireDayNightDiff();
1079 if(m_gamedef->rollback())
1081 RollbackNode rollback_newnode(this, p, m_gamedef);
1082 RollbackAction action;
1083 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1084 m_gamedef->rollback()->reportAction(action);
1088 Add neighboring liquid nodes and the node itself if it is
1089 liquid (=water node was added) to transform queue.
1092 v3s16(0,0,0), // self
1093 v3s16(0,0,1), // back
1094 v3s16(0,1,0), // top
1095 v3s16(1,0,0), // right
1096 v3s16(0,0,-1), // front
1097 v3s16(0,-1,0), // bottom
1098 v3s16(-1,0,0), // left
1100 for(u16 i=0; i<7; i++)
1102 v3s16 p2 = p + dirs[i];
1104 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1105 if(is_valid_position
1106 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1108 m_transforming_liquid.push_back(p2);
1115 void Map::removeNodeAndUpdate(v3s16 p,
1116 std::map<v3s16, MapBlock*> &modified_blocks)
1118 INodeDefManager *ndef = m_gamedef->ndef();
1120 /*PrintInfo(m_dout);
1121 m_dout<<"Map::removeNodeAndUpdate(): p=("
1122 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1124 bool node_under_sunlight = true;
1126 v3s16 toppos = p + v3s16(0,1,0);
1128 // Node will be replaced with this
1129 content_t replace_material = CONTENT_AIR;
1132 Collect old node for rollback
1134 RollbackNode rollback_oldnode(this, p, m_gamedef);
1137 If there is a node at top and it doesn't have sunlight,
1138 there will be no sunlight going down.
1140 bool is_valid_position;
1141 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1143 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1144 node_under_sunlight = false;
1146 std::set<v3s16> light_sources;
1148 enum LightBank banks[] =
1153 for(s32 i=0; i<2; i++)
1155 enum LightBank bank = banks[i];
1158 Unlight neighbors (in case the node is a light source)
1160 unLightNeighbors(bank, p,
1161 getNodeNoEx(p).getLight(bank, ndef),
1162 light_sources, modified_blocks);
1166 Remove node metadata
1169 removeNodeMetadata(p);
1173 This also clears the lighting.
1176 MapNode n(replace_material);
1179 for(s32 i=0; i<2; i++)
1181 enum LightBank bank = banks[i];
1184 Recalculate lighting
1186 spreadLight(bank, light_sources, modified_blocks);
1189 // Add the block of the removed node to modified_blocks
1190 v3s16 blockpos = getNodeBlockPos(p);
1191 MapBlock * block = getBlockNoCreate(blockpos);
1192 assert(block != NULL);
1193 modified_blocks[blockpos] = block;
1196 If the removed node was under sunlight, propagate the
1197 sunlight down from it and then light all neighbors
1198 of the propagated blocks.
1200 if(node_under_sunlight)
1202 s16 ybottom = propagateSunlight(p, modified_blocks);
1203 /*m_dout<<"Node was under sunlight. "
1204 "Propagating sunlight";
1205 m_dout<<" -> ybottom="<<ybottom<<std::endl;*/
1207 for(; y >= ybottom; y--)
1209 v3s16 p2(p.X, y, p.Z);
1210 /*m_dout<<"lighting neighbors of node ("
1211 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1213 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1218 // Set the lighting of this node to 0
1219 // TODO: Is this needed? Lighting is cleared up there already.
1220 MapNode n = getNodeNoEx(p, &is_valid_position);
1221 if (is_valid_position) {
1222 n.setLight(LIGHTBANK_DAY, 0, ndef);
1225 FATAL_ERROR("Invalid position");
1229 for(s32 i=0; i<2; i++)
1231 enum LightBank bank = banks[i];
1233 // Get the brightest neighbour node and propagate light from it
1234 v3s16 n2p = getBrightestNeighbour(bank, p);
1236 //MapNode n2 = getNode(n2p);
1237 lightNeighbors(bank, n2p, modified_blocks);
1239 catch(InvalidPositionException &e)
1245 Update information about whether day and night light differ
1247 for(std::map<v3s16, MapBlock*>::iterator
1248 i = modified_blocks.begin();
1249 i != modified_blocks.end(); ++i)
1251 i->second->expireDayNightDiff();
1257 if(m_gamedef->rollback())
1259 RollbackNode rollback_newnode(this, p, m_gamedef);
1260 RollbackAction action;
1261 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1262 m_gamedef->rollback()->reportAction(action);
1266 Add neighboring liquid nodes and this node to transform queue.
1267 (it's vital for the node itself to get updated last.)
1270 v3s16(0,0,1), // back
1271 v3s16(0,1,0), // top
1272 v3s16(1,0,0), // right
1273 v3s16(0,0,-1), // front
1274 v3s16(0,-1,0), // bottom
1275 v3s16(-1,0,0), // left
1276 v3s16(0,0,0), // self
1278 for(u16 i=0; i<7; i++)
1280 v3s16 p2 = p + dirs[i];
1282 bool is_position_valid;
1283 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1284 if (is_position_valid
1285 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1287 m_transforming_liquid.push_back(p2);
1292 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1295 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1299 bool succeeded = true;
1301 std::map<v3s16, MapBlock*> modified_blocks;
1302 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1304 // Copy modified_blocks to event
1305 for(std::map<v3s16, MapBlock*>::iterator
1306 i = modified_blocks.begin();
1307 i != modified_blocks.end(); ++i)
1309 event.modified_blocks.insert(i->first);
1312 catch(InvalidPositionException &e){
1316 dispatchEvent(&event);
1321 bool Map::removeNodeWithEvent(v3s16 p)
1324 event.type = MEET_REMOVENODE;
1327 bool succeeded = true;
1329 std::map<v3s16, MapBlock*> modified_blocks;
1330 removeNodeAndUpdate(p, modified_blocks);
1332 // Copy modified_blocks to event
1333 for(std::map<v3s16, MapBlock*>::iterator
1334 i = modified_blocks.begin();
1335 i != modified_blocks.end(); ++i)
1337 event.modified_blocks.insert(i->first);
1340 catch(InvalidPositionException &e){
1344 dispatchEvent(&event);
1349 bool Map::getDayNightDiff(v3s16 blockpos)
1352 v3s16 p = blockpos + v3s16(0,0,0);
1353 MapBlock *b = getBlockNoCreate(p);
1354 if(b->getDayNightDiff())
1357 catch(InvalidPositionException &e){}
1360 v3s16 p = blockpos + v3s16(-1,0,0);
1361 MapBlock *b = getBlockNoCreate(p);
1362 if(b->getDayNightDiff())
1365 catch(InvalidPositionException &e){}
1367 v3s16 p = blockpos + v3s16(0,-1,0);
1368 MapBlock *b = getBlockNoCreate(p);
1369 if(b->getDayNightDiff())
1372 catch(InvalidPositionException &e){}
1374 v3s16 p = blockpos + v3s16(0,0,-1);
1375 MapBlock *b = getBlockNoCreate(p);
1376 if(b->getDayNightDiff())
1379 catch(InvalidPositionException &e){}
1382 v3s16 p = blockpos + v3s16(1,0,0);
1383 MapBlock *b = getBlockNoCreate(p);
1384 if(b->getDayNightDiff())
1387 catch(InvalidPositionException &e){}
1389 v3s16 p = blockpos + v3s16(0,1,0);
1390 MapBlock *b = getBlockNoCreate(p);
1391 if(b->getDayNightDiff())
1394 catch(InvalidPositionException &e){}
1396 v3s16 p = blockpos + v3s16(0,0,1);
1397 MapBlock *b = getBlockNoCreate(p);
1398 if(b->getDayNightDiff())
1401 catch(InvalidPositionException &e){}
1406 struct TimeOrderedMapBlock {
1410 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
1415 bool operator<(const TimeOrderedMapBlock &b) const
1417 return block->getUsageTimer() < b.block->getUsageTimer();
1422 Updates usage timers
1424 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
1425 std::vector<v3s16> *unloaded_blocks)
1427 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1429 // Profile modified reasons
1430 Profiler modprofiler;
1432 std::vector<v2s16> sector_deletion_queue;
1433 u32 deleted_blocks_count = 0;
1434 u32 saved_blocks_count = 0;
1435 u32 block_count_all = 0;
1439 // If there is no practical limit, we spare creation of mapblock_queue
1440 if (max_loaded_blocks == U32_MAX) {
1441 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1442 si != m_sectors.end(); ++si) {
1443 MapSector *sector = si->second;
1445 bool all_blocks_deleted = true;
1447 MapBlockVect blocks;
1448 sector->getBlocks(blocks);
1450 for (MapBlockVect::iterator i = blocks.begin();
1451 i != blocks.end(); ++i) {
1452 MapBlock *block = (*i);
1454 block->incrementUsageTimer(dtime);
1456 if (block->refGet() == 0
1457 && block->getUsageTimer() > unload_timeout) {
1458 v3s16 p = block->getPos();
1461 if (block->getModified() != MOD_STATE_CLEAN
1462 && save_before_unloading) {
1463 modprofiler.add(block->getModifiedReasonString(), 1);
1464 if (!saveBlock(block))
1466 saved_blocks_count++;
1469 // Delete from memory
1470 sector->deleteBlock(block);
1472 if (unloaded_blocks)
1473 unloaded_blocks->push_back(p);
1475 deleted_blocks_count++;
1477 all_blocks_deleted = false;
1482 if (all_blocks_deleted) {
1483 sector_deletion_queue.push_back(si->first);
1487 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
1488 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1489 si != m_sectors.end(); ++si) {
1490 MapSector *sector = si->second;
1492 MapBlockVect blocks;
1493 sector->getBlocks(blocks);
1495 for(MapBlockVect::iterator i = blocks.begin();
1496 i != blocks.end(); ++i) {
1497 MapBlock *block = (*i);
1499 block->incrementUsageTimer(dtime);
1500 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
1503 block_count_all = mapblock_queue.size();
1504 // Delete old blocks, and blocks over the limit from the memory
1505 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
1506 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
1507 TimeOrderedMapBlock b = mapblock_queue.top();
1508 mapblock_queue.pop();
1510 MapBlock *block = b.block;
1512 if (block->refGet() != 0)
1515 v3s16 p = block->getPos();
1518 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1519 modprofiler.add(block->getModifiedReasonString(), 1);
1520 if (!saveBlock(block))
1522 saved_blocks_count++;
1525 // Delete from memory
1526 b.sect->deleteBlock(block);
1528 if (unloaded_blocks)
1529 unloaded_blocks->push_back(p);
1531 deleted_blocks_count++;
1534 // Delete empty sectors
1535 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1536 si != m_sectors.end(); ++si) {
1537 if (si->second->empty()) {
1538 sector_deletion_queue.push_back(si->first);
1544 // Finally delete the empty sectors
1545 deleteSectors(sector_deletion_queue);
1547 if(deleted_blocks_count != 0)
1549 PrintInfo(infostream); // ServerMap/ClientMap:
1550 infostream<<"Unloaded "<<deleted_blocks_count
1551 <<" blocks from memory";
1552 if(save_before_unloading)
1553 infostream<<", of which "<<saved_blocks_count<<" were written";
1554 infostream<<", "<<block_count_all<<" blocks in memory";
1555 infostream<<"."<<std::endl;
1556 if(saved_blocks_count != 0){
1557 PrintInfo(infostream); // ServerMap/ClientMap:
1558 infostream<<"Blocks modified by: "<<std::endl;
1559 modprofiler.print(infostream);
1564 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1566 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
1569 void Map::deleteSectors(std::vector<v2s16> §orList)
1571 for(std::vector<v2s16>::iterator j = sectorList.begin();
1572 j != sectorList.end(); ++j) {
1573 MapSector *sector = m_sectors[*j];
1574 // If sector is in sector cache, remove it from there
1575 if(m_sector_cache == sector)
1576 m_sector_cache = NULL;
1577 // Remove from map and delete
1578 m_sectors.erase(*j);
1583 void Map::PrintInfo(std::ostream &out)
1588 #define WATER_DROP_BOOST 4
1592 NEIGHBOR_SAME_LEVEL,
1595 struct NodeNeighbor {
1599 bool l; //can liquid
1605 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1612 void Map::transforming_liquid_add(v3s16 p) {
1613 m_transforming_liquid.push_back(p);
1616 s32 Map::transforming_liquid_size() {
1617 return m_transforming_liquid.size();
1620 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
1623 INodeDefManager *nodemgr = m_gamedef->ndef();
1625 DSTACK(FUNCTION_NAME);
1626 //TimeTaker timer("transformLiquids()");
1629 u32 initial_size = m_transforming_liquid.size();
1631 /*if(initial_size != 0)
1632 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1634 // list of nodes that due to viscosity have not reached their max level height
1635 std::deque<v3s16> must_reflow;
1637 // List of MapBlocks that will require a lighting update (due to lava)
1638 std::map<v3s16, MapBlock *> lighting_modified_blocks;
1640 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1641 u32 loop_max = liquid_loop_max;
1645 /* If liquid_loop_max is not keeping up with the queue size increase
1646 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1648 if (m_transforming_liquid.size() > loop_max * 2) {
1650 float server_step = g_settings->getFloat("dedicated_server_step");
1651 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1652 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1654 m_transforming_liquid_loop_count_multiplier = 1.0;
1657 loop_max *= m_transforming_liquid_loop_count_multiplier;
1660 while (m_transforming_liquid.size() != 0)
1662 // This should be done here so that it is done when continue is used
1663 if (loopcount >= initial_size || loopcount >= loop_max)
1668 Get a queued transforming liquid node
1670 v3s16 p0 = m_transforming_liquid.front();
1671 m_transforming_liquid.pop_front();
1673 MapNode n0 = getNodeNoEx(p0);
1676 Collect information about current node
1678 s8 liquid_level = -1;
1679 content_t liquid_kind = CONTENT_IGNORE;
1680 content_t floodable_node = CONTENT_AIR;
1681 const ContentFeatures &cf = nodemgr->get(n0);
1682 LiquidType liquid_type = cf.liquid_type;
1683 switch (liquid_type) {
1685 liquid_level = LIQUID_LEVEL_SOURCE;
1686 liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing);
1688 case LIQUID_FLOWING:
1689 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1690 liquid_kind = n0.getContent();
1693 // if this node is 'floodable', it *could* be transformed
1694 // into a liquid, otherwise, continue with the next node.
1697 floodable_node = n0.getContent();
1698 liquid_kind = CONTENT_AIR;
1703 Collect information about the environment
1705 const v3s16 *dirs = g_6dirs;
1706 NodeNeighbor sources[6]; // surrounding sources
1707 int num_sources = 0;
1708 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1710 NodeNeighbor airs[6]; // surrounding air
1712 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1713 int num_neutrals = 0;
1714 bool flowing_down = false;
1715 for (u16 i = 0; i < 6; i++) {
1716 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1719 nt = NEIGHBOR_UPPER;
1722 nt = NEIGHBOR_LOWER;
1725 v3s16 npos = p0 + dirs[i];
1726 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1727 const ContentFeatures &cfnb = nodemgr->get(nb.n);
1728 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1730 if (cfnb.floodable) {
1731 airs[num_airs++] = nb;
1732 // if the current node is a water source the neighbor
1733 // should be enqueded for transformation regardless of whether the
1734 // current node changes or not.
1735 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1736 m_transforming_liquid.push_back(npos);
1737 // if the current node happens to be a flowing node, it will start to flow down here.
1738 if (nb.t == NEIGHBOR_LOWER)
1739 flowing_down = true;
1741 neutrals[num_neutrals++] = nb;
1742 // If neutral below is ignore prevent water spreading outwards
1743 if (nb.t == NEIGHBOR_LOWER &&
1744 nb.n.getContent() == CONTENT_IGNORE)
1745 flowing_down = true;
1749 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1750 if (liquid_kind == CONTENT_AIR)
1751 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1752 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1753 neutrals[num_neutrals++] = nb;
1755 // Do not count bottom source, it will screw things up
1757 sources[num_sources++] = nb;
1760 case LIQUID_FLOWING:
1761 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1762 if (liquid_kind == CONTENT_AIR)
1763 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1764 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1765 neutrals[num_neutrals++] = nb;
1767 flows[num_flows++] = nb;
1768 if (nb.t == NEIGHBOR_LOWER)
1769 flowing_down = true;
1776 decide on the type (and possibly level) of the current node
1778 content_t new_node_content;
1779 s8 new_node_level = -1;
1780 s8 max_node_level = -1;
1782 u8 range = nodemgr->get(liquid_kind).liquid_range;
1783 if (range > LIQUID_LEVEL_MAX + 1)
1784 range = LIQUID_LEVEL_MAX + 1;
1786 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1787 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1788 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1789 // it's perfectly safe to use liquid_kind here to determine the new node content.
1790 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1791 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1792 // liquid_kind is set properly, see above
1793 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1794 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1795 new_node_content = liquid_kind;
1797 new_node_content = floodable_node;
1799 // no surrounding sources, so get the maximum level that can flow into this node
1800 for (u16 i = 0; i < num_flows; i++) {
1801 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1802 switch (flows[i].t) {
1803 case NEIGHBOR_UPPER:
1804 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1805 max_node_level = LIQUID_LEVEL_MAX;
1806 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1807 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1808 } else if (nb_liquid_level > max_node_level) {
1809 max_node_level = nb_liquid_level;
1812 case NEIGHBOR_LOWER:
1814 case NEIGHBOR_SAME_LEVEL:
1815 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1816 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
1817 max_node_level = nb_liquid_level - 1;
1822 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1823 if (viscosity > 1 && max_node_level != liquid_level) {
1824 // amount to gain, limited by viscosity
1825 // must be at least 1 in absolute value
1826 s8 level_inc = max_node_level - liquid_level;
1827 if (level_inc < -viscosity || level_inc > viscosity)
1828 new_node_level = liquid_level + level_inc/viscosity;
1829 else if (level_inc < 0)
1830 new_node_level = liquid_level - 1;
1831 else if (level_inc > 0)
1832 new_node_level = liquid_level + 1;
1833 if (new_node_level != max_node_level)
1834 must_reflow.push_back(p0);
1836 new_node_level = max_node_level;
1839 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1840 new_node_content = liquid_kind;
1842 new_node_content = floodable_node;
1847 check if anything has changed. if not, just continue with the next node.
1849 if (new_node_content == n0.getContent() &&
1850 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1851 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1852 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1858 update the current node
1861 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1862 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1863 // set level to last 3 bits, flowing down bit to 4th bit
1864 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1866 // set the liquid level and flow bit to 0
1867 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1869 n0.setContent(new_node_content);
1871 // Find out whether there is a suspect for this action
1872 std::string suspect;
1873 if (m_gamedef->rollback())
1874 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1876 if (m_gamedef->rollback() && !suspect.empty()) {
1878 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1879 // Get old node for rollback
1880 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1884 RollbackNode rollback_newnode(this, p0, m_gamedef);
1885 RollbackAction action;
1886 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1887 m_gamedef->rollback()->reportAction(action);
1893 v3s16 blockpos = getNodeBlockPos(p0);
1894 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1895 if (block != NULL) {
1896 modified_blocks[blockpos] = block;
1897 // If new or old node emits light, MapBlock requires lighting update
1898 if (nodemgr->get(n0).light_source != 0 ||
1899 nodemgr->get(n00).light_source != 0)
1900 lighting_modified_blocks[block->getPos()] = block;
1904 enqueue neighbors for update if neccessary
1906 switch (nodemgr->get(n0.getContent()).liquid_type) {
1908 case LIQUID_FLOWING:
1909 // make sure source flows into all neighboring nodes
1910 for (u16 i = 0; i < num_flows; i++)
1911 if (flows[i].t != NEIGHBOR_UPPER)
1912 m_transforming_liquid.push_back(flows[i].p);
1913 for (u16 i = 0; i < num_airs; i++)
1914 if (airs[i].t != NEIGHBOR_UPPER)
1915 m_transforming_liquid.push_back(airs[i].p);
1918 // this flow has turned to air; neighboring flows might need to do the same
1919 for (u16 i = 0; i < num_flows; i++)
1920 m_transforming_liquid.push_back(flows[i].p);
1924 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1926 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1927 m_transforming_liquid.push_back(*iter);
1929 updateLighting(lighting_modified_blocks, modified_blocks);
1932 /* ----------------------------------------------------------------------
1933 * Manage the queue so that it does not grow indefinately
1935 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1937 if (time_until_purge == 0)
1938 return; // Feature disabled
1940 time_until_purge *= 1000; // seconds -> milliseconds
1942 u32 curr_time = getTime(PRECISION_MILLI);
1943 u32 prev_unprocessed = m_unprocessed_count;
1944 m_unprocessed_count = m_transforming_liquid.size();
1946 // if unprocessed block count is decreasing or stable
1947 if (m_unprocessed_count <= prev_unprocessed) {
1948 m_queue_size_timer_started = false;
1950 if (!m_queue_size_timer_started)
1951 m_inc_trending_up_start_time = curr_time;
1952 m_queue_size_timer_started = true;
1955 // Account for curr_time overflowing
1956 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1957 m_queue_size_timer_started = false;
1959 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1960 * and the number of unprocessed blocks is still > liquid_loop_max then we
1961 * cannot keep up; dump the oldest blocks from the queue so that the queue
1962 * has liquid_loop_max items in it
1964 if (m_queue_size_timer_started
1965 && curr_time - m_inc_trending_up_start_time > time_until_purge
1966 && m_unprocessed_count > liquid_loop_max) {
1968 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1970 infostream << "transformLiquids(): DUMPING " << dump_qty
1971 << " blocks from the queue" << std::endl;
1974 m_transforming_liquid.pop_front();
1976 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1977 m_unprocessed_count = m_transforming_liquid.size();
1981 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1983 std::vector<v3s16> positions_with_meta;
1985 sortBoxVerticies(p1, p2);
1986 v3s16 bpmin = getNodeBlockPos(p1);
1987 v3s16 bpmax = getNodeBlockPos(p2);
1989 VoxelArea area(p1, p2);
1991 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1992 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1993 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1994 v3s16 blockpos(x, y, z);
1996 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1998 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1999 << PP(blockpos) << std::endl;
2000 block = emergeBlock(blockpos, false);
2003 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
2008 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
2009 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
2010 for (size_t i = 0; i != keys.size(); i++) {
2011 v3s16 p(keys[i] + p_base);
2012 if (!area.contains(p))
2015 positions_with_meta.push_back(p);
2019 return positions_with_meta;
2022 NodeMetadata *Map::getNodeMetadata(v3s16 p)
2024 v3s16 blockpos = getNodeBlockPos(p);
2025 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2026 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2028 infostream<<"Map::getNodeMetadata(): Need to emerge "
2029 <<PP(blockpos)<<std::endl;
2030 block = emergeBlock(blockpos, false);
2033 warningstream<<"Map::getNodeMetadata(): Block not found"
2037 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2041 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2043 v3s16 blockpos = getNodeBlockPos(p);
2044 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2045 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2047 infostream<<"Map::setNodeMetadata(): Need to emerge "
2048 <<PP(blockpos)<<std::endl;
2049 block = emergeBlock(blockpos, false);
2052 warningstream<<"Map::setNodeMetadata(): Block not found"
2056 block->m_node_metadata.set(p_rel, meta);
2060 void Map::removeNodeMetadata(v3s16 p)
2062 v3s16 blockpos = getNodeBlockPos(p);
2063 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2064 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2067 warningstream<<"Map::removeNodeMetadata(): Block not found"
2071 block->m_node_metadata.remove(p_rel);
2074 NodeTimer Map::getNodeTimer(v3s16 p)
2076 v3s16 blockpos = getNodeBlockPos(p);
2077 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2078 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2080 infostream<<"Map::getNodeTimer(): Need to emerge "
2081 <<PP(blockpos)<<std::endl;
2082 block = emergeBlock(blockpos, false);
2085 warningstream<<"Map::getNodeTimer(): Block not found"
2089 NodeTimer t = block->m_node_timers.get(p_rel);
2093 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2095 v3s16 blockpos = getNodeBlockPos(p);
2096 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2097 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2099 infostream<<"Map::setNodeTimer(): Need to emerge "
2100 <<PP(blockpos)<<std::endl;
2101 block = emergeBlock(blockpos, false);
2104 warningstream<<"Map::setNodeTimer(): Block not found"
2108 block->m_node_timers.set(p_rel, t);
2111 void Map::removeNodeTimer(v3s16 p)
2113 v3s16 blockpos = getNodeBlockPos(p);
2114 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2115 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2118 warningstream<<"Map::removeNodeTimer(): Block not found"
2122 block->m_node_timers.remove(p_rel);
2128 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2129 Map(dout_server, gamedef),
2131 m_map_metadata_changed(true)
2133 verbosestream<<FUNCTION_NAME<<std::endl;
2136 Try to load map; if not found, create a new one.
2139 // Determine which database backend to use
2140 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2142 bool succeeded = conf.readConfigFile(conf_path.c_str());
2143 if (!succeeded || !conf.exists("backend")) {
2144 // fall back to sqlite3
2145 conf.set("backend", "sqlite3");
2147 std::string backend = conf.get("backend");
2148 dbase = createDatabase(backend, savedir, conf);
2150 if (!conf.updateConfigFile(conf_path.c_str()))
2151 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
2153 m_savedir = savedir;
2154 m_map_saving_enabled = false;
2158 // If directory exists, check contents and load if possible
2159 if(fs::PathExists(m_savedir))
2161 // If directory is empty, it is safe to save into it.
2162 if(fs::GetDirListing(m_savedir).size() == 0)
2164 infostream<<"ServerMap: Empty save directory is valid."
2166 m_map_saving_enabled = true;
2171 // Load map metadata (seed, chunksize)
2174 catch(SettingNotFoundException &e){
2175 infostream<<"ServerMap: Some metadata not found."
2176 <<" Using default settings."<<std::endl;
2178 catch(FileNotGoodException &e){
2179 warningstream<<"Could not load map metadata"
2180 //<<" Disabling chunk-based generator."
2185 infostream<<"ServerMap: Successfully loaded map "
2186 <<"metadata from "<<savedir
2187 <<", assuming valid save directory."
2188 <<" seed="<< m_emerge->params.seed <<"."
2191 m_map_saving_enabled = true;
2192 // Map loaded, not creating new one
2196 // If directory doesn't exist, it is safe to save to it
2198 m_map_saving_enabled = true;
2201 catch(std::exception &e)
2203 warningstream<<"ServerMap: Failed to load map from "<<savedir
2204 <<", exception: "<<e.what()<<std::endl;
2205 infostream<<"Please remove the map or fix it."<<std::endl;
2206 warningstream<<"Map saving will be disabled."<<std::endl;
2209 infostream<<"Initializing new map."<<std::endl;
2211 // Create zero sector
2212 emergeSector(v2s16(0,0));
2214 // Initially write whole map
2215 save(MOD_STATE_CLEAN);
2218 ServerMap::~ServerMap()
2220 verbosestream<<FUNCTION_NAME<<std::endl;
2224 if(m_map_saving_enabled)
2226 // Save only changed parts
2227 save(MOD_STATE_WRITE_AT_UNLOAD);
2228 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2232 infostream<<"ServerMap: Map not saved"<<std::endl;
2235 catch(std::exception &e)
2237 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2238 <<", exception: "<<e.what()<<std::endl;
2242 Close database if it was opened
2250 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2251 for(; i.atEnd() == false; i++)
2253 MapChunk *chunk = i.getNode()->getValue();
2259 u64 ServerMap::getSeed()
2261 return m_emerge->params.seed;
2264 s16 ServerMap::getWaterLevel()
2266 return m_emerge->params.water_level;
2269 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
2271 s16 csize = m_emerge->params.chunksize;
2272 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
2273 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
2275 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
2276 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
2278 v3s16 extra_borders(1, 1, 1);
2279 v3s16 full_bpmin = bpmin - extra_borders;
2280 v3s16 full_bpmax = bpmax + extra_borders;
2282 // Do nothing if not inside limits (+-1 because of neighbors)
2283 if (blockpos_over_limit(full_bpmin) ||
2284 blockpos_over_limit(full_bpmax))
2287 data->seed = m_emerge->params.seed;
2288 data->blockpos_min = bpmin;
2289 data->blockpos_max = bpmax;
2290 data->blockpos_requested = blockpos;
2291 data->nodedef = m_gamedef->ndef();
2294 Create the whole area of this and the neighboring blocks
2296 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
2297 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
2298 v2s16 sectorpos(x, z);
2299 // Sector metadata is loaded from disk if not already loaded.
2300 ServerMapSector *sector = createSector(sectorpos);
2301 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
2303 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
2306 MapBlock *block = emergeBlock(p, false);
2307 if (block == NULL) {
2308 block = createBlock(p);
2310 // Block gets sunlight if this is true.
2311 // Refer to the map generator heuristics.
2312 bool ug = m_emerge->isBlockUnderground(p);
2313 block->setIsUnderground(ug);
2319 Now we have a big empty area.
2321 Make a ManualMapVoxelManipulator that contains this and the
2325 data->vmanip = new MMVManip(this);
2326 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
2328 // Note: we may need this again at some point.
2330 // Ensure none of the blocks to be generated were marked as
2331 // containing CONTENT_IGNORE
2332 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2333 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2334 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2335 core::map<v3s16, u8>::Node *n;
2336 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2339 u8 flags = n->getValue();
2340 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2347 // Data is ready now.
2351 void ServerMap::finishBlockMake(BlockMakeData *data,
2352 std::map<v3s16, MapBlock*> *changed_blocks)
2354 v3s16 bpmin = data->blockpos_min;
2355 v3s16 bpmax = data->blockpos_max;
2357 v3s16 extra_borders(1, 1, 1);
2358 v3s16 full_bpmin = bpmin - extra_borders;
2359 v3s16 full_bpmax = bpmax + extra_borders;
2361 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
2362 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
2365 Set lighting to non-expired state in all of them.
2366 This is cheating, but it is not fast enough if all of them
2367 would actually be updated.
2369 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
2370 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
2371 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
2372 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
2376 block->setLightingExpired(false);
2380 Blit generated stuff to map
2381 NOTE: blitBackAll adds nearly everything to changed_blocks
2383 data->vmanip->blitBackAll(changed_blocks);
2385 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
2386 << changed_blocks->size());
2389 Copy transforming liquid information
2391 while (data->transforming_liquid.size()) {
2392 m_transforming_liquid.push_back(data->transforming_liquid.front());
2393 data->transforming_liquid.pop_front();
2396 for (std::map<v3s16, MapBlock *>::iterator
2397 it = changed_blocks->begin();
2398 it != changed_blocks->end(); ++it) {
2399 MapBlock *block = it->second;
2403 Update day/night difference cache of the MapBlocks
2405 block->expireDayNightDiff();
2407 Set block as modified
2409 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2410 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2414 Set central blocks as generated
2416 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2417 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2418 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2419 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2423 block->setGenerated(true);
2427 Save changed parts of map
2428 NOTE: Will be saved later.
2430 //save(MOD_STATE_WRITE_AT_UNLOAD);
2433 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2435 DSTACKF("%s: p2d=(%d,%d)",
2440 Check if it exists already in memory
2442 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2447 Try to load it from disk (with blocks)
2449 //if(loadSectorFull(p2d) == true)
2452 Try to load metadata from disk
2455 if(loadSectorMeta(p2d) == true)
2457 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2460 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2461 throw InvalidPositionException("");
2467 Do not create over-limit
2469 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2470 g_settings->getU16("map_generation_limit"));
2471 if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2472 || p2d.X > map_gen_limit / MAP_BLOCKSIZE
2473 || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2474 || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
2475 throw InvalidPositionException("createSector(): pos. over limit");
2478 Generate blank sector
2481 sector = new ServerMapSector(this, p2d, m_gamedef);
2483 // Sector position on map in nodes
2484 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2489 m_sectors[p2d] = sector;
2496 This is a quick-hand function for calling makeBlock().
2498 MapBlock * ServerMap::generateBlock(
2500 std::map<v3s16, MapBlock*> &modified_blocks
2503 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2505 /*infostream<<"generateBlock(): "
2506 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2509 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2511 TimeTaker timer("generateBlock");
2513 //MapBlock *block = original_dummy;
2515 v2s16 p2d(p.X, p.Z);
2516 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2519 Do not generate over-limit
2521 if(blockpos_over_limit(p))
2523 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2524 throw InvalidPositionException("generateBlock(): pos. over limit");
2528 Create block make data
2531 initBlockMake(&data, p);
2537 TimeTaker t("mapgen::make_block()");
2538 mapgen->makeChunk(&data);
2539 //mapgen::make_block(&data);
2541 if(enable_mapgen_debug_info == false)
2542 t.stop(true); // Hide output
2546 Blit data back on map, update lighting, add mobs and whatever this does
2548 finishBlockMake(&data, modified_blocks);
2553 MapBlock *block = getBlockNoCreateNoEx(p);
2561 bool erroneus_content = false;
2562 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2563 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2564 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2567 MapNode n = block->getNode(p);
2568 if(n.getContent() == CONTENT_IGNORE)
2570 infostream<<"CONTENT_IGNORE at "
2571 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2573 erroneus_content = true;
2577 if(erroneus_content)
2586 Generate a completely empty block
2590 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2591 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2593 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2596 n.setContent(CONTENT_AIR);
2597 block->setNode(v3s16(x0,y0,z0), n);
2603 if(enable_mapgen_debug_info == false)
2604 timer.stop(true); // Hide output
2610 MapBlock * ServerMap::createBlock(v3s16 p)
2612 DSTACKF("%s: p=(%d,%d,%d)",
2613 FUNCTION_NAME, p.X, p.Y, p.Z);
2616 Do not create over-limit
2618 if (blockpos_over_limit(p))
2619 throw InvalidPositionException("createBlock(): pos. over limit");
2621 v2s16 p2d(p.X, p.Z);
2624 This will create or load a sector if not found in memory.
2625 If block exists on disk, it will be loaded.
2627 NOTE: On old save formats, this will be slow, as it generates
2628 lighting on blocks for them.
2630 ServerMapSector *sector;
2632 sector = (ServerMapSector*)createSector(p2d);
2633 assert(sector->getId() == MAPSECTOR_SERVER);
2635 catch(InvalidPositionException &e)
2637 infostream<<"createBlock: createSector() failed"<<std::endl;
2641 NOTE: This should not be done, or at least the exception
2642 should not be passed on as std::exception, because it
2643 won't be catched at all.
2645 /*catch(std::exception &e)
2647 infostream<<"createBlock: createSector() failed: "
2648 <<e.what()<<std::endl;
2653 Try to get a block from the sector
2656 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2659 if(block->isDummy())
2664 block = sector->createBlankBlock(block_y);
2669 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2671 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2673 p.X, p.Y, p.Z, create_blank);
2676 MapBlock *block = getBlockNoCreateNoEx(p);
2677 if(block && block->isDummy() == false)
2682 MapBlock *block = loadBlock(p);
2688 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2689 MapBlock *block = sector->createBlankBlock(p.Y);
2697 std::map<v3s16, MapBlock*> modified_blocks;
2698 MapBlock *block = generateBlock(p, modified_blocks);
2702 event.type = MEET_OTHER;
2705 // Copy modified_blocks to event
2706 for(std::map<v3s16, MapBlock*>::iterator
2707 i = modified_blocks.begin();
2708 i != modified_blocks.end(); ++i)
2710 event.modified_blocks.insert(i->first);
2714 dispatchEvent(&event);
2724 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2726 MapBlock *block = getBlockNoCreateNoEx(p3d);
2728 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2733 void ServerMap::prepareBlock(MapBlock *block) {
2736 // N.B. This requires no synchronization, since data will not be modified unless
2737 // the VoxelManipulator being updated belongs to the same thread.
2738 void ServerMap::updateVManip(v3s16 pos)
2740 Mapgen *mg = m_emerge->getCurrentMapgen();
2744 MMVManip *vm = mg->vm;
2748 if (!vm->m_area.contains(pos))
2751 s32 idx = vm->m_area.index(pos);
2752 vm->m_data[idx] = getNodeNoEx(pos);
2753 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2755 vm->m_is_dirty = true;
2758 s16 ServerMap::findGroundLevel(v2s16 p2d)
2762 Uh, just do something random...
2764 // Find existing map from top to down
2767 v3s16 p(p2d.X, max, p2d.Y);
2768 for(; p.Y>min; p.Y--)
2770 MapNode n = getNodeNoEx(p);
2771 if(n.getContent() != CONTENT_IGNORE)
2776 // If this node is not air, go to plan b
2777 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2779 // Search existing walkable and return it
2780 for(; p.Y>min; p.Y--)
2782 MapNode n = getNodeNoEx(p);
2783 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2792 Determine from map generator noise functions
2795 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2798 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2799 //return (s16)level;
2802 bool ServerMap::loadFromFolders() {
2803 if (!dbase->initialized() &&
2804 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2809 void ServerMap::createDirs(std::string path)
2811 if(fs::CreateAllDirs(path) == false)
2813 m_dout<<"ServerMap: Failed to create directory "
2814 <<"\""<<path<<"\""<<std::endl;
2815 throw BaseException("ServerMap failed to create directory");
2819 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2825 snprintf(cc, 9, "%.4x%.4x",
2826 (unsigned int) pos.X & 0xffff,
2827 (unsigned int) pos.Y & 0xffff);
2829 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2831 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2832 (unsigned int) pos.X & 0xfff,
2833 (unsigned int) pos.Y & 0xfff);
2835 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2842 v2s16 ServerMap::getSectorPos(std::string dirname)
2844 unsigned int x = 0, y = 0;
2846 std::string component;
2847 fs::RemoveLastPathComponent(dirname, &component, 1);
2848 if(component.size() == 8)
2851 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2853 else if(component.size() == 3)
2856 fs::RemoveLastPathComponent(dirname, &component, 2);
2857 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2858 // Sign-extend the 12 bit values up to 16 bits...
2859 if(x & 0x800) x |= 0xF000;
2860 if(y & 0x800) y |= 0xF000;
2867 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2868 v2s16 pos((s16)x, (s16)y);
2872 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2874 v2s16 p2d = getSectorPos(sectordir);
2876 if(blockfile.size() != 4){
2877 throw InvalidFilenameException("Invalid block filename");
2880 int r = sscanf(blockfile.c_str(), "%4x", &y);
2882 throw InvalidFilenameException("Invalid block filename");
2883 return v3s16(p2d.X, y, p2d.Y);
2886 std::string ServerMap::getBlockFilename(v3s16 p)
2889 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2893 void ServerMap::save(ModifiedState save_level)
2895 DSTACK(FUNCTION_NAME);
2896 if(m_map_saving_enabled == false) {
2897 warningstream<<"Not saving map, saving disabled."<<std::endl;
2901 if(save_level == MOD_STATE_CLEAN)
2902 infostream<<"ServerMap: Saving whole map, this can take time."
2905 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2909 // Profile modified reasons
2910 Profiler modprofiler;
2912 u32 sector_meta_count = 0;
2913 u32 block_count = 0;
2914 u32 block_count_all = 0; // Number of blocks in memory
2916 // Don't do anything with sqlite unless something is really saved
2917 bool save_started = false;
2919 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2920 i != m_sectors.end(); ++i) {
2921 ServerMapSector *sector = (ServerMapSector*)i->second;
2922 assert(sector->getId() == MAPSECTOR_SERVER);
2924 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2925 saveSectorMeta(sector);
2926 sector_meta_count++;
2929 MapBlockVect blocks;
2930 sector->getBlocks(blocks);
2932 for(MapBlockVect::iterator j = blocks.begin();
2933 j != blocks.end(); ++j) {
2934 MapBlock *block = *j;
2938 if(block->getModified() >= (u32)save_level) {
2942 save_started = true;
2945 modprofiler.add(block->getModifiedReasonString(), 1);
2950 /*infostream<<"ServerMap: Written block ("
2951 <<block->getPos().X<<","
2952 <<block->getPos().Y<<","
2953 <<block->getPos().Z<<")"
2963 Only print if something happened or saved whole map
2965 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2966 || block_count != 0) {
2967 infostream<<"ServerMap: Written: "
2968 <<sector_meta_count<<" sector metadata files, "
2969 <<block_count<<" block files"
2970 <<", "<<block_count_all<<" blocks in memory."
2972 PrintInfo(infostream); // ServerMap/ClientMap:
2973 infostream<<"Blocks modified by: "<<std::endl;
2974 modprofiler.print(infostream);
2978 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2980 if (loadFromFolders()) {
2981 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2982 << "all blocks that are stored in flat files." << std::endl;
2984 dbase->listAllLoadableBlocks(dst);
2987 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2989 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2990 si != m_sectors.end(); ++si)
2992 MapSector *sector = si->second;
2994 MapBlockVect blocks;
2995 sector->getBlocks(blocks);
2997 for(MapBlockVect::iterator i = blocks.begin();
2998 i != blocks.end(); ++i) {
2999 v3s16 p = (*i)->getPos();
3005 void ServerMap::saveMapMeta()
3007 DSTACK(FUNCTION_NAME);
3009 createDirs(m_savedir);
3011 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3012 std::ostringstream oss(std::ios_base::binary);
3015 m_emerge->params.save(conf);
3016 conf.writeLines(oss);
3018 oss << "[end_of_params]\n";
3020 if(!fs::safeWriteToFile(fullpath, oss.str())) {
3021 errorstream << "ServerMap::saveMapMeta(): "
3022 << "could not write " << fullpath << std::endl;
3023 throw FileNotGoodException("Cannot save chunk metadata");
3026 m_map_metadata_changed = false;
3029 void ServerMap::loadMapMeta()
3031 DSTACK(FUNCTION_NAME);
3034 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3036 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3038 errorstream << "ServerMap::loadMapMeta(): "
3039 "could not open " << fullpath << std::endl;
3040 throw FileNotGoodException("Cannot open map metadata");
3043 if (!conf.parseConfigLines(is, "[end_of_params]")) {
3044 throw SerializationError("ServerMap::loadMapMeta(): "
3045 "[end_of_params] not found!");
3048 m_emerge->params.load(conf);
3050 verbosestream << "ServerMap::loadMapMeta(): seed="
3051 << m_emerge->params.seed << std::endl;
3054 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3056 DSTACK(FUNCTION_NAME);
3057 // Format used for writing
3058 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3060 v2s16 pos = sector->getPos();
3061 std::string dir = getSectorDir(pos);
3064 std::string fullpath = dir + DIR_DELIM + "meta";
3065 std::ostringstream ss(std::ios_base::binary);
3067 sector->serialize(ss, version);
3069 if(!fs::safeWriteToFile(fullpath, ss.str()))
3070 throw FileNotGoodException("Cannot write sector metafile");
3072 sector->differs_from_disk = false;
3075 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3077 DSTACK(FUNCTION_NAME);
3079 v2s16 p2d = getSectorPos(sectordir);
3081 ServerMapSector *sector = NULL;
3083 std::string fullpath = sectordir + DIR_DELIM + "meta";
3084 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3085 if(is.good() == false)
3087 // If the directory exists anyway, it probably is in some old
3088 // format. Just go ahead and create the sector.
3089 if(fs::PathExists(sectordir))
3091 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3092 <<fullpath<<" doesn't exist but directory does."
3093 <<" Continuing with a sector with no metadata."
3095 sector = new ServerMapSector(this, p2d, m_gamedef);
3096 m_sectors[p2d] = sector;
3100 throw FileNotGoodException("Cannot open sector metafile");
3105 sector = ServerMapSector::deSerialize
3106 (is, this, p2d, m_sectors, m_gamedef);
3108 saveSectorMeta(sector);
3111 sector->differs_from_disk = false;
3116 bool ServerMap::loadSectorMeta(v2s16 p2d)
3118 DSTACK(FUNCTION_NAME);
3120 // The directory layout we're going to load from.
3121 // 1 - original sectors/xxxxzzzz/
3122 // 2 - new sectors2/xxx/zzz/
3123 // If we load from anything but the latest structure, we will
3124 // immediately save to the new one, and remove the old.
3126 std::string sectordir1 = getSectorDir(p2d, 1);
3127 std::string sectordir;
3128 if(fs::PathExists(sectordir1))
3130 sectordir = sectordir1;
3135 sectordir = getSectorDir(p2d, 2);
3139 loadSectorMeta(sectordir, loadlayout != 2);
3141 catch(InvalidFilenameException &e)
3145 catch(FileNotGoodException &e)
3149 catch(std::exception &e)
3158 bool ServerMap::loadSectorFull(v2s16 p2d)
3160 DSTACK(FUNCTION_NAME);
3162 MapSector *sector = NULL;
3164 // The directory layout we're going to load from.
3165 // 1 - original sectors/xxxxzzzz/
3166 // 2 - new sectors2/xxx/zzz/
3167 // If we load from anything but the latest structure, we will
3168 // immediately save to the new one, and remove the old.
3170 std::string sectordir1 = getSectorDir(p2d, 1);
3171 std::string sectordir;
3172 if(fs::PathExists(sectordir1))
3174 sectordir = sectordir1;
3179 sectordir = getSectorDir(p2d, 2);
3183 sector = loadSectorMeta(sectordir, loadlayout != 2);
3185 catch(InvalidFilenameException &e)
3189 catch(FileNotGoodException &e)
3193 catch(std::exception &e)
3201 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3203 std::vector<fs::DirListNode>::iterator i2;
3204 for(i2=list2.begin(); i2!=list2.end(); i2++)
3210 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3212 catch(InvalidFilenameException &e)
3214 // This catches unknown crap in directory
3220 infostream<<"Sector converted to new layout - deleting "<<
3221 sectordir1<<std::endl;
3222 fs::RecursiveDelete(sectordir1);
3229 Database *ServerMap::createDatabase(
3230 const std::string &name,
3231 const std::string &savedir,
3234 if (name == "sqlite3")
3235 return new Database_SQLite3(savedir);
3236 if (name == "dummy")
3237 return new Database_Dummy();
3239 else if (name == "leveldb")
3240 return new Database_LevelDB(savedir);
3243 else if (name == "redis")
3244 return new Database_Redis(conf);
3247 else if (name == "postgresql")
3248 return new Database_PostgreSQL(conf);
3251 throw BaseException(std::string("Database backend ") + name + " not supported.");
3254 void ServerMap::beginSave()
3259 void ServerMap::endSave()
3264 bool ServerMap::saveBlock(MapBlock *block)
3266 return saveBlock(block, dbase);
3269 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3271 v3s16 p3d = block->getPos();
3273 // Dummy blocks are not written
3274 if (block->isDummy()) {
3275 warningstream << "saveBlock: Not writing dummy block "
3276 << PP(p3d) << std::endl;
3280 // Format used for writing
3281 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3284 [0] u8 serialization version
3287 std::ostringstream o(std::ios_base::binary);
3288 o.write((char*) &version, 1);
3289 block->serialize(o, version, true);
3291 std::string data = o.str();
3292 bool ret = db->saveBlock(p3d, data);
3294 // We just wrote it to the disk so clear modified flag
3295 block->resetModified();
3300 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3301 MapSector *sector, bool save_after_load)
3303 DSTACK(FUNCTION_NAME);
3305 std::string fullpath = sectordir + DIR_DELIM + blockfile;
3308 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3309 if(is.good() == false)
3310 throw FileNotGoodException("Cannot open block file");
3312 v3s16 p3d = getBlockPos(sectordir, blockfile);
3313 v2s16 p2d(p3d.X, p3d.Z);
3315 assert(sector->getPos() == p2d);
3317 u8 version = SER_FMT_VER_INVALID;
3318 is.read((char*)&version, 1);
3321 throw SerializationError("ServerMap::loadBlock(): Failed"
3322 " to read MapBlock version");
3324 /*u32 block_size = MapBlock::serializedLength(version);
3325 SharedBuffer<u8> data(block_size);
3326 is.read((char*)*data, block_size);*/
3328 // This will always return a sector because we're the server
3329 //MapSector *sector = emergeSector(p2d);
3331 MapBlock *block = NULL;
3332 bool created_new = false;
3333 block = sector->getBlockNoCreateNoEx(p3d.Y);
3336 block = sector->createBlankBlockNoInsert(p3d.Y);
3341 block->deSerialize(is, version, true);
3343 // If it's a new block, insert it to the map
3345 sector->insertBlock(block);
3348 Save blocks loaded in old format in new format
3351 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3355 // Should be in database now, so delete the old file
3356 fs::RecursiveDelete(fullpath);
3359 // We just loaded it from the disk, so it's up-to-date.
3360 block->resetModified();
3363 catch(SerializationError &e)
3365 warningstream<<"Invalid block data on disk "
3366 <<"fullpath="<<fullpath
3367 <<" (SerializationError). "
3368 <<"what()="<<e.what()
3370 // Ignoring. A new one will be generated.
3373 // TODO: Backup file; name is in fullpath.
3377 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3379 DSTACK(FUNCTION_NAME);
3382 std::istringstream is(*blob, std::ios_base::binary);
3384 u8 version = SER_FMT_VER_INVALID;
3385 is.read((char*)&version, 1);
3388 throw SerializationError("ServerMap::loadBlock(): Failed"
3389 " to read MapBlock version");
3391 /*u32 block_size = MapBlock::serializedLength(version);
3392 SharedBuffer<u8> data(block_size);
3393 is.read((char*)*data, block_size);*/
3395 // This will always return a sector because we're the server
3396 //MapSector *sector = emergeSector(p2d);
3398 MapBlock *block = NULL;
3399 bool created_new = false;
3400 block = sector->getBlockNoCreateNoEx(p3d.Y);
3403 block = sector->createBlankBlockNoInsert(p3d.Y);
3408 block->deSerialize(is, version, true);
3410 // If it's a new block, insert it to the map
3412 sector->insertBlock(block);
3415 Save blocks loaded in old format in new format
3418 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3419 // Only save if asked to; no need to update version
3423 // We just loaded it from, so it's up-to-date.
3424 block->resetModified();
3427 catch(SerializationError &e)
3429 errorstream<<"Invalid block data in database"
3430 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3431 <<" (SerializationError): "<<e.what()<<std::endl;
3433 // TODO: Block should be marked as invalid in memory so that it is
3434 // not touched but the game can run
3436 if(g_settings->getBool("ignore_world_load_errors")){
3437 errorstream<<"Ignoring block load error. Duck and cover! "
3438 <<"(ignore_world_load_errors)"<<std::endl;
3440 throw SerializationError("Invalid block data in database");
3445 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3447 DSTACK(FUNCTION_NAME);
3449 v2s16 p2d(blockpos.X, blockpos.Z);
3452 dbase->loadBlock(blockpos, &ret);
3454 loadBlock(&ret, blockpos, createSector(p2d), false);
3455 return getBlockNoCreateNoEx(blockpos);
3457 // Not found in database, try the files
3459 // The directory layout we're going to load from.
3460 // 1 - original sectors/xxxxzzzz/
3461 // 2 - new sectors2/xxx/zzz/
3462 // If we load from anything but the latest structure, we will
3463 // immediately save to the new one, and remove the old.
3465 std::string sectordir1 = getSectorDir(p2d, 1);
3466 std::string sectordir;
3467 if(fs::PathExists(sectordir1))
3469 sectordir = sectordir1;
3474 sectordir = getSectorDir(p2d, 2);
3478 Make sure sector is loaded
3481 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3485 sector = loadSectorMeta(sectordir, loadlayout != 2);
3487 catch(InvalidFilenameException &e)
3491 catch(FileNotGoodException &e)
3495 catch(std::exception &e)
3502 Make sure file exists
3505 std::string blockfilename = getBlockFilename(blockpos);
3506 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3510 Load block and save it to the database
3512 loadBlock(sectordir, blockfilename, sector, true);
3513 return getBlockNoCreateNoEx(blockpos);
3516 bool ServerMap::deleteBlock(v3s16 blockpos)
3518 if (!dbase->deleteBlock(blockpos))
3521 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3523 v2s16 p2d(blockpos.X, blockpos.Z);
3524 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3527 sector->deleteBlock(block);
3533 void ServerMap::PrintInfo(std::ostream &out)
3538 MMVManip::MMVManip(Map *map):
3541 m_create_area(false),
3546 MMVManip::~MMVManip()
3550 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3551 bool load_if_inexistent)
3553 TimeTaker timer1("initialEmerge", &emerge_time);
3555 // Units of these are MapBlocks
3556 v3s16 p_min = blockpos_min;
3557 v3s16 p_max = blockpos_max;
3559 VoxelArea block_area_nodes
3560 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3562 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3565 infostream<<"initialEmerge: area: ";
3566 block_area_nodes.print(infostream);
3567 infostream<<" ("<<size_MB<<"MB)";
3568 infostream<<std::endl;
3571 addArea(block_area_nodes);
3573 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3574 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3575 for(s32 x=p_min.X; x<=p_max.X; x++)
3580 std::map<v3s16, u8>::iterator n;
3581 n = m_loaded_blocks.find(p);
3582 if(n != m_loaded_blocks.end())
3585 bool block_data_inexistent = false;
3588 TimeTaker timer1("emerge load", &emerge_load_time);
3590 block = m_map->getBlockNoCreate(p);
3591 if(block->isDummy())
3592 block_data_inexistent = true;
3594 block->copyTo(*this);
3596 catch(InvalidPositionException &e)
3598 block_data_inexistent = true;
3601 if(block_data_inexistent)
3604 if (load_if_inexistent) {
3605 ServerMap *svrmap = (ServerMap *)m_map;
3606 block = svrmap->emergeBlock(p, false);
3608 block = svrmap->createBlock(p);
3609 block->copyTo(*this);
3611 flags |= VMANIP_BLOCK_DATA_INEXIST;
3614 Mark area inexistent
3616 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3617 // Fill with VOXELFLAG_NO_DATA
3618 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3619 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3621 s32 i = m_area.index(a.MinEdge.X,y,z);
3622 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3626 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3628 // Mark that block was loaded as blank
3629 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3632 m_loaded_blocks[p] = flags;
3638 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3639 bool overwrite_generated)
3641 if(m_area.getExtent() == v3s16(0,0,0))
3645 Copy data of all blocks
3647 for(std::map<v3s16, u8>::iterator
3648 i = m_loaded_blocks.begin();
3649 i != m_loaded_blocks.end(); ++i)
3652 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3653 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3654 if ((existed == false) || (block == NULL) ||
3655 (overwrite_generated == false && block->isGenerated() == true))
3658 block->copyFrom(*this);
3661 (*modified_blocks)[p] = block;