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);
2090 NodeTimer nt(t.timeout, t.elapsed, p);
2094 void Map::setNodeTimer(const NodeTimer &t)
2096 v3s16 p = t.position;
2097 v3s16 blockpos = getNodeBlockPos(p);
2098 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2099 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2101 infostream<<"Map::setNodeTimer(): Need to emerge "
2102 <<PP(blockpos)<<std::endl;
2103 block = emergeBlock(blockpos, false);
2106 warningstream<<"Map::setNodeTimer(): Block not found"
2110 NodeTimer nt(t.timeout, t.elapsed, p_rel);
2111 block->m_node_timers.set(nt);
2114 void Map::removeNodeTimer(v3s16 p)
2116 v3s16 blockpos = getNodeBlockPos(p);
2117 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2118 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2121 warningstream<<"Map::removeNodeTimer(): Block not found"
2125 block->m_node_timers.remove(p_rel);
2131 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2132 Map(dout_server, gamedef),
2133 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
2135 m_map_metadata_changed(true)
2137 verbosestream<<FUNCTION_NAME<<std::endl;
2139 // Tell the EmergeManager about our MapSettingsManager
2140 emerge->map_settings_mgr = &settings_mgr;
2143 Try to load map; if not found, create a new one.
2146 // Determine which database backend to use
2147 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2149 bool succeeded = conf.readConfigFile(conf_path.c_str());
2150 if (!succeeded || !conf.exists("backend")) {
2151 // fall back to sqlite3
2152 conf.set("backend", "sqlite3");
2154 std::string backend = conf.get("backend");
2155 dbase = createDatabase(backend, savedir, conf);
2157 if (!conf.updateConfigFile(conf_path.c_str()))
2158 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
2160 m_savedir = savedir;
2161 m_map_saving_enabled = false;
2165 // If directory exists, check contents and load if possible
2166 if(fs::PathExists(m_savedir))
2168 // If directory is empty, it is safe to save into it.
2169 if(fs::GetDirListing(m_savedir).size() == 0)
2171 infostream<<"ServerMap: Empty save directory is valid."
2173 m_map_saving_enabled = true;
2178 if (settings_mgr.loadMapMeta()) {
2179 infostream << "ServerMap: Metadata loaded from "
2180 << savedir << std::endl;
2182 infostream << "ServerMap: Metadata could not be loaded "
2183 "from " << savedir << ", assuming valid save "
2184 "directory." << std::endl;
2187 m_map_saving_enabled = true;
2188 // Map loaded, not creating new one
2192 // If directory doesn't exist, it is safe to save to it
2194 m_map_saving_enabled = true;
2197 catch(std::exception &e)
2199 warningstream<<"ServerMap: Failed to load map from "<<savedir
2200 <<", exception: "<<e.what()<<std::endl;
2201 infostream<<"Please remove the map or fix it."<<std::endl;
2202 warningstream<<"Map saving will be disabled."<<std::endl;
2205 infostream<<"Initializing new map."<<std::endl;
2207 // Create zero sector
2208 emergeSector(v2s16(0,0));
2210 // Initially write whole map
2211 save(MOD_STATE_CLEAN);
2214 ServerMap::~ServerMap()
2216 verbosestream<<FUNCTION_NAME<<std::endl;
2220 if(m_map_saving_enabled)
2222 // Save only changed parts
2223 save(MOD_STATE_WRITE_AT_UNLOAD);
2224 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2228 infostream<<"ServerMap: Map not saved"<<std::endl;
2231 catch(std::exception &e)
2233 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2234 <<", exception: "<<e.what()<<std::endl;
2238 Close database if it was opened
2246 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2247 for(; i.atEnd() == false; i++)
2249 MapChunk *chunk = i.getNode()->getValue();
2255 MapgenParams *ServerMap::getMapgenParams()
2257 // getMapgenParams() should only ever be called after Server is initialized
2258 assert(settings_mgr.mapgen_params != NULL);
2259 return settings_mgr.mapgen_params;
2262 u64 ServerMap::getSeed()
2264 return getMapgenParams()->seed;
2267 s16 ServerMap::getWaterLevel()
2269 return getMapgenParams()->water_level;
2272 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
2274 s16 csize = getMapgenParams()->chunksize;
2275 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
2276 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
2278 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
2279 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
2281 v3s16 extra_borders(1, 1, 1);
2282 v3s16 full_bpmin = bpmin - extra_borders;
2283 v3s16 full_bpmax = bpmax + extra_borders;
2285 // Do nothing if not inside limits (+-1 because of neighbors)
2286 if (blockpos_over_limit(full_bpmin) ||
2287 blockpos_over_limit(full_bpmax))
2290 data->seed = getSeed();
2291 data->blockpos_min = bpmin;
2292 data->blockpos_max = bpmax;
2293 data->blockpos_requested = blockpos;
2294 data->nodedef = m_gamedef->ndef();
2297 Create the whole area of this and the neighboring blocks
2299 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
2300 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
2301 v2s16 sectorpos(x, z);
2302 // Sector metadata is loaded from disk if not already loaded.
2303 ServerMapSector *sector = createSector(sectorpos);
2304 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
2306 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
2309 MapBlock *block = emergeBlock(p, false);
2310 if (block == NULL) {
2311 block = createBlock(p);
2313 // Block gets sunlight if this is true.
2314 // Refer to the map generator heuristics.
2315 bool ug = m_emerge->isBlockUnderground(p);
2316 block->setIsUnderground(ug);
2322 Now we have a big empty area.
2324 Make a ManualMapVoxelManipulator that contains this and the
2328 data->vmanip = new MMVManip(this);
2329 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
2331 // Note: we may need this again at some point.
2333 // Ensure none of the blocks to be generated were marked as
2334 // containing CONTENT_IGNORE
2335 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2336 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2337 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2338 core::map<v3s16, u8>::Node *n;
2339 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2342 u8 flags = n->getValue();
2343 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2350 // Data is ready now.
2354 void ServerMap::finishBlockMake(BlockMakeData *data,
2355 std::map<v3s16, MapBlock*> *changed_blocks)
2357 v3s16 bpmin = data->blockpos_min;
2358 v3s16 bpmax = data->blockpos_max;
2360 v3s16 extra_borders(1, 1, 1);
2361 v3s16 full_bpmin = bpmin - extra_borders;
2362 v3s16 full_bpmax = bpmax + extra_borders;
2364 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
2365 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
2368 Set lighting to non-expired state in all of them.
2369 This is cheating, but it is not fast enough if all of them
2370 would actually be updated.
2372 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
2373 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
2374 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
2375 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
2379 block->setLightingExpired(false);
2383 Blit generated stuff to map
2384 NOTE: blitBackAll adds nearly everything to changed_blocks
2386 data->vmanip->blitBackAll(changed_blocks);
2388 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
2389 << changed_blocks->size());
2392 Copy transforming liquid information
2394 while (data->transforming_liquid.size()) {
2395 m_transforming_liquid.push_back(data->transforming_liquid.front());
2396 data->transforming_liquid.pop_front();
2399 for (std::map<v3s16, MapBlock *>::iterator
2400 it = changed_blocks->begin();
2401 it != changed_blocks->end(); ++it) {
2402 MapBlock *block = it->second;
2406 Update day/night difference cache of the MapBlocks
2408 block->expireDayNightDiff();
2410 Set block as modified
2412 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2413 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2417 Set central blocks as generated
2419 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2420 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2421 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2422 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2426 block->setGenerated(true);
2430 Save changed parts of map
2431 NOTE: Will be saved later.
2433 //save(MOD_STATE_WRITE_AT_UNLOAD);
2436 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2438 DSTACKF("%s: p2d=(%d,%d)",
2443 Check if it exists already in memory
2445 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2450 Try to load it from disk (with blocks)
2452 //if(loadSectorFull(p2d) == true)
2455 Try to load metadata from disk
2458 if(loadSectorMeta(p2d) == true)
2460 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2463 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2464 throw InvalidPositionException("");
2470 Do not create over-limit
2472 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2473 g_settings->getU16("map_generation_limit"));
2474 if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2475 || p2d.X > map_gen_limit / MAP_BLOCKSIZE
2476 || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2477 || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
2478 throw InvalidPositionException("createSector(): pos. over limit");
2481 Generate blank sector
2484 sector = new ServerMapSector(this, p2d, m_gamedef);
2486 // Sector position on map in nodes
2487 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2492 m_sectors[p2d] = sector;
2499 This is a quick-hand function for calling makeBlock().
2501 MapBlock * ServerMap::generateBlock(
2503 std::map<v3s16, MapBlock*> &modified_blocks
2506 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2508 /*infostream<<"generateBlock(): "
2509 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2512 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2514 TimeTaker timer("generateBlock");
2516 //MapBlock *block = original_dummy;
2518 v2s16 p2d(p.X, p.Z);
2519 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2522 Do not generate over-limit
2524 if(blockpos_over_limit(p))
2526 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2527 throw InvalidPositionException("generateBlock(): pos. over limit");
2531 Create block make data
2534 initBlockMake(&data, p);
2540 TimeTaker t("mapgen::make_block()");
2541 mapgen->makeChunk(&data);
2542 //mapgen::make_block(&data);
2544 if(enable_mapgen_debug_info == false)
2545 t.stop(true); // Hide output
2549 Blit data back on map, update lighting, add mobs and whatever this does
2551 finishBlockMake(&data, modified_blocks);
2556 MapBlock *block = getBlockNoCreateNoEx(p);
2564 bool erroneus_content = false;
2565 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2566 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2567 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2570 MapNode n = block->getNode(p);
2571 if(n.getContent() == CONTENT_IGNORE)
2573 infostream<<"CONTENT_IGNORE at "
2574 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2576 erroneus_content = true;
2580 if(erroneus_content)
2589 Generate a completely empty block
2593 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2594 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2596 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2599 n.setContent(CONTENT_AIR);
2600 block->setNode(v3s16(x0,y0,z0), n);
2606 if(enable_mapgen_debug_info == false)
2607 timer.stop(true); // Hide output
2613 MapBlock * ServerMap::createBlock(v3s16 p)
2615 DSTACKF("%s: p=(%d,%d,%d)",
2616 FUNCTION_NAME, p.X, p.Y, p.Z);
2619 Do not create over-limit
2621 if (blockpos_over_limit(p))
2622 throw InvalidPositionException("createBlock(): pos. over limit");
2624 v2s16 p2d(p.X, p.Z);
2627 This will create or load a sector if not found in memory.
2628 If block exists on disk, it will be loaded.
2630 NOTE: On old save formats, this will be slow, as it generates
2631 lighting on blocks for them.
2633 ServerMapSector *sector;
2635 sector = (ServerMapSector*)createSector(p2d);
2636 assert(sector->getId() == MAPSECTOR_SERVER);
2638 catch(InvalidPositionException &e)
2640 infostream<<"createBlock: createSector() failed"<<std::endl;
2644 NOTE: This should not be done, or at least the exception
2645 should not be passed on as std::exception, because it
2646 won't be catched at all.
2648 /*catch(std::exception &e)
2650 infostream<<"createBlock: createSector() failed: "
2651 <<e.what()<<std::endl;
2656 Try to get a block from the sector
2659 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2662 if(block->isDummy())
2667 block = sector->createBlankBlock(block_y);
2672 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2674 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2676 p.X, p.Y, p.Z, create_blank);
2679 MapBlock *block = getBlockNoCreateNoEx(p);
2680 if(block && block->isDummy() == false)
2685 MapBlock *block = loadBlock(p);
2691 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2692 MapBlock *block = sector->createBlankBlock(p.Y);
2700 std::map<v3s16, MapBlock*> modified_blocks;
2701 MapBlock *block = generateBlock(p, modified_blocks);
2705 event.type = MEET_OTHER;
2708 // Copy modified_blocks to event
2709 for(std::map<v3s16, MapBlock*>::iterator
2710 i = modified_blocks.begin();
2711 i != modified_blocks.end(); ++i)
2713 event.modified_blocks.insert(i->first);
2717 dispatchEvent(&event);
2727 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2729 MapBlock *block = getBlockNoCreateNoEx(p3d);
2731 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2736 void ServerMap::prepareBlock(MapBlock *block) {
2739 // N.B. This requires no synchronization, since data will not be modified unless
2740 // the VoxelManipulator being updated belongs to the same thread.
2741 void ServerMap::updateVManip(v3s16 pos)
2743 Mapgen *mg = m_emerge->getCurrentMapgen();
2747 MMVManip *vm = mg->vm;
2751 if (!vm->m_area.contains(pos))
2754 s32 idx = vm->m_area.index(pos);
2755 vm->m_data[idx] = getNodeNoEx(pos);
2756 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2758 vm->m_is_dirty = true;
2761 s16 ServerMap::findGroundLevel(v2s16 p2d)
2765 Uh, just do something random...
2767 // Find existing map from top to down
2770 v3s16 p(p2d.X, max, p2d.Y);
2771 for(; p.Y>min; p.Y--)
2773 MapNode n = getNodeNoEx(p);
2774 if(n.getContent() != CONTENT_IGNORE)
2779 // If this node is not air, go to plan b
2780 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2782 // Search existing walkable and return it
2783 for(; p.Y>min; p.Y--)
2785 MapNode n = getNodeNoEx(p);
2786 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2795 Determine from map generator noise functions
2798 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2801 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2802 //return (s16)level;
2805 bool ServerMap::loadFromFolders() {
2806 if (!dbase->initialized() &&
2807 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2812 void ServerMap::createDirs(std::string path)
2814 if(fs::CreateAllDirs(path) == false)
2816 m_dout<<"ServerMap: Failed to create directory "
2817 <<"\""<<path<<"\""<<std::endl;
2818 throw BaseException("ServerMap failed to create directory");
2822 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2828 snprintf(cc, 9, "%.4x%.4x",
2829 (unsigned int) pos.X & 0xffff,
2830 (unsigned int) pos.Y & 0xffff);
2832 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2834 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2835 (unsigned int) pos.X & 0xfff,
2836 (unsigned int) pos.Y & 0xfff);
2838 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2845 v2s16 ServerMap::getSectorPos(std::string dirname)
2847 unsigned int x = 0, y = 0;
2849 std::string component;
2850 fs::RemoveLastPathComponent(dirname, &component, 1);
2851 if(component.size() == 8)
2854 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2856 else if(component.size() == 3)
2859 fs::RemoveLastPathComponent(dirname, &component, 2);
2860 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2861 // Sign-extend the 12 bit values up to 16 bits...
2862 if(x & 0x800) x |= 0xF000;
2863 if(y & 0x800) y |= 0xF000;
2870 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2871 v2s16 pos((s16)x, (s16)y);
2875 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2877 v2s16 p2d = getSectorPos(sectordir);
2879 if(blockfile.size() != 4){
2880 throw InvalidFilenameException("Invalid block filename");
2883 int r = sscanf(blockfile.c_str(), "%4x", &y);
2885 throw InvalidFilenameException("Invalid block filename");
2886 return v3s16(p2d.X, y, p2d.Y);
2889 std::string ServerMap::getBlockFilename(v3s16 p)
2892 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2896 void ServerMap::save(ModifiedState save_level)
2898 DSTACK(FUNCTION_NAME);
2899 if(m_map_saving_enabled == false) {
2900 warningstream<<"Not saving map, saving disabled."<<std::endl;
2904 if(save_level == MOD_STATE_CLEAN)
2905 infostream<<"ServerMap: Saving whole map, this can take time."
2908 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2909 if (settings_mgr.saveMapMeta())
2910 m_map_metadata_changed = false;
2913 // Profile modified reasons
2914 Profiler modprofiler;
2916 u32 sector_meta_count = 0;
2917 u32 block_count = 0;
2918 u32 block_count_all = 0; // Number of blocks in memory
2920 // Don't do anything with sqlite unless something is really saved
2921 bool save_started = false;
2923 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2924 i != m_sectors.end(); ++i) {
2925 ServerMapSector *sector = (ServerMapSector*)i->second;
2926 assert(sector->getId() == MAPSECTOR_SERVER);
2928 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2929 saveSectorMeta(sector);
2930 sector_meta_count++;
2933 MapBlockVect blocks;
2934 sector->getBlocks(blocks);
2936 for(MapBlockVect::iterator j = blocks.begin();
2937 j != blocks.end(); ++j) {
2938 MapBlock *block = *j;
2942 if(block->getModified() >= (u32)save_level) {
2946 save_started = true;
2949 modprofiler.add(block->getModifiedReasonString(), 1);
2954 /*infostream<<"ServerMap: Written block ("
2955 <<block->getPos().X<<","
2956 <<block->getPos().Y<<","
2957 <<block->getPos().Z<<")"
2967 Only print if something happened or saved whole map
2969 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2970 || block_count != 0) {
2971 infostream<<"ServerMap: Written: "
2972 <<sector_meta_count<<" sector metadata files, "
2973 <<block_count<<" block files"
2974 <<", "<<block_count_all<<" blocks in memory."
2976 PrintInfo(infostream); // ServerMap/ClientMap:
2977 infostream<<"Blocks modified by: "<<std::endl;
2978 modprofiler.print(infostream);
2982 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2984 if (loadFromFolders()) {
2985 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2986 << "all blocks that are stored in flat files." << std::endl;
2988 dbase->listAllLoadableBlocks(dst);
2991 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2993 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2994 si != m_sectors.end(); ++si)
2996 MapSector *sector = si->second;
2998 MapBlockVect blocks;
2999 sector->getBlocks(blocks);
3001 for(MapBlockVect::iterator i = blocks.begin();
3002 i != blocks.end(); ++i) {
3003 v3s16 p = (*i)->getPos();
3009 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3011 DSTACK(FUNCTION_NAME);
3012 // Format used for writing
3013 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3015 v2s16 pos = sector->getPos();
3016 std::string dir = getSectorDir(pos);
3019 std::string fullpath = dir + DIR_DELIM + "meta";
3020 std::ostringstream ss(std::ios_base::binary);
3022 sector->serialize(ss, version);
3024 if(!fs::safeWriteToFile(fullpath, ss.str()))
3025 throw FileNotGoodException("Cannot write sector metafile");
3027 sector->differs_from_disk = false;
3030 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3032 DSTACK(FUNCTION_NAME);
3034 v2s16 p2d = getSectorPos(sectordir);
3036 ServerMapSector *sector = NULL;
3038 std::string fullpath = sectordir + DIR_DELIM + "meta";
3039 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3040 if(is.good() == false)
3042 // If the directory exists anyway, it probably is in some old
3043 // format. Just go ahead and create the sector.
3044 if(fs::PathExists(sectordir))
3046 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3047 <<fullpath<<" doesn't exist but directory does."
3048 <<" Continuing with a sector with no metadata."
3050 sector = new ServerMapSector(this, p2d, m_gamedef);
3051 m_sectors[p2d] = sector;
3055 throw FileNotGoodException("Cannot open sector metafile");
3060 sector = ServerMapSector::deSerialize
3061 (is, this, p2d, m_sectors, m_gamedef);
3063 saveSectorMeta(sector);
3066 sector->differs_from_disk = false;
3071 bool ServerMap::loadSectorMeta(v2s16 p2d)
3073 DSTACK(FUNCTION_NAME);
3075 // The directory layout we're going to load from.
3076 // 1 - original sectors/xxxxzzzz/
3077 // 2 - new sectors2/xxx/zzz/
3078 // If we load from anything but the latest structure, we will
3079 // immediately save to the new one, and remove the old.
3081 std::string sectordir1 = getSectorDir(p2d, 1);
3082 std::string sectordir;
3083 if(fs::PathExists(sectordir1))
3085 sectordir = sectordir1;
3090 sectordir = getSectorDir(p2d, 2);
3094 loadSectorMeta(sectordir, loadlayout != 2);
3096 catch(InvalidFilenameException &e)
3100 catch(FileNotGoodException &e)
3104 catch(std::exception &e)
3113 bool ServerMap::loadSectorFull(v2s16 p2d)
3115 DSTACK(FUNCTION_NAME);
3117 MapSector *sector = NULL;
3119 // The directory layout we're going to load from.
3120 // 1 - original sectors/xxxxzzzz/
3121 // 2 - new sectors2/xxx/zzz/
3122 // If we load from anything but the latest structure, we will
3123 // immediately save to the new one, and remove the old.
3125 std::string sectordir1 = getSectorDir(p2d, 1);
3126 std::string sectordir;
3127 if(fs::PathExists(sectordir1))
3129 sectordir = sectordir1;
3134 sectordir = getSectorDir(p2d, 2);
3138 sector = loadSectorMeta(sectordir, loadlayout != 2);
3140 catch(InvalidFilenameException &e)
3144 catch(FileNotGoodException &e)
3148 catch(std::exception &e)
3156 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3158 std::vector<fs::DirListNode>::iterator i2;
3159 for(i2=list2.begin(); i2!=list2.end(); i2++)
3165 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3167 catch(InvalidFilenameException &e)
3169 // This catches unknown crap in directory
3175 infostream<<"Sector converted to new layout - deleting "<<
3176 sectordir1<<std::endl;
3177 fs::RecursiveDelete(sectordir1);
3184 Database *ServerMap::createDatabase(
3185 const std::string &name,
3186 const std::string &savedir,
3189 if (name == "sqlite3")
3190 return new Database_SQLite3(savedir);
3191 if (name == "dummy")
3192 return new Database_Dummy();
3194 else if (name == "leveldb")
3195 return new Database_LevelDB(savedir);
3198 else if (name == "redis")
3199 return new Database_Redis(conf);
3202 else if (name == "postgresql")
3203 return new Database_PostgreSQL(conf);
3206 throw BaseException(std::string("Database backend ") + name + " not supported.");
3209 void ServerMap::beginSave()
3214 void ServerMap::endSave()
3219 bool ServerMap::saveBlock(MapBlock *block)
3221 return saveBlock(block, dbase);
3224 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3226 v3s16 p3d = block->getPos();
3228 // Dummy blocks are not written
3229 if (block->isDummy()) {
3230 warningstream << "saveBlock: Not writing dummy block "
3231 << PP(p3d) << std::endl;
3235 // Format used for writing
3236 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3239 [0] u8 serialization version
3242 std::ostringstream o(std::ios_base::binary);
3243 o.write((char*) &version, 1);
3244 block->serialize(o, version, true);
3246 std::string data = o.str();
3247 bool ret = db->saveBlock(p3d, data);
3249 // We just wrote it to the disk so clear modified flag
3250 block->resetModified();
3255 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3256 MapSector *sector, bool save_after_load)
3258 DSTACK(FUNCTION_NAME);
3260 std::string fullpath = sectordir + DIR_DELIM + blockfile;
3263 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3264 if(is.good() == false)
3265 throw FileNotGoodException("Cannot open block file");
3267 v3s16 p3d = getBlockPos(sectordir, blockfile);
3268 v2s16 p2d(p3d.X, p3d.Z);
3270 assert(sector->getPos() == p2d);
3272 u8 version = SER_FMT_VER_INVALID;
3273 is.read((char*)&version, 1);
3276 throw SerializationError("ServerMap::loadBlock(): Failed"
3277 " to read MapBlock version");
3279 /*u32 block_size = MapBlock::serializedLength(version);
3280 SharedBuffer<u8> data(block_size);
3281 is.read((char*)*data, block_size);*/
3283 // This will always return a sector because we're the server
3284 //MapSector *sector = emergeSector(p2d);
3286 MapBlock *block = NULL;
3287 bool created_new = false;
3288 block = sector->getBlockNoCreateNoEx(p3d.Y);
3291 block = sector->createBlankBlockNoInsert(p3d.Y);
3296 block->deSerialize(is, version, true);
3298 // If it's a new block, insert it to the map
3300 sector->insertBlock(block);
3303 Save blocks loaded in old format in new format
3306 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3310 // Should be in database now, so delete the old file
3311 fs::RecursiveDelete(fullpath);
3314 // We just loaded it from the disk, so it's up-to-date.
3315 block->resetModified();
3318 catch(SerializationError &e)
3320 warningstream<<"Invalid block data on disk "
3321 <<"fullpath="<<fullpath
3322 <<" (SerializationError). "
3323 <<"what()="<<e.what()
3325 // Ignoring. A new one will be generated.
3328 // TODO: Backup file; name is in fullpath.
3332 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3334 DSTACK(FUNCTION_NAME);
3337 std::istringstream is(*blob, std::ios_base::binary);
3339 u8 version = SER_FMT_VER_INVALID;
3340 is.read((char*)&version, 1);
3343 throw SerializationError("ServerMap::loadBlock(): Failed"
3344 " to read MapBlock version");
3346 /*u32 block_size = MapBlock::serializedLength(version);
3347 SharedBuffer<u8> data(block_size);
3348 is.read((char*)*data, block_size);*/
3350 // This will always return a sector because we're the server
3351 //MapSector *sector = emergeSector(p2d);
3353 MapBlock *block = NULL;
3354 bool created_new = false;
3355 block = sector->getBlockNoCreateNoEx(p3d.Y);
3358 block = sector->createBlankBlockNoInsert(p3d.Y);
3363 block->deSerialize(is, version, true);
3365 // If it's a new block, insert it to the map
3367 sector->insertBlock(block);
3370 Save blocks loaded in old format in new format
3373 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3374 // Only save if asked to; no need to update version
3378 // We just loaded it from, so it's up-to-date.
3379 block->resetModified();
3382 catch(SerializationError &e)
3384 errorstream<<"Invalid block data in database"
3385 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3386 <<" (SerializationError): "<<e.what()<<std::endl;
3388 // TODO: Block should be marked as invalid in memory so that it is
3389 // not touched but the game can run
3391 if(g_settings->getBool("ignore_world_load_errors")){
3392 errorstream<<"Ignoring block load error. Duck and cover! "
3393 <<"(ignore_world_load_errors)"<<std::endl;
3395 throw SerializationError("Invalid block data in database");
3400 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3402 DSTACK(FUNCTION_NAME);
3404 v2s16 p2d(blockpos.X, blockpos.Z);
3407 dbase->loadBlock(blockpos, &ret);
3409 loadBlock(&ret, blockpos, createSector(p2d), false);
3410 return getBlockNoCreateNoEx(blockpos);
3412 // Not found in database, try the files
3414 // The directory layout we're going to load from.
3415 // 1 - original sectors/xxxxzzzz/
3416 // 2 - new sectors2/xxx/zzz/
3417 // If we load from anything but the latest structure, we will
3418 // immediately save to the new one, and remove the old.
3420 std::string sectordir1 = getSectorDir(p2d, 1);
3421 std::string sectordir;
3422 if(fs::PathExists(sectordir1))
3424 sectordir = sectordir1;
3429 sectordir = getSectorDir(p2d, 2);
3433 Make sure sector is loaded
3436 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3440 sector = loadSectorMeta(sectordir, loadlayout != 2);
3442 catch(InvalidFilenameException &e)
3446 catch(FileNotGoodException &e)
3450 catch(std::exception &e)
3457 Make sure file exists
3460 std::string blockfilename = getBlockFilename(blockpos);
3461 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3465 Load block and save it to the database
3467 loadBlock(sectordir, blockfilename, sector, true);
3468 return getBlockNoCreateNoEx(blockpos);
3471 bool ServerMap::deleteBlock(v3s16 blockpos)
3473 if (!dbase->deleteBlock(blockpos))
3476 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3478 v2s16 p2d(blockpos.X, blockpos.Z);
3479 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3482 sector->deleteBlock(block);
3488 void ServerMap::PrintInfo(std::ostream &out)
3493 MMVManip::MMVManip(Map *map):
3496 m_create_area(false),
3501 MMVManip::~MMVManip()
3505 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3506 bool load_if_inexistent)
3508 TimeTaker timer1("initialEmerge", &emerge_time);
3510 // Units of these are MapBlocks
3511 v3s16 p_min = blockpos_min;
3512 v3s16 p_max = blockpos_max;
3514 VoxelArea block_area_nodes
3515 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3517 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3520 infostream<<"initialEmerge: area: ";
3521 block_area_nodes.print(infostream);
3522 infostream<<" ("<<size_MB<<"MB)";
3523 infostream<<std::endl;
3526 addArea(block_area_nodes);
3528 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3529 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3530 for(s32 x=p_min.X; x<=p_max.X; x++)
3535 std::map<v3s16, u8>::iterator n;
3536 n = m_loaded_blocks.find(p);
3537 if(n != m_loaded_blocks.end())
3540 bool block_data_inexistent = false;
3543 TimeTaker timer1("emerge load", &emerge_load_time);
3545 block = m_map->getBlockNoCreate(p);
3546 if(block->isDummy())
3547 block_data_inexistent = true;
3549 block->copyTo(*this);
3551 catch(InvalidPositionException &e)
3553 block_data_inexistent = true;
3556 if(block_data_inexistent)
3559 if (load_if_inexistent) {
3560 ServerMap *svrmap = (ServerMap *)m_map;
3561 block = svrmap->emergeBlock(p, false);
3563 block = svrmap->createBlock(p);
3564 block->copyTo(*this);
3566 flags |= VMANIP_BLOCK_DATA_INEXIST;
3569 Mark area inexistent
3571 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3572 // Fill with VOXELFLAG_NO_DATA
3573 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3574 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3576 s32 i = m_area.index(a.MinEdge.X,y,z);
3577 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3581 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3583 // Mark that block was loaded as blank
3584 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3587 m_loaded_blocks[p] = flags;
3593 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3594 bool overwrite_generated)
3596 if(m_area.getExtent() == v3s16(0,0,0))
3600 Copy data of all blocks
3602 for(std::map<v3s16, u8>::iterator
3603 i = m_loaded_blocks.begin();
3604 i != m_loaded_blocks.end(); ++i)
3607 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3608 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3609 if ((existed == false) || (block == NULL) ||
3610 (overwrite_generated == false && block->isGenerated() == true))
3613 block->copyFrom(*this);
3616 (*modified_blocks)[p] = block;