3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/mathconstants.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
44 #include "database-dummy.h"
45 #include "database-sqlite3.h"
47 #include "database-leveldb.h"
50 #include "database-redis.h"
53 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
56 SQLite format specification:
57 - Initially only replaces sectors/ and sectors2/
59 If map.sqlite does not exist in the save dir
60 or the block was not found in the database
61 the map will try to load from sectors folder.
62 In either case, map.sqlite will be created
63 and all future saves will save there.
65 Structure of map.sqlite:
76 Map::Map(std::ostream &dout, IGameDef *gamedef):
88 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
89 i != m_sectors.end(); ++i)
95 void Map::addEventReceiver(MapEventReceiver *event_receiver)
97 m_event_receivers.insert(event_receiver);
100 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
102 m_event_receivers.erase(event_receiver);
105 void Map::dispatchEvent(MapEditEvent *event)
107 for(std::set<MapEventReceiver*>::iterator
108 i = m_event_receivers.begin();
109 i != m_event_receivers.end(); ++i)
111 (*i)->onMapEditEvent(event);
115 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
117 if(m_sector_cache != NULL && p == m_sector_cache_p){
118 MapSector * sector = m_sector_cache;
122 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
124 if(n == m_sectors.end())
127 MapSector *sector = n->second;
129 // Cache the last result
130 m_sector_cache_p = p;
131 m_sector_cache = sector;
136 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
138 return getSectorNoGenerateNoExNoLock(p);
141 MapSector * Map::getSectorNoGenerate(v2s16 p)
143 MapSector *sector = getSectorNoGenerateNoEx(p);
145 throw InvalidPositionException();
150 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
152 v2s16 p2d(p3d.X, p3d.Z);
153 MapSector * sector = getSectorNoGenerateNoEx(p2d);
156 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
160 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
162 MapBlock *block = getBlockNoCreateNoEx(p3d);
164 throw InvalidPositionException();
168 bool Map::isNodeUnderground(v3s16 p)
170 v3s16 blockpos = getNodeBlockPos(p);
172 MapBlock * block = getBlockNoCreate(blockpos);
173 return block->getIsUnderground();
175 catch(InvalidPositionException &e)
181 bool Map::isValidPosition(v3s16 p)
183 v3s16 blockpos = getNodeBlockPos(p);
184 MapBlock *block = getBlockNoCreate(blockpos);
185 return (block != NULL);
188 // Returns a CONTENT_IGNORE node if not found
189 MapNode Map::getNodeNoEx(v3s16 p)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 return MapNode(CONTENT_IGNORE);
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 return block->getNodeNoCheck(relpos);
199 // throws InvalidPositionException if not found
200 MapNode Map::getNode(v3s16 p)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreateNoEx(blockpos);
205 throw InvalidPositionException();
206 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
207 return block->getNodeNoCheck(relpos);
210 // throws InvalidPositionException if not found
211 void Map::setNode(v3s16 p, MapNode & n)
213 v3s16 blockpos = getNodeBlockPos(p);
214 MapBlock *block = getBlockNoCreate(blockpos);
215 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
216 // Never allow placing CONTENT_IGNORE, it fucks up stuff
217 if(n.getContent() == CONTENT_IGNORE){
218 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
219 <<" while trying to replace \""
220 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
221 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
222 debug_stacks_print_to(infostream);
225 block->setNodeNoCheck(relpos, n);
230 Goes recursively through the neighbours of the node.
232 Alters only transparent nodes.
234 If the lighting of the neighbour is lower than the lighting of
235 the node was (before changing it to 0 at the step before), the
236 lighting of the neighbour is set to 0 and then the same stuff
237 repeats for the neighbour.
239 The ending nodes of the routine are stored in light_sources.
240 This is useful when a light is removed. In such case, this
241 routine can be called for the light node and then again for
242 light_sources to re-light the area without the removed light.
244 values of from_nodes are lighting values.
246 void Map::unspreadLight(enum LightBank bank,
247 std::map<v3s16, u8> & from_nodes,
248 std::set<v3s16> & light_sources,
249 std::map<v3s16, MapBlock*> & modified_blocks)
251 INodeDefManager *nodemgr = m_gamedef->ndef();
254 v3s16(0,0,1), // back
256 v3s16(1,0,0), // right
257 v3s16(0,0,-1), // front
258 v3s16(0,-1,0), // bottom
259 v3s16(-1,0,0), // left
262 if(from_nodes.size() == 0)
265 u32 blockchangecount = 0;
267 std::map<v3s16, u8> unlighted_nodes;
270 Initialize block cache
273 MapBlock *block = NULL;
274 // Cache this a bit, too
275 bool block_checked_in_modified = false;
277 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
278 j != from_nodes.end(); ++j)
280 v3s16 pos = j->first;
281 v3s16 blockpos = getNodeBlockPos(pos);
283 // Only fetch a new block if the block position has changed
285 if(block == NULL || blockpos != blockpos_last){
286 block = getBlockNoCreate(blockpos);
287 blockpos_last = blockpos;
289 block_checked_in_modified = false;
293 catch(InvalidPositionException &e)
301 // Calculate relative position in block
302 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
304 // Get node straight from the block
305 //MapNode n = block->getNode(relpos);
307 u8 oldlight = j->second;
309 // Loop through 6 neighbors
310 for(u16 i=0; i<6; i++)
312 // Get the position of the neighbor node
313 v3s16 n2pos = pos + dirs[i];
315 // Get the block where the node is located
316 v3s16 blockpos = getNodeBlockPos(n2pos);
320 // Only fetch a new block if the block position has changed
322 if(block == NULL || blockpos != blockpos_last){
323 block = getBlockNoCreate(blockpos);
324 blockpos_last = blockpos;
326 block_checked_in_modified = false;
330 catch(InvalidPositionException &e)
335 // Calculate relative position in block
336 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
337 // Get node straight from the block
338 MapNode n2 = block->getNode(relpos);
340 bool changed = false;
342 //TODO: Optimize output by optimizing light_sources?
345 If the neighbor is dimmer than what was specified
346 as oldlight (the light of the previous node)
348 if(n2.getLight(bank, nodemgr) < oldlight)
351 And the neighbor is transparent and it has some light
353 if(nodemgr->get(n2).light_propagates
354 && n2.getLight(bank, nodemgr) != 0)
357 Set light to 0 and add to queue
360 u8 current_light = n2.getLight(bank, nodemgr);
361 n2.setLight(bank, 0, nodemgr);
362 block->setNode(relpos, n2);
364 unlighted_nodes[n2pos] = current_light;
368 Remove from light_sources if it is there
369 NOTE: This doesn't happen nearly at all
371 /*if(light_sources.find(n2pos))
373 infostream<<"Removed from light_sources"<<std::endl;
374 light_sources.remove(n2pos);
379 if(light_sources.find(n2pos) != NULL)
380 light_sources.remove(n2pos);*/
383 light_sources.insert(n2pos);
386 // Add to modified_blocks
387 if(changed == true && block_checked_in_modified == false)
389 // If the block is not found in modified_blocks, add.
390 if(modified_blocks.find(blockpos) == modified_blocks.end())
392 modified_blocks[blockpos] = block;
394 block_checked_in_modified = true;
397 catch(InvalidPositionException &e)
404 /*infostream<<"unspreadLight(): Changed block "
405 <<blockchangecount<<" times"
406 <<" for "<<from_nodes.size()<<" nodes"
409 if(unlighted_nodes.size() > 0)
410 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
414 A single-node wrapper of the above
416 void Map::unLightNeighbors(enum LightBank bank,
417 v3s16 pos, u8 lightwas,
418 std::set<v3s16> & light_sources,
419 std::map<v3s16, MapBlock*> & modified_blocks)
421 std::map<v3s16, u8> from_nodes;
422 from_nodes[pos] = lightwas;
424 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
428 Lights neighbors of from_nodes, collects all them and then
431 void Map::spreadLight(enum LightBank bank,
432 std::set<v3s16> & from_nodes,
433 std::map<v3s16, MapBlock*> & modified_blocks)
435 INodeDefManager *nodemgr = m_gamedef->ndef();
437 const v3s16 dirs[6] = {
438 v3s16(0,0,1), // back
440 v3s16(1,0,0), // right
441 v3s16(0,0,-1), // front
442 v3s16(0,-1,0), // bottom
443 v3s16(-1,0,0), // left
446 if(from_nodes.size() == 0)
449 u32 blockchangecount = 0;
451 std::set<v3s16> lighted_nodes;
454 Initialize block cache
457 MapBlock *block = NULL;
458 // Cache this a bit, too
459 bool block_checked_in_modified = false;
461 for(std::set<v3s16>::iterator j = from_nodes.begin();
462 j != from_nodes.end(); ++j)
465 v3s16 blockpos = getNodeBlockPos(pos);
467 // Only fetch a new block if the block position has changed
469 if(block == NULL || blockpos != blockpos_last){
470 block = getBlockNoCreate(blockpos);
471 blockpos_last = blockpos;
473 block_checked_in_modified = false;
477 catch(InvalidPositionException &e)
485 // Calculate relative position in block
486 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
488 // Get node straight from the block
489 MapNode n = block->getNode(relpos);
491 u8 oldlight = n.getLight(bank, nodemgr);
492 u8 newlight = diminish_light(oldlight);
494 // Loop through 6 neighbors
495 for(u16 i=0; i<6; i++){
496 // Get the position of the neighbor node
497 v3s16 n2pos = pos + dirs[i];
499 // Get the block where the node is located
500 v3s16 blockpos = getNodeBlockPos(n2pos);
504 // Only fetch a new block if the block position has changed
506 if(block == NULL || blockpos != blockpos_last){
507 block = getBlockNoCreate(blockpos);
508 blockpos_last = blockpos;
510 block_checked_in_modified = false;
514 catch(InvalidPositionException &e)
519 // Calculate relative position in block
520 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
521 // Get node straight from the block
522 MapNode n2 = block->getNode(relpos);
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;
560 catch(InvalidPositionException &e)
567 /*infostream<<"spreadLight(): Changed block "
568 <<blockchangecount<<" times"
569 <<" for "<<from_nodes.size()<<" nodes"
572 if(lighted_nodes.size() > 0)
573 spreadLight(bank, lighted_nodes, modified_blocks);
577 A single-node source variation of the above.
579 void Map::lightNeighbors(enum LightBank bank,
581 std::map<v3s16, MapBlock*> & modified_blocks)
583 std::set<v3s16> from_nodes;
584 from_nodes.insert(pos);
585 spreadLight(bank, from_nodes, modified_blocks);
588 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
590 INodeDefManager *nodemgr = m_gamedef->ndef();
593 v3s16(0,0,1), // back
595 v3s16(1,0,0), // right
596 v3s16(0,0,-1), // front
597 v3s16(0,-1,0), // bottom
598 v3s16(-1,0,0), // left
601 u8 brightest_light = 0;
602 v3s16 brightest_pos(0,0,0);
603 bool found_something = false;
605 // Loop through 6 neighbors
606 for(u16 i=0; i<6; i++){
607 // Get the position of the neighbor node
608 v3s16 n2pos = p + dirs[i];
613 catch(InvalidPositionException &e)
617 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
618 brightest_light = n2.getLight(bank, nodemgr);
619 brightest_pos = n2pos;
620 found_something = true;
624 if(found_something == false)
625 throw InvalidPositionException();
627 return brightest_pos;
631 Propagates sunlight down from a node.
632 Starting point gets sunlight.
634 Returns the lowest y value of where the sunlight went.
636 Mud is turned into grass in where the sunlight stops.
638 s16 Map::propagateSunlight(v3s16 start,
639 std::map<v3s16, MapBlock*> & modified_blocks)
641 INodeDefManager *nodemgr = m_gamedef->ndef();
646 v3s16 pos(start.X, y, start.Z);
648 v3s16 blockpos = getNodeBlockPos(pos);
651 block = getBlockNoCreate(blockpos);
653 catch(InvalidPositionException &e)
658 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
659 MapNode n = block->getNode(relpos);
661 if(nodemgr->get(n).sunlight_propagates)
663 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
664 block->setNode(relpos, n);
666 modified_blocks[blockpos] = block;
670 // Sunlight goes no further
677 void Map::updateLighting(enum LightBank bank,
678 std::map<v3s16, MapBlock*> & a_blocks,
679 std::map<v3s16, MapBlock*> & modified_blocks)
681 INodeDefManager *nodemgr = m_gamedef->ndef();
683 /*m_dout<<DTIME<<"Map::updateLighting(): "
684 <<a_blocks.size()<<" blocks."<<std::endl;*/
686 //TimeTaker timer("updateLighting");
690 //u32 count_was = modified_blocks.size();
692 std::map<v3s16, MapBlock*> blocks_to_update;
694 std::set<v3s16> light_sources;
696 std::map<v3s16, u8> unlight_from;
698 int num_bottom_invalid = 0;
701 //TimeTaker t("first stuff");
703 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
704 i != a_blocks.end(); ++i)
706 MapBlock *block = i->second;
710 // Don't bother with dummy blocks.
714 v3s16 pos = block->getPos();
715 v3s16 posnodes = block->getPosRelative();
716 modified_blocks[pos] = block;
717 blocks_to_update[pos] = block;
720 Clear all light from block
722 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
723 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
724 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
729 MapNode n = block->getNode(p);
730 u8 oldlight = n.getLight(bank, nodemgr);
731 n.setLight(bank, 0, nodemgr);
732 block->setNode(p, n);
734 // If node sources light, add to list
735 u8 source = nodemgr->get(n).light_source;
737 light_sources.insert(p + posnodes);
739 // Collect borders for unlighting
740 if((x==0 || x == MAP_BLOCKSIZE-1
741 || y==0 || y == MAP_BLOCKSIZE-1
742 || z==0 || z == MAP_BLOCKSIZE-1)
745 v3s16 p_map = p + posnodes;
746 unlight_from[p_map] = oldlight;
749 catch(InvalidPositionException &e)
752 This would happen when dealing with a
756 infostream<<"updateLighting(): InvalidPositionException"
761 if(bank == LIGHTBANK_DAY)
763 bool bottom_valid = block->propagateSunlight(light_sources);
766 num_bottom_invalid++;
768 // If bottom is valid, we're done.
772 else if(bank == LIGHTBANK_NIGHT)
774 // For night lighting, sunlight is not propagated
779 // Invalid lighting bank
783 /*infostream<<"Bottom for sunlight-propagated block ("
784 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
787 // Bottom sunlight is not valid; get the block and loop to it
791 block = getBlockNoCreate(pos);
793 catch(InvalidPositionException &e)
804 Enable this to disable proper lighting for speeding up map
805 generation for testing or whatever
808 //if(g_settings->get(""))
810 core::map<v3s16, MapBlock*>::Iterator i;
811 i = blocks_to_update.getIterator();
812 for(; i.atEnd() == false; i++)
814 MapBlock *block = i.getNode()->getValue();
815 v3s16 p = block->getPos();
816 block->setLightingExpired(false);
824 //TimeTaker timer("unspreadLight");
825 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
830 u32 diff = modified_blocks.size() - count_was;
831 count_was = modified_blocks.size();
832 infostream<<"unspreadLight modified "<<diff<<std::endl;
836 //TimeTaker timer("spreadLight");
837 spreadLight(bank, light_sources, modified_blocks);
842 u32 diff = modified_blocks.size() - count_was;
843 count_was = modified_blocks.size();
844 infostream<<"spreadLight modified "<<diff<<std::endl;
850 //MapVoxelManipulator vmanip(this);
852 // Make a manual voxel manipulator and load all the blocks
853 // that touch the requested blocks
854 ManualMapVoxelManipulator vmanip(this);
857 //TimeTaker timer("initialEmerge");
859 core::map<v3s16, MapBlock*>::Iterator i;
860 i = blocks_to_update.getIterator();
861 for(; i.atEnd() == false; i++)
863 MapBlock *block = i.getNode()->getValue();
864 v3s16 p = block->getPos();
866 // Add all surrounding blocks
867 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
870 Add all surrounding blocks that have up-to-date lighting
871 NOTE: This doesn't quite do the job (not everything
872 appropriate is lighted)
874 /*for(s16 z=-1; z<=1; z++)
875 for(s16 y=-1; y<=1; y++)
876 for(s16 x=-1; x<=1; x++)
878 v3s16 p2 = p + v3s16(x,y,z);
879 MapBlock *block = getBlockNoCreateNoEx(p2);
884 if(block->getLightingExpired())
886 vmanip.initialEmerge(p2, p2);
889 // Lighting of block will be updated completely
890 block->setLightingExpired(false);
895 //TimeTaker timer("unSpreadLight");
896 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
899 //TimeTaker timer("spreadLight");
900 vmanip.spreadLight(bank, light_sources, nodemgr);
903 //TimeTaker timer("blitBack");
904 vmanip.blitBack(modified_blocks);
906 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
911 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
914 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
915 std::map<v3s16, MapBlock*> & modified_blocks)
917 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
918 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
921 Update information about whether day and night light differ
923 for(std::map<v3s16, MapBlock*>::iterator
924 i = modified_blocks.begin();
925 i != modified_blocks.end(); ++i)
927 MapBlock *block = i->second;
928 block->expireDayNightDiff();
934 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
935 std::map<v3s16, MapBlock*> &modified_blocks,
936 bool remove_metadata)
938 INodeDefManager *ndef = m_gamedef->ndef();
941 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
942 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
945 From this node to nodes underneath:
946 If lighting is sunlight (1.0), unlight neighbours and
951 v3s16 toppos = p + v3s16(0,1,0);
952 //v3s16 bottompos = p + v3s16(0,-1,0);
954 bool node_under_sunlight = true;
955 std::set<v3s16> light_sources;
958 Collect old node for rollback
960 RollbackNode rollback_oldnode(this, p, m_gamedef);
963 If there is a node at top and it doesn't have sunlight,
964 there has not been any sunlight going down.
966 Otherwise there probably is.
969 MapNode topnode = getNode(toppos);
971 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
972 node_under_sunlight = false;
974 catch(InvalidPositionException &e)
979 Remove all light that has come out of this node
982 enum LightBank banks[] =
987 for(s32 i=0; i<2; i++)
989 enum LightBank bank = banks[i];
991 u8 lightwas = getNode(p).getLight(bank, ndef);
993 // Add the block of the added node to modified_blocks
994 v3s16 blockpos = getNodeBlockPos(p);
995 MapBlock * block = getBlockNoCreate(blockpos);
996 assert(block != NULL);
997 modified_blocks[blockpos] = block;
999 assert(isValidPosition(p));
1001 // Unlight neighbours of node.
1002 // This means setting light of all consequent dimmer nodes
1004 // This also collects the nodes at the border which will spread
1005 // light again into this.
1006 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1008 n.setLight(bank, 0, ndef);
1012 If node lets sunlight through and is under sunlight, it has
1015 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1017 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1021 Remove node metadata
1023 if (remove_metadata) {
1024 removeNodeMetadata(p);
1028 Set the node on the map
1034 If node is under sunlight and doesn't let sunlight through,
1035 take all sunlighted nodes under it and clear light from them
1036 and from where the light has been spread.
1037 TODO: This could be optimized by mass-unlighting instead
1040 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1044 //m_dout<<DTIME<<"y="<<y<<std::endl;
1045 v3s16 n2pos(p.X, y, p.Z);
1049 n2 = getNode(n2pos);
1051 catch(InvalidPositionException &e)
1056 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1058 unLightNeighbors(LIGHTBANK_DAY,
1059 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1060 light_sources, modified_blocks);
1061 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1069 for(s32 i=0; i<2; i++)
1071 enum LightBank bank = banks[i];
1074 Spread light from all nodes that might be capable of doing so
1076 spreadLight(bank, light_sources, modified_blocks);
1080 Update information about whether day and night light differ
1082 for(std::map<v3s16, MapBlock*>::iterator
1083 i = modified_blocks.begin();
1084 i != modified_blocks.end(); ++i)
1086 i->second->expireDayNightDiff();
1092 if(m_gamedef->rollback())
1094 RollbackNode rollback_newnode(this, p, m_gamedef);
1095 RollbackAction action;
1096 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1097 m_gamedef->rollback()->reportAction(action);
1101 Add neighboring liquid nodes and the node itself if it is
1102 liquid (=water node was added) to transform queue.
1105 v3s16(0,0,0), // self
1106 v3s16(0,0,1), // back
1107 v3s16(0,1,0), // top
1108 v3s16(1,0,0), // right
1109 v3s16(0,0,-1), // front
1110 v3s16(0,-1,0), // bottom
1111 v3s16(-1,0,0), // left
1113 for(u16 i=0; i<7; i++)
1118 v3s16 p2 = p + dirs[i];
1120 MapNode n2 = getNode(p2);
1121 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1123 m_transforming_liquid.push_back(p2);
1126 }catch(InvalidPositionException &e)
1134 void Map::removeNodeAndUpdate(v3s16 p,
1135 std::map<v3s16, MapBlock*> &modified_blocks)
1137 INodeDefManager *ndef = m_gamedef->ndef();
1139 /*PrintInfo(m_dout);
1140 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1141 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1143 bool node_under_sunlight = true;
1145 v3s16 toppos = p + v3s16(0,1,0);
1147 // Node will be replaced with this
1148 content_t replace_material = CONTENT_AIR;
1151 Collect old node for rollback
1153 RollbackNode rollback_oldnode(this, p, m_gamedef);
1156 If there is a node at top and it doesn't have sunlight,
1157 there will be no sunlight going down.
1160 MapNode topnode = getNode(toppos);
1162 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1163 node_under_sunlight = false;
1165 catch(InvalidPositionException &e)
1169 std::set<v3s16> light_sources;
1171 enum LightBank banks[] =
1176 for(s32 i=0; i<2; i++)
1178 enum LightBank bank = banks[i];
1181 Unlight neighbors (in case the node is a light source)
1183 unLightNeighbors(bank, p,
1184 getNode(p).getLight(bank, ndef),
1185 light_sources, modified_blocks);
1189 Remove node metadata
1192 removeNodeMetadata(p);
1196 This also clears the lighting.
1200 n.setContent(replace_material);
1203 for(s32 i=0; i<2; i++)
1205 enum LightBank bank = banks[i];
1208 Recalculate lighting
1210 spreadLight(bank, light_sources, modified_blocks);
1213 // Add the block of the removed node to modified_blocks
1214 v3s16 blockpos = getNodeBlockPos(p);
1215 MapBlock * block = getBlockNoCreate(blockpos);
1216 assert(block != NULL);
1217 modified_blocks[blockpos] = block;
1220 If the removed node was under sunlight, propagate the
1221 sunlight down from it and then light all neighbors
1222 of the propagated blocks.
1224 if(node_under_sunlight)
1226 s16 ybottom = propagateSunlight(p, modified_blocks);
1227 /*m_dout<<DTIME<<"Node was under sunlight. "
1228 "Propagating sunlight";
1229 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1231 for(; y >= ybottom; y--)
1233 v3s16 p2(p.X, y, p.Z);
1234 /*m_dout<<DTIME<<"lighting neighbors of node ("
1235 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1237 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1242 // Set the lighting of this node to 0
1243 // TODO: Is this needed? Lighting is cleared up there already.
1245 MapNode n = getNode(p);
1246 n.setLight(LIGHTBANK_DAY, 0, ndef);
1249 catch(InvalidPositionException &e)
1255 for(s32 i=0; i<2; i++)
1257 enum LightBank bank = banks[i];
1259 // Get the brightest neighbour node and propagate light from it
1260 v3s16 n2p = getBrightestNeighbour(bank, p);
1262 //MapNode n2 = getNode(n2p);
1263 lightNeighbors(bank, n2p, modified_blocks);
1265 catch(InvalidPositionException &e)
1271 Update information about whether day and night light differ
1273 for(std::map<v3s16, MapBlock*>::iterator
1274 i = modified_blocks.begin();
1275 i != modified_blocks.end(); ++i)
1277 i->second->expireDayNightDiff();
1283 if(m_gamedef->rollback())
1285 RollbackNode rollback_newnode(this, p, m_gamedef);
1286 RollbackAction action;
1287 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1288 m_gamedef->rollback()->reportAction(action);
1292 Add neighboring liquid nodes and this node to transform queue.
1293 (it's vital for the node itself to get updated last.)
1296 v3s16(0,0,1), // back
1297 v3s16(0,1,0), // top
1298 v3s16(1,0,0), // right
1299 v3s16(0,0,-1), // front
1300 v3s16(0,-1,0), // bottom
1301 v3s16(-1,0,0), // left
1302 v3s16(0,0,0), // self
1304 for(u16 i=0; i<7; i++)
1309 v3s16 p2 = p + dirs[i];
1311 MapNode n2 = getNode(p2);
1312 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1314 m_transforming_liquid.push_back(p2);
1317 }catch(InvalidPositionException &e)
1323 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1326 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1330 bool succeeded = true;
1332 std::map<v3s16, MapBlock*> modified_blocks;
1333 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1335 // Copy modified_blocks to event
1336 for(std::map<v3s16, MapBlock*>::iterator
1337 i = modified_blocks.begin();
1338 i != modified_blocks.end(); ++i)
1340 event.modified_blocks.insert(i->first);
1343 catch(InvalidPositionException &e){
1347 dispatchEvent(&event);
1352 bool Map::removeNodeWithEvent(v3s16 p)
1355 event.type = MEET_REMOVENODE;
1358 bool succeeded = true;
1360 std::map<v3s16, MapBlock*> modified_blocks;
1361 removeNodeAndUpdate(p, modified_blocks);
1363 // Copy modified_blocks to event
1364 for(std::map<v3s16, MapBlock*>::iterator
1365 i = modified_blocks.begin();
1366 i != modified_blocks.end(); ++i)
1368 event.modified_blocks.insert(i->first);
1371 catch(InvalidPositionException &e){
1375 dispatchEvent(&event);
1380 bool Map::getDayNightDiff(v3s16 blockpos)
1383 v3s16 p = blockpos + v3s16(0,0,0);
1384 MapBlock *b = getBlockNoCreate(p);
1385 if(b->getDayNightDiff())
1388 catch(InvalidPositionException &e){}
1391 v3s16 p = blockpos + v3s16(-1,0,0);
1392 MapBlock *b = getBlockNoCreate(p);
1393 if(b->getDayNightDiff())
1396 catch(InvalidPositionException &e){}
1398 v3s16 p = blockpos + v3s16(0,-1,0);
1399 MapBlock *b = getBlockNoCreate(p);
1400 if(b->getDayNightDiff())
1403 catch(InvalidPositionException &e){}
1405 v3s16 p = blockpos + v3s16(0,0,-1);
1406 MapBlock *b = getBlockNoCreate(p);
1407 if(b->getDayNightDiff())
1410 catch(InvalidPositionException &e){}
1413 v3s16 p = blockpos + v3s16(1,0,0);
1414 MapBlock *b = getBlockNoCreate(p);
1415 if(b->getDayNightDiff())
1418 catch(InvalidPositionException &e){}
1420 v3s16 p = blockpos + v3s16(0,1,0);
1421 MapBlock *b = getBlockNoCreate(p);
1422 if(b->getDayNightDiff())
1425 catch(InvalidPositionException &e){}
1427 v3s16 p = blockpos + v3s16(0,0,1);
1428 MapBlock *b = getBlockNoCreate(p);
1429 if(b->getDayNightDiff())
1432 catch(InvalidPositionException &e){}
1438 Updates usage timers
1440 void Map::timerUpdate(float dtime, float unload_timeout,
1441 std::list<v3s16> *unloaded_blocks)
1443 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1445 // Profile modified reasons
1446 Profiler modprofiler;
1448 std::list<v2s16> sector_deletion_queue;
1449 u32 deleted_blocks_count = 0;
1450 u32 saved_blocks_count = 0;
1451 u32 block_count_all = 0;
1454 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1455 si != m_sectors.end(); ++si)
1457 MapSector *sector = si->second;
1459 bool all_blocks_deleted = true;
1461 std::list<MapBlock*> blocks;
1462 sector->getBlocks(blocks);
1464 for(std::list<MapBlock*>::iterator i = blocks.begin();
1465 i != blocks.end(); ++i)
1467 MapBlock *block = (*i);
1469 block->incrementUsageTimer(dtime);
1471 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1473 v3s16 p = block->getPos();
1476 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading)
1478 modprofiler.add(block->getModifiedReason(), 1);
1479 if (!saveBlock(block))
1481 saved_blocks_count++;
1484 // Delete from memory
1485 sector->deleteBlock(block);
1488 unloaded_blocks->push_back(p);
1490 deleted_blocks_count++;
1494 all_blocks_deleted = false;
1499 if(all_blocks_deleted)
1501 sector_deletion_queue.push_back(si->first);
1506 // Finally delete the empty sectors
1507 deleteSectors(sector_deletion_queue);
1509 if(deleted_blocks_count != 0)
1511 PrintInfo(infostream); // ServerMap/ClientMap:
1512 infostream<<"Unloaded "<<deleted_blocks_count
1513 <<" blocks from memory";
1514 if(save_before_unloading)
1515 infostream<<", of which "<<saved_blocks_count<<" were written";
1516 infostream<<", "<<block_count_all<<" blocks in memory";
1517 infostream<<"."<<std::endl;
1518 if(saved_blocks_count != 0){
1519 PrintInfo(infostream); // ServerMap/ClientMap:
1520 infostream<<"Blocks modified by: "<<std::endl;
1521 modprofiler.print(infostream);
1526 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1528 timerUpdate(0.0, -1.0, unloaded_blocks);
1531 void Map::deleteSectors(std::list<v2s16> &list)
1533 for(std::list<v2s16>::iterator j = list.begin();
1534 j != list.end(); ++j)
1536 MapSector *sector = m_sectors[*j];
1537 // If sector is in sector cache, remove it from there
1538 if(m_sector_cache == sector)
1539 m_sector_cache = NULL;
1540 // Remove from map and delete
1541 m_sectors.erase(*j);
1547 void Map::unloadUnusedData(float timeout,
1548 core::list<v3s16> *deleted_blocks)
1550 core::list<v2s16> sector_deletion_queue;
1551 u32 deleted_blocks_count = 0;
1552 u32 saved_blocks_count = 0;
1554 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1555 for(; si.atEnd() == false; si++)
1557 MapSector *sector = si.getNode()->getValue();
1559 bool all_blocks_deleted = true;
1561 core::list<MapBlock*> blocks;
1562 sector->getBlocks(blocks);
1563 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1564 i != blocks.end(); i++)
1566 MapBlock *block = (*i);
1568 if(block->getUsageTimer() > timeout)
1571 if(block->getModified() != MOD_STATE_CLEAN)
1574 saved_blocks_count++;
1576 // Delete from memory
1577 sector->deleteBlock(block);
1578 deleted_blocks_count++;
1582 all_blocks_deleted = false;
1586 if(all_blocks_deleted)
1588 sector_deletion_queue.push_back(si.getNode()->getKey());
1592 deleteSectors(sector_deletion_queue);
1594 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1595 <<", of which "<<saved_blocks_count<<" were wr."
1598 //return sector_deletion_queue.getSize();
1599 //return deleted_blocks_count;
1603 void Map::PrintInfo(std::ostream &out)
1608 #define WATER_DROP_BOOST 4
1612 NEIGHBOR_SAME_LEVEL,
1615 struct NodeNeighbor {
1619 bool l; //can liquid
1622 void Map::transforming_liquid_add(v3s16 p) {
1623 m_transforming_liquid.push_back(p);
1626 s32 Map::transforming_liquid_size() {
1627 return m_transforming_liquid.size();
1630 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1632 INodeDefManager *nodemgr = m_gamedef->ndef();
1634 DSTACK(__FUNCTION_NAME);
1635 //TimeTaker timer("transformLiquids()");
1638 u32 initial_size = m_transforming_liquid.size();
1640 /*if(initial_size != 0)
1641 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1643 // list of nodes that due to viscosity have not reached their max level height
1644 UniqueQueue<v3s16> must_reflow;
1646 // List of MapBlocks that will require a lighting update (due to lava)
1647 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1649 u16 loop_max = g_settings->getU16("liquid_loop_max");
1651 while(m_transforming_liquid.size() != 0)
1653 // This should be done here so that it is done when continue is used
1654 if(loopcount >= initial_size || loopcount >= loop_max)
1659 Get a queued transforming liquid node
1661 v3s16 p0 = m_transforming_liquid.pop_front();
1663 MapNode n0 = getNodeNoEx(p0);
1666 Collect information about current node
1668 s8 liquid_level = -1;
1669 content_t liquid_kind = CONTENT_IGNORE;
1670 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1671 switch (liquid_type) {
1673 liquid_level = LIQUID_LEVEL_SOURCE;
1674 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1676 case LIQUID_FLOWING:
1677 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1678 liquid_kind = n0.getContent();
1681 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1682 // continue with the next node.
1683 if (n0.getContent() != CONTENT_AIR)
1685 liquid_kind = CONTENT_AIR;
1690 Collect information about the environment
1692 const v3s16 *dirs = g_6dirs;
1693 NodeNeighbor sources[6]; // surrounding sources
1694 int num_sources = 0;
1695 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1697 NodeNeighbor airs[6]; // surrounding air
1699 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1700 int num_neutrals = 0;
1701 bool flowing_down = false;
1702 for (u16 i = 0; i < 6; i++) {
1703 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1706 nt = NEIGHBOR_UPPER;
1709 nt = NEIGHBOR_LOWER;
1712 v3s16 npos = p0 + dirs[i];
1713 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1714 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1716 if (nb.n.getContent() == CONTENT_AIR) {
1717 airs[num_airs++] = nb;
1718 // if the current node is a water source the neighbor
1719 // should be enqueded for transformation regardless of whether the
1720 // current node changes or not.
1721 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1722 m_transforming_liquid.push_back(npos);
1723 // if the current node happens to be a flowing node, it will start to flow down here.
1724 if (nb.t == NEIGHBOR_LOWER) {
1725 flowing_down = true;
1728 neutrals[num_neutrals++] = nb;
1732 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1733 if (liquid_kind == CONTENT_AIR)
1734 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1735 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1736 neutrals[num_neutrals++] = nb;
1738 // Do not count bottom source, it will screw things up
1740 sources[num_sources++] = nb;
1743 case LIQUID_FLOWING:
1744 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1745 if (liquid_kind == CONTENT_AIR)
1746 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1747 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1748 neutrals[num_neutrals++] = nb;
1750 flows[num_flows++] = nb;
1751 if (nb.t == NEIGHBOR_LOWER)
1752 flowing_down = true;
1759 decide on the type (and possibly level) of the current node
1761 content_t new_node_content;
1762 s8 new_node_level = -1;
1763 s8 max_node_level = -1;
1764 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
1765 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1766 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1767 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1768 // it's perfectly safe to use liquid_kind here to determine the new node content.
1769 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1770 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1771 // liquid_kind is set properly, see above
1772 new_node_content = liquid_kind;
1773 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1774 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1775 new_node_content = CONTENT_AIR;
1777 // no surrounding sources, so get the maximum level that can flow into this node
1778 for (u16 i = 0; i < num_flows; i++) {
1779 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1780 switch (flows[i].t) {
1781 case NEIGHBOR_UPPER:
1782 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1783 max_node_level = LIQUID_LEVEL_MAX;
1784 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1785 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1786 } else if (nb_liquid_level > max_node_level)
1787 max_node_level = nb_liquid_level;
1789 case NEIGHBOR_LOWER:
1791 case NEIGHBOR_SAME_LEVEL:
1792 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1793 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1794 max_node_level = nb_liquid_level - 1;
1800 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1801 if (viscosity > 1 && max_node_level != liquid_level) {
1802 // amount to gain, limited by viscosity
1803 // must be at least 1 in absolute value
1804 s8 level_inc = max_node_level - liquid_level;
1805 if (level_inc < -viscosity || level_inc > viscosity)
1806 new_node_level = liquid_level + level_inc/viscosity;
1807 else if (level_inc < 0)
1808 new_node_level = liquid_level - 1;
1809 else if (level_inc > 0)
1810 new_node_level = liquid_level + 1;
1811 if (new_node_level != max_node_level)
1812 must_reflow.push_back(p0);
1814 new_node_level = max_node_level;
1816 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1817 new_node_content = liquid_kind;
1819 new_node_content = CONTENT_AIR;
1824 check if anything has changed. if not, just continue with the next node.
1826 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1827 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1828 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1834 update the current node
1837 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1838 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1839 // set level to last 3 bits, flowing down bit to 4th bit
1840 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1842 // set the liquid level and flow bit to 0
1843 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1845 n0.setContent(new_node_content);
1847 // Find out whether there is a suspect for this action
1848 std::string suspect;
1849 if(m_gamedef->rollback()){
1850 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1853 if(!suspect.empty()){
1855 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1856 // Get old node for rollback
1857 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1861 RollbackNode rollback_newnode(this, p0, m_gamedef);
1862 RollbackAction action;
1863 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1864 m_gamedef->rollback()->reportAction(action);
1870 v3s16 blockpos = getNodeBlockPos(p0);
1871 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1873 modified_blocks[blockpos] = block;
1874 // If new or old node emits light, MapBlock requires lighting update
1875 if(nodemgr->get(n0).light_source != 0 ||
1876 nodemgr->get(n00).light_source != 0)
1877 lighting_modified_blocks[block->getPos()] = block;
1881 enqueue neighbors for update if neccessary
1883 switch (nodemgr->get(n0.getContent()).liquid_type) {
1885 case LIQUID_FLOWING:
1886 // make sure source flows into all neighboring nodes
1887 for (u16 i = 0; i < num_flows; i++)
1888 if (flows[i].t != NEIGHBOR_UPPER)
1889 m_transforming_liquid.push_back(flows[i].p);
1890 for (u16 i = 0; i < num_airs; i++)
1891 if (airs[i].t != NEIGHBOR_UPPER)
1892 m_transforming_liquid.push_back(airs[i].p);
1895 // this flow has turned to air; neighboring flows might need to do the same
1896 for (u16 i = 0; i < num_flows; i++)
1897 m_transforming_liquid.push_back(flows[i].p);
1901 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1902 while (must_reflow.size() > 0)
1903 m_transforming_liquid.push_back(must_reflow.pop_front());
1904 updateLighting(lighting_modified_blocks, modified_blocks);
1907 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1909 v3s16 blockpos = getNodeBlockPos(p);
1910 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1911 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1913 infostream<<"Map::getNodeMetadata(): Need to emerge "
1914 <<PP(blockpos)<<std::endl;
1915 block = emergeBlock(blockpos, false);
1918 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1922 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1926 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1928 v3s16 blockpos = getNodeBlockPos(p);
1929 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1930 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1932 infostream<<"Map::setNodeMetadata(): Need to emerge "
1933 <<PP(blockpos)<<std::endl;
1934 block = emergeBlock(blockpos, false);
1937 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1941 block->m_node_metadata.set(p_rel, meta);
1945 void Map::removeNodeMetadata(v3s16 p)
1947 v3s16 blockpos = getNodeBlockPos(p);
1948 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1949 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1952 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1956 block->m_node_metadata.remove(p_rel);
1959 NodeTimer Map::getNodeTimer(v3s16 p)
1961 v3s16 blockpos = getNodeBlockPos(p);
1962 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1963 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1965 infostream<<"Map::getNodeTimer(): Need to emerge "
1966 <<PP(blockpos)<<std::endl;
1967 block = emergeBlock(blockpos, false);
1970 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1974 NodeTimer t = block->m_node_timers.get(p_rel);
1978 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1980 v3s16 blockpos = getNodeBlockPos(p);
1981 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1982 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1984 infostream<<"Map::setNodeTimer(): Need to emerge "
1985 <<PP(blockpos)<<std::endl;
1986 block = emergeBlock(blockpos, false);
1989 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1993 block->m_node_timers.set(p_rel, t);
1996 void Map::removeNodeTimer(v3s16 p)
1998 v3s16 blockpos = getNodeBlockPos(p);
1999 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2000 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2003 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2007 block->m_node_timers.remove(p_rel);
2013 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2014 Map(dout_server, gamedef),
2016 m_map_metadata_changed(true)
2018 verbosestream<<__FUNCTION_NAME<<std::endl;
2021 Try to load map; if not found, create a new one.
2024 // Determine which database backend to use
2025 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2027 bool succeeded = conf.readConfigFile(conf_path.c_str());
2028 if (!succeeded || !conf.exists("backend")) {
2029 // fall back to sqlite3
2030 dbase = new Database_SQLite3(this, savedir);
2031 conf.set("backend", "sqlite3");
2033 std::string backend = conf.get("backend");
2034 if (backend == "dummy")
2035 dbase = new Database_Dummy(this);
2036 else if (backend == "sqlite3")
2037 dbase = new Database_SQLite3(this, savedir);
2039 else if (backend == "leveldb")
2040 dbase = new Database_LevelDB(this, savedir);
2043 else if (backend == "redis")
2044 dbase = new Database_Redis(this, savedir);
2047 throw BaseException("Unknown map backend");
2050 m_savedir = savedir;
2051 m_map_saving_enabled = false;
2055 // If directory exists, check contents and load if possible
2056 if(fs::PathExists(m_savedir))
2058 // If directory is empty, it is safe to save into it.
2059 if(fs::GetDirListing(m_savedir).size() == 0)
2061 infostream<<"ServerMap: Empty save directory is valid."
2063 m_map_saving_enabled = true;
2068 // Load map metadata (seed, chunksize)
2071 catch(SettingNotFoundException &e){
2072 infostream<<"ServerMap: Some metadata not found."
2073 <<" Using default settings."<<std::endl;
2075 catch(FileNotGoodException &e){
2076 infostream<<"WARNING: Could not load map metadata"
2077 //<<" Disabling chunk-based generator."
2082 infostream<<"ServerMap: Successfully loaded map "
2083 <<"metadata from "<<savedir
2084 <<", assuming valid save directory."
2085 <<" seed="<< m_emerge->params.seed <<"."
2088 m_map_saving_enabled = true;
2089 // Map loaded, not creating new one
2093 // If directory doesn't exist, it is safe to save to it
2095 m_map_saving_enabled = true;
2098 catch(std::exception &e)
2100 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2101 <<", exception: "<<e.what()<<std::endl;
2102 infostream<<"Please remove the map or fix it."<<std::endl;
2103 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2106 infostream<<"Initializing new map."<<std::endl;
2108 // Create zero sector
2109 emergeSector(v2s16(0,0));
2111 // Initially write whole map
2112 save(MOD_STATE_CLEAN);
2115 ServerMap::~ServerMap()
2117 verbosestream<<__FUNCTION_NAME<<std::endl;
2121 if(m_map_saving_enabled)
2123 // Save only changed parts
2124 save(MOD_STATE_WRITE_AT_UNLOAD);
2125 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2129 infostream<<"ServerMap: Map not saved"<<std::endl;
2132 catch(std::exception &e)
2134 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2135 <<", exception: "<<e.what()<<std::endl;
2139 Close database if it was opened
2147 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2148 for(; i.atEnd() == false; i++)
2150 MapChunk *chunk = i.getNode()->getValue();
2156 u64 ServerMap::getSeed()
2158 return m_emerge->params.seed;
2161 s16 ServerMap::getWaterLevel()
2163 return m_emerge->params.water_level;
2166 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2168 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2169 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2171 s16 chunksize = m_emerge->params.chunksize;
2172 s16 coffset = -chunksize / 2;
2173 v3s16 chunk_offset(coffset, coffset, coffset);
2174 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2175 v3s16 blockpos_min = blockpos_div * chunksize;
2176 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2177 blockpos_min += chunk_offset;
2178 blockpos_max += chunk_offset;
2180 v3s16 extra_borders(1,1,1);
2182 // Do nothing if not inside limits (+-1 because of neighbors)
2183 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2184 blockpos_over_limit(blockpos_max + extra_borders))
2187 data->seed = m_emerge->params.seed;
2188 data->blockpos_min = blockpos_min;
2189 data->blockpos_max = blockpos_max;
2190 data->blockpos_requested = blockpos;
2191 data->nodedef = m_gamedef->ndef();
2194 Create the whole area of this and the neighboring blocks
2197 //TimeTaker timer("initBlockMake() create area");
2199 for(s16 x=blockpos_min.X-extra_borders.X;
2200 x<=blockpos_max.X+extra_borders.X; x++)
2201 for(s16 z=blockpos_min.Z-extra_borders.Z;
2202 z<=blockpos_max.Z+extra_borders.Z; z++)
2204 v2s16 sectorpos(x, z);
2205 // Sector metadata is loaded from disk if not already loaded.
2206 ServerMapSector *sector = createSector(sectorpos);
2210 for(s16 y=blockpos_min.Y-extra_borders.Y;
2211 y<=blockpos_max.Y+extra_borders.Y; y++)
2214 //MapBlock *block = createBlock(p);
2215 // 1) get from memory, 2) load from disk
2216 MapBlock *block = emergeBlock(p, false);
2217 // 3) create a blank one
2220 block = createBlock(p);
2223 Block gets sunlight if this is true.
2225 Refer to the map generator heuristics.
2227 bool ug = m_emerge->isBlockUnderground(p);
2228 block->setIsUnderground(ug);
2231 // Lighting will not be valid after make_chunk is called
2232 block->setLightingExpired(true);
2233 // Lighting will be calculated
2234 //block->setLightingExpired(false);
2240 Now we have a big empty area.
2242 Make a ManualMapVoxelManipulator that contains this and the
2246 // The area that contains this block and it's neighbors
2247 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2248 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2250 data->vmanip = new ManualMapVoxelManipulator(this);
2251 //data->vmanip->setMap(this);
2255 //TimeTaker timer("initBlockMake() initialEmerge");
2256 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2259 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2260 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2261 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2262 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2263 core::map<v3s16, u8>::Node *n;
2264 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2267 u8 flags = n->getValue();
2268 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2274 // Data is ready now.
2278 void ServerMap::finishBlockMake(BlockMakeData *data,
2279 std::map<v3s16, MapBlock*> &changed_blocks)
2281 v3s16 blockpos_min = data->blockpos_min;
2282 v3s16 blockpos_max = data->blockpos_max;
2283 v3s16 blockpos_requested = data->blockpos_requested;
2284 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2285 <<blockpos_requested.Y<<","
2286 <<blockpos_requested.Z<<")"<<std::endl;*/
2288 v3s16 extra_borders(1,1,1);
2290 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2292 /*infostream<<"Resulting vmanip:"<<std::endl;
2293 data->vmanip.print(infostream);*/
2295 // Make sure affected blocks are loaded
2296 for(s16 x=blockpos_min.X-extra_borders.X;
2297 x<=blockpos_max.X+extra_borders.X; x++)
2298 for(s16 z=blockpos_min.Z-extra_borders.Z;
2299 z<=blockpos_max.Z+extra_borders.Z; z++)
2300 for(s16 y=blockpos_min.Y-extra_borders.Y;
2301 y<=blockpos_max.Y+extra_borders.Y; y++)
2304 // Load from disk if not already in memory
2305 emergeBlock(p, false);
2309 Blit generated stuff to map
2310 NOTE: blitBackAll adds nearly everything to changed_blocks
2314 //TimeTaker timer("finishBlockMake() blitBackAll");
2315 data->vmanip->blitBackAll(&changed_blocks);
2318 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2321 Copy transforming liquid information
2323 while(data->transforming_liquid.size() > 0)
2325 v3s16 p = data->transforming_liquid.pop_front();
2326 m_transforming_liquid.push_back(p);
2330 Do stuff in central blocks
2338 TimeTaker t("finishBlockMake lighting update");
2340 core::map<v3s16, MapBlock*> lighting_update_blocks;
2343 for(s16 x=blockpos_min.X-extra_borders.X;
2344 x<=blockpos_max.X+extra_borders.X; x++)
2345 for(s16 z=blockpos_min.Z-extra_borders.Z;
2346 z<=blockpos_max.Z+extra_borders.Z; z++)
2347 for(s16 y=blockpos_min.Y-extra_borders.Y;
2348 y<=blockpos_max.Y+extra_borders.Y; y++)
2351 MapBlock *block = getBlockNoCreateNoEx(p);
2353 lighting_update_blocks.insert(block->getPos(), block);
2356 updateLighting(lighting_update_blocks, changed_blocks);
2360 Set lighting to non-expired state in all of them.
2361 This is cheating, but it is not fast enough if all of them
2362 would actually be updated.
2364 for(s16 x=blockpos_min.X-extra_borders.X;
2365 x<=blockpos_max.X+extra_borders.X; x++)
2366 for(s16 z=blockpos_min.Z-extra_borders.Z;
2367 z<=blockpos_max.Z+extra_borders.Z; z++)
2368 for(s16 y=blockpos_min.Y-extra_borders.Y;
2369 y<=blockpos_max.Y+extra_borders.Y; y++)
2372 MapBlock * block = getBlockNoCreateNoEx(p);
2374 block->setLightingExpired(false);
2378 if(enable_mapgen_debug_info == false)
2379 t.stop(true); // Hide output
2384 Go through changed blocks
2386 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2387 i != changed_blocks.end(); ++i)
2389 MapBlock *block = i->second;
2393 Update day/night difference cache of the MapBlocks
2395 block->expireDayNightDiff();
2397 Set block as modified
2399 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2400 "finishBlockMake expireDayNightDiff");
2404 Set central blocks as generated
2406 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2407 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2408 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2411 MapBlock *block = getBlockNoCreateNoEx(p);
2414 block->setGenerated(true);
2418 Save changed parts of map
2419 NOTE: Will be saved later.
2421 //save(MOD_STATE_WRITE_AT_UNLOAD);
2423 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2424 <<","<<blockpos_requested.Y<<","
2425 <<blockpos_requested.Z<<")"<<std::endl;*/
2429 if(enable_mapgen_debug_info)
2432 Analyze resulting blocks
2434 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2435 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2436 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2437 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2438 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2439 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2441 v3s16 p = v3s16(x,y,z);
2442 MapBlock *block = getBlockNoCreateNoEx(p);
2444 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2445 infostream<<"Generated "<<spos<<": "
2446 <<analyze_block(block)<<std::endl;
2451 getBlockNoCreateNoEx(blockpos_requested);
2454 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2456 DSTACKF("%s: p2d=(%d,%d)",
2461 Check if it exists already in memory
2463 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2468 Try to load it from disk (with blocks)
2470 //if(loadSectorFull(p2d) == true)
2473 Try to load metadata from disk
2476 if(loadSectorMeta(p2d) == true)
2478 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2481 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2482 throw InvalidPositionException("");
2488 Do not create over-limit
2490 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2491 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2492 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2493 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2494 throw InvalidPositionException("createSector(): pos. over limit");
2497 Generate blank sector
2500 sector = new ServerMapSector(this, p2d, m_gamedef);
2502 // Sector position on map in nodes
2503 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2508 m_sectors[p2d] = sector;
2515 This is a quick-hand function for calling makeBlock().
2517 MapBlock * ServerMap::generateBlock(
2519 std::map<v3s16, MapBlock*> &modified_blocks
2522 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2524 /*infostream<<"generateBlock(): "
2525 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2528 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2530 TimeTaker timer("generateBlock");
2532 //MapBlock *block = original_dummy;
2534 v2s16 p2d(p.X, p.Z);
2535 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2538 Do not generate over-limit
2540 if(blockpos_over_limit(p))
2542 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2543 throw InvalidPositionException("generateBlock(): pos. over limit");
2547 Create block make data
2550 initBlockMake(&data, p);
2556 TimeTaker t("mapgen::make_block()");
2557 mapgen->makeChunk(&data);
2558 //mapgen::make_block(&data);
2560 if(enable_mapgen_debug_info == false)
2561 t.stop(true); // Hide output
2565 Blit data back on map, update lighting, add mobs and whatever this does
2567 finishBlockMake(&data, modified_blocks);
2572 MapBlock *block = getBlockNoCreateNoEx(p);
2580 bool erroneus_content = false;
2581 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2582 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2583 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2586 MapNode n = block->getNode(p);
2587 if(n.getContent() == CONTENT_IGNORE)
2589 infostream<<"CONTENT_IGNORE at "
2590 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2592 erroneus_content = true;
2596 if(erroneus_content)
2605 Generate a completely empty block
2609 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2610 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2612 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2615 n.setContent(CONTENT_AIR);
2616 block->setNode(v3s16(x0,y0,z0), n);
2622 if(enable_mapgen_debug_info == false)
2623 timer.stop(true); // Hide output
2629 MapBlock * ServerMap::createBlock(v3s16 p)
2631 DSTACKF("%s: p=(%d,%d,%d)",
2632 __FUNCTION_NAME, p.X, p.Y, p.Z);
2635 Do not create over-limit
2637 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2638 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2639 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2640 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2641 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2642 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2643 throw InvalidPositionException("createBlock(): pos. over limit");
2645 v2s16 p2d(p.X, p.Z);
2648 This will create or load a sector if not found in memory.
2649 If block exists on disk, it will be loaded.
2651 NOTE: On old save formats, this will be slow, as it generates
2652 lighting on blocks for them.
2654 ServerMapSector *sector;
2656 sector = (ServerMapSector*)createSector(p2d);
2657 assert(sector->getId() == MAPSECTOR_SERVER);
2659 catch(InvalidPositionException &e)
2661 infostream<<"createBlock: createSector() failed"<<std::endl;
2665 NOTE: This should not be done, or at least the exception
2666 should not be passed on as std::exception, because it
2667 won't be catched at all.
2669 /*catch(std::exception &e)
2671 infostream<<"createBlock: createSector() failed: "
2672 <<e.what()<<std::endl;
2677 Try to get a block from the sector
2680 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2683 if(block->isDummy())
2688 block = sector->createBlankBlock(block_y);
2693 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2695 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2697 p.X, p.Y, p.Z, create_blank);
2700 MapBlock *block = getBlockNoCreateNoEx(p);
2701 if(block && block->isDummy() == false)
2706 MapBlock *block = loadBlock(p);
2712 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2713 MapBlock *block = sector->createBlankBlock(p.Y);
2721 std::map<v3s16, MapBlock*> modified_blocks;
2722 MapBlock *block = generateBlock(p, modified_blocks);
2726 event.type = MEET_OTHER;
2729 // Copy modified_blocks to event
2730 for(std::map<v3s16, MapBlock*>::iterator
2731 i = modified_blocks.begin();
2732 i != modified_blocks.end(); ++i)
2734 event.modified_blocks.insert(i->first);
2738 dispatchEvent(&event);
2748 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2750 MapBlock *block = getBlockNoCreateNoEx(p3d);
2752 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2757 void ServerMap::prepareBlock(MapBlock *block) {
2760 // N.B. This requires no synchronization, since data will not be modified unless
2761 // the VoxelManipulator being updated belongs to the same thread.
2762 void ServerMap::updateVManip(v3s16 pos)
2764 Mapgen *mg = m_emerge->getCurrentMapgen();
2768 ManualMapVoxelManipulator *vm = mg->vm;
2772 if (!vm->m_area.contains(pos))
2775 s32 idx = vm->m_area.index(pos);
2776 vm->m_data[idx] = getNodeNoEx(pos);
2777 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2779 vm->m_is_dirty = true;
2782 s16 ServerMap::findGroundLevel(v2s16 p2d)
2786 Uh, just do something random...
2788 // Find existing map from top to down
2791 v3s16 p(p2d.X, max, p2d.Y);
2792 for(; p.Y>min; p.Y--)
2794 MapNode n = getNodeNoEx(p);
2795 if(n.getContent() != CONTENT_IGNORE)
2800 // If this node is not air, go to plan b
2801 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2803 // Search existing walkable and return it
2804 for(; p.Y>min; p.Y--)
2806 MapNode n = getNodeNoEx(p);
2807 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2816 Determine from map generator noise functions
2819 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2822 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2823 //return (s16)level;
2826 bool ServerMap::loadFromFolders() {
2827 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2832 void ServerMap::createDirs(std::string path)
2834 if(fs::CreateAllDirs(path) == false)
2836 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2837 <<"\""<<path<<"\""<<std::endl;
2838 throw BaseException("ServerMap failed to create directory");
2842 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2848 snprintf(cc, 9, "%.4x%.4x",
2849 (unsigned int)pos.X&0xffff,
2850 (unsigned int)pos.Y&0xffff);
2852 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2854 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2855 (unsigned int)pos.X&0xfff,
2856 (unsigned int)pos.Y&0xfff);
2858 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2865 v2s16 ServerMap::getSectorPos(std::string dirname)
2867 unsigned int x = 0, y = 0;
2869 std::string component;
2870 fs::RemoveLastPathComponent(dirname, &component, 1);
2871 if(component.size() == 8)
2874 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2876 else if(component.size() == 3)
2879 fs::RemoveLastPathComponent(dirname, &component, 2);
2880 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2881 // Sign-extend the 12 bit values up to 16 bits...
2882 if(x&0x800) x|=0xF000;
2883 if(y&0x800) y|=0xF000;
2890 v2s16 pos((s16)x, (s16)y);
2894 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2896 v2s16 p2d = getSectorPos(sectordir);
2898 if(blockfile.size() != 4){
2899 throw InvalidFilenameException("Invalid block filename");
2902 int r = sscanf(blockfile.c_str(), "%4x", &y);
2904 throw InvalidFilenameException("Invalid block filename");
2905 return v3s16(p2d.X, y, p2d.Y);
2908 std::string ServerMap::getBlockFilename(v3s16 p)
2911 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2915 void ServerMap::save(ModifiedState save_level)
2917 DSTACK(__FUNCTION_NAME);
2918 if(m_map_saving_enabled == false)
2920 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2924 if(save_level == MOD_STATE_CLEAN)
2925 infostream<<"ServerMap: Saving whole map, this can take time."
2928 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2933 // Profile modified reasons
2934 Profiler modprofiler;
2936 u32 sector_meta_count = 0;
2937 u32 block_count = 0;
2938 u32 block_count_all = 0; // Number of blocks in memory
2940 // Don't do anything with sqlite unless something is really saved
2941 bool save_started = false;
2943 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2944 i != m_sectors.end(); ++i)
2946 ServerMapSector *sector = (ServerMapSector*)i->second;
2947 assert(sector->getId() == MAPSECTOR_SERVER);
2949 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2951 saveSectorMeta(sector);
2952 sector_meta_count++;
2954 std::list<MapBlock*> blocks;
2955 sector->getBlocks(blocks);
2957 for(std::list<MapBlock*>::iterator j = blocks.begin();
2958 j != blocks.end(); ++j)
2960 MapBlock *block = *j;
2964 if(block->getModified() >= (u32)save_level)
2969 save_started = true;
2972 modprofiler.add(block->getModifiedReason(), 1);
2977 /*infostream<<"ServerMap: Written block ("
2978 <<block->getPos().X<<","
2979 <<block->getPos().Y<<","
2980 <<block->getPos().Z<<")"
2989 Only print if something happened or saved whole map
2991 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2992 || block_count != 0)
2994 infostream<<"ServerMap: Written: "
2995 <<sector_meta_count<<" sector metadata files, "
2996 <<block_count<<" block files"
2997 <<", "<<block_count_all<<" blocks in memory."
2999 PrintInfo(infostream); // ServerMap/ClientMap:
3000 infostream<<"Blocks modified by: "<<std::endl;
3001 modprofiler.print(infostream);
3005 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3007 if(loadFromFolders()){
3008 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3009 <<"all blocks that are stored in flat files"<<std::endl;
3011 dbase->listAllLoadableBlocks(dst);
3014 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3016 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3017 si != m_sectors.end(); ++si)
3019 MapSector *sector = si->second;
3021 std::list<MapBlock*> blocks;
3022 sector->getBlocks(blocks);
3024 for(std::list<MapBlock*>::iterator i = blocks.begin();
3025 i != blocks.end(); ++i)
3027 MapBlock *block = (*i);
3028 v3s16 p = block->getPos();
3034 void ServerMap::saveMapMeta()
3036 DSTACK(__FUNCTION_NAME);
3038 /*infostream<<"ServerMap::saveMapMeta(): "
3042 createDirs(m_savedir);
3044 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3045 std::ostringstream ss(std::ios_base::binary);
3049 m_emerge->saveParamsToSettings(¶ms);
3050 params.writeLines(ss);
3052 ss<<"[end_of_params]\n";
3054 if(!fs::safeWriteToFile(fullpath, ss.str()))
3056 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3057 <<"could not write "<<fullpath<<std::endl;
3058 throw FileNotGoodException("Cannot save chunk metadata");
3061 m_map_metadata_changed = false;
3064 void ServerMap::loadMapMeta()
3066 DSTACK(__FUNCTION_NAME);
3068 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3071 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3072 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3073 if(is.good() == false)
3075 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3076 <<"could not open"<<fullpath<<std::endl;
3077 throw FileNotGoodException("Cannot open map metadata");
3085 throw SerializationError
3086 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3088 std::getline(is, line);
3089 std::string trimmedline = trim(line);
3090 if(trimmedline == "[end_of_params]")
3092 params.parseConfigLine(line);
3095 m_emerge->loadParamsFromSettings(¶ms);
3097 verbosestream<<"ServerMap::loadMapMeta(): seed="
3098 << m_emerge->params.seed<<std::endl;
3101 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3103 DSTACK(__FUNCTION_NAME);
3104 // Format used for writing
3105 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3107 v2s16 pos = sector->getPos();
3108 std::string dir = getSectorDir(pos);
3111 std::string fullpath = dir + DIR_DELIM + "meta";
3112 std::ostringstream ss(std::ios_base::binary);
3114 sector->serialize(ss, version);
3116 if(!fs::safeWriteToFile(fullpath, ss.str()))
3117 throw FileNotGoodException("Cannot write sector metafile");
3119 sector->differs_from_disk = false;
3122 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3124 DSTACK(__FUNCTION_NAME);
3126 v2s16 p2d = getSectorPos(sectordir);
3128 ServerMapSector *sector = NULL;
3130 std::string fullpath = sectordir + DIR_DELIM + "meta";
3131 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3132 if(is.good() == false)
3134 // If the directory exists anyway, it probably is in some old
3135 // format. Just go ahead and create the sector.
3136 if(fs::PathExists(sectordir))
3138 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3139 <<fullpath<<" doesn't exist but directory does."
3140 <<" Continuing with a sector with no metadata."
3142 sector = new ServerMapSector(this, p2d, m_gamedef);
3143 m_sectors[p2d] = sector;
3147 throw FileNotGoodException("Cannot open sector metafile");
3152 sector = ServerMapSector::deSerialize
3153 (is, this, p2d, m_sectors, m_gamedef);
3155 saveSectorMeta(sector);
3158 sector->differs_from_disk = false;
3163 bool ServerMap::loadSectorMeta(v2s16 p2d)
3165 DSTACK(__FUNCTION_NAME);
3167 MapSector *sector = NULL;
3169 // The directory layout we're going to load from.
3170 // 1 - original sectors/xxxxzzzz/
3171 // 2 - new sectors2/xxx/zzz/
3172 // If we load from anything but the latest structure, we will
3173 // immediately save to the new one, and remove the old.
3175 std::string sectordir1 = getSectorDir(p2d, 1);
3176 std::string sectordir;
3177 if(fs::PathExists(sectordir1))
3179 sectordir = sectordir1;
3184 sectordir = getSectorDir(p2d, 2);
3188 sector = loadSectorMeta(sectordir, loadlayout != 2);
3190 catch(InvalidFilenameException &e)
3194 catch(FileNotGoodException &e)
3198 catch(std::exception &e)
3207 bool ServerMap::loadSectorFull(v2s16 p2d)
3209 DSTACK(__FUNCTION_NAME);
3211 MapSector *sector = NULL;
3213 // The directory layout we're going to load from.
3214 // 1 - original sectors/xxxxzzzz/
3215 // 2 - new sectors2/xxx/zzz/
3216 // If we load from anything but the latest structure, we will
3217 // immediately save to the new one, and remove the old.
3219 std::string sectordir1 = getSectorDir(p2d, 1);
3220 std::string sectordir;
3221 if(fs::PathExists(sectordir1))
3223 sectordir = sectordir1;
3228 sectordir = getSectorDir(p2d, 2);
3232 sector = loadSectorMeta(sectordir, loadlayout != 2);
3234 catch(InvalidFilenameException &e)
3238 catch(FileNotGoodException &e)
3242 catch(std::exception &e)
3250 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3252 std::vector<fs::DirListNode>::iterator i2;
3253 for(i2=list2.begin(); i2!=list2.end(); i2++)
3259 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3261 catch(InvalidFilenameException &e)
3263 // This catches unknown crap in directory
3269 infostream<<"Sector converted to new layout - deleting "<<
3270 sectordir1<<std::endl;
3271 fs::RecursiveDelete(sectordir1);
3278 void ServerMap::beginSave()
3283 void ServerMap::endSave()
3288 bool ServerMap::saveBlock(MapBlock *block)
3290 return saveBlock(block, dbase);
3293 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3295 v3s16 p3d = block->getPos();
3297 // Dummy blocks are not written
3298 if (block->isDummy()) {
3299 errorstream << "WARNING: saveBlock: Not writing dummy block "
3300 << PP(p3d) << std::endl;
3304 // Format used for writing
3305 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3308 [0] u8 serialization version
3311 std::ostringstream o(std::ios_base::binary);
3312 o.write((char*) &version, 1);
3313 block->serialize(o, version, true);
3315 std::string data = o.str();
3316 bool ret = db->saveBlock(p3d, data);
3318 // We just wrote it to the disk so clear modified flag
3319 block->resetModified();
3324 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3325 MapSector *sector, bool save_after_load)
3327 DSTACK(__FUNCTION_NAME);
3329 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3332 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3333 if(is.good() == false)
3334 throw FileNotGoodException("Cannot open block file");
3336 v3s16 p3d = getBlockPos(sectordir, blockfile);
3337 v2s16 p2d(p3d.X, p3d.Z);
3339 assert(sector->getPos() == p2d);
3341 u8 version = SER_FMT_VER_INVALID;
3342 is.read((char*)&version, 1);
3345 throw SerializationError("ServerMap::loadBlock(): Failed"
3346 " to read MapBlock version");
3348 /*u32 block_size = MapBlock::serializedLength(version);
3349 SharedBuffer<u8> data(block_size);
3350 is.read((char*)*data, block_size);*/
3352 // This will always return a sector because we're the server
3353 //MapSector *sector = emergeSector(p2d);
3355 MapBlock *block = NULL;
3356 bool created_new = false;
3357 block = sector->getBlockNoCreateNoEx(p3d.Y);
3360 block = sector->createBlankBlockNoInsert(p3d.Y);
3365 block->deSerialize(is, version, true);
3367 // If it's a new block, insert it to the map
3369 sector->insertBlock(block);
3372 Save blocks loaded in old format in new format
3375 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3379 // Should be in database now, so delete the old file
3380 fs::RecursiveDelete(fullpath);
3383 // We just loaded it from the disk, so it's up-to-date.
3384 block->resetModified();
3387 catch(SerializationError &e)
3389 infostream<<"WARNING: Invalid block data on disk "
3390 <<"fullpath="<<fullpath
3391 <<" (SerializationError). "
3392 <<"what()="<<e.what()
3394 // Ignoring. A new one will be generated.
3397 // TODO: Backup file; name is in fullpath.
3401 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3403 DSTACK(__FUNCTION_NAME);
3406 std::istringstream is(*blob, std::ios_base::binary);
3408 u8 version = SER_FMT_VER_INVALID;
3409 is.read((char*)&version, 1);
3412 throw SerializationError("ServerMap::loadBlock(): Failed"
3413 " to read MapBlock version");
3415 /*u32 block_size = MapBlock::serializedLength(version);
3416 SharedBuffer<u8> data(block_size);
3417 is.read((char*)*data, block_size);*/
3419 // This will always return a sector because we're the server
3420 //MapSector *sector = emergeSector(p2d);
3422 MapBlock *block = NULL;
3423 bool created_new = false;
3424 block = sector->getBlockNoCreateNoEx(p3d.Y);
3427 block = sector->createBlankBlockNoInsert(p3d.Y);
3432 block->deSerialize(is, version, true);
3434 // If it's a new block, insert it to the map
3436 sector->insertBlock(block);
3439 Save blocks loaded in old format in new format
3442 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3443 // Only save if asked to; no need to update version
3447 // We just loaded it from, so it's up-to-date.
3448 block->resetModified();
3451 catch(SerializationError &e)
3453 errorstream<<"Invalid block data in database"
3454 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3455 <<" (SerializationError): "<<e.what()<<std::endl;
3457 // TODO: Block should be marked as invalid in memory so that it is
3458 // not touched but the game can run
3460 if(g_settings->getBool("ignore_world_load_errors")){
3461 errorstream<<"Ignoring block load error. Duck and cover! "
3462 <<"(ignore_world_load_errors)"<<std::endl;
3464 throw SerializationError("Invalid block data in database");
3470 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3472 DSTACK(__FUNCTION_NAME);
3474 v2s16 p2d(blockpos.X, blockpos.Z);
3478 ret = dbase->loadBlock(blockpos);
3480 loadBlock(&ret, blockpos, createSector(p2d), false);
3481 return getBlockNoCreateNoEx(blockpos);
3483 // Not found in database, try the files
3485 // The directory layout we're going to load from.
3486 // 1 - original sectors/xxxxzzzz/
3487 // 2 - new sectors2/xxx/zzz/
3488 // If we load from anything but the latest structure, we will
3489 // immediately save to the new one, and remove the old.
3491 std::string sectordir1 = getSectorDir(p2d, 1);
3492 std::string sectordir;
3493 if(fs::PathExists(sectordir1))
3495 sectordir = sectordir1;
3500 sectordir = getSectorDir(p2d, 2);
3504 Make sure sector is loaded
3506 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3510 sector = loadSectorMeta(sectordir, loadlayout != 2);
3512 catch(InvalidFilenameException &e)
3516 catch(FileNotGoodException &e)
3520 catch(std::exception &e)
3527 Make sure file exists
3530 std::string blockfilename = getBlockFilename(blockpos);
3531 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3535 Load block and save it to the database
3537 loadBlock(sectordir, blockfilename, sector, true);
3538 return getBlockNoCreateNoEx(blockpos);
3541 void ServerMap::PrintInfo(std::ostream &out)
3546 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3549 m_create_area(false),
3554 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3558 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
3559 v3s16 blockpos_max, bool load_if_inexistent)
3561 TimeTaker timer1("initialEmerge", &emerge_time);
3563 // Units of these are MapBlocks
3564 v3s16 p_min = blockpos_min;
3565 v3s16 p_max = blockpos_max;
3567 VoxelArea block_area_nodes
3568 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3570 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3573 infostream<<"initialEmerge: area: ";
3574 block_area_nodes.print(infostream);
3575 infostream<<" ("<<size_MB<<"MB)";
3576 infostream<<std::endl;
3579 addArea(block_area_nodes);
3581 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3582 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3583 for(s32 x=p_min.X; x<=p_max.X; x++)
3588 std::map<v3s16, u8>::iterator n;
3589 n = m_loaded_blocks.find(p);
3590 if(n != m_loaded_blocks.end())
3593 bool block_data_inexistent = false;
3596 TimeTaker timer1("emerge load", &emerge_load_time);
3598 block = m_map->getBlockNoCreate(p);
3599 if(block->isDummy())
3600 block_data_inexistent = true;
3602 block->copyTo(*this);
3604 catch(InvalidPositionException &e)
3606 block_data_inexistent = true;
3609 if(block_data_inexistent)
3612 if (load_if_inexistent) {
3613 ServerMap *svrmap = (ServerMap *)m_map;
3614 block = svrmap->emergeBlock(p, false);
3616 block = svrmap->createBlock(p);
3618 block->copyTo(*this);
3620 flags |= VMANIP_BLOCK_DATA_INEXIST;
3623 Mark area inexistent
3625 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3626 // Fill with VOXELFLAG_NO_DATA
3627 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3628 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3630 s32 i = m_area.index(a.MinEdge.X,y,z);
3631 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3635 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3637 // Mark that block was loaded as blank
3638 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3641 m_loaded_blocks[p] = flags;
3647 void ManualMapVoxelManipulator::blitBackAll(
3648 std::map<v3s16, MapBlock*> *modified_blocks,
3649 bool overwrite_generated)
3651 if(m_area.getExtent() == v3s16(0,0,0))
3655 Copy data of all blocks
3657 for(std::map<v3s16, u8>::iterator
3658 i = m_loaded_blocks.begin();
3659 i != m_loaded_blocks.end(); ++i)
3662 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3663 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3664 if ((existed == false) || (block == NULL) ||
3665 (overwrite_generated == false && block->isGenerated() == true))
3668 block->copyFrom(*this);
3671 (*modified_blocks)[p] = block;