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.
1103 note: todo: for liquid_finite enough to add only self node
1106 v3s16(0,0,0), // self
1107 v3s16(0,0,1), // back
1108 v3s16(0,1,0), // top
1109 v3s16(1,0,0), // right
1110 v3s16(0,0,-1), // front
1111 v3s16(0,-1,0), // bottom
1112 v3s16(-1,0,0), // left
1114 for(u16 i=0; i<7; i++)
1119 v3s16 p2 = p + dirs[i];
1121 MapNode n2 = getNode(p2);
1122 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1124 m_transforming_liquid.push_back(p2);
1127 }catch(InvalidPositionException &e)
1135 void Map::removeNodeAndUpdate(v3s16 p,
1136 std::map<v3s16, MapBlock*> &modified_blocks)
1138 INodeDefManager *ndef = m_gamedef->ndef();
1140 /*PrintInfo(m_dout);
1141 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1142 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1144 bool node_under_sunlight = true;
1146 v3s16 toppos = p + v3s16(0,1,0);
1148 // Node will be replaced with this
1149 content_t replace_material = CONTENT_AIR;
1152 Collect old node for rollback
1154 RollbackNode rollback_oldnode(this, p, m_gamedef);
1157 If there is a node at top and it doesn't have sunlight,
1158 there will be no sunlight going down.
1161 MapNode topnode = getNode(toppos);
1163 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1164 node_under_sunlight = false;
1166 catch(InvalidPositionException &e)
1170 std::set<v3s16> light_sources;
1172 enum LightBank banks[] =
1177 for(s32 i=0; i<2; i++)
1179 enum LightBank bank = banks[i];
1182 Unlight neighbors (in case the node is a light source)
1184 unLightNeighbors(bank, p,
1185 getNode(p).getLight(bank, ndef),
1186 light_sources, modified_blocks);
1190 Remove node metadata
1193 removeNodeMetadata(p);
1197 This also clears the lighting.
1201 n.setContent(replace_material);
1204 for(s32 i=0; i<2; i++)
1206 enum LightBank bank = banks[i];
1209 Recalculate lighting
1211 spreadLight(bank, light_sources, modified_blocks);
1214 // Add the block of the removed node to modified_blocks
1215 v3s16 blockpos = getNodeBlockPos(p);
1216 MapBlock * block = getBlockNoCreate(blockpos);
1217 assert(block != NULL);
1218 modified_blocks[blockpos] = block;
1221 If the removed node was under sunlight, propagate the
1222 sunlight down from it and then light all neighbors
1223 of the propagated blocks.
1225 if(node_under_sunlight)
1227 s16 ybottom = propagateSunlight(p, modified_blocks);
1228 /*m_dout<<DTIME<<"Node was under sunlight. "
1229 "Propagating sunlight";
1230 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1232 for(; y >= ybottom; y--)
1234 v3s16 p2(p.X, y, p.Z);
1235 /*m_dout<<DTIME<<"lighting neighbors of node ("
1236 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1238 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1243 // Set the lighting of this node to 0
1244 // TODO: Is this needed? Lighting is cleared up there already.
1246 MapNode n = getNode(p);
1247 n.setLight(LIGHTBANK_DAY, 0, ndef);
1250 catch(InvalidPositionException &e)
1256 for(s32 i=0; i<2; i++)
1258 enum LightBank bank = banks[i];
1260 // Get the brightest neighbour node and propagate light from it
1261 v3s16 n2p = getBrightestNeighbour(bank, p);
1263 //MapNode n2 = getNode(n2p);
1264 lightNeighbors(bank, n2p, modified_blocks);
1266 catch(InvalidPositionException &e)
1272 Update information about whether day and night light differ
1274 for(std::map<v3s16, MapBlock*>::iterator
1275 i = modified_blocks.begin();
1276 i != modified_blocks.end(); ++i)
1278 i->second->expireDayNightDiff();
1284 if(m_gamedef->rollback())
1286 RollbackNode rollback_newnode(this, p, m_gamedef);
1287 RollbackAction action;
1288 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1289 m_gamedef->rollback()->reportAction(action);
1293 Add neighboring liquid nodes and this node to transform queue.
1294 (it's vital for the node itself to get updated last.)
1295 note: todo: for liquid_finite enough to add only self node
1298 v3s16(0,0,1), // back
1299 v3s16(0,1,0), // top
1300 v3s16(1,0,0), // right
1301 v3s16(0,0,-1), // front
1302 v3s16(0,-1,0), // bottom
1303 v3s16(-1,0,0), // left
1304 v3s16(0,0,0), // self
1306 for(u16 i=0; i<7; i++)
1311 v3s16 p2 = p + dirs[i];
1313 MapNode n2 = getNode(p2);
1314 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1316 m_transforming_liquid.push_back(p2);
1319 }catch(InvalidPositionException &e)
1325 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1328 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1332 bool succeeded = true;
1334 std::map<v3s16, MapBlock*> modified_blocks;
1335 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1337 // Copy modified_blocks to event
1338 for(std::map<v3s16, MapBlock*>::iterator
1339 i = modified_blocks.begin();
1340 i != modified_blocks.end(); ++i)
1342 event.modified_blocks.insert(i->first);
1345 catch(InvalidPositionException &e){
1349 dispatchEvent(&event);
1354 bool Map::removeNodeWithEvent(v3s16 p)
1357 event.type = MEET_REMOVENODE;
1360 bool succeeded = true;
1362 std::map<v3s16, MapBlock*> modified_blocks;
1363 removeNodeAndUpdate(p, modified_blocks);
1365 // Copy modified_blocks to event
1366 for(std::map<v3s16, MapBlock*>::iterator
1367 i = modified_blocks.begin();
1368 i != modified_blocks.end(); ++i)
1370 event.modified_blocks.insert(i->first);
1373 catch(InvalidPositionException &e){
1377 dispatchEvent(&event);
1382 bool Map::getDayNightDiff(v3s16 blockpos)
1385 v3s16 p = blockpos + v3s16(0,0,0);
1386 MapBlock *b = getBlockNoCreate(p);
1387 if(b->getDayNightDiff())
1390 catch(InvalidPositionException &e){}
1393 v3s16 p = blockpos + v3s16(-1,0,0);
1394 MapBlock *b = getBlockNoCreate(p);
1395 if(b->getDayNightDiff())
1398 catch(InvalidPositionException &e){}
1400 v3s16 p = blockpos + v3s16(0,-1,0);
1401 MapBlock *b = getBlockNoCreate(p);
1402 if(b->getDayNightDiff())
1405 catch(InvalidPositionException &e){}
1407 v3s16 p = blockpos + v3s16(0,0,-1);
1408 MapBlock *b = getBlockNoCreate(p);
1409 if(b->getDayNightDiff())
1412 catch(InvalidPositionException &e){}
1415 v3s16 p = blockpos + v3s16(1,0,0);
1416 MapBlock *b = getBlockNoCreate(p);
1417 if(b->getDayNightDiff())
1420 catch(InvalidPositionException &e){}
1422 v3s16 p = blockpos + v3s16(0,1,0);
1423 MapBlock *b = getBlockNoCreate(p);
1424 if(b->getDayNightDiff())
1427 catch(InvalidPositionException &e){}
1429 v3s16 p = blockpos + v3s16(0,0,1);
1430 MapBlock *b = getBlockNoCreate(p);
1431 if(b->getDayNightDiff())
1434 catch(InvalidPositionException &e){}
1440 Updates usage timers
1442 void Map::timerUpdate(float dtime, float unload_timeout,
1443 std::list<v3s16> *unloaded_blocks)
1445 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1447 // Profile modified reasons
1448 Profiler modprofiler;
1450 std::list<v2s16> sector_deletion_queue;
1451 u32 deleted_blocks_count = 0;
1452 u32 saved_blocks_count = 0;
1453 u32 block_count_all = 0;
1456 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1457 si != m_sectors.end(); ++si)
1459 MapSector *sector = si->second;
1461 bool all_blocks_deleted = true;
1463 std::list<MapBlock*> blocks;
1464 sector->getBlocks(blocks);
1466 for(std::list<MapBlock*>::iterator i = blocks.begin();
1467 i != blocks.end(); ++i)
1469 MapBlock *block = (*i);
1471 block->incrementUsageTimer(dtime);
1473 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1475 v3s16 p = block->getPos();
1478 if(block->getModified() != MOD_STATE_CLEAN
1479 && save_before_unloading)
1481 modprofiler.add(block->getModifiedReason(), 1);
1483 saved_blocks_count++;
1486 // Delete from memory
1487 sector->deleteBlock(block);
1490 unloaded_blocks->push_back(p);
1492 deleted_blocks_count++;
1496 all_blocks_deleted = false;
1501 if(all_blocks_deleted)
1503 sector_deletion_queue.push_back(si->first);
1508 // Finally delete the empty sectors
1509 deleteSectors(sector_deletion_queue);
1511 if(deleted_blocks_count != 0)
1513 PrintInfo(infostream); // ServerMap/ClientMap:
1514 infostream<<"Unloaded "<<deleted_blocks_count
1515 <<" blocks from memory";
1516 if(save_before_unloading)
1517 infostream<<", of which "<<saved_blocks_count<<" were written";
1518 infostream<<", "<<block_count_all<<" blocks in memory";
1519 infostream<<"."<<std::endl;
1520 if(saved_blocks_count != 0){
1521 PrintInfo(infostream); // ServerMap/ClientMap:
1522 infostream<<"Blocks modified by: "<<std::endl;
1523 modprofiler.print(infostream);
1528 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1530 timerUpdate(0.0, -1.0, unloaded_blocks);
1533 void Map::deleteSectors(std::list<v2s16> &list)
1535 for(std::list<v2s16>::iterator j = list.begin();
1536 j != list.end(); ++j)
1538 MapSector *sector = m_sectors[*j];
1539 // If sector is in sector cache, remove it from there
1540 if(m_sector_cache == sector)
1541 m_sector_cache = NULL;
1542 // Remove from map and delete
1543 m_sectors.erase(*j);
1549 void Map::unloadUnusedData(float timeout,
1550 core::list<v3s16> *deleted_blocks)
1552 core::list<v2s16> sector_deletion_queue;
1553 u32 deleted_blocks_count = 0;
1554 u32 saved_blocks_count = 0;
1556 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1557 for(; si.atEnd() == false; si++)
1559 MapSector *sector = si.getNode()->getValue();
1561 bool all_blocks_deleted = true;
1563 core::list<MapBlock*> blocks;
1564 sector->getBlocks(blocks);
1565 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1566 i != blocks.end(); i++)
1568 MapBlock *block = (*i);
1570 if(block->getUsageTimer() > timeout)
1573 if(block->getModified() != MOD_STATE_CLEAN)
1576 saved_blocks_count++;
1578 // Delete from memory
1579 sector->deleteBlock(block);
1580 deleted_blocks_count++;
1584 all_blocks_deleted = false;
1588 if(all_blocks_deleted)
1590 sector_deletion_queue.push_back(si.getNode()->getKey());
1594 deleteSectors(sector_deletion_queue);
1596 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1597 <<", of which "<<saved_blocks_count<<" were wr."
1600 //return sector_deletion_queue.getSize();
1601 //return deleted_blocks_count;
1605 void Map::PrintInfo(std::ostream &out)
1610 #define WATER_DROP_BOOST 4
1614 NEIGHBOR_SAME_LEVEL,
1617 struct NodeNeighbor {
1621 bool l; //can liquid
1625 void Map::transforming_liquid_add(v3s16 p) {
1626 m_transforming_liquid.push_back(p);
1629 s32 Map::transforming_liquid_size() {
1630 return m_transforming_liquid.size();
1633 const v3s16 g_7dirs[7] =
1635 // +right, +top, +back
1636 v3s16( 0,-1, 0), // bottom
1637 v3s16( 0, 0, 0), // self
1638 v3s16( 0, 0, 1), // back
1639 v3s16( 0, 0,-1), // front
1640 v3s16( 1, 0, 0), // right
1641 v3s16(-1, 0, 0), // left
1642 v3s16( 0, 1, 0) // top
1649 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1651 INodeDefManager *nodemgr = m_gamedef->ndef();
1653 DSTACK(__FUNCTION_NAME);
1654 //TimeTaker timer("transformLiquids()");
1657 u32 initial_size = m_transforming_liquid.size();
1659 u8 relax = g_settings->getS16("liquid_relax");
1660 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1661 int water_level = g_settings->getS16("water_level");
1663 // list of nodes that due to viscosity have not reached their max level height
1664 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1666 // List of MapBlocks that will require a lighting update (due to lava)
1667 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1669 u16 loop_max = g_settings->getU16("liquid_loop_max");
1671 //if (m_transforming_liquid.size() > 0) errorstream << "Liquid queue size="<<m_transforming_liquid.size()<<std::endl;
1673 while (m_transforming_liquid.size() > 0)
1675 // This should be done here so that it is done when continue is used
1676 if (loopcount >= initial_size || loopcount >= loop_max)
1680 Get a queued transforming liquid node
1682 v3s16 p0 = m_transforming_liquid.pop_front();
1683 u16 total_level = 0;
1684 // surrounding flowing liquid nodes
1685 NodeNeighbor neighbors[7];
1686 // current level of every block
1687 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1689 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1690 s8 can_liquid_same_level = 0;
1691 content_t liquid_kind = CONTENT_IGNORE;
1692 content_t liquid_kind_flowing = CONTENT_IGNORE;
1694 Collect information about the environment
1696 const v3s16 *dirs = g_7dirs;
1697 for (u16 i = 0; i < 7; i++) {
1698 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1701 nt = NEIGHBOR_UPPER;
1704 nt = NEIGHBOR_LOWER;
1707 v3s16 npos = p0 + dirs[i];
1709 neighbors[i].n = getNodeNoEx(npos);
1710 neighbors[i].t = nt;
1711 neighbors[i].p = npos;
1714 NodeNeighbor & nb = neighbors[i];
1716 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1718 if (nb.n.getContent() == CONTENT_AIR) {
1719 liquid_levels[i] = 0;
1724 // if this node is not (yet) of a liquid type,
1725 // choose the first liquid type we encounter
1726 if (liquid_kind_flowing == CONTENT_IGNORE)
1727 liquid_kind_flowing = nodemgr->getId(
1728 nodemgr->get(nb.n).liquid_alternative_flowing);
1729 if (liquid_kind == CONTENT_IGNORE)
1730 liquid_kind = nb.n.getContent();
1731 if (nb.n.getContent() == liquid_kind) {
1732 liquid_levels[i] = nb.n.getLevel(nodemgr); //LIQUID_LEVEL_SOURCE;
1734 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1737 case LIQUID_FLOWING:
1738 // if this node is not (yet) of a liquid type,
1739 // choose the first liquid type we encounter
1740 if (liquid_kind_flowing == CONTENT_IGNORE)
1741 liquid_kind_flowing = nb.n.getContent();
1742 if (liquid_kind == CONTENT_IGNORE)
1743 liquid_kind = nodemgr->getId(
1744 nodemgr->get(nb.n).liquid_alternative_source);
1745 if (nb.n.getContent() == liquid_kind_flowing) {
1746 liquid_levels[i] = nb.n.getLevel(nodemgr); //(nb.n.param2 & LIQUID_LEVEL_MASK);
1752 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1753 ++can_liquid_same_level;
1754 if (liquid_levels[i] > 0)
1755 total_level += liquid_levels[i];
1758 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1759 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1760 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1761 << nodemgr->get(nb.n.getContent()).liquid_type
1762 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1763 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1764 << " tlevel=" << (int)total_level << " cansame="
1765 << (int)can_liquid_same_level << std::endl;
1769 if (liquid_kind == CONTENT_IGNORE ||
1770 !neighbors[D_SELF].l ||
1774 // fill bottom block
1775 if (neighbors[D_BOTTOM].l) {
1776 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1777 LIQUID_LEVEL_SOURCE : total_level;
1778 total_level -= liquid_levels_want[D_BOTTOM];
1782 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1783 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1784 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1785 (can_liquid_same_level - relax) &&
1786 can_liquid_same_level >= relax + 1) {
1787 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1790 // prevent lakes in air above unloaded blocks
1791 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE && !(loopcount % 3)) {
1795 // calculate self level 5 blocks
1797 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1798 ? LIQUID_LEVEL_SOURCE
1799 : total_level / can_liquid_same_level;
1800 total_level -= want_level * can_liquid_same_level;
1803 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1804 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1805 total_level <= (can_liquid_same_level - relax) &&
1806 can_liquid_same_level >= relax + 1) {
1810 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1811 if (!neighbors[ii].l)
1813 liquid_levels_want[ii] = want_level;
1814 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0) {
1815 if (loopcount % 3 || liquid_levels[ii] <= 0){
1816 if (liquid_levels[ii] > liquid_levels_want[ii]) {
1817 ++liquid_levels_want[ii];
1820 } else if (neighbors[ii].l > 0){
1821 ++liquid_levels_want[ii];
1827 for (u16 ii = 0; ii < 7; ++ii) {
1828 if (total_level < 1) break;
1829 if (liquid_levels_want[ii] >= 0 &&
1830 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1831 ++liquid_levels_want[ii];
1836 // fill top block if can
1837 if (neighbors[D_TOP].l) {
1838 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1839 LIQUID_LEVEL_SOURCE : total_level;
1840 total_level -= liquid_levels_want[D_TOP];
1843 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1844 if ( neighbors[ii].i ||
1845 (liquid_levels_want[ii] >= 0 &&
1846 (fast_flood && p0.Y < water_level &&
1847 (initial_size >= 1000
1849 && want_level >= LIQUID_LEVEL_SOURCE/4
1850 && can_liquid_same_level >= 5
1851 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1852 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1855 if (total_level > 0) //|| flowed != volume)
1856 infostream <<" AFTER level=" << (int)total_level
1857 //<< " flowed="<<flowed<< " volume=" << volume
1858 << " wantsame="<<(int)want_level<< " top="
1859 << (int)liquid_levels_want[D_TOP]<< " topwas="
1860 << (int)liquid_levels[D_TOP]<< " bot="
1861 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1865 for (u16 i = 0; i < 7; i++) {
1866 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1868 MapNode & n0 = neighbors[i].n;
1869 p0 = neighbors[i].p;
1871 decide on the type (and possibly level) of the current node
1873 content_t new_node_content;
1874 s8 new_node_level = -1;
1875 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1876 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1877 // amount to gain, limited by viscosity
1878 // must be at least 1 in absolute value
1879 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1880 if (level_inc < -viscosity || level_inc > viscosity)
1881 new_node_level = liquid_levels[i] + level_inc/viscosity;
1882 else if (level_inc < 0)
1883 new_node_level = liquid_levels[i] - 1;
1884 else if (level_inc > 0)
1885 new_node_level = liquid_levels[i] + 1;
1887 new_node_level = liquid_levels_want[i];
1890 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1891 new_node_content = liquid_kind;
1892 else if (new_node_level > 0)
1893 new_node_content = liquid_kind_flowing;
1895 new_node_content = CONTENT_AIR;
1897 // last level must flow down on stairs
1898 if (liquid_levels_want[i] != liquid_levels[i] &&
1899 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1900 new_node_level >= 1 && new_node_level <= 2) {
1901 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1902 if (neighbors[ii].l)
1903 must_reflow_second.push_back(p0 + dirs[ii]);
1908 check if anything has changed.
1909 if not, just continue with the next node.
1913 new_node_content == n0.getContent()
1914 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1915 (n0.getLevel(nodemgr) == (u8)new_node_level
1916 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1917 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1920 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1921 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1922 LIQUID_INFINITY_MASK) == neighbors[i].i
1925 if (liquid_levels[i] == new_node_level)
1933 update the current node
1936 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1937 // set level to last 3 bits, flowing down bit to 4th bit
1938 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1939 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1940 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1941 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1945 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1946 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1947 <<(int)new_node_level<<std::endl;
1950 n0.setContent(liquid_kind_flowing);
1951 n0.setLevel(nodemgr, new_node_level);
1952 // Find out whether there is a suspect for this action
1953 std::string suspect;
1954 if(m_gamedef->rollback()){
1955 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1958 if(!suspect.empty()){
1960 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1961 // Get old node for rollback
1962 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1966 RollbackNode rollback_newnode(this, p0, m_gamedef);
1967 RollbackAction action;
1968 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1969 m_gamedef->rollback()->reportAction(action);
1975 v3s16 blockpos = getNodeBlockPos(p0);
1976 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1978 modified_blocks[blockpos] = block;
1979 // If node emits light, MapBlock requires lighting update
1980 if(nodemgr->get(n0).light_source != 0)
1981 lighting_modified_blocks[block->getPos()] = block;
1983 must_reflow.push_back(neighbors[i].p);
1985 /* //for better relax only same level
1986 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1987 if (!neighbors[ii].l) continue;
1988 must_reflow.push_back(p0 + dirs[ii]);
1993 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1994 <<" reflow="<<must_reflow.size()
1995 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1997 while (must_reflow.size() > 0)
1998 m_transforming_liquid.push_back(must_reflow.pop_front());
1999 while (must_reflow_second.size() > 0)
2000 m_transforming_liquid.push_back(must_reflow_second.pop_front());
2001 updateLighting(lighting_modified_blocks, modified_blocks);
2004 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
2007 if (g_settings->getBool("liquid_finite"))
2008 return Map::transformLiquidsFinite(modified_blocks);
2010 INodeDefManager *nodemgr = m_gamedef->ndef();
2012 DSTACK(__FUNCTION_NAME);
2013 //TimeTaker timer("transformLiquids()");
2016 u32 initial_size = m_transforming_liquid.size();
2018 /*if(initial_size != 0)
2019 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
2021 // list of nodes that due to viscosity have not reached their max level height
2022 UniqueQueue<v3s16> must_reflow;
2024 // List of MapBlocks that will require a lighting update (due to lava)
2025 std::map<v3s16, MapBlock*> lighting_modified_blocks;
2027 u16 loop_max = g_settings->getU16("liquid_loop_max");
2029 while(m_transforming_liquid.size() != 0)
2031 // This should be done here so that it is done when continue is used
2032 if(loopcount >= initial_size || loopcount >= loop_max)
2037 Get a queued transforming liquid node
2039 v3s16 p0 = m_transforming_liquid.pop_front();
2041 MapNode n0 = getNodeNoEx(p0);
2044 Collect information about current node
2046 s8 liquid_level = -1;
2047 content_t liquid_kind = CONTENT_IGNORE;
2048 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2049 switch (liquid_type) {
2051 liquid_level = LIQUID_LEVEL_SOURCE;
2052 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2054 case LIQUID_FLOWING:
2055 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2056 liquid_kind = n0.getContent();
2059 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2060 // continue with the next node.
2061 if (n0.getContent() != CONTENT_AIR)
2063 liquid_kind = CONTENT_AIR;
2068 Collect information about the environment
2070 const v3s16 *dirs = g_6dirs;
2071 NodeNeighbor sources[6]; // surrounding sources
2072 int num_sources = 0;
2073 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2075 NodeNeighbor airs[6]; // surrounding air
2077 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2078 int num_neutrals = 0;
2079 bool flowing_down = false;
2080 for (u16 i = 0; i < 6; i++) {
2081 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2084 nt = NEIGHBOR_UPPER;
2087 nt = NEIGHBOR_LOWER;
2090 v3s16 npos = p0 + dirs[i];
2091 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2092 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2094 if (nb.n.getContent() == CONTENT_AIR) {
2095 airs[num_airs++] = nb;
2096 // if the current node is a water source the neighbor
2097 // should be enqueded for transformation regardless of whether the
2098 // current node changes or not.
2099 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2100 m_transforming_liquid.push_back(npos);
2101 // if the current node happens to be a flowing node, it will start to flow down here.
2102 if (nb.t == NEIGHBOR_LOWER) {
2103 flowing_down = true;
2106 neutrals[num_neutrals++] = nb;
2110 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2111 if (liquid_kind == CONTENT_AIR)
2112 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2113 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2114 neutrals[num_neutrals++] = nb;
2116 // Do not count bottom source, it will screw things up
2118 sources[num_sources++] = nb;
2121 case LIQUID_FLOWING:
2122 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2123 if (liquid_kind == CONTENT_AIR)
2124 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2125 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2126 neutrals[num_neutrals++] = nb;
2128 flows[num_flows++] = nb;
2129 if (nb.t == NEIGHBOR_LOWER)
2130 flowing_down = true;
2137 decide on the type (and possibly level) of the current node
2139 content_t new_node_content;
2140 s8 new_node_level = -1;
2141 s8 max_node_level = -1;
2142 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
2143 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2144 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2145 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2146 // it's perfectly safe to use liquid_kind here to determine the new node content.
2147 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2148 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2149 // liquid_kind is set properly, see above
2150 new_node_content = liquid_kind;
2151 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2152 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
2153 new_node_content = CONTENT_AIR;
2155 // no surrounding sources, so get the maximum level that can flow into this node
2156 for (u16 i = 0; i < num_flows; i++) {
2157 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2158 switch (flows[i].t) {
2159 case NEIGHBOR_UPPER:
2160 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2161 max_node_level = LIQUID_LEVEL_MAX;
2162 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2163 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2164 } else if (nb_liquid_level > max_node_level)
2165 max_node_level = nb_liquid_level;
2167 case NEIGHBOR_LOWER:
2169 case NEIGHBOR_SAME_LEVEL:
2170 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2171 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2172 max_node_level = nb_liquid_level - 1;
2178 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2179 if (viscosity > 1 && max_node_level != liquid_level) {
2180 // amount to gain, limited by viscosity
2181 // must be at least 1 in absolute value
2182 s8 level_inc = max_node_level - liquid_level;
2183 if (level_inc < -viscosity || level_inc > viscosity)
2184 new_node_level = liquid_level + level_inc/viscosity;
2185 else if (level_inc < 0)
2186 new_node_level = liquid_level - 1;
2187 else if (level_inc > 0)
2188 new_node_level = liquid_level + 1;
2189 if (new_node_level != max_node_level)
2190 must_reflow.push_back(p0);
2192 new_node_level = max_node_level;
2194 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
2195 new_node_content = liquid_kind;
2197 new_node_content = CONTENT_AIR;
2202 check if anything has changed. if not, just continue with the next node.
2204 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2205 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2206 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2212 update the current node
2215 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2216 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2217 // set level to last 3 bits, flowing down bit to 4th bit
2218 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2220 // set the liquid level and flow bit to 0
2221 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2223 n0.setContent(new_node_content);
2225 // Find out whether there is a suspect for this action
2226 std::string suspect;
2227 if(m_gamedef->rollback()){
2228 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2231 if(!suspect.empty()){
2233 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2234 // Get old node for rollback
2235 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2239 RollbackNode rollback_newnode(this, p0, m_gamedef);
2240 RollbackAction action;
2241 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2242 m_gamedef->rollback()->reportAction(action);
2248 v3s16 blockpos = getNodeBlockPos(p0);
2249 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2251 modified_blocks[blockpos] = block;
2252 // If new or old node emits light, MapBlock requires lighting update
2253 if(nodemgr->get(n0).light_source != 0 ||
2254 nodemgr->get(n00).light_source != 0)
2255 lighting_modified_blocks[block->getPos()] = block;
2259 enqueue neighbors for update if neccessary
2261 switch (nodemgr->get(n0.getContent()).liquid_type) {
2263 case LIQUID_FLOWING:
2264 // make sure source flows into all neighboring nodes
2265 for (u16 i = 0; i < num_flows; i++)
2266 if (flows[i].t != NEIGHBOR_UPPER)
2267 m_transforming_liquid.push_back(flows[i].p);
2268 for (u16 i = 0; i < num_airs; i++)
2269 if (airs[i].t != NEIGHBOR_UPPER)
2270 m_transforming_liquid.push_back(airs[i].p);
2273 // this flow has turned to air; neighboring flows might need to do the same
2274 for (u16 i = 0; i < num_flows; i++)
2275 m_transforming_liquid.push_back(flows[i].p);
2279 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2280 while (must_reflow.size() > 0)
2281 m_transforming_liquid.push_back(must_reflow.pop_front());
2282 updateLighting(lighting_modified_blocks, modified_blocks);
2285 NodeMetadata *Map::getNodeMetadata(v3s16 p)
2287 v3s16 blockpos = getNodeBlockPos(p);
2288 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2289 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2291 infostream<<"Map::getNodeMetadata(): Need to emerge "
2292 <<PP(blockpos)<<std::endl;
2293 block = emergeBlock(blockpos, false);
2296 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2300 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2304 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2306 v3s16 blockpos = getNodeBlockPos(p);
2307 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2308 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2310 infostream<<"Map::setNodeMetadata(): Need to emerge "
2311 <<PP(blockpos)<<std::endl;
2312 block = emergeBlock(blockpos, false);
2315 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2319 block->m_node_metadata.set(p_rel, meta);
2323 void Map::removeNodeMetadata(v3s16 p)
2325 v3s16 blockpos = getNodeBlockPos(p);
2326 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2327 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2330 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2334 block->m_node_metadata.remove(p_rel);
2337 NodeTimer Map::getNodeTimer(v3s16 p)
2339 v3s16 blockpos = getNodeBlockPos(p);
2340 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2341 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2343 infostream<<"Map::getNodeTimer(): Need to emerge "
2344 <<PP(blockpos)<<std::endl;
2345 block = emergeBlock(blockpos, false);
2348 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2352 NodeTimer t = block->m_node_timers.get(p_rel);
2356 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2358 v3s16 blockpos = getNodeBlockPos(p);
2359 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2360 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2362 infostream<<"Map::setNodeTimer(): Need to emerge "
2363 <<PP(blockpos)<<std::endl;
2364 block = emergeBlock(blockpos, false);
2367 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2371 block->m_node_timers.set(p_rel, t);
2374 void Map::removeNodeTimer(v3s16 p)
2376 v3s16 blockpos = getNodeBlockPos(p);
2377 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2378 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2381 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2385 block->m_node_timers.remove(p_rel);
2388 s16 Map::getHeat(v3s16 p)
2390 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2394 //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2398 s16 Map::getHumidity(v3s16 p)
2400 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2402 return block->humidity;
2404 //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2411 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2412 Map(dout_server, gamedef),
2414 m_map_metadata_changed(true)
2416 verbosestream<<__FUNCTION_NAME<<std::endl;
2419 Try to load map; if not found, create a new one.
2422 // Determine which database backend to use
2423 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2425 bool succeeded = conf.readConfigFile(conf_path.c_str());
2426 if (!succeeded || !conf.exists("backend")) {
2427 // fall back to sqlite3
2428 dbase = new Database_SQLite3(this, savedir);
2429 conf.set("backend", "sqlite3");
2431 std::string backend = conf.get("backend");
2432 if (backend == "dummy")
2433 dbase = new Database_Dummy(this);
2434 else if (backend == "sqlite3")
2435 dbase = new Database_SQLite3(this, savedir);
2437 else if (backend == "leveldb")
2438 dbase = new Database_LevelDB(this, savedir);
2441 else if (backend == "redis")
2442 dbase = new Database_Redis(this, savedir);
2445 throw BaseException("Unknown map backend");
2448 m_savedir = savedir;
2449 m_map_saving_enabled = false;
2453 // If directory exists, check contents and load if possible
2454 if(fs::PathExists(m_savedir))
2456 // If directory is empty, it is safe to save into it.
2457 if(fs::GetDirListing(m_savedir).size() == 0)
2459 infostream<<"ServerMap: Empty save directory is valid."
2461 m_map_saving_enabled = true;
2466 // Load map metadata (seed, chunksize)
2469 catch(SettingNotFoundException &e){
2470 infostream<<"ServerMap: Some metadata not found."
2471 <<" Using default settings."<<std::endl;
2473 catch(FileNotGoodException &e){
2474 infostream<<"WARNING: Could not load map metadata"
2475 //<<" Disabling chunk-based generator."
2480 infostream<<"ServerMap: Successfully loaded map "
2481 <<"metadata from "<<savedir
2482 <<", assuming valid save directory."
2483 <<" seed="<< m_emerge->params.seed <<"."
2486 m_map_saving_enabled = true;
2487 // Map loaded, not creating new one
2491 // If directory doesn't exist, it is safe to save to it
2493 m_map_saving_enabled = true;
2496 catch(std::exception &e)
2498 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2499 <<", exception: "<<e.what()<<std::endl;
2500 infostream<<"Please remove the map or fix it."<<std::endl;
2501 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2504 infostream<<"Initializing new map."<<std::endl;
2506 // Create zero sector
2507 emergeSector(v2s16(0,0));
2509 // Initially write whole map
2510 save(MOD_STATE_CLEAN);
2513 ServerMap::~ServerMap()
2515 verbosestream<<__FUNCTION_NAME<<std::endl;
2519 if(m_map_saving_enabled)
2521 // Save only changed parts
2522 save(MOD_STATE_WRITE_AT_UNLOAD);
2523 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2527 infostream<<"ServerMap: Map not saved"<<std::endl;
2530 catch(std::exception &e)
2532 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2533 <<", exception: "<<e.what()<<std::endl;
2537 Close database if it was opened
2545 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2546 for(; i.atEnd() == false; i++)
2548 MapChunk *chunk = i.getNode()->getValue();
2554 u64 ServerMap::getSeed()
2556 return m_emerge->params.seed;
2559 s16 ServerMap::getWaterLevel()
2561 return m_emerge->params.water_level;
2564 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2566 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2567 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2569 s16 chunksize = m_emerge->params.chunksize;
2570 s16 coffset = -chunksize / 2;
2571 v3s16 chunk_offset(coffset, coffset, coffset);
2572 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2573 v3s16 blockpos_min = blockpos_div * chunksize;
2574 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2575 blockpos_min += chunk_offset;
2576 blockpos_max += chunk_offset;
2578 v3s16 extra_borders(1,1,1);
2580 // Do nothing if not inside limits (+-1 because of neighbors)
2581 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2582 blockpos_over_limit(blockpos_max + extra_borders))
2585 data->seed = m_emerge->params.seed;
2586 data->blockpos_min = blockpos_min;
2587 data->blockpos_max = blockpos_max;
2588 data->blockpos_requested = blockpos;
2589 data->nodedef = m_gamedef->ndef();
2592 Create the whole area of this and the neighboring blocks
2595 //TimeTaker timer("initBlockMake() create area");
2597 for(s16 x=blockpos_min.X-extra_borders.X;
2598 x<=blockpos_max.X+extra_borders.X; x++)
2599 for(s16 z=blockpos_min.Z-extra_borders.Z;
2600 z<=blockpos_max.Z+extra_borders.Z; z++)
2602 v2s16 sectorpos(x, z);
2603 // Sector metadata is loaded from disk if not already loaded.
2604 ServerMapSector *sector = createSector(sectorpos);
2608 for(s16 y=blockpos_min.Y-extra_borders.Y;
2609 y<=blockpos_max.Y+extra_borders.Y; y++)
2612 //MapBlock *block = createBlock(p);
2613 // 1) get from memory, 2) load from disk
2614 MapBlock *block = emergeBlock(p, false);
2615 // 3) create a blank one
2618 block = createBlock(p);
2621 Block gets sunlight if this is true.
2623 Refer to the map generator heuristics.
2625 bool ug = m_emerge->isBlockUnderground(p);
2626 block->setIsUnderground(ug);
2629 // Lighting will not be valid after make_chunk is called
2630 block->setLightingExpired(true);
2631 // Lighting will be calculated
2632 //block->setLightingExpired(false);
2638 Now we have a big empty area.
2640 Make a ManualMapVoxelManipulator that contains this and the
2644 // The area that contains this block and it's neighbors
2645 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2646 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2648 data->vmanip = new ManualMapVoxelManipulator(this);
2649 //data->vmanip->setMap(this);
2653 //TimeTaker timer("initBlockMake() initialEmerge");
2654 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2657 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2658 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2659 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2660 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2661 core::map<v3s16, u8>::Node *n;
2662 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2665 u8 flags = n->getValue();
2666 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2672 // Data is ready now.
2676 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2677 std::map<v3s16, MapBlock*> &changed_blocks)
2679 v3s16 blockpos_min = data->blockpos_min;
2680 v3s16 blockpos_max = data->blockpos_max;
2681 v3s16 blockpos_requested = data->blockpos_requested;
2682 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2683 <<blockpos_requested.Y<<","
2684 <<blockpos_requested.Z<<")"<<std::endl;*/
2686 v3s16 extra_borders(1,1,1);
2688 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2690 /*infostream<<"Resulting vmanip:"<<std::endl;
2691 data->vmanip.print(infostream);*/
2693 // Make sure affected blocks are loaded
2694 for(s16 x=blockpos_min.X-extra_borders.X;
2695 x<=blockpos_max.X+extra_borders.X; x++)
2696 for(s16 z=blockpos_min.Z-extra_borders.Z;
2697 z<=blockpos_max.Z+extra_borders.Z; z++)
2698 for(s16 y=blockpos_min.Y-extra_borders.Y;
2699 y<=blockpos_max.Y+extra_borders.Y; y++)
2702 // Load from disk if not already in memory
2703 emergeBlock(p, false);
2707 Blit generated stuff to map
2708 NOTE: blitBackAll adds nearly everything to changed_blocks
2712 //TimeTaker timer("finishBlockMake() blitBackAll");
2713 data->vmanip->blitBackAll(&changed_blocks);
2716 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2719 Copy transforming liquid information
2721 while(data->transforming_liquid.size() > 0)
2723 v3s16 p = data->transforming_liquid.pop_front();
2724 m_transforming_liquid.push_back(p);
2728 Do stuff in central blocks
2736 TimeTaker t("finishBlockMake lighting update");
2738 core::map<v3s16, MapBlock*> lighting_update_blocks;
2741 for(s16 x=blockpos_min.X-extra_borders.X;
2742 x<=blockpos_max.X+extra_borders.X; x++)
2743 for(s16 z=blockpos_min.Z-extra_borders.Z;
2744 z<=blockpos_max.Z+extra_borders.Z; z++)
2745 for(s16 y=blockpos_min.Y-extra_borders.Y;
2746 y<=blockpos_max.Y+extra_borders.Y; y++)
2749 MapBlock *block = getBlockNoCreateNoEx(p);
2751 lighting_update_blocks.insert(block->getPos(), block);
2754 updateLighting(lighting_update_blocks, changed_blocks);
2758 Set lighting to non-expired state in all of them.
2759 This is cheating, but it is not fast enough if all of them
2760 would actually be updated.
2762 for(s16 x=blockpos_min.X-extra_borders.X;
2763 x<=blockpos_max.X+extra_borders.X; x++)
2764 for(s16 z=blockpos_min.Z-extra_borders.Z;
2765 z<=blockpos_max.Z+extra_borders.Z; z++)
2766 for(s16 y=blockpos_min.Y-extra_borders.Y;
2767 y<=blockpos_max.Y+extra_borders.Y; y++)
2770 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2774 if(enable_mapgen_debug_info == false)
2775 t.stop(true); // Hide output
2780 Go through changed blocks
2782 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2783 i != changed_blocks.end(); ++i)
2785 MapBlock *block = i->second;
2788 Update day/night difference cache of the MapBlocks
2790 block->expireDayNightDiff();
2792 Set block as modified
2794 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2795 "finishBlockMake expireDayNightDiff");
2799 Set central blocks as generated
2801 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2802 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2803 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2806 MapBlock *block = getBlockNoCreateNoEx(p);
2808 block->setGenerated(true);
2812 Save changed parts of map
2813 NOTE: Will be saved later.
2815 //save(MOD_STATE_WRITE_AT_UNLOAD);
2817 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2818 <<","<<blockpos_requested.Y<<","
2819 <<blockpos_requested.Z<<")"<<std::endl;*/
2822 Update weather data in blocks
2824 ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
2825 for(s16 x=blockpos_min.X-extra_borders.X;
2826 x<=blockpos_max.X+extra_borders.X; x++)
2827 for(s16 z=blockpos_min.Z-extra_borders.Z;
2828 z<=blockpos_max.Z+extra_borders.Z; z++)
2829 for(s16 y=blockpos_min.Y-extra_borders.Y;
2830 y<=blockpos_max.Y+extra_borders.Y; y++)
2833 MapBlock *block = getBlockNoCreateNoEx(p);
2834 block->heat_last_update = 0;
2835 block->humidity_last_update = 0;
2836 if (senv->m_use_weather) {
2837 updateBlockHeat(senv, p * MAP_BLOCKSIZE, block);
2838 updateBlockHumidity(senv, p * MAP_BLOCKSIZE, block);
2840 block->heat = HEAT_UNDEFINED;
2841 block->humidity = HUMIDITY_UNDEFINED;
2846 if(enable_mapgen_debug_info)
2849 Analyze resulting blocks
2851 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2852 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2853 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2854 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2855 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2856 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2858 v3s16 p = v3s16(x,y,z);
2859 MapBlock *block = getBlockNoCreateNoEx(p);
2861 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2862 infostream<<"Generated "<<spos<<": "
2863 <<analyze_block(block)<<std::endl;
2868 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2874 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2876 DSTACKF("%s: p2d=(%d,%d)",
2881 Check if it exists already in memory
2883 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2888 Try to load it from disk (with blocks)
2890 //if(loadSectorFull(p2d) == true)
2893 Try to load metadata from disk
2896 if(loadSectorMeta(p2d) == true)
2898 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2901 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2902 throw InvalidPositionException("");
2908 Do not create over-limit
2910 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2911 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2912 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2913 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2914 throw InvalidPositionException("createSector(): pos. over limit");
2917 Generate blank sector
2920 sector = new ServerMapSector(this, p2d, m_gamedef);
2922 // Sector position on map in nodes
2923 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2928 m_sectors[p2d] = sector;
2935 This is a quick-hand function for calling makeBlock().
2937 MapBlock * ServerMap::generateBlock(
2939 std::map<v3s16, MapBlock*> &modified_blocks
2942 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2944 /*infostream<<"generateBlock(): "
2945 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2948 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2950 TimeTaker timer("generateBlock");
2952 //MapBlock *block = original_dummy;
2954 v2s16 p2d(p.X, p.Z);
2955 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2958 Do not generate over-limit
2960 if(blockpos_over_limit(p))
2962 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2963 throw InvalidPositionException("generateBlock(): pos. over limit");
2967 Create block make data
2970 initBlockMake(&data, p);
2976 TimeTaker t("mapgen::make_block()");
2977 mapgen->makeChunk(&data);
2978 //mapgen::make_block(&data);
2980 if(enable_mapgen_debug_info == false)
2981 t.stop(true); // Hide output
2985 Blit data back on map, update lighting, add mobs and whatever this does
2987 finishBlockMake(&data, modified_blocks);
2992 MapBlock *block = getBlockNoCreateNoEx(p);
3000 bool erroneus_content = false;
3001 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3002 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3003 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3006 MapNode n = block->getNode(p);
3007 if(n.getContent() == CONTENT_IGNORE)
3009 infostream<<"CONTENT_IGNORE at "
3010 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3012 erroneus_content = true;
3016 if(erroneus_content)
3025 Generate a completely empty block
3029 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3030 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3032 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3035 n.setContent(CONTENT_AIR);
3036 block->setNode(v3s16(x0,y0,z0), n);
3042 if(enable_mapgen_debug_info == false)
3043 timer.stop(true); // Hide output
3049 MapBlock * ServerMap::createBlock(v3s16 p)
3051 DSTACKF("%s: p=(%d,%d,%d)",
3052 __FUNCTION_NAME, p.X, p.Y, p.Z);
3055 Do not create over-limit
3057 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3058 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3059 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3060 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3061 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3062 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3063 throw InvalidPositionException("createBlock(): pos. over limit");
3065 v2s16 p2d(p.X, p.Z);
3068 This will create or load a sector if not found in memory.
3069 If block exists on disk, it will be loaded.
3071 NOTE: On old save formats, this will be slow, as it generates
3072 lighting on blocks for them.
3074 ServerMapSector *sector;
3076 sector = (ServerMapSector*)createSector(p2d);
3077 assert(sector->getId() == MAPSECTOR_SERVER);
3079 catch(InvalidPositionException &e)
3081 infostream<<"createBlock: createSector() failed"<<std::endl;
3085 NOTE: This should not be done, or at least the exception
3086 should not be passed on as std::exception, because it
3087 won't be catched at all.
3089 /*catch(std::exception &e)
3091 infostream<<"createBlock: createSector() failed: "
3092 <<e.what()<<std::endl;
3097 Try to get a block from the sector
3100 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3103 if(block->isDummy())
3108 block = sector->createBlankBlock(block_y);
3113 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3115 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3117 p.X, p.Y, p.Z, create_blank);
3120 MapBlock *block = getBlockNoCreateNoEx(p);
3121 if(block && block->isDummy() == false)
3126 MapBlock *block = loadBlock(p);
3132 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3133 MapBlock *block = sector->createBlankBlock(p.Y);
3141 std::map<v3s16, MapBlock*> modified_blocks;
3142 MapBlock *block = generateBlock(p, modified_blocks);
3146 event.type = MEET_OTHER;
3149 // Copy modified_blocks to event
3150 for(std::map<v3s16, MapBlock*>::iterator
3151 i = modified_blocks.begin();
3152 i != modified_blocks.end(); ++i)
3154 event.modified_blocks.insert(i->first);
3158 dispatchEvent(&event);
3168 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
3170 MapBlock *block = getBlockNoCreateNoEx(p3d);
3172 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
3177 void ServerMap::prepareBlock(MapBlock *block) {
3178 ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
3180 // Calculate weather conditions
3181 block->heat_last_update = 0;
3182 block->humidity_last_update = 0;
3183 if (senv->m_use_weather) {
3184 v3s16 p = block->getPos() * MAP_BLOCKSIZE;
3185 updateBlockHeat(senv, p, block);
3186 updateBlockHumidity(senv, p, block);
3188 block->heat = HEAT_UNDEFINED;
3189 block->humidity = HUMIDITY_UNDEFINED;
3193 s16 ServerMap::findGroundLevel(v2s16 p2d)
3197 Uh, just do something random...
3199 // Find existing map from top to down
3202 v3s16 p(p2d.X, max, p2d.Y);
3203 for(; p.Y>min; p.Y--)
3205 MapNode n = getNodeNoEx(p);
3206 if(n.getContent() != CONTENT_IGNORE)
3211 // If this node is not air, go to plan b
3212 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3214 // Search existing walkable and return it
3215 for(; p.Y>min; p.Y--)
3217 MapNode n = getNodeNoEx(p);
3218 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3227 Determine from map generator noise functions
3230 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3233 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3234 //return (s16)level;
3237 bool ServerMap::loadFromFolders() {
3238 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
3243 void ServerMap::createDirs(std::string path)
3245 if(fs::CreateAllDirs(path) == false)
3247 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3248 <<"\""<<path<<"\""<<std::endl;
3249 throw BaseException("ServerMap failed to create directory");
3253 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3259 snprintf(cc, 9, "%.4x%.4x",
3260 (unsigned int)pos.X&0xffff,
3261 (unsigned int)pos.Y&0xffff);
3263 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3265 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3266 (unsigned int)pos.X&0xfff,
3267 (unsigned int)pos.Y&0xfff);
3269 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3276 v2s16 ServerMap::getSectorPos(std::string dirname)
3278 unsigned int x = 0, y = 0;
3280 std::string component;
3281 fs::RemoveLastPathComponent(dirname, &component, 1);
3282 if(component.size() == 8)
3285 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3287 else if(component.size() == 3)
3290 fs::RemoveLastPathComponent(dirname, &component, 2);
3291 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3292 // Sign-extend the 12 bit values up to 16 bits...
3293 if(x&0x800) x|=0xF000;
3294 if(y&0x800) y|=0xF000;
3301 v2s16 pos((s16)x, (s16)y);
3305 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3307 v2s16 p2d = getSectorPos(sectordir);
3309 if(blockfile.size() != 4){
3310 throw InvalidFilenameException("Invalid block filename");
3313 int r = sscanf(blockfile.c_str(), "%4x", &y);
3315 throw InvalidFilenameException("Invalid block filename");
3316 return v3s16(p2d.X, y, p2d.Y);
3319 std::string ServerMap::getBlockFilename(v3s16 p)
3322 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3326 void ServerMap::save(ModifiedState save_level)
3328 DSTACK(__FUNCTION_NAME);
3329 if(m_map_saving_enabled == false)
3331 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3335 if(save_level == MOD_STATE_CLEAN)
3336 infostream<<"ServerMap: Saving whole map, this can take time."
3339 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3344 // Profile modified reasons
3345 Profiler modprofiler;
3347 u32 sector_meta_count = 0;
3348 u32 block_count = 0;
3349 u32 block_count_all = 0; // Number of blocks in memory
3351 // Don't do anything with sqlite unless something is really saved
3352 bool save_started = false;
3354 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3355 i != m_sectors.end(); ++i)
3357 ServerMapSector *sector = (ServerMapSector*)i->second;
3358 assert(sector->getId() == MAPSECTOR_SERVER);
3360 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3362 saveSectorMeta(sector);
3363 sector_meta_count++;
3365 std::list<MapBlock*> blocks;
3366 sector->getBlocks(blocks);
3368 for(std::list<MapBlock*>::iterator j = blocks.begin();
3369 j != blocks.end(); ++j)
3371 MapBlock *block = *j;
3375 if(block->getModified() >= (u32)save_level)
3380 save_started = true;
3383 modprofiler.add(block->getModifiedReason(), 1);
3388 /*infostream<<"ServerMap: Written block ("
3389 <<block->getPos().X<<","
3390 <<block->getPos().Y<<","
3391 <<block->getPos().Z<<")"
3400 Only print if something happened or saved whole map
3402 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3403 || block_count != 0)
3405 infostream<<"ServerMap: Written: "
3406 <<sector_meta_count<<" sector metadata files, "
3407 <<block_count<<" block files"
3408 <<", "<<block_count_all<<" blocks in memory."
3410 PrintInfo(infostream); // ServerMap/ClientMap:
3411 infostream<<"Blocks modified by: "<<std::endl;
3412 modprofiler.print(infostream);
3416 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3418 if(loadFromFolders()){
3419 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3420 <<"all blocks that are stored in flat files"<<std::endl;
3422 dbase->listAllLoadableBlocks(dst);
3425 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3427 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3428 si != m_sectors.end(); ++si)
3430 MapSector *sector = si->second;
3432 std::list<MapBlock*> blocks;
3433 sector->getBlocks(blocks);
3435 for(std::list<MapBlock*>::iterator i = blocks.begin();
3436 i != blocks.end(); ++i)
3438 MapBlock *block = (*i);
3439 v3s16 p = block->getPos();
3445 void ServerMap::saveMapMeta()
3447 DSTACK(__FUNCTION_NAME);
3449 /*infostream<<"ServerMap::saveMapMeta(): "
3453 createDirs(m_savedir);
3455 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3456 std::ostringstream ss(std::ios_base::binary);
3460 m_emerge->saveParamsToSettings(¶ms);
3461 params.writeLines(ss);
3463 ss<<"[end_of_params]\n";
3465 if(!fs::safeWriteToFile(fullpath, ss.str()))
3467 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3468 <<"could not write "<<fullpath<<std::endl;
3469 throw FileNotGoodException("Cannot save chunk metadata");
3472 m_map_metadata_changed = false;
3475 void ServerMap::loadMapMeta()
3477 DSTACK(__FUNCTION_NAME);
3479 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3482 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3483 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3484 if(is.good() == false)
3486 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3487 <<"could not open"<<fullpath<<std::endl;
3488 throw FileNotGoodException("Cannot open map metadata");
3496 throw SerializationError
3497 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3499 std::getline(is, line);
3500 std::string trimmedline = trim(line);
3501 if(trimmedline == "[end_of_params]")
3503 params.parseConfigLine(line);
3506 m_emerge->loadParamsFromSettings(¶ms);
3508 verbosestream<<"ServerMap::loadMapMeta(): seed="
3509 << m_emerge->params.seed<<std::endl;
3512 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3514 DSTACK(__FUNCTION_NAME);
3515 // Format used for writing
3516 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3518 v2s16 pos = sector->getPos();
3519 std::string dir = getSectorDir(pos);
3522 std::string fullpath = dir + DIR_DELIM + "meta";
3523 std::ostringstream ss(std::ios_base::binary);
3525 sector->serialize(ss, version);
3527 if(!fs::safeWriteToFile(fullpath, ss.str()))
3528 throw FileNotGoodException("Cannot write sector metafile");
3530 sector->differs_from_disk = false;
3533 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3535 DSTACK(__FUNCTION_NAME);
3537 v2s16 p2d = getSectorPos(sectordir);
3539 ServerMapSector *sector = NULL;
3541 std::string fullpath = sectordir + DIR_DELIM + "meta";
3542 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3543 if(is.good() == false)
3545 // If the directory exists anyway, it probably is in some old
3546 // format. Just go ahead and create the sector.
3547 if(fs::PathExists(sectordir))
3549 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3550 <<fullpath<<" doesn't exist but directory does."
3551 <<" Continuing with a sector with no metadata."
3553 sector = new ServerMapSector(this, p2d, m_gamedef);
3554 m_sectors[p2d] = sector;
3558 throw FileNotGoodException("Cannot open sector metafile");
3563 sector = ServerMapSector::deSerialize
3564 (is, this, p2d, m_sectors, m_gamedef);
3566 saveSectorMeta(sector);
3569 sector->differs_from_disk = false;
3574 bool ServerMap::loadSectorMeta(v2s16 p2d)
3576 DSTACK(__FUNCTION_NAME);
3578 MapSector *sector = NULL;
3580 // The directory layout we're going to load from.
3581 // 1 - original sectors/xxxxzzzz/
3582 // 2 - new sectors2/xxx/zzz/
3583 // If we load from anything but the latest structure, we will
3584 // immediately save to the new one, and remove the old.
3586 std::string sectordir1 = getSectorDir(p2d, 1);
3587 std::string sectordir;
3588 if(fs::PathExists(sectordir1))
3590 sectordir = sectordir1;
3595 sectordir = getSectorDir(p2d, 2);
3599 sector = loadSectorMeta(sectordir, loadlayout != 2);
3601 catch(InvalidFilenameException &e)
3605 catch(FileNotGoodException &e)
3609 catch(std::exception &e)
3618 bool ServerMap::loadSectorFull(v2s16 p2d)
3620 DSTACK(__FUNCTION_NAME);
3622 MapSector *sector = NULL;
3624 // The directory layout we're going to load from.
3625 // 1 - original sectors/xxxxzzzz/
3626 // 2 - new sectors2/xxx/zzz/
3627 // If we load from anything but the latest structure, we will
3628 // immediately save to the new one, and remove the old.
3630 std::string sectordir1 = getSectorDir(p2d, 1);
3631 std::string sectordir;
3632 if(fs::PathExists(sectordir1))
3634 sectordir = sectordir1;
3639 sectordir = getSectorDir(p2d, 2);
3643 sector = loadSectorMeta(sectordir, loadlayout != 2);
3645 catch(InvalidFilenameException &e)
3649 catch(FileNotGoodException &e)
3653 catch(std::exception &e)
3661 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3663 std::vector<fs::DirListNode>::iterator i2;
3664 for(i2=list2.begin(); i2!=list2.end(); i2++)
3670 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3672 catch(InvalidFilenameException &e)
3674 // This catches unknown crap in directory
3680 infostream<<"Sector converted to new layout - deleting "<<
3681 sectordir1<<std::endl;
3682 fs::RecursiveDelete(sectordir1);
3689 void ServerMap::beginSave() {
3693 void ServerMap::endSave() {
3697 void ServerMap::saveBlock(MapBlock *block)
3699 dbase->saveBlock(block);
3702 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3704 DSTACK(__FUNCTION_NAME);
3706 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3709 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3710 if(is.good() == false)
3711 throw FileNotGoodException("Cannot open block file");
3713 v3s16 p3d = getBlockPos(sectordir, blockfile);
3714 v2s16 p2d(p3d.X, p3d.Z);
3716 assert(sector->getPos() == p2d);
3718 u8 version = SER_FMT_VER_INVALID;
3719 is.read((char*)&version, 1);
3722 throw SerializationError("ServerMap::loadBlock(): Failed"
3723 " to read MapBlock version");
3725 /*u32 block_size = MapBlock::serializedLength(version);
3726 SharedBuffer<u8> data(block_size);
3727 is.read((char*)*data, block_size);*/
3729 // This will always return a sector because we're the server
3730 //MapSector *sector = emergeSector(p2d);
3732 MapBlock *block = NULL;
3733 bool created_new = false;
3734 block = sector->getBlockNoCreateNoEx(p3d.Y);
3737 block = sector->createBlankBlockNoInsert(p3d.Y);
3742 block->deSerialize(is, version, true);
3744 // If it's a new block, insert it to the map
3746 sector->insertBlock(block);
3749 Save blocks loaded in old format in new format
3752 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3756 // Should be in database now, so delete the old file
3757 fs::RecursiveDelete(fullpath);
3760 // We just loaded it from the disk, so it's up-to-date.
3761 block->resetModified();
3764 catch(SerializationError &e)
3766 infostream<<"WARNING: Invalid block data on disk "
3767 <<"fullpath="<<fullpath
3768 <<" (SerializationError). "
3769 <<"what()="<<e.what()
3771 // Ignoring. A new one will be generated.
3774 // TODO: Backup file; name is in fullpath.
3778 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3780 DSTACK(__FUNCTION_NAME);
3783 std::istringstream is(*blob, std::ios_base::binary);
3785 u8 version = SER_FMT_VER_INVALID;
3786 is.read((char*)&version, 1);
3789 throw SerializationError("ServerMap::loadBlock(): Failed"
3790 " to read MapBlock version");
3792 /*u32 block_size = MapBlock::serializedLength(version);
3793 SharedBuffer<u8> data(block_size);
3794 is.read((char*)*data, block_size);*/
3796 // This will always return a sector because we're the server
3797 //MapSector *sector = emergeSector(p2d);
3799 MapBlock *block = NULL;
3800 bool created_new = false;
3801 block = sector->getBlockNoCreateNoEx(p3d.Y);
3804 block = sector->createBlankBlockNoInsert(p3d.Y);
3809 block->deSerialize(is, version, true);
3811 // If it's a new block, insert it to the map
3813 sector->insertBlock(block);
3816 Save blocks loaded in old format in new format
3819 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3820 // Only save if asked to; no need to update version
3824 // We just loaded it from, so it's up-to-date.
3825 block->resetModified();
3828 catch(SerializationError &e)
3830 errorstream<<"Invalid block data in database"
3831 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3832 <<" (SerializationError): "<<e.what()<<std::endl;
3834 // TODO: Block should be marked as invalid in memory so that it is
3835 // not touched but the game can run
3837 if(g_settings->getBool("ignore_world_load_errors")){
3838 errorstream<<"Ignoring block load error. Duck and cover! "
3839 <<"(ignore_world_load_errors)"<<std::endl;
3841 throw SerializationError("Invalid block data in database");
3847 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3849 DSTACK(__FUNCTION_NAME);
3851 v2s16 p2d(blockpos.X, blockpos.Z);
3855 ret = dbase->loadBlock(blockpos);
3856 if (ret) return (ret);
3857 // Not found in database, try the files
3859 // The directory layout we're going to load from.
3860 // 1 - original sectors/xxxxzzzz/
3861 // 2 - new sectors2/xxx/zzz/
3862 // If we load from anything but the latest structure, we will
3863 // immediately save to the new one, and remove the old.
3865 std::string sectordir1 = getSectorDir(p2d, 1);
3866 std::string sectordir;
3867 if(fs::PathExists(sectordir1))
3869 sectordir = sectordir1;
3874 sectordir = getSectorDir(p2d, 2);
3878 Make sure sector is loaded
3880 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3884 sector = loadSectorMeta(sectordir, loadlayout != 2);
3886 catch(InvalidFilenameException &e)
3890 catch(FileNotGoodException &e)
3894 catch(std::exception &e)
3901 Make sure file exists
3904 std::string blockfilename = getBlockFilename(blockpos);
3905 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3909 Load block and save it to the database
3911 loadBlock(sectordir, blockfilename, sector, true);
3912 return getBlockNoCreateNoEx(blockpos);
3915 void ServerMap::PrintInfo(std::ostream &out)
3920 s16 ServerMap::updateBlockHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
3922 u32 gametime = env->getGameTime();
3925 if (gametime - block->heat_last_update < 10)
3928 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3931 f32 heat = m_emerge->biomedef->calcBlockHeat(p, getSeed(),
3932 env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed());
3936 block->heat_last_update = gametime;
3941 s16 ServerMap::updateBlockHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
3943 u32 gametime = env->getGameTime();
3946 if (gametime - block->humidity_last_update < 10)
3947 return block->humidity;
3949 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3952 f32 humidity = m_emerge->biomedef->calcBlockHumidity(p, getSeed(),
3953 env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed());
3956 block->humidity = humidity;
3957 block->humidity_last_update = gametime;
3966 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3971 MapVoxelManipulator::~MapVoxelManipulator()
3973 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3977 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3979 TimeTaker timer1("emerge", &emerge_time);
3981 // Units of these are MapBlocks
3982 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3983 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3985 VoxelArea block_area_nodes
3986 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3988 addArea(block_area_nodes);
3990 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3991 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3992 for(s32 x=p_min.X; x<=p_max.X; x++)
3997 std::map<v3s16, u8>::iterator n;
3998 n = m_loaded_blocks.find(p);
3999 if(n != m_loaded_blocks.end())
4002 bool block_data_inexistent = false;
4005 TimeTaker timer1("emerge load", &emerge_load_time);
4007 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4008 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4010 a.print(infostream);
4011 infostream<<std::endl;*/
4013 block = m_map->getBlockNoCreate(p);
4014 if(block->isDummy())
4015 block_data_inexistent = true;
4017 block->copyTo(*this);
4019 catch(InvalidPositionException &e)
4021 block_data_inexistent = true;
4024 if(block_data_inexistent)
4026 flags |= VMANIP_BLOCK_DATA_INEXIST;
4028 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4029 // Fill with VOXELFLAG_INEXISTENT
4030 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4031 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4033 s32 i = m_area.index(a.MinEdge.X,y,z);
4034 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4037 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4039 // Mark that block was loaded as blank
4040 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4043 m_loaded_blocks[p] = flags;
4046 //infostream<<"emerge done"<<std::endl;
4050 SUGG: Add an option to only update eg. water and air nodes.
4051 This will make it interfere less with important stuff if
4054 void MapVoxelManipulator::blitBack
4055 (std::map<v3s16, MapBlock*> & modified_blocks)
4057 if(m_area.getExtent() == v3s16(0,0,0))
4060 //TimeTaker timer1("blitBack");
4062 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4063 <<m_loaded_blocks.size()<<std::endl;*/
4066 Initialize block cache
4068 v3s16 blockpos_last;
4069 MapBlock *block = NULL;
4070 bool block_checked_in_modified = false;
4072 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4073 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4074 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4078 u8 f = m_flags[m_area.index(p)];
4079 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4082 MapNode &n = m_data[m_area.index(p)];
4084 v3s16 blockpos = getNodeBlockPos(p);
4089 if(block == NULL || blockpos != blockpos_last){
4090 block = m_map->getBlockNoCreate(blockpos);
4091 blockpos_last = blockpos;
4092 block_checked_in_modified = false;
4095 // Calculate relative position in block
4096 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4098 // Don't continue if nothing has changed here
4099 if(block->getNode(relpos) == n)
4102 //m_map->setNode(m_area.MinEdge + p, n);
4103 block->setNode(relpos, n);
4106 Make sure block is in modified_blocks
4108 if(block_checked_in_modified == false)
4110 modified_blocks[blockpos] = block;
4111 block_checked_in_modified = true;
4114 catch(InvalidPositionException &e)
4120 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4121 MapVoxelManipulator(map),
4122 m_create_area(false)
4126 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4130 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4132 // Just create the area so that it can be pointed to
4133 VoxelManipulator::emerge(a, caller_id);
4136 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4137 v3s16 blockpos_max, bool load_if_inexistent)
4139 TimeTaker timer1("initialEmerge", &emerge_time);
4141 // Units of these are MapBlocks
4142 v3s16 p_min = blockpos_min;
4143 v3s16 p_max = blockpos_max;
4145 VoxelArea block_area_nodes
4146 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4148 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4151 infostream<<"initialEmerge: area: ";
4152 block_area_nodes.print(infostream);
4153 infostream<<" ("<<size_MB<<"MB)";
4154 infostream<<std::endl;
4157 addArea(block_area_nodes);
4159 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4160 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4161 for(s32 x=p_min.X; x<=p_max.X; x++)
4166 std::map<v3s16, u8>::iterator n;
4167 n = m_loaded_blocks.find(p);
4168 if(n != m_loaded_blocks.end())
4171 bool block_data_inexistent = false;
4174 TimeTaker timer1("emerge load", &emerge_load_time);
4176 block = m_map->getBlockNoCreate(p);
4177 if(block->isDummy())
4178 block_data_inexistent = true;
4180 block->copyTo(*this);
4182 catch(InvalidPositionException &e)
4184 block_data_inexistent = true;
4187 if(block_data_inexistent)
4190 if (load_if_inexistent) {
4191 ServerMap *svrmap = (ServerMap *)m_map;
4192 block = svrmap->emergeBlock(p, false);
4194 block = svrmap->createBlock(p);
4196 block->copyTo(*this);
4198 flags |= VMANIP_BLOCK_DATA_INEXIST;
4201 Mark area inexistent
4203 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4204 // Fill with VOXELFLAG_INEXISTENT
4205 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4206 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4208 s32 i = m_area.index(a.MinEdge.X,y,z);
4209 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4213 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4215 // Mark that block was loaded as blank
4216 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4219 m_loaded_blocks[p] = flags;
4223 void ManualMapVoxelManipulator::blitBackAll(
4224 std::map<v3s16, MapBlock*> * modified_blocks)
4226 if(m_area.getExtent() == v3s16(0,0,0))
4230 Copy data of all blocks
4232 for(std::map<v3s16, u8>::iterator
4233 i = m_loaded_blocks.begin();
4234 i != m_loaded_blocks.end(); ++i)
4237 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4238 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4239 if(existed == false)
4244 block->copyFrom(*this);
4247 (*modified_blocks)[p] = block;