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 "util/mathconstants.h"
36 #include "rollback_interface.h"
38 #include "mapgen_v6.h"
39 #include "mapgen_indev.h"
42 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
45 SQLite format specification:
46 - Initially only replaces sectors/ and sectors2/
48 If map.sqlite does not exist in the save dir
49 or the block was not found in the database
50 the map will try to load from sectors folder.
51 In either case, map.sqlite will be created
52 and all future saves will save there.
54 Structure of map.sqlite:
65 Map::Map(std::ostream &dout, IGameDef *gamedef):
70 /*m_sector_mutex.Init();
71 assert(m_sector_mutex.IsInitialized());*/
79 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
80 i != m_sectors.end(); ++i)
86 void Map::addEventReceiver(MapEventReceiver *event_receiver)
88 m_event_receivers.insert(event_receiver);
91 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
93 m_event_receivers.erase(event_receiver);
96 void Map::dispatchEvent(MapEditEvent *event)
98 for(std::set<MapEventReceiver*>::iterator
99 i = m_event_receivers.begin();
100 i != m_event_receivers.end(); ++i)
102 (*i)->onMapEditEvent(event);
106 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
108 if(m_sector_cache != NULL && p == m_sector_cache_p){
109 MapSector * sector = m_sector_cache;
113 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
115 if(n == m_sectors.end())
118 MapSector *sector = n->second;
120 // Cache the last result
121 m_sector_cache_p = p;
122 m_sector_cache = sector;
127 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
129 return getSectorNoGenerateNoExNoLock(p);
132 MapSector * Map::getSectorNoGenerate(v2s16 p)
134 MapSector *sector = getSectorNoGenerateNoEx(p);
136 throw InvalidPositionException();
141 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
143 v2s16 p2d(p3d.X, p3d.Z);
144 MapSector * sector = getSectorNoGenerateNoEx(p2d);
147 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
151 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
153 MapBlock *block = getBlockNoCreateNoEx(p3d);
155 throw InvalidPositionException();
159 bool Map::isNodeUnderground(v3s16 p)
161 v3s16 blockpos = getNodeBlockPos(p);
163 MapBlock * block = getBlockNoCreate(blockpos);
164 return block->getIsUnderground();
166 catch(InvalidPositionException &e)
172 bool Map::isValidPosition(v3s16 p)
174 v3s16 blockpos = getNodeBlockPos(p);
175 MapBlock *block = getBlockNoCreate(blockpos);
176 return (block != NULL);
179 // Returns a CONTENT_IGNORE node if not found
180 MapNode Map::getNodeNoEx(v3s16 p)
182 v3s16 blockpos = getNodeBlockPos(p);
183 MapBlock *block = getBlockNoCreateNoEx(blockpos);
185 return MapNode(CONTENT_IGNORE);
186 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
187 return block->getNodeNoCheck(relpos);
190 // throws InvalidPositionException if not found
191 MapNode Map::getNode(v3s16 p)
193 v3s16 blockpos = getNodeBlockPos(p);
194 MapBlock *block = getBlockNoCreateNoEx(blockpos);
196 throw InvalidPositionException();
197 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
198 return block->getNodeNoCheck(relpos);
201 // throws InvalidPositionException if not found
202 void Map::setNode(v3s16 p, MapNode & n)
204 v3s16 blockpos = getNodeBlockPos(p);
205 MapBlock *block = getBlockNoCreate(blockpos);
206 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
207 // Never allow placing CONTENT_IGNORE, it fucks up stuff
208 if(n.getContent() == CONTENT_IGNORE){
209 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
210 <<" while trying to replace \""
211 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
212 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
213 debug_stacks_print_to(infostream);
216 block->setNodeNoCheck(relpos, n);
221 Goes recursively through the neighbours of the node.
223 Alters only transparent nodes.
225 If the lighting of the neighbour is lower than the lighting of
226 the node was (before changing it to 0 at the step before), the
227 lighting of the neighbour is set to 0 and then the same stuff
228 repeats for the neighbour.
230 The ending nodes of the routine are stored in light_sources.
231 This is useful when a light is removed. In such case, this
232 routine can be called for the light node and then again for
233 light_sources to re-light the area without the removed light.
235 values of from_nodes are lighting values.
237 void Map::unspreadLight(enum LightBank bank,
238 std::map<v3s16, u8> & from_nodes,
239 std::set<v3s16> & light_sources,
240 std::map<v3s16, MapBlock*> & modified_blocks)
242 INodeDefManager *nodemgr = m_gamedef->ndef();
245 v3s16(0,0,1), // back
247 v3s16(1,0,0), // right
248 v3s16(0,0,-1), // front
249 v3s16(0,-1,0), // bottom
250 v3s16(-1,0,0), // left
253 if(from_nodes.size() == 0)
256 u32 blockchangecount = 0;
258 std::map<v3s16, u8> unlighted_nodes;
261 Initialize block cache
264 MapBlock *block = NULL;
265 // Cache this a bit, too
266 bool block_checked_in_modified = false;
268 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
269 j != from_nodes.end(); ++j)
271 v3s16 pos = j->first;
272 v3s16 blockpos = getNodeBlockPos(pos);
274 // Only fetch a new block if the block position has changed
276 if(block == NULL || blockpos != blockpos_last){
277 block = getBlockNoCreate(blockpos);
278 blockpos_last = blockpos;
280 block_checked_in_modified = false;
284 catch(InvalidPositionException &e)
292 // Calculate relative position in block
293 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
295 // Get node straight from the block
296 //MapNode n = block->getNode(relpos);
298 u8 oldlight = j->second;
300 // Loop through 6 neighbors
301 for(u16 i=0; i<6; i++)
303 // Get the position of the neighbor node
304 v3s16 n2pos = pos + dirs[i];
306 // Get the block where the node is located
307 v3s16 blockpos = getNodeBlockPos(n2pos);
311 // Only fetch a new block if the block position has changed
313 if(block == NULL || blockpos != blockpos_last){
314 block = getBlockNoCreate(blockpos);
315 blockpos_last = blockpos;
317 block_checked_in_modified = false;
321 catch(InvalidPositionException &e)
326 // Calculate relative position in block
327 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
328 // Get node straight from the block
329 MapNode n2 = block->getNode(relpos);
331 bool changed = false;
333 //TODO: Optimize output by optimizing light_sources?
336 If the neighbor is dimmer than what was specified
337 as oldlight (the light of the previous node)
339 if(n2.getLight(bank, nodemgr) < oldlight)
342 And the neighbor is transparent and it has some light
344 if(nodemgr->get(n2).light_propagates
345 && n2.getLight(bank, nodemgr) != 0)
348 Set light to 0 and add to queue
351 u8 current_light = n2.getLight(bank, nodemgr);
352 n2.setLight(bank, 0, nodemgr);
353 block->setNode(relpos, n2);
355 unlighted_nodes[n2pos] = current_light;
359 Remove from light_sources if it is there
360 NOTE: This doesn't happen nearly at all
362 /*if(light_sources.find(n2pos))
364 infostream<<"Removed from light_sources"<<std::endl;
365 light_sources.remove(n2pos);
370 if(light_sources.find(n2pos) != NULL)
371 light_sources.remove(n2pos);*/
374 light_sources.insert(n2pos);
377 // Add to modified_blocks
378 if(changed == true && block_checked_in_modified == false)
380 // If the block is not found in modified_blocks, add.
381 if(modified_blocks.find(blockpos) == modified_blocks.end())
383 modified_blocks[blockpos] = block;
385 block_checked_in_modified = true;
388 catch(InvalidPositionException &e)
395 /*infostream<<"unspreadLight(): Changed block "
396 <<blockchangecount<<" times"
397 <<" for "<<from_nodes.size()<<" nodes"
400 if(unlighted_nodes.size() > 0)
401 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
405 A single-node wrapper of the above
407 void Map::unLightNeighbors(enum LightBank bank,
408 v3s16 pos, u8 lightwas,
409 std::set<v3s16> & light_sources,
410 std::map<v3s16, MapBlock*> & modified_blocks)
412 std::map<v3s16, u8> from_nodes;
413 from_nodes[pos] = lightwas;
415 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
419 Lights neighbors of from_nodes, collects all them and then
422 void Map::spreadLight(enum LightBank bank,
423 std::set<v3s16> & from_nodes,
424 std::map<v3s16, MapBlock*> & modified_blocks)
426 INodeDefManager *nodemgr = m_gamedef->ndef();
428 const v3s16 dirs[6] = {
429 v3s16(0,0,1), // back
431 v3s16(1,0,0), // right
432 v3s16(0,0,-1), // front
433 v3s16(0,-1,0), // bottom
434 v3s16(-1,0,0), // left
437 if(from_nodes.size() == 0)
440 u32 blockchangecount = 0;
442 std::set<v3s16> lighted_nodes;
445 Initialize block cache
448 MapBlock *block = NULL;
449 // Cache this a bit, too
450 bool block_checked_in_modified = false;
452 for(std::set<v3s16>::iterator j = from_nodes.begin();
453 j != from_nodes.end(); ++j)
456 v3s16 blockpos = getNodeBlockPos(pos);
458 // Only fetch a new block if the block position has changed
460 if(block == NULL || blockpos != blockpos_last){
461 block = getBlockNoCreate(blockpos);
462 blockpos_last = blockpos;
464 block_checked_in_modified = false;
468 catch(InvalidPositionException &e)
476 // Calculate relative position in block
477 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
479 // Get node straight from the block
480 MapNode n = block->getNode(relpos);
482 u8 oldlight = n.getLight(bank, nodemgr);
483 u8 newlight = diminish_light(oldlight);
485 // Loop through 6 neighbors
486 for(u16 i=0; i<6; i++){
487 // Get the position of the neighbor node
488 v3s16 n2pos = pos + dirs[i];
490 // Get the block where the node is located
491 v3s16 blockpos = getNodeBlockPos(n2pos);
495 // Only fetch a new block if the block position has changed
497 if(block == NULL || blockpos != blockpos_last){
498 block = getBlockNoCreate(blockpos);
499 blockpos_last = blockpos;
501 block_checked_in_modified = false;
505 catch(InvalidPositionException &e)
510 // Calculate relative position in block
511 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
512 // Get node straight from the block
513 MapNode n2 = block->getNode(relpos);
515 bool changed = false;
517 If the neighbor is brighter than the current node,
518 add to list (it will light up this node on its turn)
520 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
522 lighted_nodes.insert(n2pos);
526 If the neighbor is dimmer than how much light this node
527 would spread on it, add to list
529 if(n2.getLight(bank, nodemgr) < newlight)
531 if(nodemgr->get(n2).light_propagates)
533 n2.setLight(bank, newlight, nodemgr);
534 block->setNode(relpos, n2);
535 lighted_nodes.insert(n2pos);
540 // Add to modified_blocks
541 if(changed == true && block_checked_in_modified == false)
543 // If the block is not found in modified_blocks, add.
544 if(modified_blocks.find(blockpos) == modified_blocks.end())
546 modified_blocks[blockpos] = block;
548 block_checked_in_modified = true;
551 catch(InvalidPositionException &e)
558 /*infostream<<"spreadLight(): Changed block "
559 <<blockchangecount<<" times"
560 <<" for "<<from_nodes.size()<<" nodes"
563 if(lighted_nodes.size() > 0)
564 spreadLight(bank, lighted_nodes, modified_blocks);
568 A single-node source variation of the above.
570 void Map::lightNeighbors(enum LightBank bank,
572 std::map<v3s16, MapBlock*> & modified_blocks)
574 std::set<v3s16> from_nodes;
575 from_nodes.insert(pos);
576 spreadLight(bank, from_nodes, modified_blocks);
579 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
581 INodeDefManager *nodemgr = m_gamedef->ndef();
584 v3s16(0,0,1), // back
586 v3s16(1,0,0), // right
587 v3s16(0,0,-1), // front
588 v3s16(0,-1,0), // bottom
589 v3s16(-1,0,0), // left
592 u8 brightest_light = 0;
593 v3s16 brightest_pos(0,0,0);
594 bool found_something = false;
596 // Loop through 6 neighbors
597 for(u16 i=0; i<6; i++){
598 // Get the position of the neighbor node
599 v3s16 n2pos = p + dirs[i];
604 catch(InvalidPositionException &e)
608 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
609 brightest_light = n2.getLight(bank, nodemgr);
610 brightest_pos = n2pos;
611 found_something = true;
615 if(found_something == false)
616 throw InvalidPositionException();
618 return brightest_pos;
622 Propagates sunlight down from a node.
623 Starting point gets sunlight.
625 Returns the lowest y value of where the sunlight went.
627 Mud is turned into grass in where the sunlight stops.
629 s16 Map::propagateSunlight(v3s16 start,
630 std::map<v3s16, MapBlock*> & modified_blocks)
632 INodeDefManager *nodemgr = m_gamedef->ndef();
637 v3s16 pos(start.X, y, start.Z);
639 v3s16 blockpos = getNodeBlockPos(pos);
642 block = getBlockNoCreate(blockpos);
644 catch(InvalidPositionException &e)
649 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
650 MapNode n = block->getNode(relpos);
652 if(nodemgr->get(n).sunlight_propagates)
654 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
655 block->setNode(relpos, n);
657 modified_blocks[blockpos] = block;
661 // Sunlight goes no further
668 void Map::updateLighting(enum LightBank bank,
669 std::map<v3s16, MapBlock*> & a_blocks,
670 std::map<v3s16, MapBlock*> & modified_blocks)
672 INodeDefManager *nodemgr = m_gamedef->ndef();
674 /*m_dout<<DTIME<<"Map::updateLighting(): "
675 <<a_blocks.size()<<" blocks."<<std::endl;*/
677 //TimeTaker timer("updateLighting");
681 //u32 count_was = modified_blocks.size();
683 std::map<v3s16, MapBlock*> blocks_to_update;
685 std::set<v3s16> light_sources;
687 std::map<v3s16, u8> unlight_from;
689 int num_bottom_invalid = 0;
692 //TimeTaker t("first stuff");
694 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
695 i != a_blocks.end(); ++i)
697 MapBlock *block = i->second;
701 // Don't bother with dummy blocks.
705 v3s16 pos = block->getPos();
706 v3s16 posnodes = block->getPosRelative();
707 modified_blocks[pos] = block;
708 blocks_to_update[pos] = block;
711 Clear all light from block
713 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
714 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
715 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
720 MapNode n = block->getNode(p);
721 u8 oldlight = n.getLight(bank, nodemgr);
722 n.setLight(bank, 0, nodemgr);
723 block->setNode(p, n);
725 // If node sources light, add to list
726 u8 source = nodemgr->get(n).light_source;
728 light_sources.insert(p + posnodes);
730 // Collect borders for unlighting
731 if((x==0 || x == MAP_BLOCKSIZE-1
732 || y==0 || y == MAP_BLOCKSIZE-1
733 || z==0 || z == MAP_BLOCKSIZE-1)
736 v3s16 p_map = p + posnodes;
737 unlight_from[p_map] = oldlight;
740 catch(InvalidPositionException &e)
743 This would happen when dealing with a
747 infostream<<"updateLighting(): InvalidPositionException"
752 if(bank == LIGHTBANK_DAY)
754 bool bottom_valid = block->propagateSunlight(light_sources);
757 num_bottom_invalid++;
759 // If bottom is valid, we're done.
763 else if(bank == LIGHTBANK_NIGHT)
765 // For night lighting, sunlight is not propagated
770 // Invalid lighting bank
774 /*infostream<<"Bottom for sunlight-propagated block ("
775 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
778 // Bottom sunlight is not valid; get the block and loop to it
782 block = getBlockNoCreate(pos);
784 catch(InvalidPositionException &e)
795 Enable this to disable proper lighting for speeding up map
796 generation for testing or whatever
799 //if(g_settings->get(""))
801 core::map<v3s16, MapBlock*>::Iterator i;
802 i = blocks_to_update.getIterator();
803 for(; i.atEnd() == false; i++)
805 MapBlock *block = i.getNode()->getValue();
806 v3s16 p = block->getPos();
807 block->setLightingExpired(false);
815 //TimeTaker timer("unspreadLight");
816 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
821 u32 diff = modified_blocks.size() - count_was;
822 count_was = modified_blocks.size();
823 infostream<<"unspreadLight modified "<<diff<<std::endl;
827 //TimeTaker timer("spreadLight");
828 spreadLight(bank, light_sources, modified_blocks);
833 u32 diff = modified_blocks.size() - count_was;
834 count_was = modified_blocks.size();
835 infostream<<"spreadLight modified "<<diff<<std::endl;
841 //MapVoxelManipulator vmanip(this);
843 // Make a manual voxel manipulator and load all the blocks
844 // that touch the requested blocks
845 ManualMapVoxelManipulator vmanip(this);
848 //TimeTaker timer("initialEmerge");
850 core::map<v3s16, MapBlock*>::Iterator i;
851 i = blocks_to_update.getIterator();
852 for(; i.atEnd() == false; i++)
854 MapBlock *block = i.getNode()->getValue();
855 v3s16 p = block->getPos();
857 // Add all surrounding blocks
858 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
861 Add all surrounding blocks that have up-to-date lighting
862 NOTE: This doesn't quite do the job (not everything
863 appropriate is lighted)
865 /*for(s16 z=-1; z<=1; z++)
866 for(s16 y=-1; y<=1; y++)
867 for(s16 x=-1; x<=1; x++)
869 v3s16 p2 = p + v3s16(x,y,z);
870 MapBlock *block = getBlockNoCreateNoEx(p2);
875 if(block->getLightingExpired())
877 vmanip.initialEmerge(p2, p2);
880 // Lighting of block will be updated completely
881 block->setLightingExpired(false);
886 //TimeTaker timer("unSpreadLight");
887 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
890 //TimeTaker timer("spreadLight");
891 vmanip.spreadLight(bank, light_sources, nodemgr);
894 //TimeTaker timer("blitBack");
895 vmanip.blitBack(modified_blocks);
897 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
902 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
905 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
906 std::map<v3s16, MapBlock*> & modified_blocks)
908 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
909 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
912 Update information about whether day and night light differ
914 for(std::map<v3s16, MapBlock*>::iterator
915 i = modified_blocks.begin();
916 i != modified_blocks.end(); ++i)
918 MapBlock *block = i->second;
919 block->expireDayNightDiff();
925 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
926 std::map<v3s16, MapBlock*> &modified_blocks)
928 INodeDefManager *ndef = m_gamedef->ndef();
931 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
932 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
935 From this node to nodes underneath:
936 If lighting is sunlight (1.0), unlight neighbours and
941 v3s16 toppos = p + v3s16(0,1,0);
942 //v3s16 bottompos = p + v3s16(0,-1,0);
944 bool node_under_sunlight = true;
945 std::set<v3s16> light_sources;
948 Collect old node for rollback
950 RollbackNode rollback_oldnode(this, p, m_gamedef);
953 If there is a node at top and it doesn't have sunlight,
954 there has not been any sunlight going down.
956 Otherwise there probably is.
959 MapNode topnode = getNode(toppos);
961 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
962 node_under_sunlight = false;
964 catch(InvalidPositionException &e)
969 Remove all light that has come out of this node
972 enum LightBank banks[] =
977 for(s32 i=0; i<2; i++)
979 enum LightBank bank = banks[i];
981 u8 lightwas = getNode(p).getLight(bank, ndef);
983 // Add the block of the added node to modified_blocks
984 v3s16 blockpos = getNodeBlockPos(p);
985 MapBlock * block = getBlockNoCreate(blockpos);
986 assert(block != NULL);
987 modified_blocks[blockpos] = block;
989 assert(isValidPosition(p));
991 // Unlight neighbours of node.
992 // This means setting light of all consequent dimmer nodes
994 // This also collects the nodes at the border which will spread
995 // light again into this.
996 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
998 n.setLight(bank, 0, ndef);
1002 If node lets sunlight through and is under sunlight, it has
1005 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1007 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1011 Remove node metadata
1014 removeNodeMetadata(p);
1017 Set the node on the map
1023 If node is under sunlight and doesn't let sunlight through,
1024 take all sunlighted nodes under it and clear light from them
1025 and from where the light has been spread.
1026 TODO: This could be optimized by mass-unlighting instead
1029 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1033 //m_dout<<DTIME<<"y="<<y<<std::endl;
1034 v3s16 n2pos(p.X, y, p.Z);
1038 n2 = getNode(n2pos);
1040 catch(InvalidPositionException &e)
1045 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1047 unLightNeighbors(LIGHTBANK_DAY,
1048 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1049 light_sources, modified_blocks);
1050 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1058 for(s32 i=0; i<2; i++)
1060 enum LightBank bank = banks[i];
1063 Spread light from all nodes that might be capable of doing so
1065 spreadLight(bank, light_sources, modified_blocks);
1069 Update information about whether day and night light differ
1071 for(std::map<v3s16, MapBlock*>::iterator
1072 i = modified_blocks.begin();
1073 i != modified_blocks.end(); ++i)
1075 i->second->expireDayNightDiff();
1081 if(m_gamedef->rollback())
1083 RollbackNode rollback_newnode(this, p, m_gamedef);
1084 RollbackAction action;
1085 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1086 m_gamedef->rollback()->reportAction(action);
1090 Add neighboring liquid nodes and the node itself if it is
1091 liquid (=water node was added) to transform queue.
1092 note: todo: for liquid_finite enough to add only self node
1095 v3s16(0,0,0), // self
1096 v3s16(0,0,1), // back
1097 v3s16(0,1,0), // top
1098 v3s16(1,0,0), // right
1099 v3s16(0,0,-1), // front
1100 v3s16(0,-1,0), // bottom
1101 v3s16(-1,0,0), // left
1103 for(u16 i=0; i<7; i++)
1108 v3s16 p2 = p + dirs[i];
1110 MapNode n2 = getNode(p2);
1111 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1113 m_transforming_liquid.push_back(p2);
1116 }catch(InvalidPositionException &e)
1124 void Map::removeNodeAndUpdate(v3s16 p,
1125 std::map<v3s16, MapBlock*> &modified_blocks)
1127 INodeDefManager *ndef = m_gamedef->ndef();
1129 /*PrintInfo(m_dout);
1130 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1131 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1133 bool node_under_sunlight = true;
1135 v3s16 toppos = p + v3s16(0,1,0);
1137 // Node will be replaced with this
1138 content_t replace_material = CONTENT_AIR;
1141 Collect old node for rollback
1143 RollbackNode rollback_oldnode(this, p, m_gamedef);
1146 If there is a node at top and it doesn't have sunlight,
1147 there will be no sunlight going down.
1150 MapNode topnode = getNode(toppos);
1152 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1153 node_under_sunlight = false;
1155 catch(InvalidPositionException &e)
1159 std::set<v3s16> light_sources;
1161 enum LightBank banks[] =
1166 for(s32 i=0; i<2; i++)
1168 enum LightBank bank = banks[i];
1171 Unlight neighbors (in case the node is a light source)
1173 unLightNeighbors(bank, p,
1174 getNode(p).getLight(bank, ndef),
1175 light_sources, modified_blocks);
1179 Remove node metadata
1182 removeNodeMetadata(p);
1186 This also clears the lighting.
1190 n.setContent(replace_material);
1193 for(s32 i=0; i<2; i++)
1195 enum LightBank bank = banks[i];
1198 Recalculate lighting
1200 spreadLight(bank, light_sources, modified_blocks);
1203 // Add the block of the removed node to modified_blocks
1204 v3s16 blockpos = getNodeBlockPos(p);
1205 MapBlock * block = getBlockNoCreate(blockpos);
1206 assert(block != NULL);
1207 modified_blocks[blockpos] = block;
1210 If the removed node was under sunlight, propagate the
1211 sunlight down from it and then light all neighbors
1212 of the propagated blocks.
1214 if(node_under_sunlight)
1216 s16 ybottom = propagateSunlight(p, modified_blocks);
1217 /*m_dout<<DTIME<<"Node was under sunlight. "
1218 "Propagating sunlight";
1219 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1221 for(; y >= ybottom; y--)
1223 v3s16 p2(p.X, y, p.Z);
1224 /*m_dout<<DTIME<<"lighting neighbors of node ("
1225 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1227 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1232 // Set the lighting of this node to 0
1233 // TODO: Is this needed? Lighting is cleared up there already.
1235 MapNode n = getNode(p);
1236 n.setLight(LIGHTBANK_DAY, 0, ndef);
1239 catch(InvalidPositionException &e)
1245 for(s32 i=0; i<2; i++)
1247 enum LightBank bank = banks[i];
1249 // Get the brightest neighbour node and propagate light from it
1250 v3s16 n2p = getBrightestNeighbour(bank, p);
1252 //MapNode n2 = getNode(n2p);
1253 lightNeighbors(bank, n2p, modified_blocks);
1255 catch(InvalidPositionException &e)
1261 Update information about whether day and night light differ
1263 for(std::map<v3s16, MapBlock*>::iterator
1264 i = modified_blocks.begin();
1265 i != modified_blocks.end(); ++i)
1267 i->second->expireDayNightDiff();
1273 if(m_gamedef->rollback())
1275 RollbackNode rollback_newnode(this, p, m_gamedef);
1276 RollbackAction action;
1277 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1278 m_gamedef->rollback()->reportAction(action);
1282 Add neighboring liquid nodes and this node to transform queue.
1283 (it's vital for the node itself to get updated last.)
1284 note: todo: for liquid_finite enough to add only self node
1287 v3s16(0,0,1), // back
1288 v3s16(0,1,0), // top
1289 v3s16(1,0,0), // right
1290 v3s16(0,0,-1), // front
1291 v3s16(0,-1,0), // bottom
1292 v3s16(-1,0,0), // left
1293 v3s16(0,0,0), // self
1295 for(u16 i=0; i<7; i++)
1300 v3s16 p2 = p + dirs[i];
1302 MapNode n2 = getNode(p2);
1303 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1305 m_transforming_liquid.push_back(p2);
1308 }catch(InvalidPositionException &e)
1314 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1317 event.type = MEET_ADDNODE;
1321 bool succeeded = true;
1323 std::map<v3s16, MapBlock*> modified_blocks;
1324 addNodeAndUpdate(p, n, modified_blocks);
1326 // Copy modified_blocks to event
1327 for(std::map<v3s16, MapBlock*>::iterator
1328 i = modified_blocks.begin();
1329 i != modified_blocks.end(); ++i)
1331 event.modified_blocks.insert(i->first);
1334 catch(InvalidPositionException &e){
1338 dispatchEvent(&event);
1343 bool Map::removeNodeWithEvent(v3s16 p)
1346 event.type = MEET_REMOVENODE;
1349 bool succeeded = true;
1351 std::map<v3s16, MapBlock*> modified_blocks;
1352 removeNodeAndUpdate(p, modified_blocks);
1354 // Copy modified_blocks to event
1355 for(std::map<v3s16, MapBlock*>::iterator
1356 i = modified_blocks.begin();
1357 i != modified_blocks.end(); ++i)
1359 event.modified_blocks.insert(i->first);
1362 catch(InvalidPositionException &e){
1366 dispatchEvent(&event);
1371 bool Map::getDayNightDiff(v3s16 blockpos)
1374 v3s16 p = blockpos + v3s16(0,0,0);
1375 MapBlock *b = getBlockNoCreate(p);
1376 if(b->getDayNightDiff())
1379 catch(InvalidPositionException &e){}
1382 v3s16 p = blockpos + v3s16(-1,0,0);
1383 MapBlock *b = getBlockNoCreate(p);
1384 if(b->getDayNightDiff())
1387 catch(InvalidPositionException &e){}
1389 v3s16 p = blockpos + v3s16(0,-1,0);
1390 MapBlock *b = getBlockNoCreate(p);
1391 if(b->getDayNightDiff())
1394 catch(InvalidPositionException &e){}
1396 v3s16 p = blockpos + v3s16(0,0,-1);
1397 MapBlock *b = getBlockNoCreate(p);
1398 if(b->getDayNightDiff())
1401 catch(InvalidPositionException &e){}
1404 v3s16 p = blockpos + v3s16(1,0,0);
1405 MapBlock *b = getBlockNoCreate(p);
1406 if(b->getDayNightDiff())
1409 catch(InvalidPositionException &e){}
1411 v3s16 p = blockpos + v3s16(0,1,0);
1412 MapBlock *b = getBlockNoCreate(p);
1413 if(b->getDayNightDiff())
1416 catch(InvalidPositionException &e){}
1418 v3s16 p = blockpos + v3s16(0,0,1);
1419 MapBlock *b = getBlockNoCreate(p);
1420 if(b->getDayNightDiff())
1423 catch(InvalidPositionException &e){}
1429 Updates usage timers
1431 void Map::timerUpdate(float dtime, float unload_timeout,
1432 std::list<v3s16> *unloaded_blocks)
1434 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1436 // Profile modified reasons
1437 Profiler modprofiler;
1439 std::list<v2s16> sector_deletion_queue;
1440 u32 deleted_blocks_count = 0;
1441 u32 saved_blocks_count = 0;
1442 u32 block_count_all = 0;
1445 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1446 si != m_sectors.end(); ++si)
1448 MapSector *sector = si->second;
1450 bool all_blocks_deleted = true;
1452 std::list<MapBlock*> blocks;
1453 sector->getBlocks(blocks);
1455 for(std::list<MapBlock*>::iterator i = blocks.begin();
1456 i != blocks.end(); ++i)
1458 MapBlock *block = (*i);
1460 block->incrementUsageTimer(dtime);
1462 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1464 v3s16 p = block->getPos();
1467 if(block->getModified() != MOD_STATE_CLEAN
1468 && save_before_unloading)
1470 modprofiler.add(block->getModifiedReason(), 1);
1472 saved_blocks_count++;
1475 // Delete from memory
1476 sector->deleteBlock(block);
1479 unloaded_blocks->push_back(p);
1481 deleted_blocks_count++;
1485 all_blocks_deleted = false;
1490 if(all_blocks_deleted)
1492 sector_deletion_queue.push_back(si->first);
1497 // Finally delete the empty sectors
1498 deleteSectors(sector_deletion_queue);
1500 if(deleted_blocks_count != 0)
1502 PrintInfo(infostream); // ServerMap/ClientMap:
1503 infostream<<"Unloaded "<<deleted_blocks_count
1504 <<" blocks from memory";
1505 if(save_before_unloading)
1506 infostream<<", of which "<<saved_blocks_count<<" were written";
1507 infostream<<", "<<block_count_all<<" blocks in memory";
1508 infostream<<"."<<std::endl;
1509 if(saved_blocks_count != 0){
1510 PrintInfo(infostream); // ServerMap/ClientMap:
1511 infostream<<"Blocks modified by: "<<std::endl;
1512 modprofiler.print(infostream);
1517 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1519 timerUpdate(0.0, -1.0, unloaded_blocks);
1522 void Map::deleteSectors(std::list<v2s16> &list)
1524 for(std::list<v2s16>::iterator j = list.begin();
1525 j != list.end(); ++j)
1527 MapSector *sector = m_sectors[*j];
1528 // If sector is in sector cache, remove it from there
1529 if(m_sector_cache == sector)
1530 m_sector_cache = NULL;
1531 // Remove from map and delete
1532 m_sectors.erase(*j);
1538 void Map::unloadUnusedData(float timeout,
1539 core::list<v3s16> *deleted_blocks)
1541 core::list<v2s16> sector_deletion_queue;
1542 u32 deleted_blocks_count = 0;
1543 u32 saved_blocks_count = 0;
1545 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1546 for(; si.atEnd() == false; si++)
1548 MapSector *sector = si.getNode()->getValue();
1550 bool all_blocks_deleted = true;
1552 core::list<MapBlock*> blocks;
1553 sector->getBlocks(blocks);
1554 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1555 i != blocks.end(); i++)
1557 MapBlock *block = (*i);
1559 if(block->getUsageTimer() > timeout)
1562 if(block->getModified() != MOD_STATE_CLEAN)
1565 saved_blocks_count++;
1567 // Delete from memory
1568 sector->deleteBlock(block);
1569 deleted_blocks_count++;
1573 all_blocks_deleted = false;
1577 if(all_blocks_deleted)
1579 sector_deletion_queue.push_back(si.getNode()->getKey());
1583 deleteSectors(sector_deletion_queue);
1585 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1586 <<", of which "<<saved_blocks_count<<" were wr."
1589 //return sector_deletion_queue.getSize();
1590 //return deleted_blocks_count;
1594 void Map::PrintInfo(std::ostream &out)
1599 #define WATER_DROP_BOOST 4
1603 NEIGHBOR_SAME_LEVEL,
1606 struct NodeNeighbor {
1610 bool l; //can liquid
1614 void Map::transforming_liquid_add(v3s16 p) {
1615 m_transforming_liquid.push_back(p);
1618 s32 Map::transforming_liquid_size() {
1619 return m_transforming_liquid.size();
1622 const v3s16 g_7dirs[7] =
1624 // +right, +top, +back
1625 v3s16( 0,-1, 0), // bottom
1626 v3s16( 0, 0, 0), // self
1627 v3s16( 0, 0, 1), // back
1628 v3s16( 0, 0,-1), // front
1629 v3s16( 1, 0, 0), // right
1630 v3s16(-1, 0, 0), // left
1631 v3s16( 0, 1, 0) // top
1638 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1640 INodeDefManager *nodemgr = m_gamedef->ndef();
1642 DSTACK(__FUNCTION_NAME);
1643 //TimeTaker timer("transformLiquids()");
1646 u32 initial_size = m_transforming_liquid.size();
1648 u8 relax = g_settings->getS16("liquid_relax");
1649 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1650 int water_level = g_settings->getS16("water_level");
1652 // list of nodes that due to viscosity have not reached their max level height
1653 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1655 // List of MapBlocks that will require a lighting update (due to lava)
1656 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1658 u16 loop_max = g_settings->getU16("liquid_loop_max");
1660 //if (m_transforming_liquid.size() > 0) errorstream << "Liquid queue size="<<m_transforming_liquid.size()<<std::endl;
1662 while (m_transforming_liquid.size() > 0)
1664 // This should be done here so that it is done when continue is used
1665 if (loopcount >= initial_size || loopcount >= loop_max)
1669 Get a queued transforming liquid node
1671 v3s16 p0 = m_transforming_liquid.pop_front();
1672 u16 total_level = 0;
1673 // surrounding flowing liquid nodes
1674 NodeNeighbor neighbors[7];
1675 // current level of every block
1676 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1678 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1679 s8 can_liquid_same_level = 0;
1680 content_t liquid_kind = CONTENT_IGNORE;
1681 content_t liquid_kind_flowing = CONTENT_IGNORE;
1683 Collect information about the environment
1685 const v3s16 *dirs = g_7dirs;
1686 for (u16 i = 0; i < 7; i++) {
1687 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1690 nt = NEIGHBOR_UPPER;
1693 nt = NEIGHBOR_LOWER;
1696 v3s16 npos = p0 + dirs[i];
1698 neighbors[i].n = getNodeNoEx(npos);
1699 neighbors[i].t = nt;
1700 neighbors[i].p = npos;
1703 NodeNeighbor & nb = neighbors[i];
1705 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1707 if (nb.n.getContent() == CONTENT_AIR) {
1708 liquid_levels[i] = 0;
1713 // if this node is not (yet) of a liquid type,
1714 // choose the first liquid type we encounter
1715 if (liquid_kind_flowing == CONTENT_IGNORE)
1716 liquid_kind_flowing = nodemgr->getId(
1717 nodemgr->get(nb.n).liquid_alternative_flowing);
1718 if (liquid_kind == CONTENT_IGNORE)
1719 liquid_kind = nb.n.getContent();
1720 if (nb.n.getContent() == liquid_kind) {
1721 liquid_levels[i] = nb.n.getLevel(nodemgr); //LIQUID_LEVEL_SOURCE;
1723 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1726 case LIQUID_FLOWING:
1727 // if this node is not (yet) of a liquid type,
1728 // choose the first liquid type we encounter
1729 if (liquid_kind_flowing == CONTENT_IGNORE)
1730 liquid_kind_flowing = nb.n.getContent();
1731 if (liquid_kind == CONTENT_IGNORE)
1732 liquid_kind = nodemgr->getId(
1733 nodemgr->get(nb.n).liquid_alternative_source);
1734 if (nb.n.getContent() == liquid_kind_flowing) {
1735 liquid_levels[i] = nb.n.getLevel(nodemgr); //(nb.n.param2 & LIQUID_LEVEL_MASK);
1741 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1742 ++can_liquid_same_level;
1743 if (liquid_levels[i] > 0)
1744 total_level += liquid_levels[i];
1747 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1748 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1749 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1750 << nodemgr->get(nb.n.getContent()).liquid_type
1751 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1752 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1753 << " tlevel=" << (int)total_level << " cansame="
1754 << (int)can_liquid_same_level << std::endl;
1758 if (liquid_kind == CONTENT_IGNORE ||
1759 !neighbors[D_SELF].l ||
1763 // fill bottom block
1764 if (neighbors[D_BOTTOM].l) {
1765 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1766 LIQUID_LEVEL_SOURCE : total_level;
1767 total_level -= liquid_levels_want[D_BOTTOM];
1771 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1772 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1773 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1774 (can_liquid_same_level - relax) &&
1775 can_liquid_same_level >= relax + 1) {
1776 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1779 // prevent lakes in air above unloaded blocks
1780 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE && !(loopcount % 3)) {
1784 // calculate self level 5 blocks
1786 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1787 ? LIQUID_LEVEL_SOURCE
1788 : total_level / can_liquid_same_level;
1789 total_level -= want_level * can_liquid_same_level;
1792 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1793 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1794 total_level <= (can_liquid_same_level - relax) &&
1795 can_liquid_same_level >= relax + 1) {
1799 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1800 if (!neighbors[ii].l)
1802 liquid_levels_want[ii] = want_level;
1803 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0) {
1804 if (loopcount % 3 || liquid_levels[ii] <= 0){
1805 if (liquid_levels[ii] > liquid_levels_want[ii]) {
1806 ++liquid_levels_want[ii];
1809 } else if (neighbors[ii].l > 0){
1810 ++liquid_levels_want[ii];
1816 for (u16 ii = 0; ii < 7; ++ii) {
1817 if (total_level < 1) break;
1818 if (liquid_levels_want[ii] >= 0 &&
1819 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1820 ++liquid_levels_want[ii];
1825 // fill top block if can
1826 if (neighbors[D_TOP].l) {
1827 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1828 LIQUID_LEVEL_SOURCE : total_level;
1829 total_level -= liquid_levels_want[D_TOP];
1832 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1833 if ( neighbors[ii].i ||
1834 (liquid_levels_want[ii] >= 0 &&
1835 (fast_flood && p0.Y < water_level &&
1836 (initial_size >= 1000
1838 && want_level >= LIQUID_LEVEL_SOURCE/4
1839 && can_liquid_same_level >= 5
1840 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1841 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1844 if (total_level > 0) //|| flowed != volume)
1845 infostream <<" AFTER level=" << (int)total_level
1846 //<< " flowed="<<flowed<< " volume=" << volume
1847 << " wantsame="<<(int)want_level<< " top="
1848 << (int)liquid_levels_want[D_TOP]<< " topwas="
1849 << (int)liquid_levels[D_TOP]<< " bot="
1850 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1854 for (u16 i = 0; i < 7; i++) {
1855 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1857 MapNode & n0 = neighbors[i].n;
1858 p0 = neighbors[i].p;
1860 decide on the type (and possibly level) of the current node
1862 content_t new_node_content;
1863 s8 new_node_level = -1;
1864 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1865 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1866 // amount to gain, limited by viscosity
1867 // must be at least 1 in absolute value
1868 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1869 if (level_inc < -viscosity || level_inc > viscosity)
1870 new_node_level = liquid_levels[i] + level_inc/viscosity;
1871 else if (level_inc < 0)
1872 new_node_level = liquid_levels[i] - 1;
1873 else if (level_inc > 0)
1874 new_node_level = liquid_levels[i] + 1;
1876 new_node_level = liquid_levels_want[i];
1879 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1880 new_node_content = liquid_kind;
1881 else if (new_node_level > 0)
1882 new_node_content = liquid_kind_flowing;
1884 new_node_content = CONTENT_AIR;
1886 // last level must flow down on stairs
1887 if (liquid_levels_want[i] != liquid_levels[i] &&
1888 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1889 new_node_level >= 1 && new_node_level <= 2) {
1890 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1891 if (neighbors[ii].l)
1892 must_reflow_second.push_back(p0 + dirs[ii]);
1897 check if anything has changed.
1898 if not, just continue with the next node.
1902 new_node_content == n0.getContent()
1903 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1904 (n0.getLevel(nodemgr) == (u8)new_node_level
1905 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1906 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1909 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1910 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1911 LIQUID_INFINITY_MASK) == neighbors[i].i
1914 if (liquid_levels[i] == new_node_level)
1922 update the current node
1925 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1926 // set level to last 3 bits, flowing down bit to 4th bit
1927 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1928 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1929 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1930 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1934 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1935 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1936 <<(int)new_node_level<<std::endl;
1939 n0.setContent(liquid_kind_flowing);
1940 n0.setLevel(nodemgr, new_node_level);
1941 // Find out whether there is a suspect for this action
1942 std::string suspect;
1943 if(m_gamedef->rollback()){
1944 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1947 if(!suspect.empty()){
1949 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1950 // Get old node for rollback
1951 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1955 RollbackNode rollback_newnode(this, p0, m_gamedef);
1956 RollbackAction action;
1957 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1958 m_gamedef->rollback()->reportAction(action);
1964 v3s16 blockpos = getNodeBlockPos(p0);
1965 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1967 modified_blocks[blockpos] = block;
1968 // If node emits light, MapBlock requires lighting update
1969 if(nodemgr->get(n0).light_source != 0)
1970 lighting_modified_blocks[block->getPos()] = block;
1972 must_reflow.push_back(neighbors[i].p);
1974 /* //for better relax only same level
1975 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1976 if (!neighbors[ii].l) continue;
1977 must_reflow.push_back(p0 + dirs[ii]);
1982 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1983 <<" reflow="<<must_reflow.size()
1984 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1986 while (must_reflow.size() > 0)
1987 m_transforming_liquid.push_back(must_reflow.pop_front());
1988 while (must_reflow_second.size() > 0)
1989 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1990 updateLighting(lighting_modified_blocks, modified_blocks);
1993 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1996 if (g_settings->getBool("liquid_finite"))
1997 return Map::transformLiquidsFinite(modified_blocks);
1999 INodeDefManager *nodemgr = m_gamedef->ndef();
2001 DSTACK(__FUNCTION_NAME);
2002 //TimeTaker timer("transformLiquids()");
2005 u32 initial_size = m_transforming_liquid.size();
2007 /*if(initial_size != 0)
2008 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
2010 // list of nodes that due to viscosity have not reached their max level height
2011 UniqueQueue<v3s16> must_reflow;
2013 // List of MapBlocks that will require a lighting update (due to lava)
2014 std::map<v3s16, MapBlock*> lighting_modified_blocks;
2016 u16 loop_max = g_settings->getU16("liquid_loop_max");
2018 while(m_transforming_liquid.size() != 0)
2020 // This should be done here so that it is done when continue is used
2021 if(loopcount >= initial_size || loopcount >= loop_max)
2026 Get a queued transforming liquid node
2028 v3s16 p0 = m_transforming_liquid.pop_front();
2030 MapNode n0 = getNodeNoEx(p0);
2033 Collect information about current node
2035 s8 liquid_level = -1;
2036 content_t liquid_kind = CONTENT_IGNORE;
2037 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2038 switch (liquid_type) {
2040 liquid_level = LIQUID_LEVEL_SOURCE;
2041 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2043 case LIQUID_FLOWING:
2044 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2045 liquid_kind = n0.getContent();
2048 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2049 // continue with the next node.
2050 if (n0.getContent() != CONTENT_AIR)
2052 liquid_kind = CONTENT_AIR;
2057 Collect information about the environment
2059 const v3s16 *dirs = g_6dirs;
2060 NodeNeighbor sources[6]; // surrounding sources
2061 int num_sources = 0;
2062 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2064 NodeNeighbor airs[6]; // surrounding air
2066 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2067 int num_neutrals = 0;
2068 bool flowing_down = false;
2069 for (u16 i = 0; i < 6; i++) {
2070 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2073 nt = NEIGHBOR_UPPER;
2076 nt = NEIGHBOR_LOWER;
2079 v3s16 npos = p0 + dirs[i];
2080 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2081 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2083 if (nb.n.getContent() == CONTENT_AIR) {
2084 airs[num_airs++] = nb;
2085 // if the current node is a water source the neighbor
2086 // should be enqueded for transformation regardless of whether the
2087 // current node changes or not.
2088 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2089 m_transforming_liquid.push_back(npos);
2090 // if the current node happens to be a flowing node, it will start to flow down here.
2091 if (nb.t == NEIGHBOR_LOWER) {
2092 flowing_down = true;
2095 neutrals[num_neutrals++] = nb;
2099 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2100 if (liquid_kind == CONTENT_AIR)
2101 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2102 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2103 neutrals[num_neutrals++] = nb;
2105 // Do not count bottom source, it will screw things up
2107 sources[num_sources++] = nb;
2110 case LIQUID_FLOWING:
2111 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2112 if (liquid_kind == CONTENT_AIR)
2113 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2114 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2115 neutrals[num_neutrals++] = nb;
2117 flows[num_flows++] = nb;
2118 if (nb.t == NEIGHBOR_LOWER)
2119 flowing_down = true;
2126 decide on the type (and possibly level) of the current node
2128 content_t new_node_content;
2129 s8 new_node_level = -1;
2130 s8 max_node_level = -1;
2131 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2132 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2133 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2134 // it's perfectly safe to use liquid_kind here to determine the new node content.
2135 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2136 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2137 // liquid_kind is set properly, see above
2138 new_node_content = liquid_kind;
2139 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2141 // no surrounding sources, so get the maximum level that can flow into this node
2142 for (u16 i = 0; i < num_flows; i++) {
2143 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2144 switch (flows[i].t) {
2145 case NEIGHBOR_UPPER:
2146 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2147 max_node_level = LIQUID_LEVEL_MAX;
2148 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2149 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2150 } else if (nb_liquid_level > max_node_level)
2151 max_node_level = nb_liquid_level;
2153 case NEIGHBOR_LOWER:
2155 case NEIGHBOR_SAME_LEVEL:
2156 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2157 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2158 max_node_level = nb_liquid_level - 1;
2164 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2165 if (viscosity > 1 && max_node_level != liquid_level) {
2166 // amount to gain, limited by viscosity
2167 // must be at least 1 in absolute value
2168 s8 level_inc = max_node_level - liquid_level;
2169 if (level_inc < -viscosity || level_inc > viscosity)
2170 new_node_level = liquid_level + level_inc/viscosity;
2171 else if (level_inc < 0)
2172 new_node_level = liquid_level - 1;
2173 else if (level_inc > 0)
2174 new_node_level = liquid_level + 1;
2175 if (new_node_level != max_node_level)
2176 must_reflow.push_back(p0);
2178 new_node_level = max_node_level;
2180 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
2181 if (new_node_level >= (LIQUID_LEVEL_MAX+1-range))
2182 new_node_content = liquid_kind;
2184 new_node_content = CONTENT_AIR;
2189 check if anything has changed. if not, just continue with the next node.
2191 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2192 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2193 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2199 update the current node
2202 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2203 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2204 // set level to last 3 bits, flowing down bit to 4th bit
2205 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2207 // set the liquid level and flow bit to 0
2208 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2210 n0.setContent(new_node_content);
2212 // Find out whether there is a suspect for this action
2213 std::string suspect;
2214 if(m_gamedef->rollback()){
2215 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2218 if(!suspect.empty()){
2220 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2221 // Get old node for rollback
2222 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2226 RollbackNode rollback_newnode(this, p0, m_gamedef);
2227 RollbackAction action;
2228 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2229 m_gamedef->rollback()->reportAction(action);
2235 v3s16 blockpos = getNodeBlockPos(p0);
2236 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2238 modified_blocks[blockpos] = block;
2239 // If new or old node emits light, MapBlock requires lighting update
2240 if(nodemgr->get(n0).light_source != 0 ||
2241 nodemgr->get(n00).light_source != 0)
2242 lighting_modified_blocks[block->getPos()] = block;
2246 enqueue neighbors for update if neccessary
2248 switch (nodemgr->get(n0.getContent()).liquid_type) {
2250 case LIQUID_FLOWING:
2251 // make sure source flows into all neighboring nodes
2252 for (u16 i = 0; i < num_flows; i++)
2253 if (flows[i].t != NEIGHBOR_UPPER)
2254 m_transforming_liquid.push_back(flows[i].p);
2255 for (u16 i = 0; i < num_airs; i++)
2256 if (airs[i].t != NEIGHBOR_UPPER)
2257 m_transforming_liquid.push_back(airs[i].p);
2260 // this flow has turned to air; neighboring flows might need to do the same
2261 for (u16 i = 0; i < num_flows; i++)
2262 m_transforming_liquid.push_back(flows[i].p);
2266 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2267 while (must_reflow.size() > 0)
2268 m_transforming_liquid.push_back(must_reflow.pop_front());
2269 updateLighting(lighting_modified_blocks, modified_blocks);
2272 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2274 v3s16 blockpos = getNodeBlockPos(p);
2275 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2276 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2278 infostream<<"Map::getNodeMetadata(): Need to emerge "
2279 <<PP(blockpos)<<std::endl;
2280 block = emergeBlock(blockpos, false);
2284 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2288 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2292 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2294 v3s16 blockpos = getNodeBlockPos(p);
2295 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2296 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2298 infostream<<"Map::setNodeMetadata(): Need to emerge "
2299 <<PP(blockpos)<<std::endl;
2300 block = emergeBlock(blockpos, false);
2304 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2308 block->m_node_metadata.set(p_rel, meta);
2311 void Map::removeNodeMetadata(v3s16 p)
2313 v3s16 blockpos = getNodeBlockPos(p);
2314 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2315 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2318 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2322 block->m_node_metadata.remove(p_rel);
2325 NodeTimer Map::getNodeTimer(v3s16 p)
2327 v3s16 blockpos = getNodeBlockPos(p);
2328 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2329 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2331 infostream<<"Map::getNodeTimer(): Need to emerge "
2332 <<PP(blockpos)<<std::endl;
2333 block = emergeBlock(blockpos, false);
2337 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2341 NodeTimer t = block->m_node_timers.get(p_rel);
2345 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2347 v3s16 blockpos = getNodeBlockPos(p);
2348 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2349 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2351 infostream<<"Map::setNodeTimer(): Need to emerge "
2352 <<PP(blockpos)<<std::endl;
2353 block = emergeBlock(blockpos, false);
2357 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2361 block->m_node_timers.set(p_rel, t);
2364 void Map::removeNodeTimer(v3s16 p)
2366 v3s16 blockpos = getNodeBlockPos(p);
2367 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2368 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2371 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2375 block->m_node_timers.remove(p_rel);
2378 s16 Map::getHeat(v3s16 p)
2380 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2384 //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2388 s16 Map::getHumidity(v3s16 p)
2390 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2392 return block->humidity;
2394 //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2401 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2402 Map(dout_server, gamedef),
2404 m_map_metadata_changed(true),
2406 m_database_read(NULL),
2407 m_database_write(NULL)
2409 verbosestream<<__FUNCTION_NAME<<std::endl;
2412 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2414 m_mgparams = new MapgenV6Params();
2416 m_seed = m_mgparams->seed;
2418 if (g_settings->get("fixed_map_seed").empty())
2420 m_seed = (((u64)(myrand() & 0xffff) << 0)
2421 | ((u64)(myrand() & 0xffff) << 16)
2422 | ((u64)(myrand() & 0xffff) << 32)
2423 | ((u64)(myrand() & 0xffff) << 48));
2424 m_mgparams->seed = m_seed;
2428 Experimental and debug stuff
2435 Try to load map; if not found, create a new one.
2438 m_savedir = savedir;
2439 m_map_saving_enabled = false;
2443 // If directory exists, check contents and load if possible
2444 if(fs::PathExists(m_savedir))
2446 // If directory is empty, it is safe to save into it.
2447 if(fs::GetDirListing(m_savedir).size() == 0)
2449 infostream<<"ServerMap: Empty save directory is valid."
2451 m_map_saving_enabled = true;
2456 // Load map metadata (seed, chunksize)
2459 catch(SettingNotFoundException &e){
2460 infostream<<"ServerMap: Some metadata not found."
2461 <<" Using default settings."<<std::endl;
2463 catch(FileNotGoodException &e){
2464 infostream<<"WARNING: Could not load map metadata"
2465 //<<" Disabling chunk-based generator."
2470 infostream<<"ServerMap: Successfully loaded map "
2471 <<"metadata from "<<savedir
2472 <<", assuming valid save directory."
2473 <<" seed="<<m_seed<<"."
2476 m_map_saving_enabled = true;
2477 // Map loaded, not creating new one
2481 // If directory doesn't exist, it is safe to save to it
2483 m_map_saving_enabled = true;
2486 catch(std::exception &e)
2488 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2489 <<", exception: "<<e.what()<<std::endl;
2490 infostream<<"Please remove the map or fix it."<<std::endl;
2491 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2494 infostream<<"Initializing new map."<<std::endl;
2496 // Create zero sector
2497 emergeSector(v2s16(0,0));
2499 // Initially write whole map
2500 save(MOD_STATE_CLEAN);
2503 ServerMap::~ServerMap()
2505 verbosestream<<__FUNCTION_NAME<<std::endl;
2509 if(m_map_saving_enabled)
2511 // Save only changed parts
2512 save(MOD_STATE_WRITE_AT_UNLOAD);
2513 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2517 infostream<<"ServerMap: Map not saved"<<std::endl;
2520 catch(std::exception &e)
2522 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2523 <<", exception: "<<e.what()<<std::endl;
2527 Close database if it was opened
2530 sqlite3_finalize(m_database_read);
2531 if(m_database_write)
2532 sqlite3_finalize(m_database_write);
2534 sqlite3_close(m_database);
2540 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2541 for(; i.atEnd() == false; i++)
2543 MapChunk *chunk = i.getNode()->getValue();
2551 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2553 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2554 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2556 s16 chunksize = m_mgparams->chunksize;
2557 s16 coffset = -chunksize / 2;
2558 v3s16 chunk_offset(coffset, coffset, coffset);
2559 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2560 v3s16 blockpos_min = blockpos_div * chunksize;
2561 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2562 blockpos_min += chunk_offset;
2563 blockpos_max += chunk_offset;
2565 v3s16 extra_borders(1,1,1);
2567 // Do nothing if not inside limits (+-1 because of neighbors)
2568 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2569 blockpos_over_limit(blockpos_max + extra_borders))
2572 data->seed = m_seed;
2573 data->blockpos_min = blockpos_min;
2574 data->blockpos_max = blockpos_max;
2575 data->blockpos_requested = blockpos;
2576 data->nodedef = m_gamedef->ndef();
2579 Create the whole area of this and the neighboring blocks
2582 //TimeTaker timer("initBlockMake() create area");
2584 for(s16 x=blockpos_min.X-extra_borders.X;
2585 x<=blockpos_max.X+extra_borders.X; x++)
2586 for(s16 z=blockpos_min.Z-extra_borders.Z;
2587 z<=blockpos_max.Z+extra_borders.Z; z++)
2589 v2s16 sectorpos(x, z);
2590 // Sector metadata is loaded from disk if not already loaded.
2591 ServerMapSector *sector = createSector(sectorpos);
2594 for(s16 y=blockpos_min.Y-extra_borders.Y;
2595 y<=blockpos_max.Y+extra_borders.Y; y++)
2598 //MapBlock *block = createBlock(p);
2599 // 1) get from memory, 2) load from disk
2600 MapBlock *block = emergeBlock(p, false);
2601 // 3) create a blank one
2604 block = createBlock(p);
2607 Block gets sunlight if this is true.
2609 Refer to the map generator heuristics.
2611 bool ug = m_emerge->isBlockUnderground(p);
2612 block->setIsUnderground(ug);
2615 // Lighting will not be valid after make_chunk is called
2616 block->setLightingExpired(true);
2617 // Lighting will be calculated
2618 //block->setLightingExpired(false);
2624 Now we have a big empty area.
2626 Make a ManualMapVoxelManipulator that contains this and the
2630 // The area that contains this block and it's neighbors
2631 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2632 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2634 data->vmanip = new ManualMapVoxelManipulator(this);
2635 //data->vmanip->setMap(this);
2639 //TimeTaker timer("initBlockMake() initialEmerge");
2640 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2643 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2644 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2645 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2646 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2647 core::map<v3s16, u8>::Node *n;
2648 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2651 u8 flags = n->getValue();
2652 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2658 // Data is ready now.
2662 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2663 std::map<v3s16, MapBlock*> &changed_blocks)
2665 v3s16 blockpos_min = data->blockpos_min;
2666 v3s16 blockpos_max = data->blockpos_max;
2667 v3s16 blockpos_requested = data->blockpos_requested;
2668 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2669 <<blockpos_requested.Y<<","
2670 <<blockpos_requested.Z<<")"<<std::endl;*/
2672 v3s16 extra_borders(1,1,1);
2674 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2676 /*infostream<<"Resulting vmanip:"<<std::endl;
2677 data->vmanip.print(infostream);*/
2679 // Make sure affected blocks are loaded
2680 for(s16 x=blockpos_min.X-extra_borders.X;
2681 x<=blockpos_max.X+extra_borders.X; x++)
2682 for(s16 z=blockpos_min.Z-extra_borders.Z;
2683 z<=blockpos_max.Z+extra_borders.Z; z++)
2684 for(s16 y=blockpos_min.Y-extra_borders.Y;
2685 y<=blockpos_max.Y+extra_borders.Y; y++)
2688 // Load from disk if not already in memory
2689 emergeBlock(p, false);
2693 Blit generated stuff to map
2694 NOTE: blitBackAll adds nearly everything to changed_blocks
2698 //TimeTaker timer("finishBlockMake() blitBackAll");
2699 data->vmanip->blitBackAll(&changed_blocks);
2702 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2705 Copy transforming liquid information
2707 while(data->transforming_liquid.size() > 0)
2709 v3s16 p = data->transforming_liquid.pop_front();
2710 m_transforming_liquid.push_back(p);
2714 Do stuff in central blocks
2722 TimeTaker t("finishBlockMake lighting update");
2724 core::map<v3s16, MapBlock*> lighting_update_blocks;
2727 for(s16 x=blockpos_min.X-extra_borders.X;
2728 x<=blockpos_max.X+extra_borders.X; x++)
2729 for(s16 z=blockpos_min.Z-extra_borders.Z;
2730 z<=blockpos_max.Z+extra_borders.Z; z++)
2731 for(s16 y=blockpos_min.Y-extra_borders.Y;
2732 y<=blockpos_max.Y+extra_borders.Y; y++)
2735 MapBlock *block = getBlockNoCreateNoEx(p);
2737 lighting_update_blocks.insert(block->getPos(), block);
2740 updateLighting(lighting_update_blocks, changed_blocks);
2744 Set lighting to non-expired state in all of them.
2745 This is cheating, but it is not fast enough if all of them
2746 would actually be updated.
2748 for(s16 x=blockpos_min.X-extra_borders.X;
2749 x<=blockpos_max.X+extra_borders.X; x++)
2750 for(s16 z=blockpos_min.Z-extra_borders.Z;
2751 z<=blockpos_max.Z+extra_borders.Z; z++)
2752 for(s16 y=blockpos_min.Y-extra_borders.Y;
2753 y<=blockpos_max.Y+extra_borders.Y; y++)
2756 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2760 if(enable_mapgen_debug_info == false)
2761 t.stop(true); // Hide output
2766 Go through changed blocks
2768 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2769 i != changed_blocks.end(); ++i)
2771 MapBlock *block = i->second;
2774 Update day/night difference cache of the MapBlocks
2776 block->expireDayNightDiff();
2778 Set block as modified
2780 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2781 "finishBlockMake expireDayNightDiff");
2785 Set central blocks as generated
2787 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2788 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2789 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2792 MapBlock *block = getBlockNoCreateNoEx(p);
2794 block->setGenerated(true);
2798 Save changed parts of map
2799 NOTE: Will be saved later.
2801 //save(MOD_STATE_WRITE_AT_UNLOAD);
2803 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2804 <<","<<blockpos_requested.Y<<","
2805 <<blockpos_requested.Z<<")"<<std::endl;*/
2807 if(enable_mapgen_debug_info)
2810 Analyze resulting blocks
2812 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2813 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2814 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2815 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2816 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2817 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2819 v3s16 p = v3s16(x,y,z);
2820 MapBlock *block = getBlockNoCreateNoEx(p);
2822 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2823 infostream<<"Generated "<<spos<<": "
2824 <<analyze_block(block)<<std::endl;
2829 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2835 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2837 DSTACKF("%s: p2d=(%d,%d)",
2842 Check if it exists already in memory
2844 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2849 Try to load it from disk (with blocks)
2851 //if(loadSectorFull(p2d) == true)
2854 Try to load metadata from disk
2857 if(loadSectorMeta(p2d) == true)
2859 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2862 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2863 throw InvalidPositionException("");
2869 Do not create over-limit
2871 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2872 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2873 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2874 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2875 throw InvalidPositionException("createSector(): pos. over limit");
2878 Generate blank sector
2881 sector = new ServerMapSector(this, p2d, m_gamedef);
2883 // Sector position on map in nodes
2884 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2889 m_sectors[p2d] = sector;
2896 This is a quick-hand function for calling makeBlock().
2898 MapBlock * ServerMap::generateBlock(
2900 std::map<v3s16, MapBlock*> &modified_blocks
2903 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2905 /*infostream<<"generateBlock(): "
2906 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2909 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2911 TimeTaker timer("generateBlock");
2913 //MapBlock *block = original_dummy;
2915 v2s16 p2d(p.X, p.Z);
2916 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2919 Do not generate over-limit
2921 if(blockpos_over_limit(p))
2923 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2924 throw InvalidPositionException("generateBlock(): pos. over limit");
2928 Create block make data
2931 initBlockMake(&data, p);
2937 TimeTaker t("mapgen::make_block()");
2938 mapgen->makeChunk(&data);
2939 //mapgen::make_block(&data);
2941 if(enable_mapgen_debug_info == false)
2942 t.stop(true); // Hide output
2946 Blit data back on map, update lighting, add mobs and whatever this does
2948 finishBlockMake(&data, modified_blocks);
2953 MapBlock *block = getBlockNoCreateNoEx(p);
2961 bool erroneus_content = false;
2962 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2963 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2964 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2967 MapNode n = block->getNode(p);
2968 if(n.getContent() == CONTENT_IGNORE)
2970 infostream<<"CONTENT_IGNORE at "
2971 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2973 erroneus_content = true;
2977 if(erroneus_content)
2986 Generate a completely empty block
2990 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2991 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2993 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2996 n.setContent(CONTENT_AIR);
2997 block->setNode(v3s16(x0,y0,z0), n);
3003 if(enable_mapgen_debug_info == false)
3004 timer.stop(true); // Hide output
3010 MapBlock * ServerMap::createBlock(v3s16 p)
3012 DSTACKF("%s: p=(%d,%d,%d)",
3013 __FUNCTION_NAME, p.X, p.Y, p.Z);
3016 Do not create over-limit
3018 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3019 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3020 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3021 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3022 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3023 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3024 throw InvalidPositionException("createBlock(): pos. over limit");
3026 v2s16 p2d(p.X, p.Z);
3029 This will create or load a sector if not found in memory.
3030 If block exists on disk, it will be loaded.
3032 NOTE: On old save formats, this will be slow, as it generates
3033 lighting on blocks for them.
3035 ServerMapSector *sector;
3037 sector = (ServerMapSector*)createSector(p2d);
3038 assert(sector->getId() == MAPSECTOR_SERVER);
3040 catch(InvalidPositionException &e)
3042 infostream<<"createBlock: createSector() failed"<<std::endl;
3046 NOTE: This should not be done, or at least the exception
3047 should not be passed on as std::exception, because it
3048 won't be catched at all.
3050 /*catch(std::exception &e)
3052 infostream<<"createBlock: createSector() failed: "
3053 <<e.what()<<std::endl;
3058 Try to get a block from the sector
3061 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3064 if(block->isDummy())
3069 block = sector->createBlankBlock(block_y);
3074 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3076 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3078 p.X, p.Y, p.Z, create_blank);
3081 MapBlock *block = getBlockNoCreateNoEx(p);
3082 if(block && block->isDummy() == false)
3087 MapBlock *block = loadBlock(p);
3093 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3094 MapBlock *block = sector->createBlankBlock(p.Y);
3098 /*if(allow_generate)
3100 std::map<v3s16, MapBlock*> modified_blocks;
3101 MapBlock *block = generateBlock(p, modified_blocks);
3105 event.type = MEET_OTHER;
3108 // Copy modified_blocks to event
3109 for(std::map<v3s16, MapBlock*>::iterator
3110 i = modified_blocks.begin();
3111 i != modified_blocks.end(); ++i)
3113 event.modified_blocks.insert(i->first);
3117 dispatchEvent(&event);
3126 s16 ServerMap::findGroundLevel(v2s16 p2d)
3130 Uh, just do something random...
3132 // Find existing map from top to down
3135 v3s16 p(p2d.X, max, p2d.Y);
3136 for(; p.Y>min; p.Y--)
3138 MapNode n = getNodeNoEx(p);
3139 if(n.getContent() != CONTENT_IGNORE)
3144 // If this node is not air, go to plan b
3145 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3147 // Search existing walkable and return it
3148 for(; p.Y>min; p.Y--)
3150 MapNode n = getNodeNoEx(p);
3151 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3160 Determine from map generator noise functions
3163 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3166 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3167 //return (s16)level;
3170 void ServerMap::createDatabase() {
3173 e = sqlite3_exec(m_database,
3174 "CREATE TABLE IF NOT EXISTS `blocks` ("
3175 "`pos` INT NOT NULL PRIMARY KEY,"
3178 , NULL, NULL, NULL);
3179 if(e == SQLITE_ABORT)
3180 throw FileNotGoodException("Could not create database structure");
3182 infostream<<"ServerMap: Database structure was created";
3185 void ServerMap::verifyDatabase() {
3190 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3191 bool needs_create = false;
3195 Open the database connection
3198 createDirs(m_savedir);
3200 if(!fs::PathExists(dbp))
3201 needs_create = true;
3203 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3204 if(d != SQLITE_OK) {
3205 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3206 throw FileNotGoodException("Cannot open database file");
3212 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3213 if(d != SQLITE_OK) {
3214 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3215 throw FileNotGoodException("Cannot prepare read statement");
3218 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3219 if(d != SQLITE_OK) {
3220 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3221 throw FileNotGoodException("Cannot prepare write statement");
3224 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3225 if(d != SQLITE_OK) {
3226 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3227 throw FileNotGoodException("Cannot prepare read statement");
3230 infostream<<"ServerMap: Database opened"<<std::endl;
3234 bool ServerMap::loadFromFolders() {
3235 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3240 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3241 return (sqlite3_int64)pos.Z*16777216 +
3242 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3245 void ServerMap::createDirs(std::string path)
3247 if(fs::CreateAllDirs(path) == false)
3249 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3250 <<"\""<<path<<"\""<<std::endl;
3251 throw BaseException("ServerMap failed to create directory");
3255 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3261 snprintf(cc, 9, "%.4x%.4x",
3262 (unsigned int)pos.X&0xffff,
3263 (unsigned int)pos.Y&0xffff);
3265 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3267 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3268 (unsigned int)pos.X&0xfff,
3269 (unsigned int)pos.Y&0xfff);
3271 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3277 v2s16 ServerMap::getSectorPos(std::string dirname)
3281 std::string component;
3282 fs::RemoveLastPathComponent(dirname, &component, 1);
3283 if(component.size() == 8)
3286 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3288 else if(component.size() == 3)
3291 fs::RemoveLastPathComponent(dirname, &component, 2);
3292 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3293 // Sign-extend the 12 bit values up to 16 bits...
3294 if(x&0x800) x|=0xF000;
3295 if(y&0x800) y|=0xF000;
3302 v2s16 pos((s16)x, (s16)y);
3306 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3308 v2s16 p2d = getSectorPos(sectordir);
3310 if(blockfile.size() != 4){
3311 throw InvalidFilenameException("Invalid block filename");
3314 int r = sscanf(blockfile.c_str(), "%4x", &y);
3316 throw InvalidFilenameException("Invalid block filename");
3317 return v3s16(p2d.X, y, p2d.Y);
3320 std::string ServerMap::getBlockFilename(v3s16 p)
3323 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3327 void ServerMap::save(ModifiedState save_level)
3329 DSTACK(__FUNCTION_NAME);
3330 if(m_map_saving_enabled == false)
3332 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3336 if(save_level == MOD_STATE_CLEAN)
3337 infostream<<"ServerMap: Saving whole map, this can take time."
3340 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3345 // Profile modified reasons
3346 Profiler modprofiler;
3348 u32 sector_meta_count = 0;
3349 u32 block_count = 0;
3350 u32 block_count_all = 0; // Number of blocks in memory
3352 // Don't do anything with sqlite unless something is really saved
3353 bool save_started = false;
3355 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3356 i != m_sectors.end(); ++i)
3358 ServerMapSector *sector = (ServerMapSector*)i->second;
3359 assert(sector->getId() == MAPSECTOR_SERVER);
3361 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3363 saveSectorMeta(sector);
3364 sector_meta_count++;
3366 std::list<MapBlock*> blocks;
3367 sector->getBlocks(blocks);
3369 for(std::list<MapBlock*>::iterator j = blocks.begin();
3370 j != blocks.end(); ++j)
3372 MapBlock *block = *j;
3376 if(block->getModified() >= (u32)save_level)
3381 save_started = true;
3384 modprofiler.add(block->getModifiedReason(), 1);
3389 /*infostream<<"ServerMap: Written block ("
3390 <<block->getPos().X<<","
3391 <<block->getPos().Y<<","
3392 <<block->getPos().Z<<")"
3401 Only print if something happened or saved whole map
3403 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3404 || block_count != 0)
3406 infostream<<"ServerMap: Written: "
3407 <<sector_meta_count<<" sector metadata files, "
3408 <<block_count<<" block files"
3409 <<", "<<block_count_all<<" blocks in memory."
3411 PrintInfo(infostream); // ServerMap/ClientMap:
3412 infostream<<"Blocks modified by: "<<std::endl;
3413 modprofiler.print(infostream);
3417 static s32 unsignedToSigned(s32 i, s32 max_positive)
3419 if(i < max_positive)
3422 return i - 2*max_positive;
3425 // modulo of a negative number does not work consistently in C
3426 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3430 return mod - ((-i) % mod);
3433 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3435 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3437 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3439 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3440 return v3s16(x,y,z);
3443 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3445 if(loadFromFolders()){
3446 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3447 <<"all blocks that are stored in flat files"<<std::endl;
3453 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3455 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3456 v3s16 p = getIntegerAsBlock(block_i);
3457 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3463 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3465 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3466 si != m_sectors.end(); ++si)
3468 MapSector *sector = si->second;
3470 std::list<MapBlock*> blocks;
3471 sector->getBlocks(blocks);
3473 for(std::list<MapBlock*>::iterator i = blocks.begin();
3474 i != blocks.end(); ++i)
3476 MapBlock *block = (*i);
3477 v3s16 p = block->getPos();
3483 void ServerMap::saveMapMeta()
3485 DSTACK(__FUNCTION_NAME);
3487 /*infostream<<"ServerMap::saveMapMeta(): "
3491 createDirs(m_savedir);
3493 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3494 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3495 if(os.good() == false)
3497 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3498 <<"could not open"<<fullpath<<std::endl;
3499 throw FileNotGoodException("Cannot open chunk metadata");
3504 m_emerge->setParamsToSettings(¶ms);
3505 params.writeLines(os);
3507 os<<"[end_of_params]\n";
3509 m_map_metadata_changed = false;
3512 void ServerMap::loadMapMeta()
3514 DSTACK(__FUNCTION_NAME);
3516 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3519 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3520 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3521 if(is.good() == false)
3523 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3524 <<"could not open"<<fullpath<<std::endl;
3525 throw FileNotGoodException("Cannot open map metadata");
3533 throw SerializationError
3534 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3536 std::getline(is, line);
3537 std::string trimmedline = trim(line);
3538 if(trimmedline == "[end_of_params]")
3540 params.parseConfigLine(line);
3543 MapgenParams *mgparams;
3545 mgparams = m_emerge->getParamsFromSettings(¶ms);
3546 } catch (SettingNotFoundException &e) {
3547 infostream << "Couldn't get a setting from map_meta.txt: "
3548 << e.what() << std::endl;
3555 m_mgparams = mgparams;
3556 m_seed = mgparams->seed;
3558 if (params.exists("seed")) {
3559 m_seed = params.getU64("seed");
3560 m_mgparams->seed = m_seed;
3564 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3567 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3569 DSTACK(__FUNCTION_NAME);
3570 // Format used for writing
3571 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3573 v2s16 pos = sector->getPos();
3574 std::string dir = getSectorDir(pos);
3577 std::string fullpath = dir + DIR_DELIM + "meta";
3578 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3579 if(o.good() == false)
3580 throw FileNotGoodException("Cannot open sector metafile");
3582 sector->serialize(o, version);
3584 sector->differs_from_disk = false;
3587 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3589 DSTACK(__FUNCTION_NAME);
3591 v2s16 p2d = getSectorPos(sectordir);
3593 ServerMapSector *sector = NULL;
3595 std::string fullpath = sectordir + DIR_DELIM + "meta";
3596 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3597 if(is.good() == false)
3599 // If the directory exists anyway, it probably is in some old
3600 // format. Just go ahead and create the sector.
3601 if(fs::PathExists(sectordir))
3603 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3604 <<fullpath<<" doesn't exist but directory does."
3605 <<" Continuing with a sector with no metadata."
3607 sector = new ServerMapSector(this, p2d, m_gamedef);
3608 m_sectors[p2d] = sector;
3612 throw FileNotGoodException("Cannot open sector metafile");
3617 sector = ServerMapSector::deSerialize
3618 (is, this, p2d, m_sectors, m_gamedef);
3620 saveSectorMeta(sector);
3623 sector->differs_from_disk = false;
3628 bool ServerMap::loadSectorMeta(v2s16 p2d)
3630 DSTACK(__FUNCTION_NAME);
3632 MapSector *sector = NULL;
3634 // The directory layout we're going to load from.
3635 // 1 - original sectors/xxxxzzzz/
3636 // 2 - new sectors2/xxx/zzz/
3637 // If we load from anything but the latest structure, we will
3638 // immediately save to the new one, and remove the old.
3640 std::string sectordir1 = getSectorDir(p2d, 1);
3641 std::string sectordir;
3642 if(fs::PathExists(sectordir1))
3644 sectordir = sectordir1;
3649 sectordir = getSectorDir(p2d, 2);
3653 sector = loadSectorMeta(sectordir, loadlayout != 2);
3655 catch(InvalidFilenameException &e)
3659 catch(FileNotGoodException &e)
3663 catch(std::exception &e)
3672 bool ServerMap::loadSectorFull(v2s16 p2d)
3674 DSTACK(__FUNCTION_NAME);
3676 MapSector *sector = NULL;
3678 // The directory layout we're going to load from.
3679 // 1 - original sectors/xxxxzzzz/
3680 // 2 - new sectors2/xxx/zzz/
3681 // If we load from anything but the latest structure, we will
3682 // immediately save to the new one, and remove the old.
3684 std::string sectordir1 = getSectorDir(p2d, 1);
3685 std::string sectordir;
3686 if(fs::PathExists(sectordir1))
3688 sectordir = sectordir1;
3693 sectordir = getSectorDir(p2d, 2);
3697 sector = loadSectorMeta(sectordir, loadlayout != 2);
3699 catch(InvalidFilenameException &e)
3703 catch(FileNotGoodException &e)
3707 catch(std::exception &e)
3715 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3717 std::vector<fs::DirListNode>::iterator i2;
3718 for(i2=list2.begin(); i2!=list2.end(); i2++)
3724 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3726 catch(InvalidFilenameException &e)
3728 // This catches unknown crap in directory
3734 infostream<<"Sector converted to new layout - deleting "<<
3735 sectordir1<<std::endl;
3736 fs::RecursiveDelete(sectordir1);
3743 void ServerMap::beginSave() {
3745 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3746 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3749 void ServerMap::endSave() {
3751 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3752 infostream<<"WARNING: endSave() failed, map might not have saved.";
3755 void ServerMap::saveBlock(MapBlock *block)
3757 DSTACK(__FUNCTION_NAME);
3759 Dummy blocks are not written
3761 if(block->isDummy())
3763 /*v3s16 p = block->getPos();
3764 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3765 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3769 // Format used for writing
3770 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3772 v3s16 p3d = block->getPos();
3776 v2s16 p2d(p3d.X, p3d.Z);
3777 std::string sectordir = getSectorDir(p2d);
3779 createDirs(sectordir);
3781 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3782 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3783 if(o.good() == false)
3784 throw FileNotGoodException("Cannot open block data");
3787 [0] u8 serialization version
3793 std::ostringstream o(std::ios_base::binary);
3795 o.write((char*)&version, 1);
3798 block->serialize(o, version, true);
3800 // Write block to database
3802 std::string tmp = o.str();
3803 const char *bytes = tmp.c_str();
3805 bool success = true;
3806 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3807 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3810 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3811 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3814 int written = sqlite3_step(m_database_write);
3815 if(written != SQLITE_DONE) {
3816 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3817 <<sqlite3_errmsg(m_database)<<std::endl;
3820 // Make ready for later reuse
3821 sqlite3_reset(m_database_write);
3823 // We just wrote it to the disk so clear modified flag
3825 block->resetModified();
3828 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3830 DSTACK(__FUNCTION_NAME);
3832 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3835 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3836 if(is.good() == false)
3837 throw FileNotGoodException("Cannot open block file");
3839 v3s16 p3d = getBlockPos(sectordir, blockfile);
3840 v2s16 p2d(p3d.X, p3d.Z);
3842 assert(sector->getPos() == p2d);
3844 u8 version = SER_FMT_VER_INVALID;
3845 is.read((char*)&version, 1);
3848 throw SerializationError("ServerMap::loadBlock(): Failed"
3849 " to read MapBlock version");
3851 /*u32 block_size = MapBlock::serializedLength(version);
3852 SharedBuffer<u8> data(block_size);
3853 is.read((char*)*data, block_size);*/
3855 // This will always return a sector because we're the server
3856 //MapSector *sector = emergeSector(p2d);
3858 MapBlock *block = NULL;
3859 bool created_new = false;
3860 block = sector->getBlockNoCreateNoEx(p3d.Y);
3863 block = sector->createBlankBlockNoInsert(p3d.Y);
3868 block->deSerialize(is, version, true);
3870 // If it's a new block, insert it to the map
3872 sector->insertBlock(block);
3875 Save blocks loaded in old format in new format
3878 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3882 // Should be in database now, so delete the old file
3883 fs::RecursiveDelete(fullpath);
3886 // We just loaded it from the disk, so it's up-to-date.
3887 block->resetModified();
3890 catch(SerializationError &e)
3892 infostream<<"WARNING: Invalid block data on disk "
3893 <<"fullpath="<<fullpath
3894 <<" (SerializationError). "
3895 <<"what()="<<e.what()
3897 // Ignoring. A new one will be generated.
3900 // TODO: Backup file; name is in fullpath.
3904 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3906 DSTACK(__FUNCTION_NAME);
3909 std::istringstream is(*blob, std::ios_base::binary);
3911 u8 version = SER_FMT_VER_INVALID;
3912 is.read((char*)&version, 1);
3915 throw SerializationError("ServerMap::loadBlock(): Failed"
3916 " to read MapBlock version");
3918 /*u32 block_size = MapBlock::serializedLength(version);
3919 SharedBuffer<u8> data(block_size);
3920 is.read((char*)*data, block_size);*/
3922 // This will always return a sector because we're the server
3923 //MapSector *sector = emergeSector(p2d);
3925 MapBlock *block = NULL;
3926 bool created_new = false;
3927 block = sector->getBlockNoCreateNoEx(p3d.Y);
3930 block = sector->createBlankBlockNoInsert(p3d.Y);
3935 block->deSerialize(is, version, true);
3937 // If it's a new block, insert it to the map
3939 sector->insertBlock(block);
3942 Save blocks loaded in old format in new format
3945 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3946 // Only save if asked to; no need to update version
3950 // We just loaded it from, so it's up-to-date.
3951 block->resetModified();
3954 catch(SerializationError &e)
3956 errorstream<<"Invalid block data in database"
3957 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3958 <<" (SerializationError): "<<e.what()<<std::endl;
3960 // TODO: Block should be marked as invalid in memory so that it is
3961 // not touched but the game can run
3963 if(g_settings->getBool("ignore_world_load_errors")){
3964 errorstream<<"Ignoring block load error. Duck and cover! "
3965 <<"(ignore_world_load_errors)"<<std::endl;
3967 throw SerializationError("Invalid block data in database");
3973 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3975 DSTACK(__FUNCTION_NAME);
3977 v2s16 p2d(blockpos.X, blockpos.Z);
3979 if(!loadFromFolders()) {
3982 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3983 infostream<<"WARNING: Could not bind block position for load: "
3984 <<sqlite3_errmsg(m_database)<<std::endl;
3985 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3987 Make sure sector is loaded
3989 MapSector *sector = createSector(p2d);
3994 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3995 size_t len = sqlite3_column_bytes(m_database_read, 0);
3997 std::string datastr(data, len);
3999 loadBlock(&datastr, blockpos, sector, false);
4001 sqlite3_step(m_database_read);
4002 // We should never get more than 1 row, so ok to reset
4003 sqlite3_reset(m_database_read);
4005 return getBlockNoCreateNoEx(blockpos);
4007 sqlite3_reset(m_database_read);
4009 // Not found in database, try the files
4012 // The directory layout we're going to load from.
4013 // 1 - original sectors/xxxxzzzz/
4014 // 2 - new sectors2/xxx/zzz/
4015 // If we load from anything but the latest structure, we will
4016 // immediately save to the new one, and remove the old.
4018 std::string sectordir1 = getSectorDir(p2d, 1);
4019 std::string sectordir;
4020 if(fs::PathExists(sectordir1))
4022 sectordir = sectordir1;
4027 sectordir = getSectorDir(p2d, 2);
4031 Make sure sector is loaded
4033 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4037 sector = loadSectorMeta(sectordir, loadlayout != 2);
4039 catch(InvalidFilenameException &e)
4043 catch(FileNotGoodException &e)
4047 catch(std::exception &e)
4054 Make sure file exists
4057 std::string blockfilename = getBlockFilename(blockpos);
4058 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
4062 Load block and save it to the database
4064 loadBlock(sectordir, blockfilename, sector, true);
4065 return getBlockNoCreateNoEx(blockpos);
4068 void ServerMap::PrintInfo(std::ostream &out)
4073 s16 ServerMap::getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
4076 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
4078 if (env->getGameTime() - block->heat_time < 10)
4082 //variant 1: full random
4083 //f32 heat = NoisePerlin3D(m_emerge->biomedef->np_heat, p.X, env->getGameTime()/100, p.Z, m_emerge->params->seed);
4085 //variant 2: season change based on default heat map
4086 f32 heat = NoisePerlin2D(m_emerge->biomedef->np_heat, p.X, p.Z, m_emerge->params->seed);
4087 heat += -30; // -30 - todo REMOVE after fixed NoiseParams nparams_biome_def_heat = {50, 50, -> 20, 50,
4088 f32 base = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
4089 base /= ( 86400 * g_settings->getS16("year_days") );
4090 base += (f32)p.X / 3000;
4091 heat += 30 * sin(base * M_PI); // season
4093 heat += p.Y / -333; // upper=colder, lower=hotter
4095 // daily change, hotter at sun +4, colder at night -4
4096 heat += 8 * (sin(cycle_shift(env->getTimeOfDayF(), -0.25) * M_PI) - 0.5);
4100 block->heat_time = env->getGameTime();
4105 s16 ServerMap::getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
4108 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
4110 if (env->getGameTime() - block->humidity_time < 10)
4111 return block->humidity;
4114 f32 humidity = NoisePerlin3D( m_emerge->biomedef->np_humidity,
4115 p.X, env->getGameTime()/10, p.Z,
4116 m_emerge->params->seed);
4117 humidity += -12 * ( sin(cycle_shift(env->getTimeOfDayF(), -0.1) * M_PI) - 0.5);
4118 //todo like heat//humidity += 20 * ( sin(((f32)p.Z / 300) * M_PI) - 0.5);
4120 if (humidity > 100) humidity = 100;
4121 if (humidity < 0) humidity = 0;
4124 block->humidity = humidity;
4125 block->humidity_time = env->getGameTime();
4134 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4139 MapVoxelManipulator::~MapVoxelManipulator()
4141 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4145 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4147 TimeTaker timer1("emerge", &emerge_time);
4149 // Units of these are MapBlocks
4150 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4151 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4153 VoxelArea block_area_nodes
4154 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4156 addArea(block_area_nodes);
4158 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4159 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4160 for(s32 x=p_min.X; x<=p_max.X; x++)
4165 std::map<v3s16, u8>::iterator n;
4166 n = m_loaded_blocks.find(p);
4167 if(n != m_loaded_blocks.end())
4170 bool block_data_inexistent = false;
4173 TimeTaker timer1("emerge load", &emerge_load_time);
4175 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4176 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4178 a.print(infostream);
4179 infostream<<std::endl;*/
4181 block = m_map->getBlockNoCreate(p);
4182 if(block->isDummy())
4183 block_data_inexistent = true;
4185 block->copyTo(*this);
4187 catch(InvalidPositionException &e)
4189 block_data_inexistent = true;
4192 if(block_data_inexistent)
4194 flags |= VMANIP_BLOCK_DATA_INEXIST;
4196 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4197 // Fill with VOXELFLAG_INEXISTENT
4198 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4199 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4201 s32 i = m_area.index(a.MinEdge.X,y,z);
4202 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4205 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4207 // Mark that block was loaded as blank
4208 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4211 m_loaded_blocks[p] = flags;
4214 //infostream<<"emerge done"<<std::endl;
4218 SUGG: Add an option to only update eg. water and air nodes.
4219 This will make it interfere less with important stuff if
4222 void MapVoxelManipulator::blitBack
4223 (std::map<v3s16, MapBlock*> & modified_blocks)
4225 if(m_area.getExtent() == v3s16(0,0,0))
4228 //TimeTaker timer1("blitBack");
4230 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4231 <<m_loaded_blocks.size()<<std::endl;*/
4234 Initialize block cache
4236 v3s16 blockpos_last;
4237 MapBlock *block = NULL;
4238 bool block_checked_in_modified = false;
4240 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4241 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4242 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4246 u8 f = m_flags[m_area.index(p)];
4247 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4250 MapNode &n = m_data[m_area.index(p)];
4252 v3s16 blockpos = getNodeBlockPos(p);
4257 if(block == NULL || blockpos != blockpos_last){
4258 block = m_map->getBlockNoCreate(blockpos);
4259 blockpos_last = blockpos;
4260 block_checked_in_modified = false;
4263 // Calculate relative position in block
4264 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4266 // Don't continue if nothing has changed here
4267 if(block->getNode(relpos) == n)
4270 //m_map->setNode(m_area.MinEdge + p, n);
4271 block->setNode(relpos, n);
4274 Make sure block is in modified_blocks
4276 if(block_checked_in_modified == false)
4278 modified_blocks[blockpos] = block;
4279 block_checked_in_modified = true;
4282 catch(InvalidPositionException &e)
4288 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4289 MapVoxelManipulator(map),
4290 m_create_area(false)
4294 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4298 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4300 // Just create the area so that it can be pointed to
4301 VoxelManipulator::emerge(a, caller_id);
4304 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4305 v3s16 blockpos_max, bool load_if_inexistent)
4307 TimeTaker timer1("initialEmerge", &emerge_time);
4309 // Units of these are MapBlocks
4310 v3s16 p_min = blockpos_min;
4311 v3s16 p_max = blockpos_max;
4313 VoxelArea block_area_nodes
4314 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4316 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4319 infostream<<"initialEmerge: area: ";
4320 block_area_nodes.print(infostream);
4321 infostream<<" ("<<size_MB<<"MB)";
4322 infostream<<std::endl;
4325 addArea(block_area_nodes);
4327 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4328 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4329 for(s32 x=p_min.X; x<=p_max.X; x++)
4334 std::map<v3s16, u8>::iterator n;
4335 n = m_loaded_blocks.find(p);
4336 if(n != m_loaded_blocks.end())
4339 bool block_data_inexistent = false;
4342 TimeTaker timer1("emerge load", &emerge_load_time);
4344 block = m_map->getBlockNoCreate(p);
4345 if(block->isDummy())
4346 block_data_inexistent = true;
4348 block->copyTo(*this);
4350 catch(InvalidPositionException &e)
4352 block_data_inexistent = true;
4355 if(block_data_inexistent)
4358 if (load_if_inexistent) {
4359 ServerMap *svrmap = (ServerMap *)m_map;
4360 block = svrmap->emergeBlock(p, false);
4362 block = svrmap->createBlock(p);
4364 block->copyTo(*this);
4366 flags |= VMANIP_BLOCK_DATA_INEXIST;
4369 Mark area inexistent
4371 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4372 // Fill with VOXELFLAG_INEXISTENT
4373 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4374 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4376 s32 i = m_area.index(a.MinEdge.X,y,z);
4377 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4381 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4383 // Mark that block was loaded as blank
4384 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4387 m_loaded_blocks[p] = flags;
4391 void ManualMapVoxelManipulator::blitBackAll(
4392 std::map<v3s16, MapBlock*> * modified_blocks)
4394 if(m_area.getExtent() == v3s16(0,0,0))
4398 Copy data of all blocks
4400 for(std::map<v3s16, u8>::iterator
4401 i = m_loaded_blocks.begin();
4402 i != m_loaded_blocks.end(); ++i)
4405 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4406 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4407 if(existed == false)
4412 block->copyFrom(*this);
4415 (*modified_blocks)[p] = block;