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 /*if(initial_size != 0)
1643 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1645 // list of nodes that due to viscosity have not reached their max level height
1646 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1648 // List of MapBlocks that will require a lighting update (due to lava)
1649 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1651 while(m_transforming_liquid.size() > 0)
1653 // This should be done here so that it is done when continue is used
1654 if(loopcount >= initial_size || loopcount >= 1000)
1658 Get a queued transforming liquid node
1660 v3s16 p0 = m_transforming_liquid.pop_front();
1661 u16 total_level = 0;
1662 NodeNeighbor neighbors[7]; // surrounding flowing liquid nodes
1663 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1}; // current level of every block
1664 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1}; // target levels
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, choose the first liquid type we encounter
1700 if (liquid_kind_flowing == CONTENT_IGNORE)
1701 liquid_kind_flowing = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1702 if (liquid_kind == CONTENT_IGNORE)
1703 liquid_kind = nb.n.getContent();
1704 if (nb.n.getContent() == liquid_kind) {
1705 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1707 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1710 case LIQUID_FLOWING:
1711 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1712 if (liquid_kind_flowing == CONTENT_IGNORE)
1713 liquid_kind_flowing = nb.n.getContent();
1714 if (liquid_kind == CONTENT_IGNORE)
1715 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_source);
1716 if (nb.n.getContent() == liquid_kind_flowing) {
1717 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1722 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL) ++can_liquid_same_level;
1723 if (liquid_levels[i] > 0) total_level += liquid_levels[i];
1726 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="<<nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="<< (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="<<nodemgr->get(nb.n.getContent()).liquid_type
1727 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1728 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i] << " tlevel=" << (int)total_level << " cansame="<<(int)can_liquid_same_level<<std::endl;
1732 if (liquid_kind == CONTENT_IGNORE || !neighbors[D_SELF].l || total_level <= 0)
1735 // fill bottom block
1736 if (neighbors[D_BOTTOM].l) {
1737 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level;
1738 total_level -= liquid_levels_want[D_BOTTOM];
1741 if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 && total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level - can_liquid_same_level + 2 && can_liquid_same_level >= relax + 1) { //relax up
1742 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1745 // calculate self level 5 blocks
1747 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1748 ? LIQUID_LEVEL_SOURCE
1749 : total_level / can_liquid_same_level;
1750 total_level -= want_level * can_liquid_same_level;
1752 if (relax && p0.Y > water_level && liquid_levels[D_TOP] == 0 && liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 && total_level <= can_liquid_same_level - 2 && can_liquid_same_level >= relax + 1) { //relax down
1756 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1757 if (!neighbors[ii].l)
1759 liquid_levels_want[ii] = want_level;
1760 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0
1761 && liquid_levels[ii] > liquid_levels_want[ii]
1763 ++liquid_levels_want[ii];
1768 for (u16 ii = 0; ii < 7; ++ii) {
1769 if (total_level < 1) break;
1770 if (liquid_levels_want[ii] >= 0 && liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1771 ++liquid_levels_want[ii];
1776 // fill top block if can
1777 if (neighbors[D_TOP].l) {
1778 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level ;
1779 total_level -= liquid_levels_want[D_TOP];
1782 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1783 if (liquid_levels_want[ii] >= 0 &&
1785 (fast_flood && p0.Y < water_level &&
1786 (initial_size >= 1000
1788 && want_level >= LIQUID_LEVEL_SOURCE/4
1789 && can_liquid_same_level >= 5
1790 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1791 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1793 //if (total_level > 0 /*|| flowed != volume*/) infostream <<" AFTER level=" << (int)total_level /*<< " flowed="<<flowed<< " volume=" <<volume*/<< " wantsame="<<(int)want_level<< " top="<< (int)liquid_levels_want[D_TOP]<< " topwas="<< (int)liquid_levels[D_TOP]<< " bot="<< (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1796 for (u16 i = 0; i < 7; i++) {
1797 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1799 MapNode & n0 = neighbors[i].n;
1800 p0 = neighbors[i].p;
1802 decide on the type (and possibly level) of the current node
1804 content_t new_node_content;
1805 s8 new_node_level = -1;
1806 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1807 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1808 // amount to gain, limited by viscosity
1809 // must be at least 1 in absolute value
1810 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1811 if (level_inc < -viscosity || level_inc > viscosity)
1812 new_node_level = liquid_levels[i] + level_inc/viscosity;
1813 else if (level_inc < 0)
1814 new_node_level = liquid_levels[i] - 1;
1815 else if (level_inc > 0)
1816 new_node_level = liquid_levels[i] + 1;
1818 new_node_level = liquid_levels_want[i];
1819 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1820 new_node_content = liquid_kind;
1821 else if (new_node_level > 0)
1822 new_node_content = liquid_kind_flowing;
1824 new_node_content = CONTENT_AIR;
1826 // last level must flow down on stairs
1827 if (liquid_levels_want[i] != liquid_levels[i] && liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l && new_node_level >= 1 && new_node_level <= 2) //maybe == 1 //
1828 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1829 if (!neighbors[ii].l)
1831 must_reflow_second.push_back(p0 + dirs[ii]);
1835 check if anything has changed. if not, just continue with the next node.
1838 new_node_content == n0.getContent()
1839 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1840 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1841 // &&((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)== flowing_down
1844 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1845 (((n0.param2 & LIQUID_INFINITY_MASK) == LIQUID_INFINITY_MASK) == neighbors[i].i
1853 update the current node
1855 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1856 // set level to last 3 bits, flowing down bit to 4th bit
1857 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1858 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1859 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1860 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1862 //infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="<<new_node_content<< " p2="<<(int)n0.param2<< " nl="<<(int)new_node_level<<std::endl;
1863 n0.setContent(new_node_content);
1864 // Find out whether there is a suspect for this action
1865 std::string suspect;
1866 if(m_gamedef->rollback()){
1867 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1870 if(!suspect.empty()){
1872 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1873 // Get old node for rollback
1874 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1878 RollbackNode rollback_newnode(this, p0, m_gamedef);
1879 RollbackAction action;
1880 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1881 m_gamedef->rollback()->reportAction(action);
1887 v3s16 blockpos = getNodeBlockPos(p0);
1888 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1890 modified_blocks[blockpos] = block;
1891 // If node emits light, MapBlock requires lighting update
1892 if(nodemgr->get(n0).light_source != 0)
1893 lighting_modified_blocks[block->getPos()] = block;
1895 must_reflow.push_back(neighbors[i].p);
1897 /* //for better relax
1898 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1899 if (!neighbors[ii].l) continue;
1900 must_reflow.push_back(p0 + dirs[ii]);
1903 //if (loopcount) infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<" reflow="<<must_reflow.size()<<" queue="<< m_transforming_liquid.size()<<std::endl;
1904 while (must_reflow.size() > 0)
1905 m_transforming_liquid.push_back(must_reflow.pop_front());
1906 while (must_reflow_second.size() > 0)
1907 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1908 updateLighting(lighting_modified_blocks, modified_blocks);
1911 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1914 if (g_settings->getBool("liquid_finite")) return Map::transformLiquidsFinite(modified_blocks);
1916 INodeDefManager *nodemgr = m_gamedef->ndef();
1918 DSTACK(__FUNCTION_NAME);
1919 //TimeTaker timer("transformLiquids()");
1922 u32 initial_size = m_transforming_liquid.size();
1924 /*if(initial_size != 0)
1925 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1927 // list of nodes that due to viscosity have not reached their max level height
1928 UniqueQueue<v3s16> must_reflow;
1930 // List of MapBlocks that will require a lighting update (due to lava)
1931 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1933 while(m_transforming_liquid.size() != 0)
1935 // This should be done here so that it is done when continue is used
1936 if(loopcount >= initial_size || loopcount >= 10000)
1941 Get a queued transforming liquid node
1943 v3s16 p0 = m_transforming_liquid.pop_front();
1945 MapNode n0 = getNodeNoEx(p0);
1948 Collect information about current node
1950 s8 liquid_level = -1;
1951 content_t liquid_kind = CONTENT_IGNORE;
1952 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1953 switch (liquid_type) {
1955 liquid_level = LIQUID_LEVEL_SOURCE;
1956 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1958 case LIQUID_FLOWING:
1959 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1960 liquid_kind = n0.getContent();
1963 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1964 // continue with the next node.
1965 if (n0.getContent() != CONTENT_AIR)
1967 liquid_kind = CONTENT_AIR;
1972 Collect information about the environment
1974 const v3s16 *dirs = g_6dirs;
1975 NodeNeighbor sources[6]; // surrounding sources
1976 int num_sources = 0;
1977 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1979 NodeNeighbor airs[6]; // surrounding air
1981 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1982 int num_neutrals = 0;
1983 bool flowing_down = false;
1984 for (u16 i = 0; i < 6; i++) {
1985 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1988 nt = NEIGHBOR_UPPER;
1991 nt = NEIGHBOR_LOWER;
1994 v3s16 npos = p0 + dirs[i];
1995 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1996 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1998 if (nb.n.getContent() == CONTENT_AIR) {
1999 airs[num_airs++] = nb;
2000 // if the current node is a water source the neighbor
2001 // should be enqueded for transformation regardless of whether the
2002 // current node changes or not.
2003 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2004 m_transforming_liquid.push_back(npos);
2005 // if the current node happens to be a flowing node, it will start to flow down here.
2006 if (nb.t == NEIGHBOR_LOWER) {
2007 flowing_down = true;
2010 neutrals[num_neutrals++] = nb;
2014 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2015 if (liquid_kind == CONTENT_AIR)
2016 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2017 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2018 neutrals[num_neutrals++] = nb;
2020 // Do not count bottom source, it will screw things up
2022 sources[num_sources++] = nb;
2025 case LIQUID_FLOWING:
2026 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2027 if (liquid_kind == CONTENT_AIR)
2028 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2029 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2030 neutrals[num_neutrals++] = nb;
2032 flows[num_flows++] = nb;
2033 if (nb.t == NEIGHBOR_LOWER)
2034 flowing_down = true;
2041 decide on the type (and possibly level) of the current node
2043 content_t new_node_content;
2044 s8 new_node_level = -1;
2045 s8 max_node_level = -1;
2046 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2047 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2048 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2049 // it's perfectly safe to use liquid_kind here to determine the new node content.
2050 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2051 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2052 // liquid_kind is set properly, see above
2053 new_node_content = liquid_kind;
2054 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2056 // no surrounding sources, so get the maximum level that can flow into this node
2057 for (u16 i = 0; i < num_flows; i++) {
2058 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2059 switch (flows[i].t) {
2060 case NEIGHBOR_UPPER:
2061 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2062 max_node_level = LIQUID_LEVEL_MAX;
2063 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2064 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2065 } else if (nb_liquid_level > max_node_level)
2066 max_node_level = nb_liquid_level;
2068 case NEIGHBOR_LOWER:
2070 case NEIGHBOR_SAME_LEVEL:
2071 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2072 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2073 max_node_level = nb_liquid_level - 1;
2079 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2080 if (viscosity > 1 && max_node_level != liquid_level) {
2081 // amount to gain, limited by viscosity
2082 // must be at least 1 in absolute value
2083 s8 level_inc = max_node_level - liquid_level;
2084 if (level_inc < -viscosity || level_inc > viscosity)
2085 new_node_level = liquid_level + level_inc/viscosity;
2086 else if (level_inc < 0)
2087 new_node_level = liquid_level - 1;
2088 else if (level_inc > 0)
2089 new_node_level = liquid_level + 1;
2090 if (new_node_level != max_node_level)
2091 must_reflow.push_back(p0);
2093 new_node_level = max_node_level;
2095 if (new_node_level >= 0)
2096 new_node_content = liquid_kind;
2098 new_node_content = CONTENT_AIR;
2103 check if anything has changed. if not, just continue with the next node.
2105 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2106 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2107 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2113 update the current node
2115 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2116 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2117 // set level to last 3 bits, flowing down bit to 4th bit
2118 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2120 // set the liquid level and flow bit to 0
2121 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2123 n0.setContent(new_node_content);
2125 // Find out whether there is a suspect for this action
2126 std::string suspect;
2127 if(m_gamedef->rollback()){
2128 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2131 if(!suspect.empty()){
2133 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2134 // Get old node for rollback
2135 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2139 RollbackNode rollback_newnode(this, p0, m_gamedef);
2140 RollbackAction action;
2141 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2142 m_gamedef->rollback()->reportAction(action);
2148 v3s16 blockpos = getNodeBlockPos(p0);
2149 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2151 modified_blocks[blockpos] = block;
2152 // If node emits light, MapBlock requires lighting update
2153 if(nodemgr->get(n0).light_source != 0)
2154 lighting_modified_blocks[block->getPos()] = block;
2158 enqueue neighbors for update if neccessary
2160 switch (nodemgr->get(n0.getContent()).liquid_type) {
2162 case LIQUID_FLOWING:
2163 // make sure source flows into all neighboring nodes
2164 for (u16 i = 0; i < num_flows; i++)
2165 if (flows[i].t != NEIGHBOR_UPPER)
2166 m_transforming_liquid.push_back(flows[i].p);
2167 for (u16 i = 0; i < num_airs; i++)
2168 if (airs[i].t != NEIGHBOR_UPPER)
2169 m_transforming_liquid.push_back(airs[i].p);
2172 // this flow has turned to air; neighboring flows might need to do the same
2173 for (u16 i = 0; i < num_flows; i++)
2174 m_transforming_liquid.push_back(flows[i].p);
2178 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2179 while (must_reflow.size() > 0)
2180 m_transforming_liquid.push_back(must_reflow.pop_front());
2181 updateLighting(lighting_modified_blocks, modified_blocks);
2184 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2186 v3s16 blockpos = getNodeBlockPos(p);
2187 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2188 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2190 infostream<<"Map::getNodeMetadata(): Need to emerge "
2191 <<PP(blockpos)<<std::endl;
2192 block = emergeBlock(blockpos, false);
2196 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2200 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2204 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2206 v3s16 blockpos = getNodeBlockPos(p);
2207 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2208 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2210 infostream<<"Map::setNodeMetadata(): Need to emerge "
2211 <<PP(blockpos)<<std::endl;
2212 block = emergeBlock(blockpos, false);
2216 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2220 block->m_node_metadata.set(p_rel, meta);
2223 void Map::removeNodeMetadata(v3s16 p)
2225 v3s16 blockpos = getNodeBlockPos(p);
2226 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2227 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2230 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2234 block->m_node_metadata.remove(p_rel);
2237 NodeTimer Map::getNodeTimer(v3s16 p)
2239 v3s16 blockpos = getNodeBlockPos(p);
2240 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2241 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2243 infostream<<"Map::getNodeTimer(): Need to emerge "
2244 <<PP(blockpos)<<std::endl;
2245 block = emergeBlock(blockpos, false);
2249 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2253 NodeTimer t = block->m_node_timers.get(p_rel);
2257 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2259 v3s16 blockpos = getNodeBlockPos(p);
2260 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2261 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2263 infostream<<"Map::setNodeTimer(): Need to emerge "
2264 <<PP(blockpos)<<std::endl;
2265 block = emergeBlock(blockpos, false);
2269 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2273 block->m_node_timers.set(p_rel, t);
2276 void Map::removeNodeTimer(v3s16 p)
2278 v3s16 blockpos = getNodeBlockPos(p);
2279 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2280 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2283 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2287 block->m_node_timers.remove(p_rel);
2293 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2294 Map(dout_server, gamedef),
2296 m_map_metadata_changed(true),
2298 m_database_read(NULL),
2299 m_database_write(NULL)
2301 verbosestream<<__FUNCTION_NAME<<std::endl;
2304 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2306 m_mgparams = new MapgenV6Params();
2308 m_seed = m_mgparams->seed;
2310 if (g_settings->get("fixed_map_seed").empty())
2312 m_seed = (((u64)(myrand() & 0xffff) << 0)
2313 | ((u64)(myrand() & 0xffff) << 16)
2314 | ((u64)(myrand() & 0xffff) << 32)
2315 | ((u64)(myrand() & 0xffff) << 48));
2316 m_mgparams->seed = m_seed;
2320 Experimental and debug stuff
2327 Try to load map; if not found, create a new one.
2330 m_savedir = savedir;
2331 m_map_saving_enabled = false;
2335 // If directory exists, check contents and load if possible
2336 if(fs::PathExists(m_savedir))
2338 // If directory is empty, it is safe to save into it.
2339 if(fs::GetDirListing(m_savedir).size() == 0)
2341 infostream<<"ServerMap: Empty save directory is valid."
2343 m_map_saving_enabled = true;
2348 // Load map metadata (seed, chunksize)
2351 catch(SettingNotFoundException &e){
2352 infostream<<"ServerMap: Some metadata not found."
2353 <<" Using default settings."<<std::endl;
2355 catch(FileNotGoodException &e){
2356 infostream<<"WARNING: Could not load map metadata"
2357 //<<" Disabling chunk-based generator."
2362 infostream<<"ServerMap: Successfully loaded map "
2363 <<"metadata from "<<savedir
2364 <<", assuming valid save directory."
2365 <<" seed="<<m_seed<<"."
2368 m_map_saving_enabled = true;
2369 // Map loaded, not creating new one
2373 // If directory doesn't exist, it is safe to save to it
2375 m_map_saving_enabled = true;
2378 catch(std::exception &e)
2380 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2381 <<", exception: "<<e.what()<<std::endl;
2382 infostream<<"Please remove the map or fix it."<<std::endl;
2383 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2386 infostream<<"Initializing new map."<<std::endl;
2388 // Create zero sector
2389 emergeSector(v2s16(0,0));
2391 // Initially write whole map
2392 save(MOD_STATE_CLEAN);
2395 ServerMap::~ServerMap()
2397 verbosestream<<__FUNCTION_NAME<<std::endl;
2401 if(m_map_saving_enabled)
2403 // Save only changed parts
2404 save(MOD_STATE_WRITE_AT_UNLOAD);
2405 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2409 infostream<<"ServerMap: Map not saved"<<std::endl;
2412 catch(std::exception &e)
2414 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2415 <<", exception: "<<e.what()<<std::endl;
2419 Close database if it was opened
2422 sqlite3_finalize(m_database_read);
2423 if(m_database_write)
2424 sqlite3_finalize(m_database_write);
2426 sqlite3_close(m_database);
2432 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2433 for(; i.atEnd() == false; i++)
2435 MapChunk *chunk = i.getNode()->getValue();
2441 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2443 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2444 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2446 //s16 chunksize = 3;
2447 //v3s16 chunk_offset(-1,-1,-1);
2448 //s16 chunksize = 4;
2449 //v3s16 chunk_offset(-1,-1,-1);
2451 v3s16 chunk_offset(-2,-2,-2);
2452 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2453 v3s16 blockpos_min = blockpos_div * chunksize;
2454 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2455 blockpos_min += chunk_offset;
2456 blockpos_max += chunk_offset;
2458 //v3s16 extra_borders(1,1,1);
2459 v3s16 extra_borders(1,1,1);
2461 // Do nothing if not inside limits (+-1 because of neighbors)
2462 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2463 blockpos_over_limit(blockpos_max + extra_borders))
2466 data->seed = m_seed;
2467 data->blockpos_min = blockpos_min;
2468 data->blockpos_max = blockpos_max;
2469 data->blockpos_requested = blockpos;
2470 data->nodedef = m_gamedef->ndef();
2473 Create the whole area of this and the neighboring blocks
2476 //TimeTaker timer("initBlockMake() create area");
2478 for(s16 x=blockpos_min.X-extra_borders.X;
2479 x<=blockpos_max.X+extra_borders.X; x++)
2480 for(s16 z=blockpos_min.Z-extra_borders.Z;
2481 z<=blockpos_max.Z+extra_borders.Z; z++)
2483 v2s16 sectorpos(x, z);
2484 // Sector metadata is loaded from disk if not already loaded.
2485 ServerMapSector *sector = createSector(sectorpos);
2488 for(s16 y=blockpos_min.Y-extra_borders.Y;
2489 y<=blockpos_max.Y+extra_borders.Y; y++)
2492 //MapBlock *block = createBlock(p);
2493 // 1) get from memory, 2) load from disk
2494 MapBlock *block = emergeBlock(p, false);
2495 // 3) create a blank one
2498 block = createBlock(p);
2501 Block gets sunlight if this is true.
2503 Refer to the map generator heuristics.
2505 bool ug = m_emerge->isBlockUnderground(p);
2506 block->setIsUnderground(ug);
2509 // Lighting will not be valid after make_chunk is called
2510 block->setLightingExpired(true);
2511 // Lighting will be calculated
2512 //block->setLightingExpired(false);
2518 Now we have a big empty area.
2520 Make a ManualMapVoxelManipulator that contains this and the
2524 // The area that contains this block and it's neighbors
2525 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2526 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2528 data->vmanip = new ManualMapVoxelManipulator(this);
2529 //data->vmanip->setMap(this);
2533 //TimeTaker timer("initBlockMake() initialEmerge");
2534 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2537 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2538 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2539 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2540 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2541 core::map<v3s16, u8>::Node *n;
2542 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2545 u8 flags = n->getValue();
2546 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2552 // Data is ready now.
2556 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2557 std::map<v3s16, MapBlock*> &changed_blocks)
2559 v3s16 blockpos_min = data->blockpos_min;
2560 v3s16 blockpos_max = data->blockpos_max;
2561 v3s16 blockpos_requested = data->blockpos_requested;
2562 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2563 <<blockpos_requested.Y<<","
2564 <<blockpos_requested.Z<<")"<<std::endl;*/
2566 v3s16 extra_borders(1,1,1);
2568 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2570 /*infostream<<"Resulting vmanip:"<<std::endl;
2571 data->vmanip.print(infostream);*/
2573 // Make sure affected blocks are loaded
2574 for(s16 x=blockpos_min.X-extra_borders.X;
2575 x<=blockpos_max.X+extra_borders.X; x++)
2576 for(s16 z=blockpos_min.Z-extra_borders.Z;
2577 z<=blockpos_max.Z+extra_borders.Z; z++)
2578 for(s16 y=blockpos_min.Y-extra_borders.Y;
2579 y<=blockpos_max.Y+extra_borders.Y; y++)
2582 // Load from disk if not already in memory
2583 emergeBlock(p, false);
2587 Blit generated stuff to map
2588 NOTE: blitBackAll adds nearly everything to changed_blocks
2592 //TimeTaker timer("finishBlockMake() blitBackAll");
2593 data->vmanip->blitBackAll(&changed_blocks);
2596 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2599 Copy transforming liquid information
2601 while(data->transforming_liquid.size() > 0)
2603 v3s16 p = data->transforming_liquid.pop_front();
2604 m_transforming_liquid.push_back(p);
2608 Do stuff in central blocks
2616 TimeTaker t("finishBlockMake lighting update");
2618 core::map<v3s16, MapBlock*> lighting_update_blocks;
2621 for(s16 x=blockpos_min.X-extra_borders.X;
2622 x<=blockpos_max.X+extra_borders.X; x++)
2623 for(s16 z=blockpos_min.Z-extra_borders.Z;
2624 z<=blockpos_max.Z+extra_borders.Z; z++)
2625 for(s16 y=blockpos_min.Y-extra_borders.Y;
2626 y<=blockpos_max.Y+extra_borders.Y; y++)
2629 MapBlock *block = getBlockNoCreateNoEx(p);
2631 lighting_update_blocks.insert(block->getPos(), block);
2634 updateLighting(lighting_update_blocks, changed_blocks);
2638 Set lighting to non-expired state in all of them.
2639 This is cheating, but it is not fast enough if all of them
2640 would actually be updated.
2642 for(s16 x=blockpos_min.X-extra_borders.X;
2643 x<=blockpos_max.X+extra_borders.X; x++)
2644 for(s16 z=blockpos_min.Z-extra_borders.Z;
2645 z<=blockpos_max.Z+extra_borders.Z; z++)
2646 for(s16 y=blockpos_min.Y-extra_borders.Y;
2647 y<=blockpos_max.Y+extra_borders.Y; y++)
2650 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2654 if(enable_mapgen_debug_info == false)
2655 t.stop(true); // Hide output
2660 Go through changed blocks
2662 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2663 i != changed_blocks.end(); ++i)
2665 MapBlock *block = i->second;
2668 Update day/night difference cache of the MapBlocks
2670 block->expireDayNightDiff();
2672 Set block as modified
2674 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2675 "finishBlockMake expireDayNightDiff");
2679 Set central blocks as generated
2681 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2682 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2683 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2686 MapBlock *block = getBlockNoCreateNoEx(p);
2688 block->setGenerated(true);
2692 Save changed parts of map
2693 NOTE: Will be saved later.
2695 //save(MOD_STATE_WRITE_AT_UNLOAD);
2697 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2698 <<","<<blockpos_requested.Y<<","
2699 <<blockpos_requested.Z<<")"<<std::endl;*/
2701 if(enable_mapgen_debug_info)
2704 Analyze resulting blocks
2706 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2707 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2708 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2709 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2710 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2711 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2713 v3s16 p = v3s16(x,y,z);
2714 MapBlock *block = getBlockNoCreateNoEx(p);
2716 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2717 infostream<<"Generated "<<spos<<": "
2718 <<analyze_block(block)<<std::endl;
2723 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2729 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2731 DSTACKF("%s: p2d=(%d,%d)",
2736 Check if it exists already in memory
2738 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2743 Try to load it from disk (with blocks)
2745 //if(loadSectorFull(p2d) == true)
2748 Try to load metadata from disk
2751 if(loadSectorMeta(p2d) == true)
2753 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2756 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2757 throw InvalidPositionException("");
2763 Do not create over-limit
2765 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2766 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2767 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2768 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2769 throw InvalidPositionException("createSector(): pos. over limit");
2772 Generate blank sector
2775 sector = new ServerMapSector(this, p2d, m_gamedef);
2777 // Sector position on map in nodes
2778 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2783 m_sectors[p2d] = sector;
2790 This is a quick-hand function for calling makeBlock().
2792 MapBlock * ServerMap::generateBlock(
2794 std::map<v3s16, MapBlock*> &modified_blocks
2797 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2799 /*infostream<<"generateBlock(): "
2800 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2803 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2805 TimeTaker timer("generateBlock");
2807 //MapBlock *block = original_dummy;
2809 v2s16 p2d(p.X, p.Z);
2810 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2813 Do not generate over-limit
2815 if(blockpos_over_limit(p))
2817 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2818 throw InvalidPositionException("generateBlock(): pos. over limit");
2822 Create block make data
2825 initBlockMake(&data, p);
2831 TimeTaker t("mapgen::make_block()");
2832 mapgen->makeChunk(&data);
2833 //mapgen::make_block(&data);
2835 if(enable_mapgen_debug_info == false)
2836 t.stop(true); // Hide output
2840 Blit data back on map, update lighting, add mobs and whatever this does
2842 finishBlockMake(&data, modified_blocks);
2847 MapBlock *block = getBlockNoCreateNoEx(p);
2855 bool erroneus_content = false;
2856 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2857 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2858 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2861 MapNode n = block->getNode(p);
2862 if(n.getContent() == CONTENT_IGNORE)
2864 infostream<<"CONTENT_IGNORE at "
2865 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2867 erroneus_content = true;
2871 if(erroneus_content)
2880 Generate a completely empty block
2884 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2885 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2887 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2890 n.setContent(CONTENT_AIR);
2891 block->setNode(v3s16(x0,y0,z0), n);
2897 if(enable_mapgen_debug_info == false)
2898 timer.stop(true); // Hide output
2904 MapBlock * ServerMap::createBlock(v3s16 p)
2906 DSTACKF("%s: p=(%d,%d,%d)",
2907 __FUNCTION_NAME, p.X, p.Y, p.Z);
2910 Do not create over-limit
2912 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2913 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2914 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2915 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2916 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2917 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2918 throw InvalidPositionException("createBlock(): pos. over limit");
2920 v2s16 p2d(p.X, p.Z);
2923 This will create or load a sector if not found in memory.
2924 If block exists on disk, it will be loaded.
2926 NOTE: On old save formats, this will be slow, as it generates
2927 lighting on blocks for them.
2929 ServerMapSector *sector;
2931 sector = (ServerMapSector*)createSector(p2d);
2932 assert(sector->getId() == MAPSECTOR_SERVER);
2934 catch(InvalidPositionException &e)
2936 infostream<<"createBlock: createSector() failed"<<std::endl;
2940 NOTE: This should not be done, or at least the exception
2941 should not be passed on as std::exception, because it
2942 won't be catched at all.
2944 /*catch(std::exception &e)
2946 infostream<<"createBlock: createSector() failed: "
2947 <<e.what()<<std::endl;
2952 Try to get a block from the sector
2955 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2958 if(block->isDummy())
2963 block = sector->createBlankBlock(block_y);
2968 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2970 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2972 p.X, p.Y, p.Z, create_blank);
2975 MapBlock *block = getBlockNoCreateNoEx(p);
2976 if(block && block->isDummy() == false)
2981 MapBlock *block = loadBlock(p);
2987 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2988 MapBlock *block = sector->createBlankBlock(p.Y);
2992 /*if(allow_generate)
2994 std::map<v3s16, MapBlock*> modified_blocks;
2995 MapBlock *block = generateBlock(p, modified_blocks);
2999 event.type = MEET_OTHER;
3002 // Copy modified_blocks to event
3003 for(std::map<v3s16, MapBlock*>::iterator
3004 i = modified_blocks.begin();
3005 i != modified_blocks.end(); ++i)
3007 event.modified_blocks.erase(i->first);
3011 dispatchEvent(&event);
3020 s16 ServerMap::findGroundLevel(v2s16 p2d)
3024 Uh, just do something random...
3026 // Find existing map from top to down
3029 v3s16 p(p2d.X, max, p2d.Y);
3030 for(; p.Y>min; p.Y--)
3032 MapNode n = getNodeNoEx(p);
3033 if(n.getContent() != CONTENT_IGNORE)
3038 // If this node is not air, go to plan b
3039 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3041 // Search existing walkable and return it
3042 for(; p.Y>min; p.Y--)
3044 MapNode n = getNodeNoEx(p);
3045 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3054 Determine from map generator noise functions
3057 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3060 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3061 //return (s16)level;
3064 void ServerMap::createDatabase() {
3067 e = sqlite3_exec(m_database,
3068 "CREATE TABLE IF NOT EXISTS `blocks` ("
3069 "`pos` INT NOT NULL PRIMARY KEY,"
3072 , NULL, NULL, NULL);
3073 if(e == SQLITE_ABORT)
3074 throw FileNotGoodException("Could not create database structure");
3076 infostream<<"ServerMap: Database structure was created";
3079 void ServerMap::verifyDatabase() {
3084 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3085 bool needs_create = false;
3089 Open the database connection
3092 createDirs(m_savedir);
3094 if(!fs::PathExists(dbp))
3095 needs_create = true;
3097 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3098 if(d != SQLITE_OK) {
3099 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3100 throw FileNotGoodException("Cannot open database file");
3106 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3107 if(d != SQLITE_OK) {
3108 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3109 throw FileNotGoodException("Cannot prepare read statement");
3112 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3113 if(d != SQLITE_OK) {
3114 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3115 throw FileNotGoodException("Cannot prepare write statement");
3118 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3119 if(d != SQLITE_OK) {
3120 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3121 throw FileNotGoodException("Cannot prepare read statement");
3124 infostream<<"ServerMap: Database opened"<<std::endl;
3128 bool ServerMap::loadFromFolders() {
3129 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3134 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3135 return (sqlite3_int64)pos.Z*16777216 +
3136 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3139 void ServerMap::createDirs(std::string path)
3141 if(fs::CreateAllDirs(path) == false)
3143 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3144 <<"\""<<path<<"\""<<std::endl;
3145 throw BaseException("ServerMap failed to create directory");
3149 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3155 snprintf(cc, 9, "%.4x%.4x",
3156 (unsigned int)pos.X&0xffff,
3157 (unsigned int)pos.Y&0xffff);
3159 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3161 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3162 (unsigned int)pos.X&0xfff,
3163 (unsigned int)pos.Y&0xfff);
3165 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3171 v2s16 ServerMap::getSectorPos(std::string dirname)
3175 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
3176 assert(spos != std::string::npos);
3177 if(dirname.size() - spos == 8)
3180 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
3182 else if(dirname.size() - spos == 3)
3185 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3186 // Sign-extend the 12 bit values up to 16 bits...
3187 if(x&0x800) x|=0xF000;
3188 if(y&0x800) y|=0xF000;
3195 v2s16 pos((s16)x, (s16)y);
3199 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3201 v2s16 p2d = getSectorPos(sectordir);
3203 if(blockfile.size() != 4){
3204 throw InvalidFilenameException("Invalid block filename");
3207 int r = sscanf(blockfile.c_str(), "%4x", &y);
3209 throw InvalidFilenameException("Invalid block filename");
3210 return v3s16(p2d.X, y, p2d.Y);
3213 std::string ServerMap::getBlockFilename(v3s16 p)
3216 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3220 void ServerMap::save(ModifiedState save_level)
3222 DSTACK(__FUNCTION_NAME);
3223 if(m_map_saving_enabled == false)
3225 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3229 if(save_level == MOD_STATE_CLEAN)
3230 infostream<<"ServerMap: Saving whole map, this can take time."
3233 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3238 // Profile modified reasons
3239 Profiler modprofiler;
3241 u32 sector_meta_count = 0;
3242 u32 block_count = 0;
3243 u32 block_count_all = 0; // Number of blocks in memory
3245 // Don't do anything with sqlite unless something is really saved
3246 bool save_started = false;
3248 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3249 i != m_sectors.end(); ++i)
3251 ServerMapSector *sector = (ServerMapSector*)i->second;
3252 assert(sector->getId() == MAPSECTOR_SERVER);
3254 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3256 saveSectorMeta(sector);
3257 sector_meta_count++;
3259 std::list<MapBlock*> blocks;
3260 sector->getBlocks(blocks);
3262 for(std::list<MapBlock*>::iterator j = blocks.begin();
3263 j != blocks.end(); ++j)
3265 MapBlock *block = *j;
3269 if(block->getModified() >= (u32)save_level)
3274 save_started = true;
3277 modprofiler.add(block->getModifiedReason(), 1);
3282 /*infostream<<"ServerMap: Written block ("
3283 <<block->getPos().X<<","
3284 <<block->getPos().Y<<","
3285 <<block->getPos().Z<<")"
3294 Only print if something happened or saved whole map
3296 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3297 || block_count != 0)
3299 infostream<<"ServerMap: Written: "
3300 <<sector_meta_count<<" sector metadata files, "
3301 <<block_count<<" block files"
3302 <<", "<<block_count_all<<" blocks in memory."
3304 PrintInfo(infostream); // ServerMap/ClientMap:
3305 infostream<<"Blocks modified by: "<<std::endl;
3306 modprofiler.print(infostream);
3310 static s32 unsignedToSigned(s32 i, s32 max_positive)
3312 if(i < max_positive)
3315 return i - 2*max_positive;
3318 // modulo of a negative number does not work consistently in C
3319 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3323 return mod - ((-i) % mod);
3326 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3328 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3330 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3332 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3333 return v3s16(x,y,z);
3336 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3338 if(loadFromFolders()){
3339 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3340 <<"all blocks that are stored in flat files"<<std::endl;
3346 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3348 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3349 v3s16 p = getIntegerAsBlock(block_i);
3350 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3356 void ServerMap::saveMapMeta()
3358 DSTACK(__FUNCTION_NAME);
3360 /*infostream<<"ServerMap::saveMapMeta(): "
3364 createDirs(m_savedir);
3366 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3367 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3368 if(os.good() == false)
3370 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3371 <<"could not open"<<fullpath<<std::endl;
3372 throw FileNotGoodException("Cannot open chunk metadata");
3377 m_emerge->setParamsToSettings(¶ms);
3378 params.writeLines(os);
3380 os<<"[end_of_params]\n";
3382 m_map_metadata_changed = false;
3385 void ServerMap::loadMapMeta()
3387 DSTACK(__FUNCTION_NAME);
3389 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3392 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3393 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3394 if(is.good() == false)
3396 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3397 <<"could not open"<<fullpath<<std::endl;
3398 throw FileNotGoodException("Cannot open map metadata");
3406 throw SerializationError
3407 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3409 std::getline(is, line);
3410 std::string trimmedline = trim(line);
3411 if(trimmedline == "[end_of_params]")
3413 params.parseConfigLine(line);
3416 MapgenParams *mgparams = m_emerge->getParamsFromSettings(¶ms);
3420 m_mgparams = mgparams;
3421 m_seed = mgparams->seed;
3423 if (params.exists("seed")) {
3424 m_seed = params.getU64("seed");
3425 m_mgparams->seed = m_seed;
3429 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3432 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3434 DSTACK(__FUNCTION_NAME);
3435 // Format used for writing
3436 u8 version = SER_FMT_VER_HIGHEST;
3438 v2s16 pos = sector->getPos();
3439 std::string dir = getSectorDir(pos);
3442 std::string fullpath = dir + DIR_DELIM + "meta";
3443 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3444 if(o.good() == false)
3445 throw FileNotGoodException("Cannot open sector metafile");
3447 sector->serialize(o, version);
3449 sector->differs_from_disk = false;
3452 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3454 DSTACK(__FUNCTION_NAME);
3456 v2s16 p2d = getSectorPos(sectordir);
3458 ServerMapSector *sector = NULL;
3460 std::string fullpath = sectordir + DIR_DELIM + "meta";
3461 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3462 if(is.good() == false)
3464 // If the directory exists anyway, it probably is in some old
3465 // format. Just go ahead and create the sector.
3466 if(fs::PathExists(sectordir))
3468 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3469 <<fullpath<<" doesn't exist but directory does."
3470 <<" Continuing with a sector with no metadata."
3472 sector = new ServerMapSector(this, p2d, m_gamedef);
3473 m_sectors[p2d] = sector;
3477 throw FileNotGoodException("Cannot open sector metafile");
3482 sector = ServerMapSector::deSerialize
3483 (is, this, p2d, m_sectors, m_gamedef);
3485 saveSectorMeta(sector);
3488 sector->differs_from_disk = false;
3493 bool ServerMap::loadSectorMeta(v2s16 p2d)
3495 DSTACK(__FUNCTION_NAME);
3497 MapSector *sector = NULL;
3499 // The directory layout we're going to load from.
3500 // 1 - original sectors/xxxxzzzz/
3501 // 2 - new sectors2/xxx/zzz/
3502 // If we load from anything but the latest structure, we will
3503 // immediately save to the new one, and remove the old.
3505 std::string sectordir1 = getSectorDir(p2d, 1);
3506 std::string sectordir;
3507 if(fs::PathExists(sectordir1))
3509 sectordir = sectordir1;
3514 sectordir = getSectorDir(p2d, 2);
3518 sector = loadSectorMeta(sectordir, loadlayout != 2);
3520 catch(InvalidFilenameException &e)
3524 catch(FileNotGoodException &e)
3528 catch(std::exception &e)
3537 bool ServerMap::loadSectorFull(v2s16 p2d)
3539 DSTACK(__FUNCTION_NAME);
3541 MapSector *sector = NULL;
3543 // The directory layout we're going to load from.
3544 // 1 - original sectors/xxxxzzzz/
3545 // 2 - new sectors2/xxx/zzz/
3546 // If we load from anything but the latest structure, we will
3547 // immediately save to the new one, and remove the old.
3549 std::string sectordir1 = getSectorDir(p2d, 1);
3550 std::string sectordir;
3551 if(fs::PathExists(sectordir1))
3553 sectordir = sectordir1;
3558 sectordir = getSectorDir(p2d, 2);
3562 sector = loadSectorMeta(sectordir, loadlayout != 2);
3564 catch(InvalidFilenameException &e)
3568 catch(FileNotGoodException &e)
3572 catch(std::exception &e)
3580 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3582 std::vector<fs::DirListNode>::iterator i2;
3583 for(i2=list2.begin(); i2!=list2.end(); i2++)
3589 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3591 catch(InvalidFilenameException &e)
3593 // This catches unknown crap in directory
3599 infostream<<"Sector converted to new layout - deleting "<<
3600 sectordir1<<std::endl;
3601 fs::RecursiveDelete(sectordir1);
3608 void ServerMap::beginSave() {
3610 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3611 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3614 void ServerMap::endSave() {
3616 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3617 infostream<<"WARNING: endSave() failed, map might not have saved.";
3620 void ServerMap::saveBlock(MapBlock *block)
3622 DSTACK(__FUNCTION_NAME);
3624 Dummy blocks are not written
3626 if(block->isDummy())
3628 /*v3s16 p = block->getPos();
3629 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3630 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3634 // Format used for writing
3635 u8 version = SER_FMT_VER_HIGHEST;
3637 v3s16 p3d = block->getPos();
3641 v2s16 p2d(p3d.X, p3d.Z);
3642 std::string sectordir = getSectorDir(p2d);
3644 createDirs(sectordir);
3646 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3647 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3648 if(o.good() == false)
3649 throw FileNotGoodException("Cannot open block data");
3652 [0] u8 serialization version
3658 std::ostringstream o(std::ios_base::binary);
3660 o.write((char*)&version, 1);
3663 block->serialize(o, version, true);
3665 // Write block to database
3667 std::string tmp = o.str();
3668 const char *bytes = tmp.c_str();
3670 bool success = true;
3671 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3672 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3675 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3676 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3679 int written = sqlite3_step(m_database_write);
3680 if(written != SQLITE_DONE) {
3681 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3682 <<sqlite3_errmsg(m_database)<<std::endl;
3685 // Make ready for later reuse
3686 sqlite3_reset(m_database_write);
3688 // We just wrote it to the disk so clear modified flag
3690 block->resetModified();
3693 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3695 DSTACK(__FUNCTION_NAME);
3697 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3700 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3701 if(is.good() == false)
3702 throw FileNotGoodException("Cannot open block file");
3704 v3s16 p3d = getBlockPos(sectordir, blockfile);
3705 v2s16 p2d(p3d.X, p3d.Z);
3707 assert(sector->getPos() == p2d);
3709 u8 version = SER_FMT_VER_INVALID;
3710 is.read((char*)&version, 1);
3713 throw SerializationError("ServerMap::loadBlock(): Failed"
3714 " to read MapBlock version");
3716 /*u32 block_size = MapBlock::serializedLength(version);
3717 SharedBuffer<u8> data(block_size);
3718 is.read((char*)*data, block_size);*/
3720 // This will always return a sector because we're the server
3721 //MapSector *sector = emergeSector(p2d);
3723 MapBlock *block = NULL;
3724 bool created_new = false;
3725 block = sector->getBlockNoCreateNoEx(p3d.Y);
3728 block = sector->createBlankBlockNoInsert(p3d.Y);
3733 block->deSerialize(is, version, true);
3735 // If it's a new block, insert it to the map
3737 sector->insertBlock(block);
3740 Save blocks loaded in old format in new format
3743 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3747 // Should be in database now, so delete the old file
3748 fs::RecursiveDelete(fullpath);
3751 // We just loaded it from the disk, so it's up-to-date.
3752 block->resetModified();
3755 catch(SerializationError &e)
3757 infostream<<"WARNING: Invalid block data on disk "
3758 <<"fullpath="<<fullpath
3759 <<" (SerializationError). "
3760 <<"what()="<<e.what()
3762 //" Ignoring. A new one will be generated.
3765 // TODO: Backup file; name is in fullpath.
3769 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3771 DSTACK(__FUNCTION_NAME);
3774 std::istringstream is(*blob, std::ios_base::binary);
3776 u8 version = SER_FMT_VER_INVALID;
3777 is.read((char*)&version, 1);
3780 throw SerializationError("ServerMap::loadBlock(): Failed"
3781 " to read MapBlock version");
3783 /*u32 block_size = MapBlock::serializedLength(version);
3784 SharedBuffer<u8> data(block_size);
3785 is.read((char*)*data, block_size);*/
3787 // This will always return a sector because we're the server
3788 //MapSector *sector = emergeSector(p2d);
3790 MapBlock *block = NULL;
3791 bool created_new = false;
3792 block = sector->getBlockNoCreateNoEx(p3d.Y);
3795 block = sector->createBlankBlockNoInsert(p3d.Y);
3800 block->deSerialize(is, version, true);
3802 // If it's a new block, insert it to the map
3804 sector->insertBlock(block);
3807 Save blocks loaded in old format in new format
3810 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3811 // Only save if asked to; no need to update version
3815 // We just loaded it from, so it's up-to-date.
3816 block->resetModified();
3819 catch(SerializationError &e)
3821 errorstream<<"Invalid block data in database"
3822 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3823 <<" (SerializationError): "<<e.what()<<std::endl;
3825 // TODO: Block should be marked as invalid in memory so that it is
3826 // not touched but the game can run
3828 if(g_settings->getBool("ignore_world_load_errors")){
3829 errorstream<<"Ignoring block load error. Duck and cover! "
3830 <<"(ignore_world_load_errors)"<<std::endl;
3832 throw SerializationError("Invalid block data in database");
3838 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3840 DSTACK(__FUNCTION_NAME);
3842 v2s16 p2d(blockpos.X, blockpos.Z);
3844 if(!loadFromFolders()) {
3847 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3848 infostream<<"WARNING: Could not bind block position for load: "
3849 <<sqlite3_errmsg(m_database)<<std::endl;
3850 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3852 Make sure sector is loaded
3854 MapSector *sector = createSector(p2d);
3859 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3860 size_t len = sqlite3_column_bytes(m_database_read, 0);
3862 std::string datastr(data, len);
3864 loadBlock(&datastr, blockpos, sector, false);
3866 sqlite3_step(m_database_read);
3867 // We should never get more than 1 row, so ok to reset
3868 sqlite3_reset(m_database_read);
3870 return getBlockNoCreateNoEx(blockpos);
3872 sqlite3_reset(m_database_read);
3874 // Not found in database, try the files
3877 // The directory layout we're going to load from.
3878 // 1 - original sectors/xxxxzzzz/
3879 // 2 - new sectors2/xxx/zzz/
3880 // If we load from anything but the latest structure, we will
3881 // immediately save to the new one, and remove the old.
3883 std::string sectordir1 = getSectorDir(p2d, 1);
3884 std::string sectordir;
3885 if(fs::PathExists(sectordir1))
3887 sectordir = sectordir1;
3892 sectordir = getSectorDir(p2d, 2);
3896 Make sure sector is loaded
3898 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3902 sector = loadSectorMeta(sectordir, loadlayout != 2);
3904 catch(InvalidFilenameException &e)
3908 catch(FileNotGoodException &e)
3912 catch(std::exception &e)
3919 Make sure file exists
3922 std::string blockfilename = getBlockFilename(blockpos);
3923 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3927 Load block and save it to the database
3929 loadBlock(sectordir, blockfilename, sector, true);
3930 return getBlockNoCreateNoEx(blockpos);
3933 void ServerMap::PrintInfo(std::ostream &out)
3942 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3947 MapVoxelManipulator::~MapVoxelManipulator()
3949 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3953 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3955 TimeTaker timer1("emerge", &emerge_time);
3957 // Units of these are MapBlocks
3958 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3959 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3961 VoxelArea block_area_nodes
3962 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3964 addArea(block_area_nodes);
3966 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3967 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3968 for(s32 x=p_min.X; x<=p_max.X; x++)
3973 std::map<v3s16, u8>::iterator n;
3974 n = m_loaded_blocks.find(p);
3975 if(n != m_loaded_blocks.end())
3978 bool block_data_inexistent = false;
3981 TimeTaker timer1("emerge load", &emerge_load_time);
3983 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3984 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3986 a.print(infostream);
3987 infostream<<std::endl;*/
3989 block = m_map->getBlockNoCreate(p);
3990 if(block->isDummy())
3991 block_data_inexistent = true;
3993 block->copyTo(*this);
3995 catch(InvalidPositionException &e)
3997 block_data_inexistent = true;
4000 if(block_data_inexistent)
4002 flags |= VMANIP_BLOCK_DATA_INEXIST;
4004 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4005 // Fill with VOXELFLAG_INEXISTENT
4006 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4007 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4009 s32 i = m_area.index(a.MinEdge.X,y,z);
4010 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4013 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4015 // Mark that block was loaded as blank
4016 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4019 m_loaded_blocks[p] = flags;
4022 //infostream<<"emerge done"<<std::endl;
4026 SUGG: Add an option to only update eg. water and air nodes.
4027 This will make it interfere less with important stuff if
4030 void MapVoxelManipulator::blitBack
4031 (std::map<v3s16, MapBlock*> & modified_blocks)
4033 if(m_area.getExtent() == v3s16(0,0,0))
4036 //TimeTaker timer1("blitBack");
4038 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4039 <<m_loaded_blocks.size()<<std::endl;*/
4042 Initialize block cache
4044 v3s16 blockpos_last;
4045 MapBlock *block = NULL;
4046 bool block_checked_in_modified = false;
4048 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4049 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4050 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4054 u8 f = m_flags[m_area.index(p)];
4055 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4058 MapNode &n = m_data[m_area.index(p)];
4060 v3s16 blockpos = getNodeBlockPos(p);
4065 if(block == NULL || blockpos != blockpos_last){
4066 block = m_map->getBlockNoCreate(blockpos);
4067 blockpos_last = blockpos;
4068 block_checked_in_modified = false;
4071 // Calculate relative position in block
4072 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4074 // Don't continue if nothing has changed here
4075 if(block->getNode(relpos) == n)
4078 //m_map->setNode(m_area.MinEdge + p, n);
4079 block->setNode(relpos, n);
4082 Make sure block is in modified_blocks
4084 if(block_checked_in_modified == false)
4086 modified_blocks[blockpos] = block;
4087 block_checked_in_modified = true;
4090 catch(InvalidPositionException &e)
4096 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4097 MapVoxelManipulator(map),
4098 m_create_area(false)
4102 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4106 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4108 // Just create the area so that it can be pointed to
4109 VoxelManipulator::emerge(a, caller_id);
4112 void ManualMapVoxelManipulator::initialEmerge(
4113 v3s16 blockpos_min, v3s16 blockpos_max)
4115 TimeTaker timer1("initialEmerge", &emerge_time);
4117 // Units of these are MapBlocks
4118 v3s16 p_min = blockpos_min;
4119 v3s16 p_max = blockpos_max;
4121 VoxelArea block_area_nodes
4122 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4124 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4127 infostream<<"initialEmerge: area: ";
4128 block_area_nodes.print(infostream);
4129 infostream<<" ("<<size_MB<<"MB)";
4130 infostream<<std::endl;
4133 addArea(block_area_nodes);
4135 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4136 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4137 for(s32 x=p_min.X; x<=p_max.X; x++)
4142 std::map<v3s16, u8>::iterator n;
4143 n = m_loaded_blocks.find(p);
4144 if(n != m_loaded_blocks.end())
4147 bool block_data_inexistent = false;
4150 TimeTaker timer1("emerge load", &emerge_load_time);
4152 block = m_map->getBlockNoCreate(p);
4153 if(block->isDummy())
4154 block_data_inexistent = true;
4156 block->copyTo(*this);
4158 catch(InvalidPositionException &e)
4160 block_data_inexistent = true;
4163 if(block_data_inexistent)
4165 flags |= VMANIP_BLOCK_DATA_INEXIST;
4168 Mark area inexistent
4170 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4171 // Fill with VOXELFLAG_INEXISTENT
4172 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4173 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4175 s32 i = m_area.index(a.MinEdge.X,y,z);
4176 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4179 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4181 // Mark that block was loaded as blank
4182 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4185 m_loaded_blocks[p] = flags;
4189 void ManualMapVoxelManipulator::blitBackAll(
4190 std::map<v3s16, MapBlock*> * modified_blocks)
4192 if(m_area.getExtent() == v3s16(0,0,0))
4196 Copy data of all blocks
4198 for(std::map<v3s16, u8>::iterator
4199 i = m_loaded_blocks.begin();
4200 i != m_loaded_blocks.end(); ++i)
4203 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4204 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4205 if(existed == false)
4210 block->copyFrom(*this);
4213 (*modified_blocks)[p] = block;