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
1477 && save_before_unloading)
1479 modprofiler.add(block->getModifiedReason(), 1);
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 s16 ServerMap::findGroundLevel(v2s16 p2d)
2764 Uh, just do something random...
2766 // Find existing map from top to down
2769 v3s16 p(p2d.X, max, p2d.Y);
2770 for(; p.Y>min; p.Y--)
2772 MapNode n = getNodeNoEx(p);
2773 if(n.getContent() != CONTENT_IGNORE)
2778 // If this node is not air, go to plan b
2779 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2781 // Search existing walkable and return it
2782 for(; p.Y>min; p.Y--)
2784 MapNode n = getNodeNoEx(p);
2785 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2794 Determine from map generator noise functions
2797 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2800 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2801 //return (s16)level;
2804 bool ServerMap::loadFromFolders() {
2805 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2810 void ServerMap::createDirs(std::string path)
2812 if(fs::CreateAllDirs(path) == false)
2814 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2815 <<"\""<<path<<"\""<<std::endl;
2816 throw BaseException("ServerMap failed to create directory");
2820 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2826 snprintf(cc, 9, "%.4x%.4x",
2827 (unsigned int)pos.X&0xffff,
2828 (unsigned int)pos.Y&0xffff);
2830 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2832 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2833 (unsigned int)pos.X&0xfff,
2834 (unsigned int)pos.Y&0xfff);
2836 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2843 v2s16 ServerMap::getSectorPos(std::string dirname)
2845 unsigned int x = 0, y = 0;
2847 std::string component;
2848 fs::RemoveLastPathComponent(dirname, &component, 1);
2849 if(component.size() == 8)
2852 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2854 else if(component.size() == 3)
2857 fs::RemoveLastPathComponent(dirname, &component, 2);
2858 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2859 // Sign-extend the 12 bit values up to 16 bits...
2860 if(x&0x800) x|=0xF000;
2861 if(y&0x800) y|=0xF000;
2868 v2s16 pos((s16)x, (s16)y);
2872 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2874 v2s16 p2d = getSectorPos(sectordir);
2876 if(blockfile.size() != 4){
2877 throw InvalidFilenameException("Invalid block filename");
2880 int r = sscanf(blockfile.c_str(), "%4x", &y);
2882 throw InvalidFilenameException("Invalid block filename");
2883 return v3s16(p2d.X, y, p2d.Y);
2886 std::string ServerMap::getBlockFilename(v3s16 p)
2889 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2893 void ServerMap::save(ModifiedState save_level)
2895 DSTACK(__FUNCTION_NAME);
2896 if(m_map_saving_enabled == false)
2898 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2902 if(save_level == MOD_STATE_CLEAN)
2903 infostream<<"ServerMap: Saving whole map, this can take time."
2906 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2911 // Profile modified reasons
2912 Profiler modprofiler;
2914 u32 sector_meta_count = 0;
2915 u32 block_count = 0;
2916 u32 block_count_all = 0; // Number of blocks in memory
2918 // Don't do anything with sqlite unless something is really saved
2919 bool save_started = false;
2921 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2922 i != m_sectors.end(); ++i)
2924 ServerMapSector *sector = (ServerMapSector*)i->second;
2925 assert(sector->getId() == MAPSECTOR_SERVER);
2927 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2929 saveSectorMeta(sector);
2930 sector_meta_count++;
2932 std::list<MapBlock*> blocks;
2933 sector->getBlocks(blocks);
2935 for(std::list<MapBlock*>::iterator j = blocks.begin();
2936 j != blocks.end(); ++j)
2938 MapBlock *block = *j;
2942 if(block->getModified() >= (u32)save_level)
2947 save_started = true;
2950 modprofiler.add(block->getModifiedReason(), 1);
2955 /*infostream<<"ServerMap: Written block ("
2956 <<block->getPos().X<<","
2957 <<block->getPos().Y<<","
2958 <<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)
2972 infostream<<"ServerMap: Written: "
2973 <<sector_meta_count<<" sector metadata files, "
2974 <<block_count<<" block files"
2975 <<", "<<block_count_all<<" blocks in memory."
2977 PrintInfo(infostream); // ServerMap/ClientMap:
2978 infostream<<"Blocks modified by: "<<std::endl;
2979 modprofiler.print(infostream);
2983 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
2985 if(loadFromFolders()){
2986 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2987 <<"all blocks that are stored in flat files"<<std::endl;
2989 dbase->listAllLoadableBlocks(dst);
2992 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
2994 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2995 si != m_sectors.end(); ++si)
2997 MapSector *sector = si->second;
2999 std::list<MapBlock*> blocks;
3000 sector->getBlocks(blocks);
3002 for(std::list<MapBlock*>::iterator i = blocks.begin();
3003 i != blocks.end(); ++i)
3005 MapBlock *block = (*i);
3006 v3s16 p = block->getPos();
3012 void ServerMap::saveMapMeta()
3014 DSTACK(__FUNCTION_NAME);
3016 /*infostream<<"ServerMap::saveMapMeta(): "
3020 createDirs(m_savedir);
3022 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3023 std::ostringstream ss(std::ios_base::binary);
3027 m_emerge->saveParamsToSettings(¶ms);
3028 params.writeLines(ss);
3030 ss<<"[end_of_params]\n";
3032 if(!fs::safeWriteToFile(fullpath, ss.str()))
3034 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3035 <<"could not write "<<fullpath<<std::endl;
3036 throw FileNotGoodException("Cannot save chunk metadata");
3039 m_map_metadata_changed = false;
3042 void ServerMap::loadMapMeta()
3044 DSTACK(__FUNCTION_NAME);
3046 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3049 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3050 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3051 if(is.good() == false)
3053 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3054 <<"could not open"<<fullpath<<std::endl;
3055 throw FileNotGoodException("Cannot open map metadata");
3063 throw SerializationError
3064 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3066 std::getline(is, line);
3067 std::string trimmedline = trim(line);
3068 if(trimmedline == "[end_of_params]")
3070 params.parseConfigLine(line);
3073 m_emerge->loadParamsFromSettings(¶ms);
3075 verbosestream<<"ServerMap::loadMapMeta(): seed="
3076 << m_emerge->params.seed<<std::endl;
3079 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3081 DSTACK(__FUNCTION_NAME);
3082 // Format used for writing
3083 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3085 v2s16 pos = sector->getPos();
3086 std::string dir = getSectorDir(pos);
3089 std::string fullpath = dir + DIR_DELIM + "meta";
3090 std::ostringstream ss(std::ios_base::binary);
3092 sector->serialize(ss, version);
3094 if(!fs::safeWriteToFile(fullpath, ss.str()))
3095 throw FileNotGoodException("Cannot write sector metafile");
3097 sector->differs_from_disk = false;
3100 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3102 DSTACK(__FUNCTION_NAME);
3104 v2s16 p2d = getSectorPos(sectordir);
3106 ServerMapSector *sector = NULL;
3108 std::string fullpath = sectordir + DIR_DELIM + "meta";
3109 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3110 if(is.good() == false)
3112 // If the directory exists anyway, it probably is in some old
3113 // format. Just go ahead and create the sector.
3114 if(fs::PathExists(sectordir))
3116 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3117 <<fullpath<<" doesn't exist but directory does."
3118 <<" Continuing with a sector with no metadata."
3120 sector = new ServerMapSector(this, p2d, m_gamedef);
3121 m_sectors[p2d] = sector;
3125 throw FileNotGoodException("Cannot open sector metafile");
3130 sector = ServerMapSector::deSerialize
3131 (is, this, p2d, m_sectors, m_gamedef);
3133 saveSectorMeta(sector);
3136 sector->differs_from_disk = false;
3141 bool ServerMap::loadSectorMeta(v2s16 p2d)
3143 DSTACK(__FUNCTION_NAME);
3145 MapSector *sector = NULL;
3147 // The directory layout we're going to load from.
3148 // 1 - original sectors/xxxxzzzz/
3149 // 2 - new sectors2/xxx/zzz/
3150 // If we load from anything but the latest structure, we will
3151 // immediately save to the new one, and remove the old.
3153 std::string sectordir1 = getSectorDir(p2d, 1);
3154 std::string sectordir;
3155 if(fs::PathExists(sectordir1))
3157 sectordir = sectordir1;
3162 sectordir = getSectorDir(p2d, 2);
3166 sector = loadSectorMeta(sectordir, loadlayout != 2);
3168 catch(InvalidFilenameException &e)
3172 catch(FileNotGoodException &e)
3176 catch(std::exception &e)
3185 bool ServerMap::loadSectorFull(v2s16 p2d)
3187 DSTACK(__FUNCTION_NAME);
3189 MapSector *sector = NULL;
3191 // The directory layout we're going to load from.
3192 // 1 - original sectors/xxxxzzzz/
3193 // 2 - new sectors2/xxx/zzz/
3194 // If we load from anything but the latest structure, we will
3195 // immediately save to the new one, and remove the old.
3197 std::string sectordir1 = getSectorDir(p2d, 1);
3198 std::string sectordir;
3199 if(fs::PathExists(sectordir1))
3201 sectordir = sectordir1;
3206 sectordir = getSectorDir(p2d, 2);
3210 sector = loadSectorMeta(sectordir, loadlayout != 2);
3212 catch(InvalidFilenameException &e)
3216 catch(FileNotGoodException &e)
3220 catch(std::exception &e)
3228 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3230 std::vector<fs::DirListNode>::iterator i2;
3231 for(i2=list2.begin(); i2!=list2.end(); i2++)
3237 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3239 catch(InvalidFilenameException &e)
3241 // This catches unknown crap in directory
3247 infostream<<"Sector converted to new layout - deleting "<<
3248 sectordir1<<std::endl;
3249 fs::RecursiveDelete(sectordir1);
3256 void ServerMap::beginSave() {
3260 void ServerMap::endSave() {
3264 void ServerMap::saveBlock(MapBlock *block)
3266 dbase->saveBlock(block);
3269 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3271 DSTACK(__FUNCTION_NAME);
3273 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3276 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3277 if(is.good() == false)
3278 throw FileNotGoodException("Cannot open block file");
3280 v3s16 p3d = getBlockPos(sectordir, blockfile);
3281 v2s16 p2d(p3d.X, p3d.Z);
3283 assert(sector->getPos() == p2d);
3285 u8 version = SER_FMT_VER_INVALID;
3286 is.read((char*)&version, 1);
3289 throw SerializationError("ServerMap::loadBlock(): Failed"
3290 " to read MapBlock version");
3292 /*u32 block_size = MapBlock::serializedLength(version);
3293 SharedBuffer<u8> data(block_size);
3294 is.read((char*)*data, block_size);*/
3296 // This will always return a sector because we're the server
3297 //MapSector *sector = emergeSector(p2d);
3299 MapBlock *block = NULL;
3300 bool created_new = false;
3301 block = sector->getBlockNoCreateNoEx(p3d.Y);
3304 block = sector->createBlankBlockNoInsert(p3d.Y);
3309 block->deSerialize(is, version, true);
3311 // If it's a new block, insert it to the map
3313 sector->insertBlock(block);
3316 Save blocks loaded in old format in new format
3319 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3323 // Should be in database now, so delete the old file
3324 fs::RecursiveDelete(fullpath);
3327 // We just loaded it from the disk, so it's up-to-date.
3328 block->resetModified();
3331 catch(SerializationError &e)
3333 infostream<<"WARNING: Invalid block data on disk "
3334 <<"fullpath="<<fullpath
3335 <<" (SerializationError). "
3336 <<"what()="<<e.what()
3338 // Ignoring. A new one will be generated.
3341 // TODO: Backup file; name is in fullpath.
3345 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3347 DSTACK(__FUNCTION_NAME);
3350 std::istringstream is(*blob, std::ios_base::binary);
3352 u8 version = SER_FMT_VER_INVALID;
3353 is.read((char*)&version, 1);
3356 throw SerializationError("ServerMap::loadBlock(): Failed"
3357 " to read MapBlock version");
3359 /*u32 block_size = MapBlock::serializedLength(version);
3360 SharedBuffer<u8> data(block_size);
3361 is.read((char*)*data, block_size);*/
3363 // This will always return a sector because we're the server
3364 //MapSector *sector = emergeSector(p2d);
3366 MapBlock *block = NULL;
3367 bool created_new = false;
3368 block = sector->getBlockNoCreateNoEx(p3d.Y);
3371 block = sector->createBlankBlockNoInsert(p3d.Y);
3376 block->deSerialize(is, version, true);
3378 // If it's a new block, insert it to the map
3380 sector->insertBlock(block);
3383 Save blocks loaded in old format in new format
3386 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3387 // Only save if asked to; no need to update version
3391 // We just loaded it from, so it's up-to-date.
3392 block->resetModified();
3395 catch(SerializationError &e)
3397 errorstream<<"Invalid block data in database"
3398 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3399 <<" (SerializationError): "<<e.what()<<std::endl;
3401 // TODO: Block should be marked as invalid in memory so that it is
3402 // not touched but the game can run
3404 if(g_settings->getBool("ignore_world_load_errors")){
3405 errorstream<<"Ignoring block load error. Duck and cover! "
3406 <<"(ignore_world_load_errors)"<<std::endl;
3408 throw SerializationError("Invalid block data in database");
3414 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3416 DSTACK(__FUNCTION_NAME);
3418 v2s16 p2d(blockpos.X, blockpos.Z);
3422 ret = dbase->loadBlock(blockpos);
3423 if (ret) return (ret);
3424 // Not found in database, try the files
3426 // The directory layout we're going to load from.
3427 // 1 - original sectors/xxxxzzzz/
3428 // 2 - new sectors2/xxx/zzz/
3429 // If we load from anything but the latest structure, we will
3430 // immediately save to the new one, and remove the old.
3432 std::string sectordir1 = getSectorDir(p2d, 1);
3433 std::string sectordir;
3434 if(fs::PathExists(sectordir1))
3436 sectordir = sectordir1;
3441 sectordir = getSectorDir(p2d, 2);
3445 Make sure sector is loaded
3447 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3451 sector = loadSectorMeta(sectordir, loadlayout != 2);
3453 catch(InvalidFilenameException &e)
3457 catch(FileNotGoodException &e)
3461 catch(std::exception &e)
3468 Make sure file exists
3471 std::string blockfilename = getBlockFilename(blockpos);
3472 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3476 Load block and save it to the database
3478 loadBlock(sectordir, blockfilename, sector, true);
3479 return getBlockNoCreateNoEx(blockpos);
3482 void ServerMap::PrintInfo(std::ostream &out)
3487 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3489 m_create_area(false),
3494 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3498 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
3499 v3s16 blockpos_max, bool load_if_inexistent)
3501 TimeTaker timer1("initialEmerge", &emerge_time);
3503 // Units of these are MapBlocks
3504 v3s16 p_min = blockpos_min;
3505 v3s16 p_max = blockpos_max;
3507 VoxelArea block_area_nodes
3508 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3510 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3513 infostream<<"initialEmerge: area: ";
3514 block_area_nodes.print(infostream);
3515 infostream<<" ("<<size_MB<<"MB)";
3516 infostream<<std::endl;
3519 addArea(block_area_nodes);
3521 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3522 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3523 for(s32 x=p_min.X; x<=p_max.X; x++)
3528 std::map<v3s16, u8>::iterator n;
3529 n = m_loaded_blocks.find(p);
3530 if(n != m_loaded_blocks.end())
3533 bool block_data_inexistent = false;
3536 TimeTaker timer1("emerge load", &emerge_load_time);
3538 block = m_map->getBlockNoCreate(p);
3539 if(block->isDummy())
3540 block_data_inexistent = true;
3542 block->copyTo(*this);
3544 catch(InvalidPositionException &e)
3546 block_data_inexistent = true;
3549 if(block_data_inexistent)
3552 if (load_if_inexistent) {
3553 ServerMap *svrmap = (ServerMap *)m_map;
3554 block = svrmap->emergeBlock(p, false);
3556 block = svrmap->createBlock(p);
3558 block->copyTo(*this);
3560 flags |= VMANIP_BLOCK_DATA_INEXIST;
3563 Mark area inexistent
3565 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3566 // Fill with VOXELFLAG_NO_DATA
3567 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3568 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3570 s32 i = m_area.index(a.MinEdge.X,y,z);
3571 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3575 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3577 // Mark that block was loaded as blank
3578 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3581 m_loaded_blocks[p] = flags;
3585 void ManualMapVoxelManipulator::blitBackAll(
3586 std::map<v3s16, MapBlock*> * modified_blocks)
3588 if(m_area.getExtent() == v3s16(0,0,0))
3592 Copy data of all blocks
3594 for(std::map<v3s16, u8>::iterator
3595 i = m_loaded_blocks.begin();
3596 i != m_loaded_blocks.end(); ++i)
3599 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3600 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3601 if((existed == false) || (block == NULL))
3606 block->copyFrom(*this);
3609 (*modified_blocks)[p] = block;