3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/mathconstants.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
44 #include "database-dummy.h"
45 #include "database-sqlite3.h"
47 #include "database-leveldb.h"
50 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
53 SQLite format specification:
54 - Initially only replaces sectors/ and sectors2/
56 If map.sqlite does not exist in the save dir
57 or the block was not found in the database
58 the map will try to load from sectors folder.
59 In either case, map.sqlite will be created
60 and all future saves will save there.
62 Structure of map.sqlite:
73 Map::Map(std::ostream &dout, IGameDef *gamedef):
78 /*m_sector_mutex.Init();
79 assert(m_sector_mutex.IsInitialized());*/
87 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
88 i != m_sectors.end(); ++i)
94 void Map::addEventReceiver(MapEventReceiver *event_receiver)
96 m_event_receivers.insert(event_receiver);
99 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
101 m_event_receivers.erase(event_receiver);
104 void Map::dispatchEvent(MapEditEvent *event)
106 for(std::set<MapEventReceiver*>::iterator
107 i = m_event_receivers.begin();
108 i != m_event_receivers.end(); ++i)
110 (*i)->onMapEditEvent(event);
114 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
116 if(m_sector_cache != NULL && p == m_sector_cache_p){
117 MapSector * sector = m_sector_cache;
121 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
123 if(n == m_sectors.end())
126 MapSector *sector = n->second;
128 // Cache the last result
129 m_sector_cache_p = p;
130 m_sector_cache = sector;
135 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
137 return getSectorNoGenerateNoExNoLock(p);
140 MapSector * Map::getSectorNoGenerate(v2s16 p)
142 MapSector *sector = getSectorNoGenerateNoEx(p);
144 throw InvalidPositionException();
149 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
151 v2s16 p2d(p3d.X, p3d.Z);
152 MapSector * sector = getSectorNoGenerateNoEx(p2d);
155 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
159 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
161 MapBlock *block = getBlockNoCreateNoEx(p3d);
163 throw InvalidPositionException();
167 bool Map::isNodeUnderground(v3s16 p)
169 v3s16 blockpos = getNodeBlockPos(p);
171 MapBlock * block = getBlockNoCreate(blockpos);
172 return block->getIsUnderground();
174 catch(InvalidPositionException &e)
180 bool Map::isValidPosition(v3s16 p)
182 v3s16 blockpos = getNodeBlockPos(p);
183 MapBlock *block = getBlockNoCreate(blockpos);
184 return (block != NULL);
187 // Returns a CONTENT_IGNORE node if not found
188 MapNode Map::getNodeNoEx(v3s16 p)
190 v3s16 blockpos = getNodeBlockPos(p);
191 MapBlock *block = getBlockNoCreateNoEx(blockpos);
193 return MapNode(CONTENT_IGNORE);
194 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
195 return block->getNodeNoCheck(relpos);
198 // throws InvalidPositionException if not found
199 MapNode Map::getNode(v3s16 p)
201 v3s16 blockpos = getNodeBlockPos(p);
202 MapBlock *block = getBlockNoCreateNoEx(blockpos);
204 throw InvalidPositionException();
205 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 return block->getNodeNoCheck(relpos);
209 // throws InvalidPositionException if not found
210 void Map::setNode(v3s16 p, MapNode & n)
212 v3s16 blockpos = getNodeBlockPos(p);
213 MapBlock *block = getBlockNoCreate(blockpos);
214 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
215 // Never allow placing CONTENT_IGNORE, it fucks up stuff
216 if(n.getContent() == CONTENT_IGNORE){
217 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
218 <<" while trying to replace \""
219 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
220 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
221 debug_stacks_print_to(infostream);
224 block->setNodeNoCheck(relpos, n);
229 Goes recursively through the neighbours of the node.
231 Alters only transparent nodes.
233 If the lighting of the neighbour is lower than the lighting of
234 the node was (before changing it to 0 at the step before), the
235 lighting of the neighbour is set to 0 and then the same stuff
236 repeats for the neighbour.
238 The ending nodes of the routine are stored in light_sources.
239 This is useful when a light is removed. In such case, this
240 routine can be called for the light node and then again for
241 light_sources to re-light the area without the removed light.
243 values of from_nodes are lighting values.
245 void Map::unspreadLight(enum LightBank bank,
246 std::map<v3s16, u8> & from_nodes,
247 std::set<v3s16> & light_sources,
248 std::map<v3s16, MapBlock*> & modified_blocks)
250 INodeDefManager *nodemgr = m_gamedef->ndef();
253 v3s16(0,0,1), // back
255 v3s16(1,0,0), // right
256 v3s16(0,0,-1), // front
257 v3s16(0,-1,0), // bottom
258 v3s16(-1,0,0), // left
261 if(from_nodes.size() == 0)
264 u32 blockchangecount = 0;
266 std::map<v3s16, u8> unlighted_nodes;
269 Initialize block cache
272 MapBlock *block = NULL;
273 // Cache this a bit, too
274 bool block_checked_in_modified = false;
276 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
277 j != from_nodes.end(); ++j)
279 v3s16 pos = j->first;
280 v3s16 blockpos = getNodeBlockPos(pos);
282 // Only fetch a new block if the block position has changed
284 if(block == NULL || blockpos != blockpos_last){
285 block = getBlockNoCreate(blockpos);
286 blockpos_last = blockpos;
288 block_checked_in_modified = false;
292 catch(InvalidPositionException &e)
300 // Calculate relative position in block
301 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
303 // Get node straight from the block
304 //MapNode n = block->getNode(relpos);
306 u8 oldlight = j->second;
308 // Loop through 6 neighbors
309 for(u16 i=0; i<6; i++)
311 // Get the position of the neighbor node
312 v3s16 n2pos = pos + dirs[i];
314 // Get the block where the node is located
315 v3s16 blockpos = getNodeBlockPos(n2pos);
319 // Only fetch a new block if the block position has changed
321 if(block == NULL || blockpos != blockpos_last){
322 block = getBlockNoCreate(blockpos);
323 blockpos_last = blockpos;
325 block_checked_in_modified = false;
329 catch(InvalidPositionException &e)
334 // Calculate relative position in block
335 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
336 // Get node straight from the block
337 MapNode n2 = block->getNode(relpos);
339 bool changed = false;
341 //TODO: Optimize output by optimizing light_sources?
344 If the neighbor is dimmer than what was specified
345 as oldlight (the light of the previous node)
347 if(n2.getLight(bank, nodemgr) < oldlight)
350 And the neighbor is transparent and it has some light
352 if(nodemgr->get(n2).light_propagates
353 && n2.getLight(bank, nodemgr) != 0)
356 Set light to 0 and add to queue
359 u8 current_light = n2.getLight(bank, nodemgr);
360 n2.setLight(bank, 0, nodemgr);
361 block->setNode(relpos, n2);
363 unlighted_nodes[n2pos] = current_light;
367 Remove from light_sources if it is there
368 NOTE: This doesn't happen nearly at all
370 /*if(light_sources.find(n2pos))
372 infostream<<"Removed from light_sources"<<std::endl;
373 light_sources.remove(n2pos);
378 if(light_sources.find(n2pos) != NULL)
379 light_sources.remove(n2pos);*/
382 light_sources.insert(n2pos);
385 // Add to modified_blocks
386 if(changed == true && block_checked_in_modified == false)
388 // If the block is not found in modified_blocks, add.
389 if(modified_blocks.find(blockpos) == modified_blocks.end())
391 modified_blocks[blockpos] = block;
393 block_checked_in_modified = true;
396 catch(InvalidPositionException &e)
403 /*infostream<<"unspreadLight(): Changed block "
404 <<blockchangecount<<" times"
405 <<" for "<<from_nodes.size()<<" nodes"
408 if(unlighted_nodes.size() > 0)
409 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
413 A single-node wrapper of the above
415 void Map::unLightNeighbors(enum LightBank bank,
416 v3s16 pos, u8 lightwas,
417 std::set<v3s16> & light_sources,
418 std::map<v3s16, MapBlock*> & modified_blocks)
420 std::map<v3s16, u8> from_nodes;
421 from_nodes[pos] = lightwas;
423 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
427 Lights neighbors of from_nodes, collects all them and then
430 void Map::spreadLight(enum LightBank bank,
431 std::set<v3s16> & from_nodes,
432 std::map<v3s16, MapBlock*> & modified_blocks)
434 INodeDefManager *nodemgr = m_gamedef->ndef();
436 const v3s16 dirs[6] = {
437 v3s16(0,0,1), // back
439 v3s16(1,0,0), // right
440 v3s16(0,0,-1), // front
441 v3s16(0,-1,0), // bottom
442 v3s16(-1,0,0), // left
445 if(from_nodes.size() == 0)
448 u32 blockchangecount = 0;
450 std::set<v3s16> lighted_nodes;
453 Initialize block cache
456 MapBlock *block = NULL;
457 // Cache this a bit, too
458 bool block_checked_in_modified = false;
460 for(std::set<v3s16>::iterator j = from_nodes.begin();
461 j != from_nodes.end(); ++j)
464 v3s16 blockpos = getNodeBlockPos(pos);
466 // Only fetch a new block if the block position has changed
468 if(block == NULL || blockpos != blockpos_last){
469 block = getBlockNoCreate(blockpos);
470 blockpos_last = blockpos;
472 block_checked_in_modified = false;
476 catch(InvalidPositionException &e)
484 // Calculate relative position in block
485 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
487 // Get node straight from the block
488 MapNode n = block->getNode(relpos);
490 u8 oldlight = n.getLight(bank, nodemgr);
491 u8 newlight = diminish_light(oldlight);
493 // Loop through 6 neighbors
494 for(u16 i=0; i<6; i++){
495 // Get the position of the neighbor node
496 v3s16 n2pos = pos + dirs[i];
498 // Get the block where the node is located
499 v3s16 blockpos = getNodeBlockPos(n2pos);
503 // Only fetch a new block if the block position has changed
505 if(block == NULL || blockpos != blockpos_last){
506 block = getBlockNoCreate(blockpos);
507 blockpos_last = blockpos;
509 block_checked_in_modified = false;
513 catch(InvalidPositionException &e)
518 // Calculate relative position in block
519 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
520 // Get node straight from the block
521 MapNode n2 = block->getNode(relpos);
523 bool changed = false;
525 If the neighbor is brighter than the current node,
526 add to list (it will light up this node on its turn)
528 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
530 lighted_nodes.insert(n2pos);
534 If the neighbor is dimmer than how much light this node
535 would spread on it, add to list
537 if(n2.getLight(bank, nodemgr) < newlight)
539 if(nodemgr->get(n2).light_propagates)
541 n2.setLight(bank, newlight, nodemgr);
542 block->setNode(relpos, n2);
543 lighted_nodes.insert(n2pos);
548 // Add to modified_blocks
549 if(changed == true && block_checked_in_modified == false)
551 // If the block is not found in modified_blocks, add.
552 if(modified_blocks.find(blockpos) == modified_blocks.end())
554 modified_blocks[blockpos] = block;
556 block_checked_in_modified = true;
559 catch(InvalidPositionException &e)
566 /*infostream<<"spreadLight(): Changed block "
567 <<blockchangecount<<" times"
568 <<" for "<<from_nodes.size()<<" nodes"
571 if(lighted_nodes.size() > 0)
572 spreadLight(bank, lighted_nodes, modified_blocks);
576 A single-node source variation of the above.
578 void Map::lightNeighbors(enum LightBank bank,
580 std::map<v3s16, MapBlock*> & modified_blocks)
582 std::set<v3s16> from_nodes;
583 from_nodes.insert(pos);
584 spreadLight(bank, from_nodes, modified_blocks);
587 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
589 INodeDefManager *nodemgr = m_gamedef->ndef();
592 v3s16(0,0,1), // back
594 v3s16(1,0,0), // right
595 v3s16(0,0,-1), // front
596 v3s16(0,-1,0), // bottom
597 v3s16(-1,0,0), // left
600 u8 brightest_light = 0;
601 v3s16 brightest_pos(0,0,0);
602 bool found_something = false;
604 // Loop through 6 neighbors
605 for(u16 i=0; i<6; i++){
606 // Get the position of the neighbor node
607 v3s16 n2pos = p + dirs[i];
612 catch(InvalidPositionException &e)
616 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
617 brightest_light = n2.getLight(bank, nodemgr);
618 brightest_pos = n2pos;
619 found_something = true;
623 if(found_something == false)
624 throw InvalidPositionException();
626 return brightest_pos;
630 Propagates sunlight down from a node.
631 Starting point gets sunlight.
633 Returns the lowest y value of where the sunlight went.
635 Mud is turned into grass in where the sunlight stops.
637 s16 Map::propagateSunlight(v3s16 start,
638 std::map<v3s16, MapBlock*> & modified_blocks)
640 INodeDefManager *nodemgr = m_gamedef->ndef();
645 v3s16 pos(start.X, y, start.Z);
647 v3s16 blockpos = getNodeBlockPos(pos);
650 block = getBlockNoCreate(blockpos);
652 catch(InvalidPositionException &e)
657 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
658 MapNode n = block->getNode(relpos);
660 if(nodemgr->get(n).sunlight_propagates)
662 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
663 block->setNode(relpos, n);
665 modified_blocks[blockpos] = block;
669 // Sunlight goes no further
676 void Map::updateLighting(enum LightBank bank,
677 std::map<v3s16, MapBlock*> & a_blocks,
678 std::map<v3s16, MapBlock*> & modified_blocks)
680 INodeDefManager *nodemgr = m_gamedef->ndef();
682 /*m_dout<<DTIME<<"Map::updateLighting(): "
683 <<a_blocks.size()<<" blocks."<<std::endl;*/
685 //TimeTaker timer("updateLighting");
689 //u32 count_was = modified_blocks.size();
691 std::map<v3s16, MapBlock*> blocks_to_update;
693 std::set<v3s16> light_sources;
695 std::map<v3s16, u8> unlight_from;
697 int num_bottom_invalid = 0;
700 //TimeTaker t("first stuff");
702 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
703 i != a_blocks.end(); ++i)
705 MapBlock *block = i->second;
709 // Don't bother with dummy blocks.
713 v3s16 pos = block->getPos();
714 v3s16 posnodes = block->getPosRelative();
715 modified_blocks[pos] = block;
716 blocks_to_update[pos] = block;
719 Clear all light from block
721 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
722 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
723 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
728 MapNode n = block->getNode(p);
729 u8 oldlight = n.getLight(bank, nodemgr);
730 n.setLight(bank, 0, nodemgr);
731 block->setNode(p, n);
733 // If node sources light, add to list
734 u8 source = nodemgr->get(n).light_source;
736 light_sources.insert(p + posnodes);
738 // Collect borders for unlighting
739 if((x==0 || x == MAP_BLOCKSIZE-1
740 || y==0 || y == MAP_BLOCKSIZE-1
741 || z==0 || z == MAP_BLOCKSIZE-1)
744 v3s16 p_map = p + posnodes;
745 unlight_from[p_map] = oldlight;
748 catch(InvalidPositionException &e)
751 This would happen when dealing with a
755 infostream<<"updateLighting(): InvalidPositionException"
760 if(bank == LIGHTBANK_DAY)
762 bool bottom_valid = block->propagateSunlight(light_sources);
765 num_bottom_invalid++;
767 // If bottom is valid, we're done.
771 else if(bank == LIGHTBANK_NIGHT)
773 // For night lighting, sunlight is not propagated
778 // Invalid lighting bank
782 /*infostream<<"Bottom for sunlight-propagated block ("
783 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
786 // Bottom sunlight is not valid; get the block and loop to it
790 block = getBlockNoCreate(pos);
792 catch(InvalidPositionException &e)
803 Enable this to disable proper lighting for speeding up map
804 generation for testing or whatever
807 //if(g_settings->get(""))
809 core::map<v3s16, MapBlock*>::Iterator i;
810 i = blocks_to_update.getIterator();
811 for(; i.atEnd() == false; i++)
813 MapBlock *block = i.getNode()->getValue();
814 v3s16 p = block->getPos();
815 block->setLightingExpired(false);
823 //TimeTaker timer("unspreadLight");
824 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
829 u32 diff = modified_blocks.size() - count_was;
830 count_was = modified_blocks.size();
831 infostream<<"unspreadLight modified "<<diff<<std::endl;
835 //TimeTaker timer("spreadLight");
836 spreadLight(bank, light_sources, modified_blocks);
841 u32 diff = modified_blocks.size() - count_was;
842 count_was = modified_blocks.size();
843 infostream<<"spreadLight modified "<<diff<<std::endl;
849 //MapVoxelManipulator vmanip(this);
851 // Make a manual voxel manipulator and load all the blocks
852 // that touch the requested blocks
853 ManualMapVoxelManipulator vmanip(this);
856 //TimeTaker timer("initialEmerge");
858 core::map<v3s16, MapBlock*>::Iterator i;
859 i = blocks_to_update.getIterator();
860 for(; i.atEnd() == false; i++)
862 MapBlock *block = i.getNode()->getValue();
863 v3s16 p = block->getPos();
865 // Add all surrounding blocks
866 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
869 Add all surrounding blocks that have up-to-date lighting
870 NOTE: This doesn't quite do the job (not everything
871 appropriate is lighted)
873 /*for(s16 z=-1; z<=1; z++)
874 for(s16 y=-1; y<=1; y++)
875 for(s16 x=-1; x<=1; x++)
877 v3s16 p2 = p + v3s16(x,y,z);
878 MapBlock *block = getBlockNoCreateNoEx(p2);
883 if(block->getLightingExpired())
885 vmanip.initialEmerge(p2, p2);
888 // Lighting of block will be updated completely
889 block->setLightingExpired(false);
894 //TimeTaker timer("unSpreadLight");
895 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
898 //TimeTaker timer("spreadLight");
899 vmanip.spreadLight(bank, light_sources, nodemgr);
902 //TimeTaker timer("blitBack");
903 vmanip.blitBack(modified_blocks);
905 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
910 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
913 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
914 std::map<v3s16, MapBlock*> & modified_blocks)
916 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
917 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
920 Update information about whether day and night light differ
922 for(std::map<v3s16, MapBlock*>::iterator
923 i = modified_blocks.begin();
924 i != modified_blocks.end(); ++i)
926 MapBlock *block = i->second;
927 block->expireDayNightDiff();
933 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
934 std::map<v3s16, MapBlock*> &modified_blocks)
936 INodeDefManager *ndef = m_gamedef->ndef();
939 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
940 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
943 From this node to nodes underneath:
944 If lighting is sunlight (1.0), unlight neighbours and
949 v3s16 toppos = p + v3s16(0,1,0);
950 //v3s16 bottompos = p + v3s16(0,-1,0);
952 bool node_under_sunlight = true;
953 std::set<v3s16> light_sources;
956 Collect old node for rollback
958 RollbackNode rollback_oldnode(this, p, m_gamedef);
961 If there is a node at top and it doesn't have sunlight,
962 there has not been any sunlight going down.
964 Otherwise there probably is.
967 MapNode topnode = getNode(toppos);
969 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
970 node_under_sunlight = false;
972 catch(InvalidPositionException &e)
977 Remove all light that has come out of this node
980 enum LightBank banks[] =
985 for(s32 i=0; i<2; i++)
987 enum LightBank bank = banks[i];
989 u8 lightwas = getNode(p).getLight(bank, ndef);
991 // Add the block of the added node to modified_blocks
992 v3s16 blockpos = getNodeBlockPos(p);
993 MapBlock * block = getBlockNoCreate(blockpos);
994 assert(block != NULL);
995 modified_blocks[blockpos] = block;
997 assert(isValidPosition(p));
999 // Unlight neighbours of node.
1000 // This means setting light of all consequent dimmer nodes
1002 // This also collects the nodes at the border which will spread
1003 // light again into this.
1004 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1006 n.setLight(bank, 0, ndef);
1010 If node lets sunlight through and is under sunlight, it has
1013 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1015 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1019 Remove node metadata
1022 removeNodeMetadata(p);
1025 Set the node on the map
1031 If node is under sunlight and doesn't let sunlight through,
1032 take all sunlighted nodes under it and clear light from them
1033 and from where the light has been spread.
1034 TODO: This could be optimized by mass-unlighting instead
1037 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1041 //m_dout<<DTIME<<"y="<<y<<std::endl;
1042 v3s16 n2pos(p.X, y, p.Z);
1046 n2 = getNode(n2pos);
1048 catch(InvalidPositionException &e)
1053 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1055 unLightNeighbors(LIGHTBANK_DAY,
1056 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1057 light_sources, modified_blocks);
1058 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1066 for(s32 i=0; i<2; i++)
1068 enum LightBank bank = banks[i];
1071 Spread light from all nodes that might be capable of doing so
1073 spreadLight(bank, light_sources, modified_blocks);
1077 Update information about whether day and night light differ
1079 for(std::map<v3s16, MapBlock*>::iterator
1080 i = modified_blocks.begin();
1081 i != modified_blocks.end(); ++i)
1083 i->second->expireDayNightDiff();
1089 if(m_gamedef->rollback())
1091 RollbackNode rollback_newnode(this, p, m_gamedef);
1092 RollbackAction action;
1093 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1094 m_gamedef->rollback()->reportAction(action);
1098 Add neighboring liquid nodes and the node itself if it is
1099 liquid (=water node was added) to transform queue.
1100 note: todo: for liquid_finite enough to add only self node
1103 v3s16(0,0,0), // self
1104 v3s16(0,0,1), // back
1105 v3s16(0,1,0), // top
1106 v3s16(1,0,0), // right
1107 v3s16(0,0,-1), // front
1108 v3s16(0,-1,0), // bottom
1109 v3s16(-1,0,0), // left
1111 for(u16 i=0; i<7; i++)
1116 v3s16 p2 = p + dirs[i];
1118 MapNode n2 = getNode(p2);
1119 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1121 m_transforming_liquid.push_back(p2);
1124 }catch(InvalidPositionException &e)
1132 void Map::removeNodeAndUpdate(v3s16 p,
1133 std::map<v3s16, MapBlock*> &modified_blocks)
1135 INodeDefManager *ndef = m_gamedef->ndef();
1137 /*PrintInfo(m_dout);
1138 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1139 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1141 bool node_under_sunlight = true;
1143 v3s16 toppos = p + v3s16(0,1,0);
1145 // Node will be replaced with this
1146 content_t replace_material = CONTENT_AIR;
1149 Collect old node for rollback
1151 RollbackNode rollback_oldnode(this, p, m_gamedef);
1154 If there is a node at top and it doesn't have sunlight,
1155 there will be no sunlight going down.
1158 MapNode topnode = getNode(toppos);
1160 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1161 node_under_sunlight = false;
1163 catch(InvalidPositionException &e)
1167 std::set<v3s16> light_sources;
1169 enum LightBank banks[] =
1174 for(s32 i=0; i<2; i++)
1176 enum LightBank bank = banks[i];
1179 Unlight neighbors (in case the node is a light source)
1181 unLightNeighbors(bank, p,
1182 getNode(p).getLight(bank, ndef),
1183 light_sources, modified_blocks);
1187 Remove node metadata
1190 removeNodeMetadata(p);
1194 This also clears the lighting.
1198 n.setContent(replace_material);
1201 for(s32 i=0; i<2; i++)
1203 enum LightBank bank = banks[i];
1206 Recalculate lighting
1208 spreadLight(bank, light_sources, modified_blocks);
1211 // Add the block of the removed node to modified_blocks
1212 v3s16 blockpos = getNodeBlockPos(p);
1213 MapBlock * block = getBlockNoCreate(blockpos);
1214 assert(block != NULL);
1215 modified_blocks[blockpos] = block;
1218 If the removed node was under sunlight, propagate the
1219 sunlight down from it and then light all neighbors
1220 of the propagated blocks.
1222 if(node_under_sunlight)
1224 s16 ybottom = propagateSunlight(p, modified_blocks);
1225 /*m_dout<<DTIME<<"Node was under sunlight. "
1226 "Propagating sunlight";
1227 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1229 for(; y >= ybottom; y--)
1231 v3s16 p2(p.X, y, p.Z);
1232 /*m_dout<<DTIME<<"lighting neighbors of node ("
1233 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1235 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1240 // Set the lighting of this node to 0
1241 // TODO: Is this needed? Lighting is cleared up there already.
1243 MapNode n = getNode(p);
1244 n.setLight(LIGHTBANK_DAY, 0, ndef);
1247 catch(InvalidPositionException &e)
1253 for(s32 i=0; i<2; i++)
1255 enum LightBank bank = banks[i];
1257 // Get the brightest neighbour node and propagate light from it
1258 v3s16 n2p = getBrightestNeighbour(bank, p);
1260 //MapNode n2 = getNode(n2p);
1261 lightNeighbors(bank, n2p, modified_blocks);
1263 catch(InvalidPositionException &e)
1269 Update information about whether day and night light differ
1271 for(std::map<v3s16, MapBlock*>::iterator
1272 i = modified_blocks.begin();
1273 i != modified_blocks.end(); ++i)
1275 i->second->expireDayNightDiff();
1281 if(m_gamedef->rollback())
1283 RollbackNode rollback_newnode(this, p, m_gamedef);
1284 RollbackAction action;
1285 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1286 m_gamedef->rollback()->reportAction(action);
1290 Add neighboring liquid nodes and this node to transform queue.
1291 (it's vital for the node itself to get updated last.)
1292 note: todo: for liquid_finite enough to add only self node
1295 v3s16(0,0,1), // back
1296 v3s16(0,1,0), // top
1297 v3s16(1,0,0), // right
1298 v3s16(0,0,-1), // front
1299 v3s16(0,-1,0), // bottom
1300 v3s16(-1,0,0), // left
1301 v3s16(0,0,0), // self
1303 for(u16 i=0; i<7; i++)
1308 v3s16 p2 = p + dirs[i];
1310 MapNode n2 = getNode(p2);
1311 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1313 m_transforming_liquid.push_back(p2);
1316 }catch(InvalidPositionException &e)
1322 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1325 event.type = MEET_ADDNODE;
1329 bool succeeded = true;
1331 std::map<v3s16, MapBlock*> modified_blocks;
1332 addNodeAndUpdate(p, n, modified_blocks);
1334 // Copy modified_blocks to event
1335 for(std::map<v3s16, MapBlock*>::iterator
1336 i = modified_blocks.begin();
1337 i != modified_blocks.end(); ++i)
1339 event.modified_blocks.insert(i->first);
1342 catch(InvalidPositionException &e){
1346 dispatchEvent(&event);
1351 bool Map::removeNodeWithEvent(v3s16 p)
1354 event.type = MEET_REMOVENODE;
1357 bool succeeded = true;
1359 std::map<v3s16, MapBlock*> modified_blocks;
1360 removeNodeAndUpdate(p, modified_blocks);
1362 // Copy modified_blocks to event
1363 for(std::map<v3s16, MapBlock*>::iterator
1364 i = modified_blocks.begin();
1365 i != modified_blocks.end(); ++i)
1367 event.modified_blocks.insert(i->first);
1370 catch(InvalidPositionException &e){
1374 dispatchEvent(&event);
1379 bool Map::getDayNightDiff(v3s16 blockpos)
1382 v3s16 p = blockpos + v3s16(0,0,0);
1383 MapBlock *b = getBlockNoCreate(p);
1384 if(b->getDayNightDiff())
1387 catch(InvalidPositionException &e){}
1390 v3s16 p = blockpos + v3s16(-1,0,0);
1391 MapBlock *b = getBlockNoCreate(p);
1392 if(b->getDayNightDiff())
1395 catch(InvalidPositionException &e){}
1397 v3s16 p = blockpos + v3s16(0,-1,0);
1398 MapBlock *b = getBlockNoCreate(p);
1399 if(b->getDayNightDiff())
1402 catch(InvalidPositionException &e){}
1404 v3s16 p = blockpos + v3s16(0,0,-1);
1405 MapBlock *b = getBlockNoCreate(p);
1406 if(b->getDayNightDiff())
1409 catch(InvalidPositionException &e){}
1412 v3s16 p = blockpos + v3s16(1,0,0);
1413 MapBlock *b = getBlockNoCreate(p);
1414 if(b->getDayNightDiff())
1417 catch(InvalidPositionException &e){}
1419 v3s16 p = blockpos + v3s16(0,1,0);
1420 MapBlock *b = getBlockNoCreate(p);
1421 if(b->getDayNightDiff())
1424 catch(InvalidPositionException &e){}
1426 v3s16 p = blockpos + v3s16(0,0,1);
1427 MapBlock *b = getBlockNoCreate(p);
1428 if(b->getDayNightDiff())
1431 catch(InvalidPositionException &e){}
1437 Updates usage timers
1439 void Map::timerUpdate(float dtime, float unload_timeout,
1440 std::list<v3s16> *unloaded_blocks)
1442 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1444 // Profile modified reasons
1445 Profiler modprofiler;
1447 std::list<v2s16> sector_deletion_queue;
1448 u32 deleted_blocks_count = 0;
1449 u32 saved_blocks_count = 0;
1450 u32 block_count_all = 0;
1453 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1454 si != m_sectors.end(); ++si)
1456 MapSector *sector = si->second;
1458 bool all_blocks_deleted = true;
1460 std::list<MapBlock*> blocks;
1461 sector->getBlocks(blocks);
1463 for(std::list<MapBlock*>::iterator i = blocks.begin();
1464 i != blocks.end(); ++i)
1466 MapBlock *block = (*i);
1468 block->incrementUsageTimer(dtime);
1470 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1472 v3s16 p = block->getPos();
1475 if(block->getModified() != MOD_STATE_CLEAN
1476 && save_before_unloading)
1478 modprofiler.add(block->getModifiedReason(), 1);
1480 saved_blocks_count++;
1483 // Delete from memory
1484 sector->deleteBlock(block);
1487 unloaded_blocks->push_back(p);
1489 deleted_blocks_count++;
1493 all_blocks_deleted = false;
1498 if(all_blocks_deleted)
1500 sector_deletion_queue.push_back(si->first);
1505 // Finally delete the empty sectors
1506 deleteSectors(sector_deletion_queue);
1508 if(deleted_blocks_count != 0)
1510 PrintInfo(infostream); // ServerMap/ClientMap:
1511 infostream<<"Unloaded "<<deleted_blocks_count
1512 <<" blocks from memory";
1513 if(save_before_unloading)
1514 infostream<<", of which "<<saved_blocks_count<<" were written";
1515 infostream<<", "<<block_count_all<<" blocks in memory";
1516 infostream<<"."<<std::endl;
1517 if(saved_blocks_count != 0){
1518 PrintInfo(infostream); // ServerMap/ClientMap:
1519 infostream<<"Blocks modified by: "<<std::endl;
1520 modprofiler.print(infostream);
1525 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1527 timerUpdate(0.0, -1.0, unloaded_blocks);
1530 void Map::deleteSectors(std::list<v2s16> &list)
1532 for(std::list<v2s16>::iterator j = list.begin();
1533 j != list.end(); ++j)
1535 MapSector *sector = m_sectors[*j];
1536 // If sector is in sector cache, remove it from there
1537 if(m_sector_cache == sector)
1538 m_sector_cache = NULL;
1539 // Remove from map and delete
1540 m_sectors.erase(*j);
1546 void Map::unloadUnusedData(float timeout,
1547 core::list<v3s16> *deleted_blocks)
1549 core::list<v2s16> sector_deletion_queue;
1550 u32 deleted_blocks_count = 0;
1551 u32 saved_blocks_count = 0;
1553 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1554 for(; si.atEnd() == false; si++)
1556 MapSector *sector = si.getNode()->getValue();
1558 bool all_blocks_deleted = true;
1560 core::list<MapBlock*> blocks;
1561 sector->getBlocks(blocks);
1562 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1563 i != blocks.end(); i++)
1565 MapBlock *block = (*i);
1567 if(block->getUsageTimer() > timeout)
1570 if(block->getModified() != MOD_STATE_CLEAN)
1573 saved_blocks_count++;
1575 // Delete from memory
1576 sector->deleteBlock(block);
1577 deleted_blocks_count++;
1581 all_blocks_deleted = false;
1585 if(all_blocks_deleted)
1587 sector_deletion_queue.push_back(si.getNode()->getKey());
1591 deleteSectors(sector_deletion_queue);
1593 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1594 <<", of which "<<saved_blocks_count<<" were wr."
1597 //return sector_deletion_queue.getSize();
1598 //return deleted_blocks_count;
1602 void Map::PrintInfo(std::ostream &out)
1607 #define WATER_DROP_BOOST 4
1611 NEIGHBOR_SAME_LEVEL,
1614 struct NodeNeighbor {
1618 bool l; //can liquid
1622 void Map::transforming_liquid_add(v3s16 p) {
1623 m_transforming_liquid.push_back(p);
1626 s32 Map::transforming_liquid_size() {
1627 return m_transforming_liquid.size();
1630 const v3s16 g_7dirs[7] =
1632 // +right, +top, +back
1633 v3s16( 0,-1, 0), // bottom
1634 v3s16( 0, 0, 0), // self
1635 v3s16( 0, 0, 1), // back
1636 v3s16( 0, 0,-1), // front
1637 v3s16( 1, 0, 0), // right
1638 v3s16(-1, 0, 0), // left
1639 v3s16( 0, 1, 0) // top
1646 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1648 INodeDefManager *nodemgr = m_gamedef->ndef();
1650 DSTACK(__FUNCTION_NAME);
1651 //TimeTaker timer("transformLiquids()");
1654 u32 initial_size = m_transforming_liquid.size();
1656 u8 relax = g_settings->getS16("liquid_relax");
1657 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1658 int water_level = g_settings->getS16("water_level");
1660 // list of nodes that due to viscosity have not reached their max level height
1661 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1663 // List of MapBlocks that will require a lighting update (due to lava)
1664 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1666 u16 loop_max = g_settings->getU16("liquid_loop_max");
1668 //if (m_transforming_liquid.size() > 0) errorstream << "Liquid queue size="<<m_transforming_liquid.size()<<std::endl;
1670 while (m_transforming_liquid.size() > 0)
1672 // This should be done here so that it is done when continue is used
1673 if (loopcount >= initial_size || loopcount >= loop_max)
1677 Get a queued transforming liquid node
1679 v3s16 p0 = m_transforming_liquid.pop_front();
1680 u16 total_level = 0;
1681 // surrounding flowing liquid nodes
1682 NodeNeighbor neighbors[7];
1683 // current level of every block
1684 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1686 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1687 s8 can_liquid_same_level = 0;
1688 content_t liquid_kind = CONTENT_IGNORE;
1689 content_t liquid_kind_flowing = CONTENT_IGNORE;
1691 Collect information about the environment
1693 const v3s16 *dirs = g_7dirs;
1694 for (u16 i = 0; i < 7; i++) {
1695 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1698 nt = NEIGHBOR_UPPER;
1701 nt = NEIGHBOR_LOWER;
1704 v3s16 npos = p0 + dirs[i];
1706 neighbors[i].n = getNodeNoEx(npos);
1707 neighbors[i].t = nt;
1708 neighbors[i].p = npos;
1711 NodeNeighbor & nb = neighbors[i];
1713 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1715 if (nb.n.getContent() == CONTENT_AIR) {
1716 liquid_levels[i] = 0;
1721 // if this node is not (yet) of a liquid type,
1722 // choose the first liquid type we encounter
1723 if (liquid_kind_flowing == CONTENT_IGNORE)
1724 liquid_kind_flowing = nodemgr->getId(
1725 nodemgr->get(nb.n).liquid_alternative_flowing);
1726 if (liquid_kind == CONTENT_IGNORE)
1727 liquid_kind = nb.n.getContent();
1728 if (nb.n.getContent() == liquid_kind) {
1729 liquid_levels[i] = nb.n.getLevel(nodemgr); //LIQUID_LEVEL_SOURCE;
1731 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1734 case LIQUID_FLOWING:
1735 // if this node is not (yet) of a liquid type,
1736 // choose the first liquid type we encounter
1737 if (liquid_kind_flowing == CONTENT_IGNORE)
1738 liquid_kind_flowing = nb.n.getContent();
1739 if (liquid_kind == CONTENT_IGNORE)
1740 liquid_kind = nodemgr->getId(
1741 nodemgr->get(nb.n).liquid_alternative_source);
1742 if (nb.n.getContent() == liquid_kind_flowing) {
1743 liquid_levels[i] = nb.n.getLevel(nodemgr); //(nb.n.param2 & LIQUID_LEVEL_MASK);
1749 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1750 ++can_liquid_same_level;
1751 if (liquid_levels[i] > 0)
1752 total_level += liquid_levels[i];
1755 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1756 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1757 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1758 << nodemgr->get(nb.n.getContent()).liquid_type
1759 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1760 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1761 << " tlevel=" << (int)total_level << " cansame="
1762 << (int)can_liquid_same_level << std::endl;
1766 if (liquid_kind == CONTENT_IGNORE ||
1767 !neighbors[D_SELF].l ||
1771 // fill bottom block
1772 if (neighbors[D_BOTTOM].l) {
1773 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1774 LIQUID_LEVEL_SOURCE : total_level;
1775 total_level -= liquid_levels_want[D_BOTTOM];
1779 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1780 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1781 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1782 (can_liquid_same_level - relax) &&
1783 can_liquid_same_level >= relax + 1) {
1784 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1787 // prevent lakes in air above unloaded blocks
1788 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE && !(loopcount % 3)) {
1792 // calculate self level 5 blocks
1794 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1795 ? LIQUID_LEVEL_SOURCE
1796 : total_level / can_liquid_same_level;
1797 total_level -= want_level * can_liquid_same_level;
1800 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1801 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1802 total_level <= (can_liquid_same_level - relax) &&
1803 can_liquid_same_level >= relax + 1) {
1807 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1808 if (!neighbors[ii].l)
1810 liquid_levels_want[ii] = want_level;
1811 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0) {
1812 if (loopcount % 3 || liquid_levels[ii] <= 0){
1813 if (liquid_levels[ii] > liquid_levels_want[ii]) {
1814 ++liquid_levels_want[ii];
1817 } else if (neighbors[ii].l > 0){
1818 ++liquid_levels_want[ii];
1824 for (u16 ii = 0; ii < 7; ++ii) {
1825 if (total_level < 1) break;
1826 if (liquid_levels_want[ii] >= 0 &&
1827 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1828 ++liquid_levels_want[ii];
1833 // fill top block if can
1834 if (neighbors[D_TOP].l) {
1835 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1836 LIQUID_LEVEL_SOURCE : total_level;
1837 total_level -= liquid_levels_want[D_TOP];
1840 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1841 if ( neighbors[ii].i ||
1842 (liquid_levels_want[ii] >= 0 &&
1843 (fast_flood && p0.Y < water_level &&
1844 (initial_size >= 1000
1846 && want_level >= LIQUID_LEVEL_SOURCE/4
1847 && can_liquid_same_level >= 5
1848 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1849 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1852 if (total_level > 0) //|| flowed != volume)
1853 infostream <<" AFTER level=" << (int)total_level
1854 //<< " flowed="<<flowed<< " volume=" << volume
1855 << " wantsame="<<(int)want_level<< " top="
1856 << (int)liquid_levels_want[D_TOP]<< " topwas="
1857 << (int)liquid_levels[D_TOP]<< " bot="
1858 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1862 for (u16 i = 0; i < 7; i++) {
1863 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1865 MapNode & n0 = neighbors[i].n;
1866 p0 = neighbors[i].p;
1868 decide on the type (and possibly level) of the current node
1870 content_t new_node_content;
1871 s8 new_node_level = -1;
1872 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1873 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1874 // amount to gain, limited by viscosity
1875 // must be at least 1 in absolute value
1876 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1877 if (level_inc < -viscosity || level_inc > viscosity)
1878 new_node_level = liquid_levels[i] + level_inc/viscosity;
1879 else if (level_inc < 0)
1880 new_node_level = liquid_levels[i] - 1;
1881 else if (level_inc > 0)
1882 new_node_level = liquid_levels[i] + 1;
1884 new_node_level = liquid_levels_want[i];
1887 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1888 new_node_content = liquid_kind;
1889 else if (new_node_level > 0)
1890 new_node_content = liquid_kind_flowing;
1892 new_node_content = CONTENT_AIR;
1894 // last level must flow down on stairs
1895 if (liquid_levels_want[i] != liquid_levels[i] &&
1896 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1897 new_node_level >= 1 && new_node_level <= 2) {
1898 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1899 if (neighbors[ii].l)
1900 must_reflow_second.push_back(p0 + dirs[ii]);
1905 check if anything has changed.
1906 if not, just continue with the next node.
1910 new_node_content == n0.getContent()
1911 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1912 (n0.getLevel(nodemgr) == (u8)new_node_level
1913 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1914 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1917 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1918 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1919 LIQUID_INFINITY_MASK) == neighbors[i].i
1922 if (liquid_levels[i] == new_node_level)
1930 update the current node
1933 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1934 // set level to last 3 bits, flowing down bit to 4th bit
1935 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1936 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1937 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1938 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1942 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1943 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1944 <<(int)new_node_level<<std::endl;
1947 n0.setContent(liquid_kind_flowing);
1948 n0.setLevel(nodemgr, new_node_level);
1949 // Find out whether there is a suspect for this action
1950 std::string suspect;
1951 if(m_gamedef->rollback()){
1952 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1955 if(!suspect.empty()){
1957 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1958 // Get old node for rollback
1959 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1963 RollbackNode rollback_newnode(this, p0, m_gamedef);
1964 RollbackAction action;
1965 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1966 m_gamedef->rollback()->reportAction(action);
1972 v3s16 blockpos = getNodeBlockPos(p0);
1973 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1975 modified_blocks[blockpos] = block;
1976 // If node emits light, MapBlock requires lighting update
1977 if(nodemgr->get(n0).light_source != 0)
1978 lighting_modified_blocks[block->getPos()] = block;
1980 must_reflow.push_back(neighbors[i].p);
1982 /* //for better relax only same level
1983 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1984 if (!neighbors[ii].l) continue;
1985 must_reflow.push_back(p0 + dirs[ii]);
1990 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1991 <<" reflow="<<must_reflow.size()
1992 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1994 while (must_reflow.size() > 0)
1995 m_transforming_liquid.push_back(must_reflow.pop_front());
1996 while (must_reflow_second.size() > 0)
1997 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1998 updateLighting(lighting_modified_blocks, modified_blocks);
2001 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
2004 if (g_settings->getBool("liquid_finite"))
2005 return Map::transformLiquidsFinite(modified_blocks);
2007 INodeDefManager *nodemgr = m_gamedef->ndef();
2009 DSTACK(__FUNCTION_NAME);
2010 //TimeTaker timer("transformLiquids()");
2013 u32 initial_size = m_transforming_liquid.size();
2015 /*if(initial_size != 0)
2016 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
2018 // list of nodes that due to viscosity have not reached their max level height
2019 UniqueQueue<v3s16> must_reflow;
2021 // List of MapBlocks that will require a lighting update (due to lava)
2022 std::map<v3s16, MapBlock*> lighting_modified_blocks;
2024 u16 loop_max = g_settings->getU16("liquid_loop_max");
2026 while(m_transforming_liquid.size() != 0)
2028 // This should be done here so that it is done when continue is used
2029 if(loopcount >= initial_size || loopcount >= loop_max)
2034 Get a queued transforming liquid node
2036 v3s16 p0 = m_transforming_liquid.pop_front();
2038 MapNode n0 = getNodeNoEx(p0);
2041 Collect information about current node
2043 s8 liquid_level = -1;
2044 content_t liquid_kind = CONTENT_IGNORE;
2045 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2046 switch (liquid_type) {
2048 liquid_level = LIQUID_LEVEL_SOURCE;
2049 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2051 case LIQUID_FLOWING:
2052 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2053 liquid_kind = n0.getContent();
2056 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2057 // continue with the next node.
2058 if (n0.getContent() != CONTENT_AIR)
2060 liquid_kind = CONTENT_AIR;
2065 Collect information about the environment
2067 const v3s16 *dirs = g_6dirs;
2068 NodeNeighbor sources[6]; // surrounding sources
2069 int num_sources = 0;
2070 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2072 NodeNeighbor airs[6]; // surrounding air
2074 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2075 int num_neutrals = 0;
2076 bool flowing_down = false;
2077 for (u16 i = 0; i < 6; i++) {
2078 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2081 nt = NEIGHBOR_UPPER;
2084 nt = NEIGHBOR_LOWER;
2087 v3s16 npos = p0 + dirs[i];
2088 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2089 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2091 if (nb.n.getContent() == CONTENT_AIR) {
2092 airs[num_airs++] = nb;
2093 // if the current node is a water source the neighbor
2094 // should be enqueded for transformation regardless of whether the
2095 // current node changes or not.
2096 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2097 m_transforming_liquid.push_back(npos);
2098 // if the current node happens to be a flowing node, it will start to flow down here.
2099 if (nb.t == NEIGHBOR_LOWER) {
2100 flowing_down = true;
2103 neutrals[num_neutrals++] = nb;
2107 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2108 if (liquid_kind == CONTENT_AIR)
2109 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2110 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2111 neutrals[num_neutrals++] = nb;
2113 // Do not count bottom source, it will screw things up
2115 sources[num_sources++] = nb;
2118 case LIQUID_FLOWING:
2119 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2120 if (liquid_kind == CONTENT_AIR)
2121 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2122 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2123 neutrals[num_neutrals++] = nb;
2125 flows[num_flows++] = nb;
2126 if (nb.t == NEIGHBOR_LOWER)
2127 flowing_down = true;
2134 decide on the type (and possibly level) of the current node
2136 content_t new_node_content;
2137 s8 new_node_level = -1;
2138 s8 max_node_level = -1;
2139 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
2140 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2141 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2142 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2143 // it's perfectly safe to use liquid_kind here to determine the new node content.
2144 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2145 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2146 // liquid_kind is set properly, see above
2147 new_node_content = liquid_kind;
2148 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2149 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
2150 new_node_content = CONTENT_AIR;
2152 // no surrounding sources, so get the maximum level that can flow into this node
2153 for (u16 i = 0; i < num_flows; i++) {
2154 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2155 switch (flows[i].t) {
2156 case NEIGHBOR_UPPER:
2157 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2158 max_node_level = LIQUID_LEVEL_MAX;
2159 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2160 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2161 } else if (nb_liquid_level > max_node_level)
2162 max_node_level = nb_liquid_level;
2164 case NEIGHBOR_LOWER:
2166 case NEIGHBOR_SAME_LEVEL:
2167 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2168 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2169 max_node_level = nb_liquid_level - 1;
2175 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2176 if (viscosity > 1 && max_node_level != liquid_level) {
2177 // amount to gain, limited by viscosity
2178 // must be at least 1 in absolute value
2179 s8 level_inc = max_node_level - liquid_level;
2180 if (level_inc < -viscosity || level_inc > viscosity)
2181 new_node_level = liquid_level + level_inc/viscosity;
2182 else if (level_inc < 0)
2183 new_node_level = liquid_level - 1;
2184 else if (level_inc > 0)
2185 new_node_level = liquid_level + 1;
2186 if (new_node_level != max_node_level)
2187 must_reflow.push_back(p0);
2189 new_node_level = max_node_level;
2191 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
2192 new_node_content = liquid_kind;
2194 new_node_content = CONTENT_AIR;
2199 check if anything has changed. if not, just continue with the next node.
2201 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2202 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2203 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2209 update the current node
2212 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2213 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2214 // set level to last 3 bits, flowing down bit to 4th bit
2215 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2217 // set the liquid level and flow bit to 0
2218 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2220 n0.setContent(new_node_content);
2222 // Find out whether there is a suspect for this action
2223 std::string suspect;
2224 if(m_gamedef->rollback()){
2225 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2228 if(!suspect.empty()){
2230 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2231 // Get old node for rollback
2232 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2236 RollbackNode rollback_newnode(this, p0, m_gamedef);
2237 RollbackAction action;
2238 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2239 m_gamedef->rollback()->reportAction(action);
2245 v3s16 blockpos = getNodeBlockPos(p0);
2246 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2248 modified_blocks[blockpos] = block;
2249 // If new or old node emits light, MapBlock requires lighting update
2250 if(nodemgr->get(n0).light_source != 0 ||
2251 nodemgr->get(n00).light_source != 0)
2252 lighting_modified_blocks[block->getPos()] = block;
2256 enqueue neighbors for update if neccessary
2258 switch (nodemgr->get(n0.getContent()).liquid_type) {
2260 case LIQUID_FLOWING:
2261 // make sure source flows into all neighboring nodes
2262 for (u16 i = 0; i < num_flows; i++)
2263 if (flows[i].t != NEIGHBOR_UPPER)
2264 m_transforming_liquid.push_back(flows[i].p);
2265 for (u16 i = 0; i < num_airs; i++)
2266 if (airs[i].t != NEIGHBOR_UPPER)
2267 m_transforming_liquid.push_back(airs[i].p);
2270 // this flow has turned to air; neighboring flows might need to do the same
2271 for (u16 i = 0; i < num_flows; i++)
2272 m_transforming_liquid.push_back(flows[i].p);
2276 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2277 while (must_reflow.size() > 0)
2278 m_transforming_liquid.push_back(must_reflow.pop_front());
2279 updateLighting(lighting_modified_blocks, modified_blocks);
2282 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2284 v3s16 blockpos = getNodeBlockPos(p);
2285 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2286 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2288 infostream<<"Map::getNodeMetadata(): Need to emerge "
2289 <<PP(blockpos)<<std::endl;
2290 block = emergeBlock(blockpos, false);
2294 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2298 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2302 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2304 v3s16 blockpos = getNodeBlockPos(p);
2305 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2306 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2308 infostream<<"Map::setNodeMetadata(): Need to emerge "
2309 <<PP(blockpos)<<std::endl;
2310 block = emergeBlock(blockpos, false);
2314 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2318 block->m_node_metadata.set(p_rel, meta);
2321 void Map::removeNodeMetadata(v3s16 p)
2323 v3s16 blockpos = getNodeBlockPos(p);
2324 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2325 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2328 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2332 block->m_node_metadata.remove(p_rel);
2335 NodeTimer Map::getNodeTimer(v3s16 p)
2337 v3s16 blockpos = getNodeBlockPos(p);
2338 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2339 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2341 infostream<<"Map::getNodeTimer(): Need to emerge "
2342 <<PP(blockpos)<<std::endl;
2343 block = emergeBlock(blockpos, false);
2347 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2351 NodeTimer t = block->m_node_timers.get(p_rel);
2355 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2357 v3s16 blockpos = getNodeBlockPos(p);
2358 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2359 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2361 infostream<<"Map::setNodeTimer(): Need to emerge "
2362 <<PP(blockpos)<<std::endl;
2363 block = emergeBlock(blockpos, false);
2367 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2371 block->m_node_timers.set(p_rel, t);
2374 void Map::removeNodeTimer(v3s16 p)
2376 v3s16 blockpos = getNodeBlockPos(p);
2377 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2378 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2381 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2385 block->m_node_timers.remove(p_rel);
2388 s16 Map::getHeat(v3s16 p)
2390 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2394 //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2398 s16 Map::getHumidity(v3s16 p)
2400 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2402 return block->humidity;
2404 //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2411 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2412 Map(dout_server, gamedef),
2414 m_map_metadata_changed(true)
2416 verbosestream<<__FUNCTION_NAME<<std::endl;
2419 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2421 m_mgparams = new MapgenV6Params();
2423 m_seed = m_mgparams->seed;
2425 if (g_settings->get("fixed_map_seed").empty())
2427 m_seed = (((u64)(myrand() & 0xffff) << 0)
2428 | ((u64)(myrand() & 0xffff) << 16)
2429 | ((u64)(myrand() & 0xffff) << 32)
2430 | ((u64)(myrand() & 0xffff) << 48));
2431 m_mgparams->seed = m_seed;
2435 Experimental and debug stuff
2442 Try to load map; if not found, create a new one.
2445 // Determine which database backend to use
2446 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2448 bool succeeded = conf.readConfigFile(conf_path.c_str());
2449 if (!succeeded || !conf.exists("backend")) {
2450 // fall back to sqlite3
2451 dbase = new Database_SQLite3(this, savedir);
2452 conf.set("backend", "sqlite3");
2454 std::string backend = conf.get("backend");
2455 if (backend == "dummy")
2456 dbase = new Database_Dummy(this);
2457 else if (backend == "sqlite3")
2458 dbase = new Database_SQLite3(this, savedir);
2460 else if (backend == "leveldb")
2461 dbase = new Database_LevelDB(this, savedir);
2464 throw BaseException("Unknown map backend");
2467 m_savedir = savedir;
2468 m_map_saving_enabled = false;
2472 // If directory exists, check contents and load if possible
2473 if(fs::PathExists(m_savedir))
2475 // If directory is empty, it is safe to save into it.
2476 if(fs::GetDirListing(m_savedir).size() == 0)
2478 infostream<<"ServerMap: Empty save directory is valid."
2480 m_map_saving_enabled = true;
2485 // Load map metadata (seed, chunksize)
2488 catch(SettingNotFoundException &e){
2489 infostream<<"ServerMap: Some metadata not found."
2490 <<" Using default settings."<<std::endl;
2492 catch(FileNotGoodException &e){
2493 infostream<<"WARNING: Could not load map metadata"
2494 //<<" Disabling chunk-based generator."
2499 infostream<<"ServerMap: Successfully loaded map "
2500 <<"metadata from "<<savedir
2501 <<", assuming valid save directory."
2502 <<" seed="<<m_seed<<"."
2505 m_map_saving_enabled = true;
2506 // Map loaded, not creating new one
2510 // If directory doesn't exist, it is safe to save to it
2512 m_map_saving_enabled = true;
2515 catch(std::exception &e)
2517 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2518 <<", exception: "<<e.what()<<std::endl;
2519 infostream<<"Please remove the map or fix it."<<std::endl;
2520 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2523 infostream<<"Initializing new map."<<std::endl;
2525 // Create zero sector
2526 emergeSector(v2s16(0,0));
2528 // Initially write whole map
2529 save(MOD_STATE_CLEAN);
2532 ServerMap::~ServerMap()
2534 verbosestream<<__FUNCTION_NAME<<std::endl;
2538 if(m_map_saving_enabled)
2540 // Save only changed parts
2541 save(MOD_STATE_WRITE_AT_UNLOAD);
2542 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2546 infostream<<"ServerMap: Map not saved"<<std::endl;
2549 catch(std::exception &e)
2551 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2552 <<", exception: "<<e.what()<<std::endl;
2556 Close database if it was opened
2564 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2565 for(; i.atEnd() == false; i++)
2567 MapChunk *chunk = i.getNode()->getValue();
2575 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2577 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2578 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2580 s16 chunksize = m_mgparams->chunksize;
2581 s16 coffset = -chunksize / 2;
2582 v3s16 chunk_offset(coffset, coffset, coffset);
2583 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2584 v3s16 blockpos_min = blockpos_div * chunksize;
2585 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2586 blockpos_min += chunk_offset;
2587 blockpos_max += chunk_offset;
2589 v3s16 extra_borders(1,1,1);
2591 // Do nothing if not inside limits (+-1 because of neighbors)
2592 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2593 blockpos_over_limit(blockpos_max + extra_borders))
2596 data->seed = m_seed;
2597 data->blockpos_min = blockpos_min;
2598 data->blockpos_max = blockpos_max;
2599 data->blockpos_requested = blockpos;
2600 data->nodedef = m_gamedef->ndef();
2603 Create the whole area of this and the neighboring blocks
2606 //TimeTaker timer("initBlockMake() create area");
2608 for(s16 x=blockpos_min.X-extra_borders.X;
2609 x<=blockpos_max.X+extra_borders.X; x++)
2610 for(s16 z=blockpos_min.Z-extra_borders.Z;
2611 z<=blockpos_max.Z+extra_borders.Z; z++)
2613 v2s16 sectorpos(x, z);
2614 // Sector metadata is loaded from disk if not already loaded.
2615 ServerMapSector *sector = createSector(sectorpos);
2618 for(s16 y=blockpos_min.Y-extra_borders.Y;
2619 y<=blockpos_max.Y+extra_borders.Y; y++)
2622 //MapBlock *block = createBlock(p);
2623 // 1) get from memory, 2) load from disk
2624 MapBlock *block = emergeBlock(p, false);
2625 // 3) create a blank one
2628 block = createBlock(p);
2631 Block gets sunlight if this is true.
2633 Refer to the map generator heuristics.
2635 bool ug = m_emerge->isBlockUnderground(p);
2636 block->setIsUnderground(ug);
2639 // Lighting will not be valid after make_chunk is called
2640 block->setLightingExpired(true);
2641 // Lighting will be calculated
2642 //block->setLightingExpired(false);
2648 Now we have a big empty area.
2650 Make a ManualMapVoxelManipulator that contains this and the
2654 // The area that contains this block and it's neighbors
2655 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2656 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2658 data->vmanip = new ManualMapVoxelManipulator(this);
2659 //data->vmanip->setMap(this);
2663 //TimeTaker timer("initBlockMake() initialEmerge");
2664 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2667 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2668 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2669 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2670 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2671 core::map<v3s16, u8>::Node *n;
2672 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2675 u8 flags = n->getValue();
2676 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2682 // Data is ready now.
2686 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2687 std::map<v3s16, MapBlock*> &changed_blocks)
2689 v3s16 blockpos_min = data->blockpos_min;
2690 v3s16 blockpos_max = data->blockpos_max;
2691 v3s16 blockpos_requested = data->blockpos_requested;
2692 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2693 <<blockpos_requested.Y<<","
2694 <<blockpos_requested.Z<<")"<<std::endl;*/
2696 v3s16 extra_borders(1,1,1);
2698 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2700 /*infostream<<"Resulting vmanip:"<<std::endl;
2701 data->vmanip.print(infostream);*/
2703 // Make sure affected blocks are loaded
2704 for(s16 x=blockpos_min.X-extra_borders.X;
2705 x<=blockpos_max.X+extra_borders.X; x++)
2706 for(s16 z=blockpos_min.Z-extra_borders.Z;
2707 z<=blockpos_max.Z+extra_borders.Z; z++)
2708 for(s16 y=blockpos_min.Y-extra_borders.Y;
2709 y<=blockpos_max.Y+extra_borders.Y; y++)
2712 // Load from disk if not already in memory
2713 emergeBlock(p, false);
2717 Blit generated stuff to map
2718 NOTE: blitBackAll adds nearly everything to changed_blocks
2722 //TimeTaker timer("finishBlockMake() blitBackAll");
2723 data->vmanip->blitBackAll(&changed_blocks);
2726 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2729 Copy transforming liquid information
2731 while(data->transforming_liquid.size() > 0)
2733 v3s16 p = data->transforming_liquid.pop_front();
2734 m_transforming_liquid.push_back(p);
2738 Do stuff in central blocks
2746 TimeTaker t("finishBlockMake lighting update");
2748 core::map<v3s16, MapBlock*> lighting_update_blocks;
2751 for(s16 x=blockpos_min.X-extra_borders.X;
2752 x<=blockpos_max.X+extra_borders.X; x++)
2753 for(s16 z=blockpos_min.Z-extra_borders.Z;
2754 z<=blockpos_max.Z+extra_borders.Z; z++)
2755 for(s16 y=blockpos_min.Y-extra_borders.Y;
2756 y<=blockpos_max.Y+extra_borders.Y; y++)
2759 MapBlock *block = getBlockNoCreateNoEx(p);
2761 lighting_update_blocks.insert(block->getPos(), block);
2764 updateLighting(lighting_update_blocks, changed_blocks);
2768 Set lighting to non-expired state in all of them.
2769 This is cheating, but it is not fast enough if all of them
2770 would actually be updated.
2772 for(s16 x=blockpos_min.X-extra_borders.X;
2773 x<=blockpos_max.X+extra_borders.X; x++)
2774 for(s16 z=blockpos_min.Z-extra_borders.Z;
2775 z<=blockpos_max.Z+extra_borders.Z; z++)
2776 for(s16 y=blockpos_min.Y-extra_borders.Y;
2777 y<=blockpos_max.Y+extra_borders.Y; y++)
2780 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2784 if(enable_mapgen_debug_info == false)
2785 t.stop(true); // Hide output
2790 Go through changed blocks
2792 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2793 i != changed_blocks.end(); ++i)
2795 MapBlock *block = i->second;
2798 Update day/night difference cache of the MapBlocks
2800 block->expireDayNightDiff();
2802 Set block as modified
2804 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2805 "finishBlockMake expireDayNightDiff");
2809 Set central blocks as generated
2811 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2812 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2813 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2816 MapBlock *block = getBlockNoCreateNoEx(p);
2818 block->setGenerated(true);
2822 Save changed parts of map
2823 NOTE: Will be saved later.
2825 //save(MOD_STATE_WRITE_AT_UNLOAD);
2827 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2828 <<","<<blockpos_requested.Y<<","
2829 <<blockpos_requested.Z<<")"<<std::endl;*/
2832 Update weather data in blocks
2834 ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
2835 for(s16 x=blockpos_min.X-extra_borders.X;
2836 x<=blockpos_max.X+extra_borders.X; x++)
2837 for(s16 z=blockpos_min.Z-extra_borders.Z;
2838 z<=blockpos_max.Z+extra_borders.Z; z++)
2839 for(s16 y=blockpos_min.Y-extra_borders.Y;
2840 y<=blockpos_max.Y+extra_borders.Y; y++)
2843 MapBlock *block = getBlockNoCreateNoEx(p);
2844 block->heat_last_update = 0;
2845 block->humidity_last_update = 0;
2846 if (senv->m_use_weather) {
2847 updateBlockHeat(senv, p * MAP_BLOCKSIZE, block);
2848 updateBlockHumidity(senv, p * MAP_BLOCKSIZE, block);
2850 block->heat = HEAT_UNDEFINED;
2851 block->humidity = HUMIDITY_UNDEFINED;
2856 if(enable_mapgen_debug_info)
2859 Analyze resulting blocks
2861 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2862 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2863 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2864 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2865 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2866 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2868 v3s16 p = v3s16(x,y,z);
2869 MapBlock *block = getBlockNoCreateNoEx(p);
2871 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2872 infostream<<"Generated "<<spos<<": "
2873 <<analyze_block(block)<<std::endl;
2878 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2884 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2886 DSTACKF("%s: p2d=(%d,%d)",
2891 Check if it exists already in memory
2893 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2898 Try to load it from disk (with blocks)
2900 //if(loadSectorFull(p2d) == true)
2903 Try to load metadata from disk
2906 if(loadSectorMeta(p2d) == true)
2908 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2911 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2912 throw InvalidPositionException("");
2918 Do not create over-limit
2920 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2921 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2922 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2923 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2924 throw InvalidPositionException("createSector(): pos. over limit");
2927 Generate blank sector
2930 sector = new ServerMapSector(this, p2d, m_gamedef);
2932 // Sector position on map in nodes
2933 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2938 m_sectors[p2d] = sector;
2945 This is a quick-hand function for calling makeBlock().
2947 MapBlock * ServerMap::generateBlock(
2949 std::map<v3s16, MapBlock*> &modified_blocks
2952 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2954 /*infostream<<"generateBlock(): "
2955 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2958 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2960 TimeTaker timer("generateBlock");
2962 //MapBlock *block = original_dummy;
2964 v2s16 p2d(p.X, p.Z);
2965 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2968 Do not generate over-limit
2970 if(blockpos_over_limit(p))
2972 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2973 throw InvalidPositionException("generateBlock(): pos. over limit");
2977 Create block make data
2980 initBlockMake(&data, p);
2986 TimeTaker t("mapgen::make_block()");
2987 mapgen->makeChunk(&data);
2988 //mapgen::make_block(&data);
2990 if(enable_mapgen_debug_info == false)
2991 t.stop(true); // Hide output
2995 Blit data back on map, update lighting, add mobs and whatever this does
2997 finishBlockMake(&data, modified_blocks);
3002 MapBlock *block = getBlockNoCreateNoEx(p);
3010 bool erroneus_content = false;
3011 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3012 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3013 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3016 MapNode n = block->getNode(p);
3017 if(n.getContent() == CONTENT_IGNORE)
3019 infostream<<"CONTENT_IGNORE at "
3020 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3022 erroneus_content = true;
3026 if(erroneus_content)
3035 Generate a completely empty block
3039 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3040 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3042 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3045 n.setContent(CONTENT_AIR);
3046 block->setNode(v3s16(x0,y0,z0), n);
3052 if(enable_mapgen_debug_info == false)
3053 timer.stop(true); // Hide output
3059 MapBlock * ServerMap::createBlock(v3s16 p)
3061 DSTACKF("%s: p=(%d,%d,%d)",
3062 __FUNCTION_NAME, p.X, p.Y, p.Z);
3065 Do not create over-limit
3067 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3068 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3069 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3070 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3071 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3072 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3073 throw InvalidPositionException("createBlock(): pos. over limit");
3075 v2s16 p2d(p.X, p.Z);
3078 This will create or load a sector if not found in memory.
3079 If block exists on disk, it will be loaded.
3081 NOTE: On old save formats, this will be slow, as it generates
3082 lighting on blocks for them.
3084 ServerMapSector *sector;
3086 sector = (ServerMapSector*)createSector(p2d);
3087 assert(sector->getId() == MAPSECTOR_SERVER);
3089 catch(InvalidPositionException &e)
3091 infostream<<"createBlock: createSector() failed"<<std::endl;
3095 NOTE: This should not be done, or at least the exception
3096 should not be passed on as std::exception, because it
3097 won't be catched at all.
3099 /*catch(std::exception &e)
3101 infostream<<"createBlock: createSector() failed: "
3102 <<e.what()<<std::endl;
3107 Try to get a block from the sector
3110 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3113 if(block->isDummy())
3118 block = sector->createBlankBlock(block_y);
3123 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3125 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3127 p.X, p.Y, p.Z, create_blank);
3130 MapBlock *block = getBlockNoCreateNoEx(p);
3131 if(block && block->isDummy() == false)
3136 MapBlock *block = loadBlock(p);
3142 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3143 MapBlock *block = sector->createBlankBlock(p.Y);
3147 /*if(allow_generate)
3149 std::map<v3s16, MapBlock*> modified_blocks;
3150 MapBlock *block = generateBlock(p, modified_blocks);
3154 event.type = MEET_OTHER;
3157 // Copy modified_blocks to event
3158 for(std::map<v3s16, MapBlock*>::iterator
3159 i = modified_blocks.begin();
3160 i != modified_blocks.end(); ++i)
3162 event.modified_blocks.insert(i->first);
3166 dispatchEvent(&event);
3175 void ServerMap::prepareBlock(MapBlock *block) {
3176 ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
3178 // Calculate weather conditions
3179 block->heat_last_update = 0;
3180 block->humidity_last_update = 0;
3181 if (senv->m_use_weather) {
3182 v3s16 p = block->getPos() * MAP_BLOCKSIZE;
3183 updateBlockHeat(senv, p, block);
3184 updateBlockHumidity(senv, p, block);
3186 block->heat = HEAT_UNDEFINED;
3187 block->humidity = HUMIDITY_UNDEFINED;
3191 s16 ServerMap::findGroundLevel(v2s16 p2d)
3195 Uh, just do something random...
3197 // Find existing map from top to down
3200 v3s16 p(p2d.X, max, p2d.Y);
3201 for(; p.Y>min; p.Y--)
3203 MapNode n = getNodeNoEx(p);
3204 if(n.getContent() != CONTENT_IGNORE)
3209 // If this node is not air, go to plan b
3210 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3212 // Search existing walkable and return it
3213 for(; p.Y>min; p.Y--)
3215 MapNode n = getNodeNoEx(p);
3216 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3225 Determine from map generator noise functions
3228 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3231 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3232 //return (s16)level;
3235 bool ServerMap::loadFromFolders() {
3236 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
3241 void ServerMap::createDirs(std::string path)
3243 if(fs::CreateAllDirs(path) == false)
3245 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3246 <<"\""<<path<<"\""<<std::endl;
3247 throw BaseException("ServerMap failed to create directory");
3251 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3257 snprintf(cc, 9, "%.4x%.4x",
3258 (unsigned int)pos.X&0xffff,
3259 (unsigned int)pos.Y&0xffff);
3261 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3263 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3264 (unsigned int)pos.X&0xfff,
3265 (unsigned int)pos.Y&0xfff);
3267 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3273 v2s16 ServerMap::getSectorPos(std::string dirname)
3277 std::string component;
3278 fs::RemoveLastPathComponent(dirname, &component, 1);
3279 if(component.size() == 8)
3282 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3284 else if(component.size() == 3)
3287 fs::RemoveLastPathComponent(dirname, &component, 2);
3288 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3289 // Sign-extend the 12 bit values up to 16 bits...
3290 if(x&0x800) x|=0xF000;
3291 if(y&0x800) y|=0xF000;
3298 v2s16 pos((s16)x, (s16)y);
3302 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3304 v2s16 p2d = getSectorPos(sectordir);
3306 if(blockfile.size() != 4){
3307 throw InvalidFilenameException("Invalid block filename");
3310 int r = sscanf(blockfile.c_str(), "%4x", &y);
3312 throw InvalidFilenameException("Invalid block filename");
3313 return v3s16(p2d.X, y, p2d.Y);
3316 std::string ServerMap::getBlockFilename(v3s16 p)
3319 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3323 void ServerMap::save(ModifiedState save_level)
3325 DSTACK(__FUNCTION_NAME);
3326 if(m_map_saving_enabled == false)
3328 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3332 if(save_level == MOD_STATE_CLEAN)
3333 infostream<<"ServerMap: Saving whole map, this can take time."
3336 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3341 // Profile modified reasons
3342 Profiler modprofiler;
3344 u32 sector_meta_count = 0;
3345 u32 block_count = 0;
3346 u32 block_count_all = 0; // Number of blocks in memory
3348 // Don't do anything with sqlite unless something is really saved
3349 bool save_started = false;
3351 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3352 i != m_sectors.end(); ++i)
3354 ServerMapSector *sector = (ServerMapSector*)i->second;
3355 assert(sector->getId() == MAPSECTOR_SERVER);
3357 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3359 saveSectorMeta(sector);
3360 sector_meta_count++;
3362 std::list<MapBlock*> blocks;
3363 sector->getBlocks(blocks);
3365 for(std::list<MapBlock*>::iterator j = blocks.begin();
3366 j != blocks.end(); ++j)
3368 MapBlock *block = *j;
3372 if(block->getModified() >= (u32)save_level)
3377 save_started = true;
3380 modprofiler.add(block->getModifiedReason(), 1);
3385 /*infostream<<"ServerMap: Written block ("
3386 <<block->getPos().X<<","
3387 <<block->getPos().Y<<","
3388 <<block->getPos().Z<<")"
3397 Only print if something happened or saved whole map
3399 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3400 || block_count != 0)
3402 infostream<<"ServerMap: Written: "
3403 <<sector_meta_count<<" sector metadata files, "
3404 <<block_count<<" block files"
3405 <<", "<<block_count_all<<" blocks in memory."
3407 PrintInfo(infostream); // ServerMap/ClientMap:
3408 infostream<<"Blocks modified by: "<<std::endl;
3409 modprofiler.print(infostream);
3413 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3415 if(loadFromFolders()){
3416 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3417 <<"all blocks that are stored in flat files"<<std::endl;
3419 dbase->listAllLoadableBlocks(dst);
3422 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3424 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3425 si != m_sectors.end(); ++si)
3427 MapSector *sector = si->second;
3429 std::list<MapBlock*> blocks;
3430 sector->getBlocks(blocks);
3432 for(std::list<MapBlock*>::iterator i = blocks.begin();
3433 i != blocks.end(); ++i)
3435 MapBlock *block = (*i);
3436 v3s16 p = block->getPos();
3442 void ServerMap::saveMapMeta()
3444 DSTACK(__FUNCTION_NAME);
3446 /*infostream<<"ServerMap::saveMapMeta(): "
3450 createDirs(m_savedir);
3452 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3453 std::ostringstream ss(std::ios_base::binary);
3457 m_emerge->setParamsToSettings(¶ms);
3458 params.writeLines(ss);
3460 ss<<"[end_of_params]\n";
3462 if(!fs::safeWriteToFile(fullpath, ss.str()))
3464 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3465 <<"could not write "<<fullpath<<std::endl;
3466 throw FileNotGoodException("Cannot save chunk metadata");
3469 m_map_metadata_changed = false;
3472 void ServerMap::loadMapMeta()
3474 DSTACK(__FUNCTION_NAME);
3476 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3479 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3480 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3481 if(is.good() == false)
3483 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3484 <<"could not open"<<fullpath<<std::endl;
3485 throw FileNotGoodException("Cannot open map metadata");
3493 throw SerializationError
3494 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3496 std::getline(is, line);
3497 std::string trimmedline = trim(line);
3498 if(trimmedline == "[end_of_params]")
3500 params.parseConfigLine(line);
3503 MapgenParams *mgparams;
3505 mgparams = m_emerge->getParamsFromSettings(¶ms);
3506 } catch (SettingNotFoundException &e) {
3507 infostream << "Couldn't get a setting from map_meta.txt: "
3508 << e.what() << std::endl;
3515 m_mgparams = mgparams;
3516 m_seed = mgparams->seed;
3518 if (params.exists("seed")) {
3519 m_seed = read_seed(params.get("seed").c_str());
3520 m_mgparams->seed = m_seed;
3524 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3527 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3529 DSTACK(__FUNCTION_NAME);
3530 // Format used for writing
3531 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3533 v2s16 pos = sector->getPos();
3534 std::string dir = getSectorDir(pos);
3537 std::string fullpath = dir + DIR_DELIM + "meta";
3538 std::ostringstream ss(std::ios_base::binary);
3540 sector->serialize(ss, version);
3542 if(!fs::safeWriteToFile(fullpath, ss.str()))
3543 throw FileNotGoodException("Cannot write sector metafile");
3545 sector->differs_from_disk = false;
3548 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3550 DSTACK(__FUNCTION_NAME);
3552 v2s16 p2d = getSectorPos(sectordir);
3554 ServerMapSector *sector = NULL;
3556 std::string fullpath = sectordir + DIR_DELIM + "meta";
3557 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3558 if(is.good() == false)
3560 // If the directory exists anyway, it probably is in some old
3561 // format. Just go ahead and create the sector.
3562 if(fs::PathExists(sectordir))
3564 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3565 <<fullpath<<" doesn't exist but directory does."
3566 <<" Continuing with a sector with no metadata."
3568 sector = new ServerMapSector(this, p2d, m_gamedef);
3569 m_sectors[p2d] = sector;
3573 throw FileNotGoodException("Cannot open sector metafile");
3578 sector = ServerMapSector::deSerialize
3579 (is, this, p2d, m_sectors, m_gamedef);
3581 saveSectorMeta(sector);
3584 sector->differs_from_disk = false;
3589 bool ServerMap::loadSectorMeta(v2s16 p2d)
3591 DSTACK(__FUNCTION_NAME);
3593 MapSector *sector = NULL;
3595 // The directory layout we're going to load from.
3596 // 1 - original sectors/xxxxzzzz/
3597 // 2 - new sectors2/xxx/zzz/
3598 // If we load from anything but the latest structure, we will
3599 // immediately save to the new one, and remove the old.
3601 std::string sectordir1 = getSectorDir(p2d, 1);
3602 std::string sectordir;
3603 if(fs::PathExists(sectordir1))
3605 sectordir = sectordir1;
3610 sectordir = getSectorDir(p2d, 2);
3614 sector = loadSectorMeta(sectordir, loadlayout != 2);
3616 catch(InvalidFilenameException &e)
3620 catch(FileNotGoodException &e)
3624 catch(std::exception &e)
3633 bool ServerMap::loadSectorFull(v2s16 p2d)
3635 DSTACK(__FUNCTION_NAME);
3637 MapSector *sector = NULL;
3639 // The directory layout we're going to load from.
3640 // 1 - original sectors/xxxxzzzz/
3641 // 2 - new sectors2/xxx/zzz/
3642 // If we load from anything but the latest structure, we will
3643 // immediately save to the new one, and remove the old.
3645 std::string sectordir1 = getSectorDir(p2d, 1);
3646 std::string sectordir;
3647 if(fs::PathExists(sectordir1))
3649 sectordir = sectordir1;
3654 sectordir = getSectorDir(p2d, 2);
3658 sector = loadSectorMeta(sectordir, loadlayout != 2);
3660 catch(InvalidFilenameException &e)
3664 catch(FileNotGoodException &e)
3668 catch(std::exception &e)
3676 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3678 std::vector<fs::DirListNode>::iterator i2;
3679 for(i2=list2.begin(); i2!=list2.end(); i2++)
3685 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3687 catch(InvalidFilenameException &e)
3689 // This catches unknown crap in directory
3695 infostream<<"Sector converted to new layout - deleting "<<
3696 sectordir1<<std::endl;
3697 fs::RecursiveDelete(sectordir1);
3704 void ServerMap::beginSave() {
3708 void ServerMap::endSave() {
3712 void ServerMap::saveBlock(MapBlock *block)
3714 dbase->saveBlock(block);
3717 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3719 DSTACK(__FUNCTION_NAME);
3721 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3724 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3725 if(is.good() == false)
3726 throw FileNotGoodException("Cannot open block file");
3728 v3s16 p3d = getBlockPos(sectordir, blockfile);
3729 v2s16 p2d(p3d.X, p3d.Z);
3731 assert(sector->getPos() == p2d);
3733 u8 version = SER_FMT_VER_INVALID;
3734 is.read((char*)&version, 1);
3737 throw SerializationError("ServerMap::loadBlock(): Failed"
3738 " to read MapBlock version");
3740 /*u32 block_size = MapBlock::serializedLength(version);
3741 SharedBuffer<u8> data(block_size);
3742 is.read((char*)*data, block_size);*/
3744 // This will always return a sector because we're the server
3745 //MapSector *sector = emergeSector(p2d);
3747 MapBlock *block = NULL;
3748 bool created_new = false;
3749 block = sector->getBlockNoCreateNoEx(p3d.Y);
3752 block = sector->createBlankBlockNoInsert(p3d.Y);
3757 block->deSerialize(is, version, true);
3759 // If it's a new block, insert it to the map
3761 sector->insertBlock(block);
3764 Save blocks loaded in old format in new format
3767 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3771 // Should be in database now, so delete the old file
3772 fs::RecursiveDelete(fullpath);
3775 // We just loaded it from the disk, so it's up-to-date.
3776 block->resetModified();
3779 catch(SerializationError &e)
3781 infostream<<"WARNING: Invalid block data on disk "
3782 <<"fullpath="<<fullpath
3783 <<" (SerializationError). "
3784 <<"what()="<<e.what()
3786 // Ignoring. A new one will be generated.
3789 // TODO: Backup file; name is in fullpath.
3793 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3795 DSTACK(__FUNCTION_NAME);
3798 std::istringstream is(*blob, std::ios_base::binary);
3800 u8 version = SER_FMT_VER_INVALID;
3801 is.read((char*)&version, 1);
3804 throw SerializationError("ServerMap::loadBlock(): Failed"
3805 " to read MapBlock version");
3807 /*u32 block_size = MapBlock::serializedLength(version);
3808 SharedBuffer<u8> data(block_size);
3809 is.read((char*)*data, block_size);*/
3811 // This will always return a sector because we're the server
3812 //MapSector *sector = emergeSector(p2d);
3814 MapBlock *block = NULL;
3815 bool created_new = false;
3816 block = sector->getBlockNoCreateNoEx(p3d.Y);
3819 block = sector->createBlankBlockNoInsert(p3d.Y);
3824 block->deSerialize(is, version, true);
3826 // If it's a new block, insert it to the map
3828 sector->insertBlock(block);
3831 Save blocks loaded in old format in new format
3834 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3835 // Only save if asked to; no need to update version
3839 // We just loaded it from, so it's up-to-date.
3840 block->resetModified();
3843 catch(SerializationError &e)
3845 errorstream<<"Invalid block data in database"
3846 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3847 <<" (SerializationError): "<<e.what()<<std::endl;
3849 // TODO: Block should be marked as invalid in memory so that it is
3850 // not touched but the game can run
3852 if(g_settings->getBool("ignore_world_load_errors")){
3853 errorstream<<"Ignoring block load error. Duck and cover! "
3854 <<"(ignore_world_load_errors)"<<std::endl;
3856 throw SerializationError("Invalid block data in database");
3862 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3864 DSTACK(__FUNCTION_NAME);
3866 v2s16 p2d(blockpos.X, blockpos.Z);
3870 ret = dbase->loadBlock(blockpos);
3871 if (ret) return (ret);
3872 // Not found in database, try the files
3874 // The directory layout we're going to load from.
3875 // 1 - original sectors/xxxxzzzz/
3876 // 2 - new sectors2/xxx/zzz/
3877 // If we load from anything but the latest structure, we will
3878 // immediately save to the new one, and remove the old.
3880 std::string sectordir1 = getSectorDir(p2d, 1);
3881 std::string sectordir;
3882 if(fs::PathExists(sectordir1))
3884 sectordir = sectordir1;
3889 sectordir = getSectorDir(p2d, 2);
3893 Make sure sector is loaded
3895 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3899 sector = loadSectorMeta(sectordir, loadlayout != 2);
3901 catch(InvalidFilenameException &e)
3905 catch(FileNotGoodException &e)
3909 catch(std::exception &e)
3916 Make sure file exists
3919 std::string blockfilename = getBlockFilename(blockpos);
3920 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3924 Load block and save it to the database
3926 loadBlock(sectordir, blockfilename, sector, true);
3927 return getBlockNoCreateNoEx(blockpos);
3930 void ServerMap::PrintInfo(std::ostream &out)
3935 s16 ServerMap::updateBlockHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
3937 u32 gametime = env->getGameTime();
3940 if (gametime - block->heat_last_update < 10)
3943 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3946 f32 heat = m_emerge->biomedef->calcBlockHeat(p, m_seed,
3947 env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed());
3951 block->heat_last_update = gametime;
3956 s16 ServerMap::updateBlockHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
3958 u32 gametime = env->getGameTime();
3961 if (gametime - block->humidity_last_update < 10)
3962 return block->humidity;
3964 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3967 f32 humidity = m_emerge->biomedef->calcBlockHumidity(p, m_seed,
3968 env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed());
3971 block->humidity = humidity;
3972 block->humidity_last_update = gametime;
3981 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3986 MapVoxelManipulator::~MapVoxelManipulator()
3988 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3992 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3994 TimeTaker timer1("emerge", &emerge_time);
3996 // Units of these are MapBlocks
3997 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3998 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4000 VoxelArea block_area_nodes
4001 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4003 addArea(block_area_nodes);
4005 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4006 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4007 for(s32 x=p_min.X; x<=p_max.X; x++)
4012 std::map<v3s16, u8>::iterator n;
4013 n = m_loaded_blocks.find(p);
4014 if(n != m_loaded_blocks.end())
4017 bool block_data_inexistent = false;
4020 TimeTaker timer1("emerge load", &emerge_load_time);
4022 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4023 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4025 a.print(infostream);
4026 infostream<<std::endl;*/
4028 block = m_map->getBlockNoCreate(p);
4029 if(block->isDummy())
4030 block_data_inexistent = true;
4032 block->copyTo(*this);
4034 catch(InvalidPositionException &e)
4036 block_data_inexistent = true;
4039 if(block_data_inexistent)
4041 flags |= VMANIP_BLOCK_DATA_INEXIST;
4043 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4044 // Fill with VOXELFLAG_INEXISTENT
4045 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4046 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4048 s32 i = m_area.index(a.MinEdge.X,y,z);
4049 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4052 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4054 // Mark that block was loaded as blank
4055 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4058 m_loaded_blocks[p] = flags;
4061 //infostream<<"emerge done"<<std::endl;
4065 SUGG: Add an option to only update eg. water and air nodes.
4066 This will make it interfere less with important stuff if
4069 void MapVoxelManipulator::blitBack
4070 (std::map<v3s16, MapBlock*> & modified_blocks)
4072 if(m_area.getExtent() == v3s16(0,0,0))
4075 //TimeTaker timer1("blitBack");
4077 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4078 <<m_loaded_blocks.size()<<std::endl;*/
4081 Initialize block cache
4083 v3s16 blockpos_last;
4084 MapBlock *block = NULL;
4085 bool block_checked_in_modified = false;
4087 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4088 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4089 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4093 u8 f = m_flags[m_area.index(p)];
4094 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4097 MapNode &n = m_data[m_area.index(p)];
4099 v3s16 blockpos = getNodeBlockPos(p);
4104 if(block == NULL || blockpos != blockpos_last){
4105 block = m_map->getBlockNoCreate(blockpos);
4106 blockpos_last = blockpos;
4107 block_checked_in_modified = false;
4110 // Calculate relative position in block
4111 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4113 // Don't continue if nothing has changed here
4114 if(block->getNode(relpos) == n)
4117 //m_map->setNode(m_area.MinEdge + p, n);
4118 block->setNode(relpos, n);
4121 Make sure block is in modified_blocks
4123 if(block_checked_in_modified == false)
4125 modified_blocks[blockpos] = block;
4126 block_checked_in_modified = true;
4129 catch(InvalidPositionException &e)
4135 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4136 MapVoxelManipulator(map),
4137 m_create_area(false)
4141 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4145 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4147 // Just create the area so that it can be pointed to
4148 VoxelManipulator::emerge(a, caller_id);
4151 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4152 v3s16 blockpos_max, bool load_if_inexistent)
4154 TimeTaker timer1("initialEmerge", &emerge_time);
4156 // Units of these are MapBlocks
4157 v3s16 p_min = blockpos_min;
4158 v3s16 p_max = blockpos_max;
4160 VoxelArea block_area_nodes
4161 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4163 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4166 infostream<<"initialEmerge: area: ";
4167 block_area_nodes.print(infostream);
4168 infostream<<" ("<<size_MB<<"MB)";
4169 infostream<<std::endl;
4172 addArea(block_area_nodes);
4174 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4175 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4176 for(s32 x=p_min.X; x<=p_max.X; x++)
4181 std::map<v3s16, u8>::iterator n;
4182 n = m_loaded_blocks.find(p);
4183 if(n != m_loaded_blocks.end())
4186 bool block_data_inexistent = false;
4189 TimeTaker timer1("emerge load", &emerge_load_time);
4191 block = m_map->getBlockNoCreate(p);
4192 if(block->isDummy())
4193 block_data_inexistent = true;
4195 block->copyTo(*this);
4197 catch(InvalidPositionException &e)
4199 block_data_inexistent = true;
4202 if(block_data_inexistent)
4205 if (load_if_inexistent) {
4206 ServerMap *svrmap = (ServerMap *)m_map;
4207 block = svrmap->emergeBlock(p, false);
4209 block = svrmap->createBlock(p);
4211 block->copyTo(*this);
4213 flags |= VMANIP_BLOCK_DATA_INEXIST;
4216 Mark area inexistent
4218 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4219 // Fill with VOXELFLAG_INEXISTENT
4220 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4221 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4223 s32 i = m_area.index(a.MinEdge.X,y,z);
4224 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4228 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4230 // Mark that block was loaded as blank
4231 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4234 m_loaded_blocks[p] = flags;
4238 void ManualMapVoxelManipulator::blitBackAll(
4239 std::map<v3s16, MapBlock*> * modified_blocks)
4241 if(m_area.getExtent() == v3s16(0,0,0))
4245 Copy data of all blocks
4247 for(std::map<v3s16, u8>::iterator
4248 i = m_loaded_blocks.begin();
4249 i != m_loaded_blocks.end(); ++i)
4252 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4253 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4254 if(existed == false)
4259 block->copyFrom(*this);
4262 (*modified_blocks)[p] = block;