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"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "rollback_interface.h"
37 #include "mapgen_v6.h"
39 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
42 SQLite format specification:
43 - Initially only replaces sectors/ and sectors2/
45 If map.sqlite does not exist in the save dir
46 or the block was not found in the database
47 the map will try to load from sectors folder.
48 In either case, map.sqlite will be created
49 and all future saves will save there.
51 Structure of map.sqlite:
62 Map::Map(std::ostream &dout, IGameDef *gamedef):
67 /*m_sector_mutex.Init();
68 assert(m_sector_mutex.IsInitialized());*/
76 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
77 i != m_sectors.end(); ++i)
83 void Map::addEventReceiver(MapEventReceiver *event_receiver)
85 m_event_receivers.insert(event_receiver);
88 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
90 m_event_receivers.erase(event_receiver);
93 void Map::dispatchEvent(MapEditEvent *event)
95 for(std::set<MapEventReceiver*>::iterator
96 i = m_event_receivers.begin();
97 i != m_event_receivers.end(); ++i)
99 (*i)->onMapEditEvent(event);
103 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
105 if(m_sector_cache != NULL && p == m_sector_cache_p){
106 MapSector * sector = m_sector_cache;
110 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
112 if(n == m_sectors.end())
115 MapSector *sector = n->second;
117 // Cache the last result
118 m_sector_cache_p = p;
119 m_sector_cache = sector;
124 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
126 return getSectorNoGenerateNoExNoLock(p);
129 MapSector * Map::getSectorNoGenerate(v2s16 p)
131 MapSector *sector = getSectorNoGenerateNoEx(p);
133 throw InvalidPositionException();
138 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
140 v2s16 p2d(p3d.X, p3d.Z);
141 MapSector * sector = getSectorNoGenerateNoEx(p2d);
144 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
148 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
150 MapBlock *block = getBlockNoCreateNoEx(p3d);
152 throw InvalidPositionException();
156 bool Map::isNodeUnderground(v3s16 p)
158 v3s16 blockpos = getNodeBlockPos(p);
160 MapBlock * block = getBlockNoCreate(blockpos);
161 return block->getIsUnderground();
163 catch(InvalidPositionException &e)
169 bool Map::isValidPosition(v3s16 p)
171 v3s16 blockpos = getNodeBlockPos(p);
172 MapBlock *block = getBlockNoCreate(blockpos);
173 return (block != NULL);
176 // Returns a CONTENT_IGNORE node if not found
177 MapNode Map::getNodeNoEx(v3s16 p)
179 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock *block = getBlockNoCreateNoEx(blockpos);
182 return MapNode(CONTENT_IGNORE);
183 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
184 return block->getNodeNoCheck(relpos);
187 // throws InvalidPositionException if not found
188 MapNode Map::getNode(v3s16 p)
190 v3s16 blockpos = getNodeBlockPos(p);
191 MapBlock *block = getBlockNoCreateNoEx(blockpos);
193 throw InvalidPositionException();
194 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
195 return block->getNodeNoCheck(relpos);
198 // throws InvalidPositionException if not found
199 void Map::setNode(v3s16 p, MapNode & n)
201 v3s16 blockpos = getNodeBlockPos(p);
202 MapBlock *block = getBlockNoCreate(blockpos);
203 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
204 // Never allow placing CONTENT_IGNORE, it fucks up stuff
205 if(n.getContent() == CONTENT_IGNORE){
206 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
207 <<" while trying to replace \""
208 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
209 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
210 debug_stacks_print_to(infostream);
213 block->setNodeNoCheck(relpos, n);
218 Goes recursively through the neighbours of the node.
220 Alters only transparent nodes.
222 If the lighting of the neighbour is lower than the lighting of
223 the node was (before changing it to 0 at the step before), the
224 lighting of the neighbour is set to 0 and then the same stuff
225 repeats for the neighbour.
227 The ending nodes of the routine are stored in light_sources.
228 This is useful when a light is removed. In such case, this
229 routine can be called for the light node and then again for
230 light_sources to re-light the area without the removed light.
232 values of from_nodes are lighting values.
234 void Map::unspreadLight(enum LightBank bank,
235 std::map<v3s16, u8> & from_nodes,
236 std::set<v3s16> & light_sources,
237 std::map<v3s16, MapBlock*> & modified_blocks)
239 INodeDefManager *nodemgr = m_gamedef->ndef();
242 v3s16(0,0,1), // back
244 v3s16(1,0,0), // right
245 v3s16(0,0,-1), // front
246 v3s16(0,-1,0), // bottom
247 v3s16(-1,0,0), // left
250 if(from_nodes.size() == 0)
253 u32 blockchangecount = 0;
255 std::map<v3s16, u8> unlighted_nodes;
258 Initialize block cache
261 MapBlock *block = NULL;
262 // Cache this a bit, too
263 bool block_checked_in_modified = false;
265 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
266 j != from_nodes.end(); ++j)
268 v3s16 pos = j->first;
269 v3s16 blockpos = getNodeBlockPos(pos);
271 // Only fetch a new block if the block position has changed
273 if(block == NULL || blockpos != blockpos_last){
274 block = getBlockNoCreate(blockpos);
275 blockpos_last = blockpos;
277 block_checked_in_modified = false;
281 catch(InvalidPositionException &e)
289 // Calculate relative position in block
290 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
292 // Get node straight from the block
293 MapNode n = block->getNode(relpos);
295 u8 oldlight = j->second;
297 // Loop through 6 neighbors
298 for(u16 i=0; i<6; i++)
300 // Get the position of the neighbor node
301 v3s16 n2pos = pos + dirs[i];
303 // Get the block where the node is located
304 v3s16 blockpos = getNodeBlockPos(n2pos);
308 // Only fetch a new block if the block position has changed
310 if(block == NULL || blockpos != blockpos_last){
311 block = getBlockNoCreate(blockpos);
312 blockpos_last = blockpos;
314 block_checked_in_modified = false;
318 catch(InvalidPositionException &e)
323 // Calculate relative position in block
324 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
325 // Get node straight from the block
326 MapNode n2 = block->getNode(relpos);
328 bool changed = false;
330 //TODO: Optimize output by optimizing light_sources?
333 If the neighbor is dimmer than what was specified
334 as oldlight (the light of the previous node)
336 if(n2.getLight(bank, nodemgr) < oldlight)
339 And the neighbor is transparent and it has some light
341 if(nodemgr->get(n2).light_propagates
342 && n2.getLight(bank, nodemgr) != 0)
345 Set light to 0 and add to queue
348 u8 current_light = n2.getLight(bank, nodemgr);
349 n2.setLight(bank, 0, nodemgr);
350 block->setNode(relpos, n2);
352 unlighted_nodes[n2pos] = current_light;
356 Remove from light_sources if it is there
357 NOTE: This doesn't happen nearly at all
359 /*if(light_sources.find(n2pos))
361 infostream<<"Removed from light_sources"<<std::endl;
362 light_sources.remove(n2pos);
367 if(light_sources.find(n2pos) != NULL)
368 light_sources.remove(n2pos);*/
371 light_sources.insert(n2pos);
374 // Add to modified_blocks
375 if(changed == true && block_checked_in_modified == false)
377 // If the block is not found in modified_blocks, add.
378 if(modified_blocks.find(blockpos) == modified_blocks.end())
380 modified_blocks[blockpos] = block;
382 block_checked_in_modified = true;
385 catch(InvalidPositionException &e)
392 /*infostream<<"unspreadLight(): Changed block "
393 <<blockchangecount<<" times"
394 <<" for "<<from_nodes.size()<<" nodes"
397 if(unlighted_nodes.size() > 0)
398 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
402 A single-node wrapper of the above
404 void Map::unLightNeighbors(enum LightBank bank,
405 v3s16 pos, u8 lightwas,
406 std::set<v3s16> & light_sources,
407 std::map<v3s16, MapBlock*> & modified_blocks)
409 std::map<v3s16, u8> from_nodes;
410 from_nodes[pos] = lightwas;
412 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
416 Lights neighbors of from_nodes, collects all them and then
419 void Map::spreadLight(enum LightBank bank,
420 std::set<v3s16> & from_nodes,
421 std::map<v3s16, MapBlock*> & modified_blocks)
423 INodeDefManager *nodemgr = m_gamedef->ndef();
425 const v3s16 dirs[6] = {
426 v3s16(0,0,1), // back
428 v3s16(1,0,0), // right
429 v3s16(0,0,-1), // front
430 v3s16(0,-1,0), // bottom
431 v3s16(-1,0,0), // left
434 if(from_nodes.size() == 0)
437 u32 blockchangecount = 0;
439 std::set<v3s16> lighted_nodes;
442 Initialize block cache
445 MapBlock *block = NULL;
446 // Cache this a bit, too
447 bool block_checked_in_modified = false;
449 for(std::set<v3s16>::iterator j = from_nodes.begin();
450 j != from_nodes.end(); ++j)
453 v3s16 blockpos = getNodeBlockPos(pos);
455 // Only fetch a new block if the block position has changed
457 if(block == NULL || blockpos != blockpos_last){
458 block = getBlockNoCreate(blockpos);
459 blockpos_last = blockpos;
461 block_checked_in_modified = false;
465 catch(InvalidPositionException &e)
473 // Calculate relative position in block
474 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
476 // Get node straight from the block
477 MapNode n = block->getNode(relpos);
479 u8 oldlight = n.getLight(bank, nodemgr);
480 u8 newlight = diminish_light(oldlight);
482 // Loop through 6 neighbors
483 for(u16 i=0; i<6; i++){
484 // Get the position of the neighbor node
485 v3s16 n2pos = pos + dirs[i];
487 // Get the block where the node is located
488 v3s16 blockpos = getNodeBlockPos(n2pos);
492 // Only fetch a new block if the block position has changed
494 if(block == NULL || blockpos != blockpos_last){
495 block = getBlockNoCreate(blockpos);
496 blockpos_last = blockpos;
498 block_checked_in_modified = false;
502 catch(InvalidPositionException &e)
507 // Calculate relative position in block
508 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
509 // Get node straight from the block
510 MapNode n2 = block->getNode(relpos);
512 bool changed = false;
514 If the neighbor is brighter than the current node,
515 add to list (it will light up this node on its turn)
517 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
519 lighted_nodes.insert(n2pos);
523 If the neighbor is dimmer than how much light this node
524 would spread on it, add to list
526 if(n2.getLight(bank, nodemgr) < newlight)
528 if(nodemgr->get(n2).light_propagates)
530 n2.setLight(bank, newlight, nodemgr);
531 block->setNode(relpos, n2);
532 lighted_nodes.insert(n2pos);
537 // Add to modified_blocks
538 if(changed == true && block_checked_in_modified == false)
540 // If the block is not found in modified_blocks, add.
541 if(modified_blocks.find(blockpos) == modified_blocks.end())
543 modified_blocks[blockpos] = block;
545 block_checked_in_modified = true;
548 catch(InvalidPositionException &e)
555 /*infostream<<"spreadLight(): Changed block "
556 <<blockchangecount<<" times"
557 <<" for "<<from_nodes.size()<<" nodes"
560 if(lighted_nodes.size() > 0)
561 spreadLight(bank, lighted_nodes, modified_blocks);
565 A single-node source variation of the above.
567 void Map::lightNeighbors(enum LightBank bank,
569 std::map<v3s16, MapBlock*> & modified_blocks)
571 std::set<v3s16> from_nodes;
572 from_nodes.insert(pos);
573 spreadLight(bank, from_nodes, modified_blocks);
576 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
578 INodeDefManager *nodemgr = m_gamedef->ndef();
581 v3s16(0,0,1), // back
583 v3s16(1,0,0), // right
584 v3s16(0,0,-1), // front
585 v3s16(0,-1,0), // bottom
586 v3s16(-1,0,0), // left
589 u8 brightest_light = 0;
590 v3s16 brightest_pos(0,0,0);
591 bool found_something = false;
593 // Loop through 6 neighbors
594 for(u16 i=0; i<6; i++){
595 // Get the position of the neighbor node
596 v3s16 n2pos = p + dirs[i];
601 catch(InvalidPositionException &e)
605 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
606 brightest_light = n2.getLight(bank, nodemgr);
607 brightest_pos = n2pos;
608 found_something = true;
612 if(found_something == false)
613 throw InvalidPositionException();
615 return brightest_pos;
619 Propagates sunlight down from a node.
620 Starting point gets sunlight.
622 Returns the lowest y value of where the sunlight went.
624 Mud is turned into grass in where the sunlight stops.
626 s16 Map::propagateSunlight(v3s16 start,
627 std::map<v3s16, MapBlock*> & modified_blocks)
629 INodeDefManager *nodemgr = m_gamedef->ndef();
634 v3s16 pos(start.X, y, start.Z);
636 v3s16 blockpos = getNodeBlockPos(pos);
639 block = getBlockNoCreate(blockpos);
641 catch(InvalidPositionException &e)
646 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
647 MapNode n = block->getNode(relpos);
649 if(nodemgr->get(n).sunlight_propagates)
651 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
652 block->setNode(relpos, n);
654 modified_blocks[blockpos] = block;
658 // Sunlight goes no further
665 void Map::updateLighting(enum LightBank bank,
666 std::map<v3s16, MapBlock*> & a_blocks,
667 std::map<v3s16, MapBlock*> & modified_blocks)
669 INodeDefManager *nodemgr = m_gamedef->ndef();
671 /*m_dout<<DTIME<<"Map::updateLighting(): "
672 <<a_blocks.size()<<" blocks."<<std::endl;*/
674 //TimeTaker timer("updateLighting");
678 //u32 count_was = modified_blocks.size();
680 std::map<v3s16, MapBlock*> blocks_to_update;
682 std::set<v3s16> light_sources;
684 std::map<v3s16, u8> unlight_from;
686 int num_bottom_invalid = 0;
689 //TimeTaker t("first stuff");
691 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
692 i != a_blocks.end(); ++i)
694 MapBlock *block = i->second;
698 // Don't bother with dummy blocks.
702 v3s16 pos = block->getPos();
703 v3s16 posnodes = block->getPosRelative();
704 modified_blocks[pos] = block;
705 blocks_to_update[pos] = block;
708 Clear all light from block
710 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
711 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
712 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
717 MapNode n = block->getNode(p);
718 u8 oldlight = n.getLight(bank, nodemgr);
719 n.setLight(bank, 0, nodemgr);
720 block->setNode(p, n);
722 // If node sources light, add to list
723 u8 source = nodemgr->get(n).light_source;
725 light_sources.insert(p + posnodes);
727 // Collect borders for unlighting
728 if((x==0 || x == MAP_BLOCKSIZE-1
729 || y==0 || y == MAP_BLOCKSIZE-1
730 || z==0 || z == MAP_BLOCKSIZE-1)
733 v3s16 p_map = p + posnodes;
734 unlight_from[p_map] = oldlight;
737 catch(InvalidPositionException &e)
740 This would happen when dealing with a
744 infostream<<"updateLighting(): InvalidPositionException"
749 if(bank == LIGHTBANK_DAY)
751 bool bottom_valid = block->propagateSunlight(light_sources);
754 num_bottom_invalid++;
756 // If bottom is valid, we're done.
760 else if(bank == LIGHTBANK_NIGHT)
762 // For night lighting, sunlight is not propagated
767 // Invalid lighting bank
771 /*infostream<<"Bottom for sunlight-propagated block ("
772 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
775 // Bottom sunlight is not valid; get the block and loop to it
779 block = getBlockNoCreate(pos);
781 catch(InvalidPositionException &e)
792 Enable this to disable proper lighting for speeding up map
793 generation for testing or whatever
796 //if(g_settings->get(""))
798 core::map<v3s16, MapBlock*>::Iterator i;
799 i = blocks_to_update.getIterator();
800 for(; i.atEnd() == false; i++)
802 MapBlock *block = i.getNode()->getValue();
803 v3s16 p = block->getPos();
804 block->setLightingExpired(false);
812 //TimeTaker timer("unspreadLight");
813 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
818 u32 diff = modified_blocks.size() - count_was;
819 count_was = modified_blocks.size();
820 infostream<<"unspreadLight modified "<<diff<<std::endl;
824 //TimeTaker timer("spreadLight");
825 spreadLight(bank, light_sources, modified_blocks);
830 u32 diff = modified_blocks.size() - count_was;
831 count_was = modified_blocks.size();
832 infostream<<"spreadLight modified "<<diff<<std::endl;
838 //MapVoxelManipulator vmanip(this);
840 // Make a manual voxel manipulator and load all the blocks
841 // that touch the requested blocks
842 ManualMapVoxelManipulator vmanip(this);
845 //TimeTaker timer("initialEmerge");
847 core::map<v3s16, MapBlock*>::Iterator i;
848 i = blocks_to_update.getIterator();
849 for(; i.atEnd() == false; i++)
851 MapBlock *block = i.getNode()->getValue();
852 v3s16 p = block->getPos();
854 // Add all surrounding blocks
855 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
858 Add all surrounding blocks that have up-to-date lighting
859 NOTE: This doesn't quite do the job (not everything
860 appropriate is lighted)
862 /*for(s16 z=-1; z<=1; z++)
863 for(s16 y=-1; y<=1; y++)
864 for(s16 x=-1; x<=1; x++)
866 v3s16 p2 = p + v3s16(x,y,z);
867 MapBlock *block = getBlockNoCreateNoEx(p2);
872 if(block->getLightingExpired())
874 vmanip.initialEmerge(p2, p2);
877 // Lighting of block will be updated completely
878 block->setLightingExpired(false);
883 //TimeTaker timer("unSpreadLight");
884 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
887 //TimeTaker timer("spreadLight");
888 vmanip.spreadLight(bank, light_sources, nodemgr);
891 //TimeTaker timer("blitBack");
892 vmanip.blitBack(modified_blocks);
894 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
899 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
902 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
903 std::map<v3s16, MapBlock*> & modified_blocks)
905 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
906 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
909 Update information about whether day and night light differ
911 for(std::map<v3s16, MapBlock*>::iterator
912 i = modified_blocks.begin();
913 i != modified_blocks.end(); ++i)
915 MapBlock *block = i->second;
916 block->expireDayNightDiff();
922 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
923 std::map<v3s16, MapBlock*> &modified_blocks)
925 INodeDefManager *ndef = m_gamedef->ndef();
928 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
929 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
932 From this node to nodes underneath:
933 If lighting is sunlight (1.0), unlight neighbours and
938 v3s16 toppos = p + v3s16(0,1,0);
939 v3s16 bottompos = p + v3s16(0,-1,0);
941 bool node_under_sunlight = true;
942 std::set<v3s16> light_sources;
945 Collect old node for rollback
947 RollbackNode rollback_oldnode(this, p, m_gamedef);
950 If there is a node at top and it doesn't have sunlight,
951 there has not been any sunlight going down.
953 Otherwise there probably is.
956 MapNode topnode = getNode(toppos);
958 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
959 node_under_sunlight = false;
961 catch(InvalidPositionException &e)
966 Remove all light that has come out of this node
969 enum LightBank banks[] =
974 for(s32 i=0; i<2; i++)
976 enum LightBank bank = banks[i];
978 u8 lightwas = getNode(p).getLight(bank, ndef);
980 // Add the block of the added node to modified_blocks
981 v3s16 blockpos = getNodeBlockPos(p);
982 MapBlock * block = getBlockNoCreate(blockpos);
983 assert(block != NULL);
984 modified_blocks[blockpos] = block;
986 assert(isValidPosition(p));
988 // Unlight neighbours of node.
989 // This means setting light of all consequent dimmer nodes
991 // This also collects the nodes at the border which will spread
992 // light again into this.
993 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
995 n.setLight(bank, 0, ndef);
999 If node lets sunlight through and is under sunlight, it has
1002 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1004 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1008 Remove node metadata
1011 removeNodeMetadata(p);
1014 Set the node on the map
1020 If node is under sunlight and doesn't let sunlight through,
1021 take all sunlighted nodes under it and clear light from them
1022 and from where the light has been spread.
1023 TODO: This could be optimized by mass-unlighting instead
1026 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1030 //m_dout<<DTIME<<"y="<<y<<std::endl;
1031 v3s16 n2pos(p.X, y, p.Z);
1035 n2 = getNode(n2pos);
1037 catch(InvalidPositionException &e)
1042 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1044 unLightNeighbors(LIGHTBANK_DAY,
1045 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1046 light_sources, modified_blocks);
1047 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1055 for(s32 i=0; i<2; i++)
1057 enum LightBank bank = banks[i];
1060 Spread light from all nodes that might be capable of doing so
1062 spreadLight(bank, light_sources, modified_blocks);
1066 Update information about whether day and night light differ
1068 for(std::map<v3s16, MapBlock*>::iterator
1069 i = modified_blocks.begin();
1070 i != modified_blocks.end(); ++i)
1072 i->second->expireDayNightDiff();
1078 if(m_gamedef->rollback())
1080 RollbackNode rollback_newnode(this, p, m_gamedef);
1081 RollbackAction action;
1082 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1083 m_gamedef->rollback()->reportAction(action);
1087 Add neighboring liquid nodes and the node itself if it is
1088 liquid (=water node was added) to transform queue.
1091 v3s16(0,0,0), // self
1092 v3s16(0,0,1), // back
1093 v3s16(0,1,0), // top
1094 v3s16(1,0,0), // right
1095 v3s16(0,0,-1), // front
1096 v3s16(0,-1,0), // bottom
1097 v3s16(-1,0,0), // left
1099 for(u16 i=0; i<7; i++)
1104 v3s16 p2 = p + dirs[i];
1106 MapNode n2 = getNode(p2);
1107 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1109 m_transforming_liquid.push_back(p2);
1112 }catch(InvalidPositionException &e)
1120 void Map::removeNodeAndUpdate(v3s16 p,
1121 std::map<v3s16, MapBlock*> &modified_blocks)
1123 INodeDefManager *ndef = m_gamedef->ndef();
1125 /*PrintInfo(m_dout);
1126 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1127 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1129 bool node_under_sunlight = true;
1131 v3s16 toppos = p + v3s16(0,1,0);
1133 // Node will be replaced with this
1134 content_t replace_material = CONTENT_AIR;
1137 Collect old node for rollback
1139 RollbackNode rollback_oldnode(this, p, m_gamedef);
1142 If there is a node at top and it doesn't have sunlight,
1143 there will be no sunlight going down.
1146 MapNode topnode = getNode(toppos);
1148 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1149 node_under_sunlight = false;
1151 catch(InvalidPositionException &e)
1155 std::set<v3s16> light_sources;
1157 enum LightBank banks[] =
1162 for(s32 i=0; i<2; i++)
1164 enum LightBank bank = banks[i];
1167 Unlight neighbors (in case the node is a light source)
1169 unLightNeighbors(bank, p,
1170 getNode(p).getLight(bank, ndef),
1171 light_sources, modified_blocks);
1175 Remove node metadata
1178 removeNodeMetadata(p);
1182 This also clears the lighting.
1186 n.setContent(replace_material);
1189 for(s32 i=0; i<2; i++)
1191 enum LightBank bank = banks[i];
1194 Recalculate lighting
1196 spreadLight(bank, light_sources, modified_blocks);
1199 // Add the block of the removed node to modified_blocks
1200 v3s16 blockpos = getNodeBlockPos(p);
1201 MapBlock * block = getBlockNoCreate(blockpos);
1202 assert(block != NULL);
1203 modified_blocks[blockpos] = block;
1206 If the removed node was under sunlight, propagate the
1207 sunlight down from it and then light all neighbors
1208 of the propagated blocks.
1210 if(node_under_sunlight)
1212 s16 ybottom = propagateSunlight(p, modified_blocks);
1213 /*m_dout<<DTIME<<"Node was under sunlight. "
1214 "Propagating sunlight";
1215 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1217 for(; y >= ybottom; y--)
1219 v3s16 p2(p.X, y, p.Z);
1220 /*m_dout<<DTIME<<"lighting neighbors of node ("
1221 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1223 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1228 // Set the lighting of this node to 0
1229 // TODO: Is this needed? Lighting is cleared up there already.
1231 MapNode n = getNode(p);
1232 n.setLight(LIGHTBANK_DAY, 0, ndef);
1235 catch(InvalidPositionException &e)
1241 for(s32 i=0; i<2; i++)
1243 enum LightBank bank = banks[i];
1245 // Get the brightest neighbour node and propagate light from it
1246 v3s16 n2p = getBrightestNeighbour(bank, p);
1248 MapNode n2 = getNode(n2p);
1249 lightNeighbors(bank, n2p, modified_blocks);
1251 catch(InvalidPositionException &e)
1257 Update information about whether day and night light differ
1259 for(std::map<v3s16, MapBlock*>::iterator
1260 i = modified_blocks.begin();
1261 i != modified_blocks.end(); ++i)
1263 i->second->expireDayNightDiff();
1269 if(m_gamedef->rollback())
1271 RollbackNode rollback_newnode(this, p, m_gamedef);
1272 RollbackAction action;
1273 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1274 m_gamedef->rollback()->reportAction(action);
1278 Add neighboring liquid nodes and this node to transform queue.
1279 (it's vital for the node itself to get updated last.)
1282 v3s16(0,0,1), // back
1283 v3s16(0,1,0), // top
1284 v3s16(1,0,0), // right
1285 v3s16(0,0,-1), // front
1286 v3s16(0,-1,0), // bottom
1287 v3s16(-1,0,0), // left
1288 v3s16(0,0,0), // self
1290 for(u16 i=0; i<7; i++)
1295 v3s16 p2 = p + dirs[i];
1297 MapNode n2 = getNode(p2);
1298 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1300 m_transforming_liquid.push_back(p2);
1303 }catch(InvalidPositionException &e)
1309 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1312 event.type = MEET_ADDNODE;
1316 bool succeeded = true;
1318 std::map<v3s16, MapBlock*> modified_blocks;
1319 addNodeAndUpdate(p, n, modified_blocks);
1321 // Copy modified_blocks to event
1322 for(std::map<v3s16, MapBlock*>::iterator
1323 i = modified_blocks.begin();
1324 i != modified_blocks.end(); ++i)
1326 event.modified_blocks.erase(i->first);
1329 catch(InvalidPositionException &e){
1333 dispatchEvent(&event);
1338 bool Map::removeNodeWithEvent(v3s16 p)
1341 event.type = MEET_REMOVENODE;
1344 bool succeeded = true;
1346 std::map<v3s16, MapBlock*> modified_blocks;
1347 removeNodeAndUpdate(p, modified_blocks);
1349 // Copy modified_blocks to event
1350 for(std::map<v3s16, MapBlock*>::iterator
1351 i = modified_blocks.begin();
1352 i != modified_blocks.end(); ++i)
1354 event.modified_blocks.erase(i->first);
1357 catch(InvalidPositionException &e){
1361 dispatchEvent(&event);
1366 bool Map::getDayNightDiff(v3s16 blockpos)
1369 v3s16 p = blockpos + v3s16(0,0,0);
1370 MapBlock *b = getBlockNoCreate(p);
1371 if(b->getDayNightDiff())
1374 catch(InvalidPositionException &e){}
1377 v3s16 p = blockpos + v3s16(-1,0,0);
1378 MapBlock *b = getBlockNoCreate(p);
1379 if(b->getDayNightDiff())
1382 catch(InvalidPositionException &e){}
1384 v3s16 p = blockpos + v3s16(0,-1,0);
1385 MapBlock *b = getBlockNoCreate(p);
1386 if(b->getDayNightDiff())
1389 catch(InvalidPositionException &e){}
1391 v3s16 p = blockpos + v3s16(0,0,-1);
1392 MapBlock *b = getBlockNoCreate(p);
1393 if(b->getDayNightDiff())
1396 catch(InvalidPositionException &e){}
1399 v3s16 p = blockpos + v3s16(1,0,0);
1400 MapBlock *b = getBlockNoCreate(p);
1401 if(b->getDayNightDiff())
1404 catch(InvalidPositionException &e){}
1406 v3s16 p = blockpos + v3s16(0,1,0);
1407 MapBlock *b = getBlockNoCreate(p);
1408 if(b->getDayNightDiff())
1411 catch(InvalidPositionException &e){}
1413 v3s16 p = blockpos + v3s16(0,0,1);
1414 MapBlock *b = getBlockNoCreate(p);
1415 if(b->getDayNightDiff())
1418 catch(InvalidPositionException &e){}
1424 Updates usage timers
1426 void Map::timerUpdate(float dtime, float unload_timeout,
1427 std::list<v3s16> *unloaded_blocks)
1429 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1431 // Profile modified reasons
1432 Profiler modprofiler;
1434 std::list<v2s16> sector_deletion_queue;
1435 u32 deleted_blocks_count = 0;
1436 u32 saved_blocks_count = 0;
1437 u32 block_count_all = 0;
1440 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1441 si != m_sectors.end(); ++si)
1443 MapSector *sector = si->second;
1445 bool all_blocks_deleted = true;
1447 std::list<MapBlock*> blocks;
1448 sector->getBlocks(blocks);
1450 for(std::list<MapBlock*>::iterator i = blocks.begin();
1451 i != blocks.end(); ++i)
1453 MapBlock *block = (*i);
1455 block->incrementUsageTimer(dtime);
1457 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1459 v3s16 p = block->getPos();
1462 if(block->getModified() != MOD_STATE_CLEAN
1463 && save_before_unloading)
1465 modprofiler.add(block->getModifiedReason(), 1);
1467 saved_blocks_count++;
1470 // Delete from memory
1471 sector->deleteBlock(block);
1474 unloaded_blocks->push_back(p);
1476 deleted_blocks_count++;
1480 all_blocks_deleted = false;
1485 if(all_blocks_deleted)
1487 sector_deletion_queue.push_back(si->first);
1492 // Finally delete the empty sectors
1493 deleteSectors(sector_deletion_queue);
1495 if(deleted_blocks_count != 0)
1497 PrintInfo(infostream); // ServerMap/ClientMap:
1498 infostream<<"Unloaded "<<deleted_blocks_count
1499 <<" blocks from memory";
1500 if(save_before_unloading)
1501 infostream<<", of which "<<saved_blocks_count<<" were written";
1502 infostream<<", "<<block_count_all<<" blocks in memory";
1503 infostream<<"."<<std::endl;
1504 if(saved_blocks_count != 0){
1505 PrintInfo(infostream); // ServerMap/ClientMap:
1506 infostream<<"Blocks modified by: "<<std::endl;
1507 modprofiler.print(infostream);
1512 void Map::deleteSectors(std::list<v2s16> &list)
1514 for(std::list<v2s16>::iterator j = list.begin();
1515 j != list.end(); ++j)
1517 MapSector *sector = m_sectors[*j];
1518 // If sector is in sector cache, remove it from there
1519 if(m_sector_cache == sector)
1520 m_sector_cache = NULL;
1521 // Remove from map and delete
1522 m_sectors.erase(*j);
1528 void Map::unloadUnusedData(float timeout,
1529 core::list<v3s16> *deleted_blocks)
1531 core::list<v2s16> sector_deletion_queue;
1532 u32 deleted_blocks_count = 0;
1533 u32 saved_blocks_count = 0;
1535 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1536 for(; si.atEnd() == false; si++)
1538 MapSector *sector = si.getNode()->getValue();
1540 bool all_blocks_deleted = true;
1542 core::list<MapBlock*> blocks;
1543 sector->getBlocks(blocks);
1544 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1545 i != blocks.end(); i++)
1547 MapBlock *block = (*i);
1549 if(block->getUsageTimer() > timeout)
1552 if(block->getModified() != MOD_STATE_CLEAN)
1555 saved_blocks_count++;
1557 // Delete from memory
1558 sector->deleteBlock(block);
1559 deleted_blocks_count++;
1563 all_blocks_deleted = false;
1567 if(all_blocks_deleted)
1569 sector_deletion_queue.push_back(si.getNode()->getKey());
1573 deleteSectors(sector_deletion_queue);
1575 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1576 <<", of which "<<saved_blocks_count<<" were wr."
1579 //return sector_deletion_queue.getSize();
1580 //return deleted_blocks_count;
1584 void Map::PrintInfo(std::ostream &out)
1589 #define WATER_DROP_BOOST 4
1593 NEIGHBOR_SAME_LEVEL,
1596 struct NodeNeighbor {
1600 bool l; //can liquid
1604 void Map::transforming_liquid_add(v3s16 p) {
1605 m_transforming_liquid.push_back(p);
1608 s32 Map::transforming_liquid_size() {
1609 return m_transforming_liquid.size();
1612 const v3s16 g_7dirs[7] =
1614 // +right, +top, +back
1615 v3s16( 0,-1, 0), // bottom
1616 v3s16( 0, 0, 0), // self
1617 v3s16( 0, 0, 1), // back
1618 v3s16( 0, 0,-1), // front
1619 v3s16( 1, 0, 0), // right
1620 v3s16(-1, 0, 0), // left
1621 v3s16( 0, 1, 0) // top
1628 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1630 INodeDefManager *nodemgr = m_gamedef->ndef();
1632 DSTACK(__FUNCTION_NAME);
1633 //TimeTaker timer("transformLiquids()");
1636 u32 initial_size = m_transforming_liquid.size();
1638 u8 relax = g_settings->getS16("liquid_relax");
1639 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1640 int water_level = g_settings->getS16("water_level");
1642 // list of nodes that due to viscosity have not reached their max level height
1643 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1645 // List of MapBlocks that will require a lighting update (due to lava)
1646 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1648 while (m_transforming_liquid.size() > 0)
1650 // This should be done here so that it is done when continue is used
1651 if (loopcount >= initial_size || loopcount >= 1000)
1655 Get a queued transforming liquid node
1657 v3s16 p0 = m_transforming_liquid.pop_front();
1658 u16 total_level = 0;
1659 // surrounding flowing liquid nodes
1660 NodeNeighbor neighbors[7];
1661 // current level of every block
1662 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1664 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1665 s8 can_liquid_same_level = 0;
1666 content_t liquid_kind = CONTENT_IGNORE;
1667 content_t liquid_kind_flowing = CONTENT_IGNORE;
1669 Collect information about the environment
1671 const v3s16 *dirs = g_7dirs;
1672 for (u16 i = 0; i < 7; i++) {
1673 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1676 nt = NEIGHBOR_UPPER;
1679 nt = NEIGHBOR_LOWER;
1682 v3s16 npos = p0 + dirs[i];
1684 neighbors[i].n = getNodeNoEx(npos);
1685 neighbors[i].t = nt;
1686 neighbors[i].p = npos;
1689 NodeNeighbor & nb = neighbors[i];
1691 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1693 if (nb.n.getContent() == CONTENT_AIR) {
1694 liquid_levels[i] = 0;
1699 // if this node is not (yet) of a liquid type,
1700 // choose the first liquid type we encounter
1701 if (liquid_kind_flowing == CONTENT_IGNORE)
1702 liquid_kind_flowing = nodemgr->getId(
1703 nodemgr->get(nb.n).liquid_alternative_flowing);
1704 if (liquid_kind == CONTENT_IGNORE)
1705 liquid_kind = nb.n.getContent();
1706 if (nb.n.getContent() == liquid_kind) {
1707 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1709 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1712 case LIQUID_FLOWING:
1713 // if this node is not (yet) of a liquid type,
1714 // choose the first liquid type we encounter
1715 if (liquid_kind_flowing == CONTENT_IGNORE)
1716 liquid_kind_flowing = nb.n.getContent();
1717 if (liquid_kind == CONTENT_IGNORE)
1718 liquid_kind = nodemgr->getId(
1719 nodemgr->get(nb.n).liquid_alternative_source);
1720 if (nb.n.getContent() == liquid_kind_flowing) {
1721 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1727 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1728 ++can_liquid_same_level;
1729 if (liquid_levels[i] > 0)
1730 total_level += liquid_levels[i];
1733 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1734 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1735 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1736 << nodemgr->get(nb.n.getContent()).liquid_type
1737 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1738 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1739 << " tlevel=" << (int)total_level << " cansame="
1740 << (int)can_liquid_same_level << std::endl;
1744 if (liquid_kind == CONTENT_IGNORE ||
1745 !neighbors[D_SELF].l ||
1749 // fill bottom block
1750 if (neighbors[D_BOTTOM].l) {
1751 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1752 LIQUID_LEVEL_SOURCE : total_level;
1753 total_level -= liquid_levels_want[D_BOTTOM];
1757 if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 &&
1758 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1759 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1760 (can_liquid_same_level - relax) &&
1761 can_liquid_same_level >= relax + 1) {
1762 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1765 // calculate self level 5 blocks
1767 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1768 ? LIQUID_LEVEL_SOURCE
1769 : total_level / can_liquid_same_level;
1770 total_level -= want_level * can_liquid_same_level;
1773 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1774 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1775 total_level <= (can_liquid_same_level - relax) &&
1776 can_liquid_same_level >= relax + 1) {
1780 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1781 if (!neighbors[ii].l)
1783 liquid_levels_want[ii] = want_level;
1784 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0
1785 && liquid_levels[ii] > liquid_levels_want[ii]
1787 ++liquid_levels_want[ii];
1792 for (u16 ii = 0; ii < 7; ++ii) {
1793 if (total_level < 1) break;
1794 if (liquid_levels_want[ii] >= 0 &&
1795 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1796 ++liquid_levels_want[ii];
1801 // fill top block if can
1802 if (neighbors[D_TOP].l) {
1803 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1804 LIQUID_LEVEL_SOURCE : total_level;
1805 total_level -= liquid_levels_want[D_TOP];
1808 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1809 if (liquid_levels_want[ii] >= 0 &&
1811 (fast_flood && p0.Y < water_level &&
1812 (initial_size >= 1000
1814 && want_level >= LIQUID_LEVEL_SOURCE/4
1815 && can_liquid_same_level >= 5
1816 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1817 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1820 if (total_level > 0) //|| flowed != volume)
1821 infostream <<" AFTER level=" << (int)total_level
1822 //<< " flowed="<<flowed<< " volume=" << volume
1823 << " wantsame="<<(int)want_level<< " top="
1824 << (int)liquid_levels_want[D_TOP]<< " topwas="
1825 << (int)liquid_levels[D_TOP]<< " bot="
1826 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1830 for (u16 i = 0; i < 7; i++) {
1831 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1833 MapNode & n0 = neighbors[i].n;
1834 p0 = neighbors[i].p;
1836 decide on the type (and possibly level) of the current node
1838 content_t new_node_content;
1839 s8 new_node_level = -1;
1840 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1841 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1842 // amount to gain, limited by viscosity
1843 // must be at least 1 in absolute value
1844 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1845 if (level_inc < -viscosity || level_inc > viscosity)
1846 new_node_level = liquid_levels[i] + level_inc/viscosity;
1847 else if (level_inc < 0)
1848 new_node_level = liquid_levels[i] - 1;
1849 else if (level_inc > 0)
1850 new_node_level = liquid_levels[i] + 1;
1852 new_node_level = liquid_levels_want[i];
1855 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1856 new_node_content = liquid_kind;
1857 else if (new_node_level > 0)
1858 new_node_content = liquid_kind_flowing;
1860 new_node_content = CONTENT_AIR;
1862 // last level must flow down on stairs
1863 if (liquid_levels_want[i] != liquid_levels[i] &&
1864 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1865 new_node_level >= 1 && new_node_level <= 2) {
1866 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1867 if (neighbors[ii].l)
1868 must_reflow_second.push_back(p0 + dirs[ii]);
1873 check if anything has changed.
1874 if not, just continue with the next node.
1877 new_node_content == n0.getContent()
1878 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1879 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1880 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1881 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1884 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1885 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1886 LIQUID_INFINITY_MASK) == neighbors[i].i
1894 update the current node
1896 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1897 // set level to last 3 bits, flowing down bit to 4th bit
1898 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1899 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1900 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1901 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1904 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1905 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1906 <<(int)new_node_level<<std::endl;
1909 n0.setContent(new_node_content);
1910 // Find out whether there is a suspect for this action
1911 std::string suspect;
1912 if(m_gamedef->rollback()){
1913 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1916 if(!suspect.empty()){
1918 RollbackScopeActor rollback_scope(m_gamedef->rollback(),
1920 // Get old node for rollback
1921 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1925 RollbackNode rollback_newnode(this, p0, m_gamedef);
1926 RollbackAction action;
1927 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1928 m_gamedef->rollback()->reportAction(action);
1934 v3s16 blockpos = getNodeBlockPos(p0);
1935 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1937 modified_blocks[blockpos] = block;
1938 // If node emits light, MapBlock requires lighting update
1939 if(nodemgr->get(n0).light_source != 0)
1940 lighting_modified_blocks[block->getPos()] = block;
1942 must_reflow.push_back(neighbors[i].p);
1944 /* //for better relax only same level
1945 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1946 if (!neighbors[ii].l) continue;
1947 must_reflow.push_back(p0 + dirs[ii]);
1952 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1953 <<" reflow="<<must_reflow.size()
1954 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1956 while (must_reflow.size() > 0)
1957 m_transforming_liquid.push_back(must_reflow.pop_front());
1958 while (must_reflow_second.size() > 0)
1959 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1960 updateLighting(lighting_modified_blocks, modified_blocks);
1963 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1966 if (g_settings->getBool("liquid_finite"))
1967 return Map::transformLiquidsFinite(modified_blocks);
1969 INodeDefManager *nodemgr = m_gamedef->ndef();
1971 DSTACK(__FUNCTION_NAME);
1972 //TimeTaker timer("transformLiquids()");
1975 u32 initial_size = m_transforming_liquid.size();
1977 /*if(initial_size != 0)
1978 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1980 // list of nodes that due to viscosity have not reached their max level height
1981 UniqueQueue<v3s16> must_reflow;
1983 // List of MapBlocks that will require a lighting update (due to lava)
1984 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1986 while(m_transforming_liquid.size() != 0)
1988 // This should be done here so that it is done when continue is used
1989 if(loopcount >= initial_size || loopcount >= 10000)
1994 Get a queued transforming liquid node
1996 v3s16 p0 = m_transforming_liquid.pop_front();
1998 MapNode n0 = getNodeNoEx(p0);
2001 Collect information about current node
2003 s8 liquid_level = -1;
2004 content_t liquid_kind = CONTENT_IGNORE;
2005 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2006 switch (liquid_type) {
2008 liquid_level = LIQUID_LEVEL_SOURCE;
2009 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2011 case LIQUID_FLOWING:
2012 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2013 liquid_kind = n0.getContent();
2016 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2017 // continue with the next node.
2018 if (n0.getContent() != CONTENT_AIR)
2020 liquid_kind = CONTENT_AIR;
2025 Collect information about the environment
2027 const v3s16 *dirs = g_6dirs;
2028 NodeNeighbor sources[6]; // surrounding sources
2029 int num_sources = 0;
2030 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2032 NodeNeighbor airs[6]; // surrounding air
2034 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2035 int num_neutrals = 0;
2036 bool flowing_down = false;
2037 for (u16 i = 0; i < 6; i++) {
2038 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2041 nt = NEIGHBOR_UPPER;
2044 nt = NEIGHBOR_LOWER;
2047 v3s16 npos = p0 + dirs[i];
2048 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2049 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2051 if (nb.n.getContent() == CONTENT_AIR) {
2052 airs[num_airs++] = nb;
2053 // if the current node is a water source the neighbor
2054 // should be enqueded for transformation regardless of whether the
2055 // current node changes or not.
2056 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2057 m_transforming_liquid.push_back(npos);
2058 // if the current node happens to be a flowing node, it will start to flow down here.
2059 if (nb.t == NEIGHBOR_LOWER) {
2060 flowing_down = true;
2063 neutrals[num_neutrals++] = nb;
2067 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2068 if (liquid_kind == CONTENT_AIR)
2069 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2070 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2071 neutrals[num_neutrals++] = nb;
2073 // Do not count bottom source, it will screw things up
2075 sources[num_sources++] = nb;
2078 case LIQUID_FLOWING:
2079 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2080 if (liquid_kind == CONTENT_AIR)
2081 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2082 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2083 neutrals[num_neutrals++] = nb;
2085 flows[num_flows++] = nb;
2086 if (nb.t == NEIGHBOR_LOWER)
2087 flowing_down = true;
2094 decide on the type (and possibly level) of the current node
2096 content_t new_node_content;
2097 s8 new_node_level = -1;
2098 s8 max_node_level = -1;
2099 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2100 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2101 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2102 // it's perfectly safe to use liquid_kind here to determine the new node content.
2103 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2104 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2105 // liquid_kind is set properly, see above
2106 new_node_content = liquid_kind;
2107 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2109 // no surrounding sources, so get the maximum level that can flow into this node
2110 for (u16 i = 0; i < num_flows; i++) {
2111 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2112 switch (flows[i].t) {
2113 case NEIGHBOR_UPPER:
2114 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2115 max_node_level = LIQUID_LEVEL_MAX;
2116 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2117 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2118 } else if (nb_liquid_level > max_node_level)
2119 max_node_level = nb_liquid_level;
2121 case NEIGHBOR_LOWER:
2123 case NEIGHBOR_SAME_LEVEL:
2124 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2125 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2126 max_node_level = nb_liquid_level - 1;
2132 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2133 if (viscosity > 1 && max_node_level != liquid_level) {
2134 // amount to gain, limited by viscosity
2135 // must be at least 1 in absolute value
2136 s8 level_inc = max_node_level - liquid_level;
2137 if (level_inc < -viscosity || level_inc > viscosity)
2138 new_node_level = liquid_level + level_inc/viscosity;
2139 else if (level_inc < 0)
2140 new_node_level = liquid_level - 1;
2141 else if (level_inc > 0)
2142 new_node_level = liquid_level + 1;
2143 if (new_node_level != max_node_level)
2144 must_reflow.push_back(p0);
2146 new_node_level = max_node_level;
2148 if (new_node_level >= 0)
2149 new_node_content = liquid_kind;
2151 new_node_content = CONTENT_AIR;
2156 check if anything has changed. if not, just continue with the next node.
2158 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2159 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2160 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2166 update the current node
2168 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2169 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2170 // set level to last 3 bits, flowing down bit to 4th bit
2171 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2173 // set the liquid level and flow bit to 0
2174 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2176 n0.setContent(new_node_content);
2178 // Find out whether there is a suspect for this action
2179 std::string suspect;
2180 if(m_gamedef->rollback()){
2181 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2184 if(!suspect.empty()){
2186 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2187 // Get old node for rollback
2188 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2192 RollbackNode rollback_newnode(this, p0, m_gamedef);
2193 RollbackAction action;
2194 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2195 m_gamedef->rollback()->reportAction(action);
2201 v3s16 blockpos = getNodeBlockPos(p0);
2202 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2204 modified_blocks[blockpos] = block;
2205 // If node emits light, MapBlock requires lighting update
2206 if(nodemgr->get(n0).light_source != 0)
2207 lighting_modified_blocks[block->getPos()] = block;
2211 enqueue neighbors for update if neccessary
2213 switch (nodemgr->get(n0.getContent()).liquid_type) {
2215 case LIQUID_FLOWING:
2216 // make sure source flows into all neighboring nodes
2217 for (u16 i = 0; i < num_flows; i++)
2218 if (flows[i].t != NEIGHBOR_UPPER)
2219 m_transforming_liquid.push_back(flows[i].p);
2220 for (u16 i = 0; i < num_airs; i++)
2221 if (airs[i].t != NEIGHBOR_UPPER)
2222 m_transforming_liquid.push_back(airs[i].p);
2225 // this flow has turned to air; neighboring flows might need to do the same
2226 for (u16 i = 0; i < num_flows; i++)
2227 m_transforming_liquid.push_back(flows[i].p);
2231 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2232 while (must_reflow.size() > 0)
2233 m_transforming_liquid.push_back(must_reflow.pop_front());
2234 updateLighting(lighting_modified_blocks, modified_blocks);
2237 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2239 v3s16 blockpos = getNodeBlockPos(p);
2240 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2241 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2243 infostream<<"Map::getNodeMetadata(): Need to emerge "
2244 <<PP(blockpos)<<std::endl;
2245 block = emergeBlock(blockpos, false);
2249 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2253 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2257 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2259 v3s16 blockpos = getNodeBlockPos(p);
2260 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2261 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2263 infostream<<"Map::setNodeMetadata(): Need to emerge "
2264 <<PP(blockpos)<<std::endl;
2265 block = emergeBlock(blockpos, false);
2269 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2273 block->m_node_metadata.set(p_rel, meta);
2276 void Map::removeNodeMetadata(v3s16 p)
2278 v3s16 blockpos = getNodeBlockPos(p);
2279 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2280 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2283 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2287 block->m_node_metadata.remove(p_rel);
2290 NodeTimer Map::getNodeTimer(v3s16 p)
2292 v3s16 blockpos = getNodeBlockPos(p);
2293 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2294 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2296 infostream<<"Map::getNodeTimer(): Need to emerge "
2297 <<PP(blockpos)<<std::endl;
2298 block = emergeBlock(blockpos, false);
2302 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2306 NodeTimer t = block->m_node_timers.get(p_rel);
2310 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2312 v3s16 blockpos = getNodeBlockPos(p);
2313 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2314 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2316 infostream<<"Map::setNodeTimer(): Need to emerge "
2317 <<PP(blockpos)<<std::endl;
2318 block = emergeBlock(blockpos, false);
2322 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2326 block->m_node_timers.set(p_rel, t);
2329 void Map::removeNodeTimer(v3s16 p)
2331 v3s16 blockpos = getNodeBlockPos(p);
2332 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2333 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2336 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2340 block->m_node_timers.remove(p_rel);
2346 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2347 Map(dout_server, gamedef),
2349 m_map_metadata_changed(true),
2351 m_database_read(NULL),
2352 m_database_write(NULL)
2354 verbosestream<<__FUNCTION_NAME<<std::endl;
2357 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2359 m_mgparams = new MapgenV6Params();
2361 m_seed = m_mgparams->seed;
2363 if (g_settings->get("fixed_map_seed").empty())
2365 m_seed = (((u64)(myrand() & 0xffff) << 0)
2366 | ((u64)(myrand() & 0xffff) << 16)
2367 | ((u64)(myrand() & 0xffff) << 32)
2368 | ((u64)(myrand() & 0xffff) << 48));
2369 m_mgparams->seed = m_seed;
2373 Experimental and debug stuff
2380 Try to load map; if not found, create a new one.
2383 m_savedir = savedir;
2384 m_map_saving_enabled = false;
2388 // If directory exists, check contents and load if possible
2389 if(fs::PathExists(m_savedir))
2391 // If directory is empty, it is safe to save into it.
2392 if(fs::GetDirListing(m_savedir).size() == 0)
2394 infostream<<"ServerMap: Empty save directory is valid."
2396 m_map_saving_enabled = true;
2401 // Load map metadata (seed, chunksize)
2404 catch(SettingNotFoundException &e){
2405 infostream<<"ServerMap: Some metadata not found."
2406 <<" Using default settings."<<std::endl;
2408 catch(FileNotGoodException &e){
2409 infostream<<"WARNING: Could not load map metadata"
2410 //<<" Disabling chunk-based generator."
2415 infostream<<"ServerMap: Successfully loaded map "
2416 <<"metadata from "<<savedir
2417 <<", assuming valid save directory."
2418 <<" seed="<<m_seed<<"."
2421 m_map_saving_enabled = true;
2422 // Map loaded, not creating new one
2426 // If directory doesn't exist, it is safe to save to it
2428 m_map_saving_enabled = true;
2431 catch(std::exception &e)
2433 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2434 <<", exception: "<<e.what()<<std::endl;
2435 infostream<<"Please remove the map or fix it."<<std::endl;
2436 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2439 infostream<<"Initializing new map."<<std::endl;
2441 // Create zero sector
2442 emergeSector(v2s16(0,0));
2444 // Initially write whole map
2445 save(MOD_STATE_CLEAN);
2448 ServerMap::~ServerMap()
2450 verbosestream<<__FUNCTION_NAME<<std::endl;
2454 if(m_map_saving_enabled)
2456 // Save only changed parts
2457 save(MOD_STATE_WRITE_AT_UNLOAD);
2458 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2462 infostream<<"ServerMap: Map not saved"<<std::endl;
2465 catch(std::exception &e)
2467 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2468 <<", exception: "<<e.what()<<std::endl;
2472 Close database if it was opened
2475 sqlite3_finalize(m_database_read);
2476 if(m_database_write)
2477 sqlite3_finalize(m_database_write);
2479 sqlite3_close(m_database);
2485 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2486 for(; i.atEnd() == false; i++)
2488 MapChunk *chunk = i.getNode()->getValue();
2494 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2496 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2497 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2499 //s16 chunksize = 3;
2500 //v3s16 chunk_offset(-1,-1,-1);
2501 //s16 chunksize = 4;
2502 //v3s16 chunk_offset(-1,-1,-1);
2504 v3s16 chunk_offset(-2,-2,-2);
2505 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2506 v3s16 blockpos_min = blockpos_div * chunksize;
2507 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2508 blockpos_min += chunk_offset;
2509 blockpos_max += chunk_offset;
2511 //v3s16 extra_borders(1,1,1);
2512 v3s16 extra_borders(1,1,1);
2514 // Do nothing if not inside limits (+-1 because of neighbors)
2515 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2516 blockpos_over_limit(blockpos_max + extra_borders))
2519 data->seed = m_seed;
2520 data->blockpos_min = blockpos_min;
2521 data->blockpos_max = blockpos_max;
2522 data->blockpos_requested = blockpos;
2523 data->nodedef = m_gamedef->ndef();
2526 Create the whole area of this and the neighboring blocks
2529 //TimeTaker timer("initBlockMake() create area");
2531 for(s16 x=blockpos_min.X-extra_borders.X;
2532 x<=blockpos_max.X+extra_borders.X; x++)
2533 for(s16 z=blockpos_min.Z-extra_borders.Z;
2534 z<=blockpos_max.Z+extra_borders.Z; z++)
2536 v2s16 sectorpos(x, z);
2537 // Sector metadata is loaded from disk if not already loaded.
2538 ServerMapSector *sector = createSector(sectorpos);
2541 for(s16 y=blockpos_min.Y-extra_borders.Y;
2542 y<=blockpos_max.Y+extra_borders.Y; y++)
2545 //MapBlock *block = createBlock(p);
2546 // 1) get from memory, 2) load from disk
2547 MapBlock *block = emergeBlock(p, false);
2548 // 3) create a blank one
2551 block = createBlock(p);
2554 Block gets sunlight if this is true.
2556 Refer to the map generator heuristics.
2558 bool ug = m_emerge->isBlockUnderground(p);
2559 block->setIsUnderground(ug);
2562 // Lighting will not be valid after make_chunk is called
2563 block->setLightingExpired(true);
2564 // Lighting will be calculated
2565 //block->setLightingExpired(false);
2571 Now we have a big empty area.
2573 Make a ManualMapVoxelManipulator that contains this and the
2577 // The area that contains this block and it's neighbors
2578 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2579 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2581 data->vmanip = new ManualMapVoxelManipulator(this);
2582 //data->vmanip->setMap(this);
2586 //TimeTaker timer("initBlockMake() initialEmerge");
2587 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2590 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2591 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2592 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2593 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2594 core::map<v3s16, u8>::Node *n;
2595 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2598 u8 flags = n->getValue();
2599 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2605 // Data is ready now.
2609 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2610 std::map<v3s16, MapBlock*> &changed_blocks)
2612 v3s16 blockpos_min = data->blockpos_min;
2613 v3s16 blockpos_max = data->blockpos_max;
2614 v3s16 blockpos_requested = data->blockpos_requested;
2615 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2616 <<blockpos_requested.Y<<","
2617 <<blockpos_requested.Z<<")"<<std::endl;*/
2619 v3s16 extra_borders(1,1,1);
2621 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2623 /*infostream<<"Resulting vmanip:"<<std::endl;
2624 data->vmanip.print(infostream);*/
2626 // Make sure affected blocks are loaded
2627 for(s16 x=blockpos_min.X-extra_borders.X;
2628 x<=blockpos_max.X+extra_borders.X; x++)
2629 for(s16 z=blockpos_min.Z-extra_borders.Z;
2630 z<=blockpos_max.Z+extra_borders.Z; z++)
2631 for(s16 y=blockpos_min.Y-extra_borders.Y;
2632 y<=blockpos_max.Y+extra_borders.Y; y++)
2635 // Load from disk if not already in memory
2636 emergeBlock(p, false);
2640 Blit generated stuff to map
2641 NOTE: blitBackAll adds nearly everything to changed_blocks
2645 //TimeTaker timer("finishBlockMake() blitBackAll");
2646 data->vmanip->blitBackAll(&changed_blocks);
2649 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2652 Copy transforming liquid information
2654 while(data->transforming_liquid.size() > 0)
2656 v3s16 p = data->transforming_liquid.pop_front();
2657 m_transforming_liquid.push_back(p);
2661 Do stuff in central blocks
2669 TimeTaker t("finishBlockMake lighting update");
2671 core::map<v3s16, MapBlock*> lighting_update_blocks;
2674 for(s16 x=blockpos_min.X-extra_borders.X;
2675 x<=blockpos_max.X+extra_borders.X; x++)
2676 for(s16 z=blockpos_min.Z-extra_borders.Z;
2677 z<=blockpos_max.Z+extra_borders.Z; z++)
2678 for(s16 y=blockpos_min.Y-extra_borders.Y;
2679 y<=blockpos_max.Y+extra_borders.Y; y++)
2682 MapBlock *block = getBlockNoCreateNoEx(p);
2684 lighting_update_blocks.insert(block->getPos(), block);
2687 updateLighting(lighting_update_blocks, changed_blocks);
2691 Set lighting to non-expired state in all of them.
2692 This is cheating, but it is not fast enough if all of them
2693 would actually be updated.
2695 for(s16 x=blockpos_min.X-extra_borders.X;
2696 x<=blockpos_max.X+extra_borders.X; x++)
2697 for(s16 z=blockpos_min.Z-extra_borders.Z;
2698 z<=blockpos_max.Z+extra_borders.Z; z++)
2699 for(s16 y=blockpos_min.Y-extra_borders.Y;
2700 y<=blockpos_max.Y+extra_borders.Y; y++)
2703 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2707 if(enable_mapgen_debug_info == false)
2708 t.stop(true); // Hide output
2713 Go through changed blocks
2715 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2716 i != changed_blocks.end(); ++i)
2718 MapBlock *block = i->second;
2721 Update day/night difference cache of the MapBlocks
2723 block->expireDayNightDiff();
2725 Set block as modified
2727 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2728 "finishBlockMake expireDayNightDiff");
2732 Set central blocks as generated
2734 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2735 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2736 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2739 MapBlock *block = getBlockNoCreateNoEx(p);
2741 block->setGenerated(true);
2745 Save changed parts of map
2746 NOTE: Will be saved later.
2748 //save(MOD_STATE_WRITE_AT_UNLOAD);
2750 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2751 <<","<<blockpos_requested.Y<<","
2752 <<blockpos_requested.Z<<")"<<std::endl;*/
2754 if(enable_mapgen_debug_info)
2757 Analyze resulting blocks
2759 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2760 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2761 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2762 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2763 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2764 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2766 v3s16 p = v3s16(x,y,z);
2767 MapBlock *block = getBlockNoCreateNoEx(p);
2769 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2770 infostream<<"Generated "<<spos<<": "
2771 <<analyze_block(block)<<std::endl;
2776 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2782 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2784 DSTACKF("%s: p2d=(%d,%d)",
2789 Check if it exists already in memory
2791 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2796 Try to load it from disk (with blocks)
2798 //if(loadSectorFull(p2d) == true)
2801 Try to load metadata from disk
2804 if(loadSectorMeta(p2d) == true)
2806 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2809 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2810 throw InvalidPositionException("");
2816 Do not create over-limit
2818 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2819 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2820 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2821 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2822 throw InvalidPositionException("createSector(): pos. over limit");
2825 Generate blank sector
2828 sector = new ServerMapSector(this, p2d, m_gamedef);
2830 // Sector position on map in nodes
2831 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2836 m_sectors[p2d] = sector;
2843 This is a quick-hand function for calling makeBlock().
2845 MapBlock * ServerMap::generateBlock(
2847 std::map<v3s16, MapBlock*> &modified_blocks
2850 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2852 /*infostream<<"generateBlock(): "
2853 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2856 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2858 TimeTaker timer("generateBlock");
2860 //MapBlock *block = original_dummy;
2862 v2s16 p2d(p.X, p.Z);
2863 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2866 Do not generate over-limit
2868 if(blockpos_over_limit(p))
2870 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2871 throw InvalidPositionException("generateBlock(): pos. over limit");
2875 Create block make data
2878 initBlockMake(&data, p);
2884 TimeTaker t("mapgen::make_block()");
2885 mapgen->makeChunk(&data);
2886 //mapgen::make_block(&data);
2888 if(enable_mapgen_debug_info == false)
2889 t.stop(true); // Hide output
2893 Blit data back on map, update lighting, add mobs and whatever this does
2895 finishBlockMake(&data, modified_blocks);
2900 MapBlock *block = getBlockNoCreateNoEx(p);
2908 bool erroneus_content = false;
2909 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2910 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2911 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2914 MapNode n = block->getNode(p);
2915 if(n.getContent() == CONTENT_IGNORE)
2917 infostream<<"CONTENT_IGNORE at "
2918 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2920 erroneus_content = true;
2924 if(erroneus_content)
2933 Generate a completely empty block
2937 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2938 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2940 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2943 n.setContent(CONTENT_AIR);
2944 block->setNode(v3s16(x0,y0,z0), n);
2950 if(enable_mapgen_debug_info == false)
2951 timer.stop(true); // Hide output
2957 MapBlock * ServerMap::createBlock(v3s16 p)
2959 DSTACKF("%s: p=(%d,%d,%d)",
2960 __FUNCTION_NAME, p.X, p.Y, p.Z);
2963 Do not create over-limit
2965 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2966 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2967 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2968 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2969 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2970 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2971 throw InvalidPositionException("createBlock(): pos. over limit");
2973 v2s16 p2d(p.X, p.Z);
2976 This will create or load a sector if not found in memory.
2977 If block exists on disk, it will be loaded.
2979 NOTE: On old save formats, this will be slow, as it generates
2980 lighting on blocks for them.
2982 ServerMapSector *sector;
2984 sector = (ServerMapSector*)createSector(p2d);
2985 assert(sector->getId() == MAPSECTOR_SERVER);
2987 catch(InvalidPositionException &e)
2989 infostream<<"createBlock: createSector() failed"<<std::endl;
2993 NOTE: This should not be done, or at least the exception
2994 should not be passed on as std::exception, because it
2995 won't be catched at all.
2997 /*catch(std::exception &e)
2999 infostream<<"createBlock: createSector() failed: "
3000 <<e.what()<<std::endl;
3005 Try to get a block from the sector
3008 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3011 if(block->isDummy())
3016 block = sector->createBlankBlock(block_y);
3021 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3023 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3025 p.X, p.Y, p.Z, create_blank);
3028 MapBlock *block = getBlockNoCreateNoEx(p);
3029 if(block && block->isDummy() == false)
3034 MapBlock *block = loadBlock(p);
3040 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3041 MapBlock *block = sector->createBlankBlock(p.Y);
3045 /*if(allow_generate)
3047 std::map<v3s16, MapBlock*> modified_blocks;
3048 MapBlock *block = generateBlock(p, modified_blocks);
3052 event.type = MEET_OTHER;
3055 // Copy modified_blocks to event
3056 for(std::map<v3s16, MapBlock*>::iterator
3057 i = modified_blocks.begin();
3058 i != modified_blocks.end(); ++i)
3060 event.modified_blocks.erase(i->first);
3064 dispatchEvent(&event);
3073 s16 ServerMap::findGroundLevel(v2s16 p2d)
3077 Uh, just do something random...
3079 // Find existing map from top to down
3082 v3s16 p(p2d.X, max, p2d.Y);
3083 for(; p.Y>min; p.Y--)
3085 MapNode n = getNodeNoEx(p);
3086 if(n.getContent() != CONTENT_IGNORE)
3091 // If this node is not air, go to plan b
3092 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3094 // Search existing walkable and return it
3095 for(; p.Y>min; p.Y--)
3097 MapNode n = getNodeNoEx(p);
3098 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3107 Determine from map generator noise functions
3110 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3113 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3114 //return (s16)level;
3117 void ServerMap::createDatabase() {
3120 e = sqlite3_exec(m_database,
3121 "CREATE TABLE IF NOT EXISTS `blocks` ("
3122 "`pos` INT NOT NULL PRIMARY KEY,"
3125 , NULL, NULL, NULL);
3126 if(e == SQLITE_ABORT)
3127 throw FileNotGoodException("Could not create database structure");
3129 infostream<<"ServerMap: Database structure was created";
3132 void ServerMap::verifyDatabase() {
3137 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3138 bool needs_create = false;
3142 Open the database connection
3145 createDirs(m_savedir);
3147 if(!fs::PathExists(dbp))
3148 needs_create = true;
3150 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3151 if(d != SQLITE_OK) {
3152 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3153 throw FileNotGoodException("Cannot open database file");
3159 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3160 if(d != SQLITE_OK) {
3161 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3162 throw FileNotGoodException("Cannot prepare read statement");
3165 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3166 if(d != SQLITE_OK) {
3167 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3168 throw FileNotGoodException("Cannot prepare write statement");
3171 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3172 if(d != SQLITE_OK) {
3173 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3174 throw FileNotGoodException("Cannot prepare read statement");
3177 infostream<<"ServerMap: Database opened"<<std::endl;
3181 bool ServerMap::loadFromFolders() {
3182 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3187 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3188 return (sqlite3_int64)pos.Z*16777216 +
3189 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3192 void ServerMap::createDirs(std::string path)
3194 if(fs::CreateAllDirs(path) == false)
3196 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3197 <<"\""<<path<<"\""<<std::endl;
3198 throw BaseException("ServerMap failed to create directory");
3202 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3208 snprintf(cc, 9, "%.4x%.4x",
3209 (unsigned int)pos.X&0xffff,
3210 (unsigned int)pos.Y&0xffff);
3212 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3214 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3215 (unsigned int)pos.X&0xfff,
3216 (unsigned int)pos.Y&0xfff);
3218 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3224 v2s16 ServerMap::getSectorPos(std::string dirname)
3228 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
3229 assert(spos != std::string::npos);
3230 if(dirname.size() - spos == 8)
3233 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
3235 else if(dirname.size() - spos == 3)
3238 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3239 // Sign-extend the 12 bit values up to 16 bits...
3240 if(x&0x800) x|=0xF000;
3241 if(y&0x800) y|=0xF000;
3248 v2s16 pos((s16)x, (s16)y);
3252 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3254 v2s16 p2d = getSectorPos(sectordir);
3256 if(blockfile.size() != 4){
3257 throw InvalidFilenameException("Invalid block filename");
3260 int r = sscanf(blockfile.c_str(), "%4x", &y);
3262 throw InvalidFilenameException("Invalid block filename");
3263 return v3s16(p2d.X, y, p2d.Y);
3266 std::string ServerMap::getBlockFilename(v3s16 p)
3269 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3273 void ServerMap::save(ModifiedState save_level)
3275 DSTACK(__FUNCTION_NAME);
3276 if(m_map_saving_enabled == false)
3278 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3282 if(save_level == MOD_STATE_CLEAN)
3283 infostream<<"ServerMap: Saving whole map, this can take time."
3286 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3291 // Profile modified reasons
3292 Profiler modprofiler;
3294 u32 sector_meta_count = 0;
3295 u32 block_count = 0;
3296 u32 block_count_all = 0; // Number of blocks in memory
3298 // Don't do anything with sqlite unless something is really saved
3299 bool save_started = false;
3301 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3302 i != m_sectors.end(); ++i)
3304 ServerMapSector *sector = (ServerMapSector*)i->second;
3305 assert(sector->getId() == MAPSECTOR_SERVER);
3307 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3309 saveSectorMeta(sector);
3310 sector_meta_count++;
3312 std::list<MapBlock*> blocks;
3313 sector->getBlocks(blocks);
3315 for(std::list<MapBlock*>::iterator j = blocks.begin();
3316 j != blocks.end(); ++j)
3318 MapBlock *block = *j;
3322 if(block->getModified() >= (u32)save_level)
3327 save_started = true;
3330 modprofiler.add(block->getModifiedReason(), 1);
3335 /*infostream<<"ServerMap: Written block ("
3336 <<block->getPos().X<<","
3337 <<block->getPos().Y<<","
3338 <<block->getPos().Z<<")"
3347 Only print if something happened or saved whole map
3349 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3350 || block_count != 0)
3352 infostream<<"ServerMap: Written: "
3353 <<sector_meta_count<<" sector metadata files, "
3354 <<block_count<<" block files"
3355 <<", "<<block_count_all<<" blocks in memory."
3357 PrintInfo(infostream); // ServerMap/ClientMap:
3358 infostream<<"Blocks modified by: "<<std::endl;
3359 modprofiler.print(infostream);
3363 static s32 unsignedToSigned(s32 i, s32 max_positive)
3365 if(i < max_positive)
3368 return i - 2*max_positive;
3371 // modulo of a negative number does not work consistently in C
3372 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3376 return mod - ((-i) % mod);
3379 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3381 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3383 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3385 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3386 return v3s16(x,y,z);
3389 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3391 if(loadFromFolders()){
3392 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3393 <<"all blocks that are stored in flat files"<<std::endl;
3399 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3401 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3402 v3s16 p = getIntegerAsBlock(block_i);
3403 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3409 void ServerMap::saveMapMeta()
3411 DSTACK(__FUNCTION_NAME);
3413 /*infostream<<"ServerMap::saveMapMeta(): "
3417 createDirs(m_savedir);
3419 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3420 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3421 if(os.good() == false)
3423 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3424 <<"could not open"<<fullpath<<std::endl;
3425 throw FileNotGoodException("Cannot open chunk metadata");
3430 m_emerge->setParamsToSettings(¶ms);
3431 params.writeLines(os);
3433 os<<"[end_of_params]\n";
3435 m_map_metadata_changed = false;
3438 void ServerMap::loadMapMeta()
3440 DSTACK(__FUNCTION_NAME);
3442 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3445 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3446 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3447 if(is.good() == false)
3449 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3450 <<"could not open"<<fullpath<<std::endl;
3451 throw FileNotGoodException("Cannot open map metadata");
3459 throw SerializationError
3460 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3462 std::getline(is, line);
3463 std::string trimmedline = trim(line);
3464 if(trimmedline == "[end_of_params]")
3466 params.parseConfigLine(line);
3469 MapgenParams *mgparams = m_emerge->getParamsFromSettings(¶ms);
3473 m_mgparams = mgparams;
3474 m_seed = mgparams->seed;
3476 if (params.exists("seed")) {
3477 m_seed = params.getU64("seed");
3478 m_mgparams->seed = m_seed;
3482 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3485 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3487 DSTACK(__FUNCTION_NAME);
3488 // Format used for writing
3489 u8 version = SER_FMT_VER_HIGHEST;
3491 v2s16 pos = sector->getPos();
3492 std::string dir = getSectorDir(pos);
3495 std::string fullpath = dir + DIR_DELIM + "meta";
3496 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3497 if(o.good() == false)
3498 throw FileNotGoodException("Cannot open sector metafile");
3500 sector->serialize(o, version);
3502 sector->differs_from_disk = false;
3505 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3507 DSTACK(__FUNCTION_NAME);
3509 v2s16 p2d = getSectorPos(sectordir);
3511 ServerMapSector *sector = NULL;
3513 std::string fullpath = sectordir + DIR_DELIM + "meta";
3514 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3515 if(is.good() == false)
3517 // If the directory exists anyway, it probably is in some old
3518 // format. Just go ahead and create the sector.
3519 if(fs::PathExists(sectordir))
3521 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3522 <<fullpath<<" doesn't exist but directory does."
3523 <<" Continuing with a sector with no metadata."
3525 sector = new ServerMapSector(this, p2d, m_gamedef);
3526 m_sectors[p2d] = sector;
3530 throw FileNotGoodException("Cannot open sector metafile");
3535 sector = ServerMapSector::deSerialize
3536 (is, this, p2d, m_sectors, m_gamedef);
3538 saveSectorMeta(sector);
3541 sector->differs_from_disk = false;
3546 bool ServerMap::loadSectorMeta(v2s16 p2d)
3548 DSTACK(__FUNCTION_NAME);
3550 MapSector *sector = NULL;
3552 // The directory layout we're going to load from.
3553 // 1 - original sectors/xxxxzzzz/
3554 // 2 - new sectors2/xxx/zzz/
3555 // If we load from anything but the latest structure, we will
3556 // immediately save to the new one, and remove the old.
3558 std::string sectordir1 = getSectorDir(p2d, 1);
3559 std::string sectordir;
3560 if(fs::PathExists(sectordir1))
3562 sectordir = sectordir1;
3567 sectordir = getSectorDir(p2d, 2);
3571 sector = loadSectorMeta(sectordir, loadlayout != 2);
3573 catch(InvalidFilenameException &e)
3577 catch(FileNotGoodException &e)
3581 catch(std::exception &e)
3590 bool ServerMap::loadSectorFull(v2s16 p2d)
3592 DSTACK(__FUNCTION_NAME);
3594 MapSector *sector = NULL;
3596 // The directory layout we're going to load from.
3597 // 1 - original sectors/xxxxzzzz/
3598 // 2 - new sectors2/xxx/zzz/
3599 // If we load from anything but the latest structure, we will
3600 // immediately save to the new one, and remove the old.
3602 std::string sectordir1 = getSectorDir(p2d, 1);
3603 std::string sectordir;
3604 if(fs::PathExists(sectordir1))
3606 sectordir = sectordir1;
3611 sectordir = getSectorDir(p2d, 2);
3615 sector = loadSectorMeta(sectordir, loadlayout != 2);
3617 catch(InvalidFilenameException &e)
3621 catch(FileNotGoodException &e)
3625 catch(std::exception &e)
3633 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3635 std::vector<fs::DirListNode>::iterator i2;
3636 for(i2=list2.begin(); i2!=list2.end(); i2++)
3642 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3644 catch(InvalidFilenameException &e)
3646 // This catches unknown crap in directory
3652 infostream<<"Sector converted to new layout - deleting "<<
3653 sectordir1<<std::endl;
3654 fs::RecursiveDelete(sectordir1);
3661 void ServerMap::beginSave() {
3663 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3664 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3667 void ServerMap::endSave() {
3669 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3670 infostream<<"WARNING: endSave() failed, map might not have saved.";
3673 void ServerMap::saveBlock(MapBlock *block)
3675 DSTACK(__FUNCTION_NAME);
3677 Dummy blocks are not written
3679 if(block->isDummy())
3681 /*v3s16 p = block->getPos();
3682 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3683 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3687 // Format used for writing
3688 u8 version = SER_FMT_VER_HIGHEST;
3690 v3s16 p3d = block->getPos();
3694 v2s16 p2d(p3d.X, p3d.Z);
3695 std::string sectordir = getSectorDir(p2d);
3697 createDirs(sectordir);
3699 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3700 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3701 if(o.good() == false)
3702 throw FileNotGoodException("Cannot open block data");
3705 [0] u8 serialization version
3711 std::ostringstream o(std::ios_base::binary);
3713 o.write((char*)&version, 1);
3716 block->serialize(o, version, true);
3718 // Write block to database
3720 std::string tmp = o.str();
3721 const char *bytes = tmp.c_str();
3723 bool success = true;
3724 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3725 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3728 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3729 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3732 int written = sqlite3_step(m_database_write);
3733 if(written != SQLITE_DONE) {
3734 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3735 <<sqlite3_errmsg(m_database)<<std::endl;
3738 // Make ready for later reuse
3739 sqlite3_reset(m_database_write);
3741 // We just wrote it to the disk so clear modified flag
3743 block->resetModified();
3746 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3748 DSTACK(__FUNCTION_NAME);
3750 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3753 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3754 if(is.good() == false)
3755 throw FileNotGoodException("Cannot open block file");
3757 v3s16 p3d = getBlockPos(sectordir, blockfile);
3758 v2s16 p2d(p3d.X, p3d.Z);
3760 assert(sector->getPos() == p2d);
3762 u8 version = SER_FMT_VER_INVALID;
3763 is.read((char*)&version, 1);
3766 throw SerializationError("ServerMap::loadBlock(): Failed"
3767 " to read MapBlock version");
3769 /*u32 block_size = MapBlock::serializedLength(version);
3770 SharedBuffer<u8> data(block_size);
3771 is.read((char*)*data, block_size);*/
3773 // This will always return a sector because we're the server
3774 //MapSector *sector = emergeSector(p2d);
3776 MapBlock *block = NULL;
3777 bool created_new = false;
3778 block = sector->getBlockNoCreateNoEx(p3d.Y);
3781 block = sector->createBlankBlockNoInsert(p3d.Y);
3786 block->deSerialize(is, version, true);
3788 // If it's a new block, insert it to the map
3790 sector->insertBlock(block);
3793 Save blocks loaded in old format in new format
3796 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3800 // Should be in database now, so delete the old file
3801 fs::RecursiveDelete(fullpath);
3804 // We just loaded it from the disk, so it's up-to-date.
3805 block->resetModified();
3808 catch(SerializationError &e)
3810 infostream<<"WARNING: Invalid block data on disk "
3811 <<"fullpath="<<fullpath
3812 <<" (SerializationError). "
3813 <<"what()="<<e.what()
3815 //" Ignoring. A new one will be generated.
3818 // TODO: Backup file; name is in fullpath.
3822 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3824 DSTACK(__FUNCTION_NAME);
3827 std::istringstream is(*blob, std::ios_base::binary);
3829 u8 version = SER_FMT_VER_INVALID;
3830 is.read((char*)&version, 1);
3833 throw SerializationError("ServerMap::loadBlock(): Failed"
3834 " to read MapBlock version");
3836 /*u32 block_size = MapBlock::serializedLength(version);
3837 SharedBuffer<u8> data(block_size);
3838 is.read((char*)*data, block_size);*/
3840 // This will always return a sector because we're the server
3841 //MapSector *sector = emergeSector(p2d);
3843 MapBlock *block = NULL;
3844 bool created_new = false;
3845 block = sector->getBlockNoCreateNoEx(p3d.Y);
3848 block = sector->createBlankBlockNoInsert(p3d.Y);
3853 block->deSerialize(is, version, true);
3855 // If it's a new block, insert it to the map
3857 sector->insertBlock(block);
3860 Save blocks loaded in old format in new format
3863 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3864 // Only save if asked to; no need to update version
3868 // We just loaded it from, so it's up-to-date.
3869 block->resetModified();
3872 catch(SerializationError &e)
3874 errorstream<<"Invalid block data in database"
3875 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3876 <<" (SerializationError): "<<e.what()<<std::endl;
3878 // TODO: Block should be marked as invalid in memory so that it is
3879 // not touched but the game can run
3881 if(g_settings->getBool("ignore_world_load_errors")){
3882 errorstream<<"Ignoring block load error. Duck and cover! "
3883 <<"(ignore_world_load_errors)"<<std::endl;
3885 throw SerializationError("Invalid block data in database");
3891 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3893 DSTACK(__FUNCTION_NAME);
3895 v2s16 p2d(blockpos.X, blockpos.Z);
3897 if(!loadFromFolders()) {
3900 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3901 infostream<<"WARNING: Could not bind block position for load: "
3902 <<sqlite3_errmsg(m_database)<<std::endl;
3903 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3905 Make sure sector is loaded
3907 MapSector *sector = createSector(p2d);
3912 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3913 size_t len = sqlite3_column_bytes(m_database_read, 0);
3915 std::string datastr(data, len);
3917 loadBlock(&datastr, blockpos, sector, false);
3919 sqlite3_step(m_database_read);
3920 // We should never get more than 1 row, so ok to reset
3921 sqlite3_reset(m_database_read);
3923 return getBlockNoCreateNoEx(blockpos);
3925 sqlite3_reset(m_database_read);
3927 // Not found in database, try the files
3930 // The directory layout we're going to load from.
3931 // 1 - original sectors/xxxxzzzz/
3932 // 2 - new sectors2/xxx/zzz/
3933 // If we load from anything but the latest structure, we will
3934 // immediately save to the new one, and remove the old.
3936 std::string sectordir1 = getSectorDir(p2d, 1);
3937 std::string sectordir;
3938 if(fs::PathExists(sectordir1))
3940 sectordir = sectordir1;
3945 sectordir = getSectorDir(p2d, 2);
3949 Make sure sector is loaded
3951 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3955 sector = loadSectorMeta(sectordir, loadlayout != 2);
3957 catch(InvalidFilenameException &e)
3961 catch(FileNotGoodException &e)
3965 catch(std::exception &e)
3972 Make sure file exists
3975 std::string blockfilename = getBlockFilename(blockpos);
3976 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3980 Load block and save it to the database
3982 loadBlock(sectordir, blockfilename, sector, true);
3983 return getBlockNoCreateNoEx(blockpos);
3986 void ServerMap::PrintInfo(std::ostream &out)
3995 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4000 MapVoxelManipulator::~MapVoxelManipulator()
4002 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4006 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4008 TimeTaker timer1("emerge", &emerge_time);
4010 // Units of these are MapBlocks
4011 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4012 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4014 VoxelArea block_area_nodes
4015 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4017 addArea(block_area_nodes);
4019 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4020 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4021 for(s32 x=p_min.X; x<=p_max.X; x++)
4026 std::map<v3s16, u8>::iterator n;
4027 n = m_loaded_blocks.find(p);
4028 if(n != m_loaded_blocks.end())
4031 bool block_data_inexistent = false;
4034 TimeTaker timer1("emerge load", &emerge_load_time);
4036 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4037 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4039 a.print(infostream);
4040 infostream<<std::endl;*/
4042 block = m_map->getBlockNoCreate(p);
4043 if(block->isDummy())
4044 block_data_inexistent = true;
4046 block->copyTo(*this);
4048 catch(InvalidPositionException &e)
4050 block_data_inexistent = true;
4053 if(block_data_inexistent)
4055 flags |= VMANIP_BLOCK_DATA_INEXIST;
4057 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4058 // Fill with VOXELFLAG_INEXISTENT
4059 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4060 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4062 s32 i = m_area.index(a.MinEdge.X,y,z);
4063 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4066 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4068 // Mark that block was loaded as blank
4069 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4072 m_loaded_blocks[p] = flags;
4075 //infostream<<"emerge done"<<std::endl;
4079 SUGG: Add an option to only update eg. water and air nodes.
4080 This will make it interfere less with important stuff if
4083 void MapVoxelManipulator::blitBack
4084 (std::map<v3s16, MapBlock*> & modified_blocks)
4086 if(m_area.getExtent() == v3s16(0,0,0))
4089 //TimeTaker timer1("blitBack");
4091 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4092 <<m_loaded_blocks.size()<<std::endl;*/
4095 Initialize block cache
4097 v3s16 blockpos_last;
4098 MapBlock *block = NULL;
4099 bool block_checked_in_modified = false;
4101 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4102 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4103 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4107 u8 f = m_flags[m_area.index(p)];
4108 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4111 MapNode &n = m_data[m_area.index(p)];
4113 v3s16 blockpos = getNodeBlockPos(p);
4118 if(block == NULL || blockpos != blockpos_last){
4119 block = m_map->getBlockNoCreate(blockpos);
4120 blockpos_last = blockpos;
4121 block_checked_in_modified = false;
4124 // Calculate relative position in block
4125 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4127 // Don't continue if nothing has changed here
4128 if(block->getNode(relpos) == n)
4131 //m_map->setNode(m_area.MinEdge + p, n);
4132 block->setNode(relpos, n);
4135 Make sure block is in modified_blocks
4137 if(block_checked_in_modified == false)
4139 modified_blocks[blockpos] = block;
4140 block_checked_in_modified = true;
4143 catch(InvalidPositionException &e)
4149 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4150 MapVoxelManipulator(map),
4151 m_create_area(false)
4155 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4159 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4161 // Just create the area so that it can be pointed to
4162 VoxelManipulator::emerge(a, caller_id);
4165 void ManualMapVoxelManipulator::initialEmerge(
4166 v3s16 blockpos_min, v3s16 blockpos_max)
4168 TimeTaker timer1("initialEmerge", &emerge_time);
4170 // Units of these are MapBlocks
4171 v3s16 p_min = blockpos_min;
4172 v3s16 p_max = blockpos_max;
4174 VoxelArea block_area_nodes
4175 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4177 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4180 infostream<<"initialEmerge: area: ";
4181 block_area_nodes.print(infostream);
4182 infostream<<" ("<<size_MB<<"MB)";
4183 infostream<<std::endl;
4186 addArea(block_area_nodes);
4188 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4189 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4190 for(s32 x=p_min.X; x<=p_max.X; x++)
4195 std::map<v3s16, u8>::iterator n;
4196 n = m_loaded_blocks.find(p);
4197 if(n != m_loaded_blocks.end())
4200 bool block_data_inexistent = false;
4203 TimeTaker timer1("emerge load", &emerge_load_time);
4205 block = m_map->getBlockNoCreate(p);
4206 if(block->isDummy())
4207 block_data_inexistent = true;
4209 block->copyTo(*this);
4211 catch(InvalidPositionException &e)
4213 block_data_inexistent = true;
4216 if(block_data_inexistent)
4218 flags |= VMANIP_BLOCK_DATA_INEXIST;
4221 Mark area inexistent
4223 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4224 // Fill with VOXELFLAG_INEXISTENT
4225 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4226 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4228 s32 i = m_area.index(a.MinEdge.X,y,z);
4229 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4232 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4234 // Mark that block was loaded as blank
4235 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4238 m_loaded_blocks[p] = flags;
4242 void ManualMapVoxelManipulator::blitBackAll(
4243 std::map<v3s16, MapBlock*> * modified_blocks)
4245 if(m_area.getExtent() == v3s16(0,0,0))
4249 Copy data of all blocks
4251 for(std::map<v3s16, u8>::iterator
4252 i = m_loaded_blocks.begin();
4253 i != m_loaded_blocks.end(); ++i)
4256 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4257 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4258 if(existed == false)
4263 block->copyFrom(*this);
4266 (*modified_blocks)[p] = block;