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):
85 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
86 i != m_sectors.end(); ++i)
92 void Map::addEventReceiver(MapEventReceiver *event_receiver)
94 m_event_receivers.insert(event_receiver);
97 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
99 m_event_receivers.erase(event_receiver);
102 void Map::dispatchEvent(MapEditEvent *event)
104 for(std::set<MapEventReceiver*>::iterator
105 i = m_event_receivers.begin();
106 i != m_event_receivers.end(); ++i)
108 (*i)->onMapEditEvent(event);
112 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
114 if(m_sector_cache != NULL && p == m_sector_cache_p){
115 MapSector * sector = m_sector_cache;
119 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
121 if(n == m_sectors.end())
124 MapSector *sector = n->second;
126 // Cache the last result
127 m_sector_cache_p = p;
128 m_sector_cache = sector;
133 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
135 return getSectorNoGenerateNoExNoLock(p);
138 MapSector * Map::getSectorNoGenerate(v2s16 p)
140 MapSector *sector = getSectorNoGenerateNoEx(p);
142 throw InvalidPositionException();
147 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
149 v2s16 p2d(p3d.X, p3d.Z);
150 MapSector * sector = getSectorNoGenerateNoEx(p2d);
153 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
157 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
159 MapBlock *block = getBlockNoCreateNoEx(p3d);
161 throw InvalidPositionException();
165 bool Map::isNodeUnderground(v3s16 p)
167 v3s16 blockpos = getNodeBlockPos(p);
169 MapBlock * block = getBlockNoCreate(blockpos);
170 return block->getIsUnderground();
172 catch(InvalidPositionException &e)
178 bool Map::isValidPosition(v3s16 p)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreate(blockpos);
182 return (block != NULL);
185 // Returns a CONTENT_IGNORE node if not found
186 MapNode Map::getNodeNoEx(v3s16 p)
188 v3s16 blockpos = getNodeBlockPos(p);
189 MapBlock *block = getBlockNoCreateNoEx(blockpos);
191 return MapNode(CONTENT_IGNORE);
192 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
193 return block->getNodeNoCheck(relpos);
196 // throws InvalidPositionException if not found
197 MapNode Map::getNode(v3s16 p)
199 v3s16 blockpos = getNodeBlockPos(p);
200 MapBlock *block = getBlockNoCreateNoEx(blockpos);
202 throw InvalidPositionException();
203 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
204 return block->getNodeNoCheck(relpos);
207 // throws InvalidPositionException if not found
208 void Map::setNode(v3s16 p, MapNode & n)
210 v3s16 blockpos = getNodeBlockPos(p);
211 MapBlock *block = getBlockNoCreate(blockpos);
212 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
213 // Never allow placing CONTENT_IGNORE, it fucks up stuff
214 if(n.getContent() == CONTENT_IGNORE){
215 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
216 <<" while trying to replace \""
217 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
218 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
219 debug_stacks_print_to(infostream);
222 block->setNodeNoCheck(relpos, n);
227 Goes recursively through the neighbours of the node.
229 Alters only transparent nodes.
231 If the lighting of the neighbour is lower than the lighting of
232 the node was (before changing it to 0 at the step before), the
233 lighting of the neighbour is set to 0 and then the same stuff
234 repeats for the neighbour.
236 The ending nodes of the routine are stored in light_sources.
237 This is useful when a light is removed. In such case, this
238 routine can be called for the light node and then again for
239 light_sources to re-light the area without the removed light.
241 values of from_nodes are lighting values.
243 void Map::unspreadLight(enum LightBank bank,
244 std::map<v3s16, u8> & from_nodes,
245 std::set<v3s16> & light_sources,
246 std::map<v3s16, MapBlock*> & modified_blocks)
248 INodeDefManager *nodemgr = m_gamedef->ndef();
251 v3s16(0,0,1), // back
253 v3s16(1,0,0), // right
254 v3s16(0,0,-1), // front
255 v3s16(0,-1,0), // bottom
256 v3s16(-1,0,0), // left
259 if(from_nodes.size() == 0)
262 u32 blockchangecount = 0;
264 std::map<v3s16, u8> unlighted_nodes;
267 Initialize block cache
270 MapBlock *block = NULL;
271 // Cache this a bit, too
272 bool block_checked_in_modified = false;
274 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
275 j != from_nodes.end(); ++j)
277 v3s16 pos = j->first;
278 v3s16 blockpos = getNodeBlockPos(pos);
280 // Only fetch a new block if the block position has changed
282 if(block == NULL || blockpos != blockpos_last){
283 block = getBlockNoCreate(blockpos);
284 blockpos_last = blockpos;
286 block_checked_in_modified = false;
290 catch(InvalidPositionException &e)
298 // Calculate relative position in block
299 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
301 // Get node straight from the block
302 //MapNode n = block->getNode(relpos);
304 u8 oldlight = j->second;
306 // Loop through 6 neighbors
307 for(u16 i=0; i<6; i++)
309 // Get the position of the neighbor node
310 v3s16 n2pos = pos + dirs[i];
312 // Get the block where the node is located
313 v3s16 blockpos = getNodeBlockPos(n2pos);
317 // Only fetch a new block if the block position has changed
319 if(block == NULL || blockpos != blockpos_last){
320 block = getBlockNoCreate(blockpos);
321 blockpos_last = blockpos;
323 block_checked_in_modified = false;
327 catch(InvalidPositionException &e)
332 // Calculate relative position in block
333 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
334 // Get node straight from the block
335 MapNode n2 = block->getNode(relpos);
337 bool changed = false;
339 //TODO: Optimize output by optimizing light_sources?
342 If the neighbor is dimmer than what was specified
343 as oldlight (the light of the previous node)
345 if(n2.getLight(bank, nodemgr) < oldlight)
348 And the neighbor is transparent and it has some light
350 if(nodemgr->get(n2).light_propagates
351 && n2.getLight(bank, nodemgr) != 0)
354 Set light to 0 and add to queue
357 u8 current_light = n2.getLight(bank, nodemgr);
358 n2.setLight(bank, 0, nodemgr);
359 block->setNode(relpos, n2);
361 unlighted_nodes[n2pos] = current_light;
365 Remove from light_sources if it is there
366 NOTE: This doesn't happen nearly at all
368 /*if(light_sources.find(n2pos))
370 infostream<<"Removed from light_sources"<<std::endl;
371 light_sources.remove(n2pos);
376 if(light_sources.find(n2pos) != NULL)
377 light_sources.remove(n2pos);*/
380 light_sources.insert(n2pos);
383 // Add to modified_blocks
384 if(changed == true && block_checked_in_modified == false)
386 // If the block is not found in modified_blocks, add.
387 if(modified_blocks.find(blockpos) == modified_blocks.end())
389 modified_blocks[blockpos] = block;
391 block_checked_in_modified = true;
394 catch(InvalidPositionException &e)
401 /*infostream<<"unspreadLight(): Changed block "
402 <<blockchangecount<<" times"
403 <<" for "<<from_nodes.size()<<" nodes"
406 if(unlighted_nodes.size() > 0)
407 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
411 A single-node wrapper of the above
413 void Map::unLightNeighbors(enum LightBank bank,
414 v3s16 pos, u8 lightwas,
415 std::set<v3s16> & light_sources,
416 std::map<v3s16, MapBlock*> & modified_blocks)
418 std::map<v3s16, u8> from_nodes;
419 from_nodes[pos] = lightwas;
421 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
425 Lights neighbors of from_nodes, collects all them and then
428 void Map::spreadLight(enum LightBank bank,
429 std::set<v3s16> & from_nodes,
430 std::map<v3s16, MapBlock*> & modified_blocks)
432 INodeDefManager *nodemgr = m_gamedef->ndef();
434 const v3s16 dirs[6] = {
435 v3s16(0,0,1), // back
437 v3s16(1,0,0), // right
438 v3s16(0,0,-1), // front
439 v3s16(0,-1,0), // bottom
440 v3s16(-1,0,0), // left
443 if(from_nodes.size() == 0)
446 u32 blockchangecount = 0;
448 std::set<v3s16> lighted_nodes;
451 Initialize block cache
454 MapBlock *block = NULL;
455 // Cache this a bit, too
456 bool block_checked_in_modified = false;
458 for(std::set<v3s16>::iterator j = from_nodes.begin();
459 j != from_nodes.end(); ++j)
462 v3s16 blockpos = getNodeBlockPos(pos);
464 // Only fetch a new block if the block position has changed
466 if(block == NULL || blockpos != blockpos_last){
467 block = getBlockNoCreate(blockpos);
468 blockpos_last = blockpos;
470 block_checked_in_modified = false;
474 catch(InvalidPositionException &e)
482 // Calculate relative position in block
483 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
485 // Get node straight from the block
486 MapNode n = block->getNode(relpos);
488 u8 oldlight = n.getLight(bank, nodemgr);
489 u8 newlight = diminish_light(oldlight);
491 // Loop through 6 neighbors
492 for(u16 i=0; i<6; i++){
493 // Get the position of the neighbor node
494 v3s16 n2pos = pos + dirs[i];
496 // Get the block where the node is located
497 v3s16 blockpos = getNodeBlockPos(n2pos);
501 // Only fetch a new block if the block position has changed
503 if(block == NULL || blockpos != blockpos_last){
504 block = getBlockNoCreate(blockpos);
505 blockpos_last = blockpos;
507 block_checked_in_modified = false;
511 catch(InvalidPositionException &e)
516 // Calculate relative position in block
517 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
518 // Get node straight from the block
519 MapNode n2 = block->getNode(relpos);
521 bool changed = false;
523 If the neighbor is brighter than the current node,
524 add to list (it will light up this node on its turn)
526 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
528 lighted_nodes.insert(n2pos);
532 If the neighbor is dimmer than how much light this node
533 would spread on it, add to list
535 if(n2.getLight(bank, nodemgr) < newlight)
537 if(nodemgr->get(n2).light_propagates)
539 n2.setLight(bank, newlight, nodemgr);
540 block->setNode(relpos, n2);
541 lighted_nodes.insert(n2pos);
546 // Add to modified_blocks
547 if(changed == true && block_checked_in_modified == false)
549 // If the block is not found in modified_blocks, add.
550 if(modified_blocks.find(blockpos) == modified_blocks.end())
552 modified_blocks[blockpos] = block;
554 block_checked_in_modified = true;
557 catch(InvalidPositionException &e)
564 /*infostream<<"spreadLight(): Changed block "
565 <<blockchangecount<<" times"
566 <<" for "<<from_nodes.size()<<" nodes"
569 if(lighted_nodes.size() > 0)
570 spreadLight(bank, lighted_nodes, modified_blocks);
574 A single-node source variation of the above.
576 void Map::lightNeighbors(enum LightBank bank,
578 std::map<v3s16, MapBlock*> & modified_blocks)
580 std::set<v3s16> from_nodes;
581 from_nodes.insert(pos);
582 spreadLight(bank, from_nodes, modified_blocks);
585 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
587 INodeDefManager *nodemgr = m_gamedef->ndef();
590 v3s16(0,0,1), // back
592 v3s16(1,0,0), // right
593 v3s16(0,0,-1), // front
594 v3s16(0,-1,0), // bottom
595 v3s16(-1,0,0), // left
598 u8 brightest_light = 0;
599 v3s16 brightest_pos(0,0,0);
600 bool found_something = false;
602 // Loop through 6 neighbors
603 for(u16 i=0; i<6; i++){
604 // Get the position of the neighbor node
605 v3s16 n2pos = p + dirs[i];
610 catch(InvalidPositionException &e)
614 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
615 brightest_light = n2.getLight(bank, nodemgr);
616 brightest_pos = n2pos;
617 found_something = true;
621 if(found_something == false)
622 throw InvalidPositionException();
624 return brightest_pos;
628 Propagates sunlight down from a node.
629 Starting point gets sunlight.
631 Returns the lowest y value of where the sunlight went.
633 Mud is turned into grass in where the sunlight stops.
635 s16 Map::propagateSunlight(v3s16 start,
636 std::map<v3s16, MapBlock*> & modified_blocks)
638 INodeDefManager *nodemgr = m_gamedef->ndef();
643 v3s16 pos(start.X, y, start.Z);
645 v3s16 blockpos = getNodeBlockPos(pos);
648 block = getBlockNoCreate(blockpos);
650 catch(InvalidPositionException &e)
655 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
656 MapNode n = block->getNode(relpos);
658 if(nodemgr->get(n).sunlight_propagates)
660 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
661 block->setNode(relpos, n);
663 modified_blocks[blockpos] = block;
667 // Sunlight goes no further
674 void Map::updateLighting(enum LightBank bank,
675 std::map<v3s16, MapBlock*> & a_blocks,
676 std::map<v3s16, MapBlock*> & modified_blocks)
678 INodeDefManager *nodemgr = m_gamedef->ndef();
680 /*m_dout<<DTIME<<"Map::updateLighting(): "
681 <<a_blocks.size()<<" blocks."<<std::endl;*/
683 //TimeTaker timer("updateLighting");
687 //u32 count_was = modified_blocks.size();
689 std::map<v3s16, MapBlock*> blocks_to_update;
691 std::set<v3s16> light_sources;
693 std::map<v3s16, u8> unlight_from;
695 int num_bottom_invalid = 0;
698 //TimeTaker t("first stuff");
700 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
701 i != a_blocks.end(); ++i)
703 MapBlock *block = i->second;
707 // Don't bother with dummy blocks.
711 v3s16 pos = block->getPos();
712 v3s16 posnodes = block->getPosRelative();
713 modified_blocks[pos] = block;
714 blocks_to_update[pos] = block;
717 Clear all light from block
719 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
720 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
721 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
726 MapNode n = block->getNode(p);
727 u8 oldlight = n.getLight(bank, nodemgr);
728 n.setLight(bank, 0, nodemgr);
729 block->setNode(p, n);
731 // If node sources light, add to list
732 u8 source = nodemgr->get(n).light_source;
734 light_sources.insert(p + posnodes);
736 // Collect borders for unlighting
737 if((x==0 || x == MAP_BLOCKSIZE-1
738 || y==0 || y == MAP_BLOCKSIZE-1
739 || z==0 || z == MAP_BLOCKSIZE-1)
742 v3s16 p_map = p + posnodes;
743 unlight_from[p_map] = oldlight;
746 catch(InvalidPositionException &e)
749 This would happen when dealing with a
753 infostream<<"updateLighting(): InvalidPositionException"
758 if(bank == LIGHTBANK_DAY)
760 bool bottom_valid = block->propagateSunlight(light_sources);
763 num_bottom_invalid++;
765 // If bottom is valid, we're done.
769 else if(bank == LIGHTBANK_NIGHT)
771 // For night lighting, sunlight is not propagated
776 // Invalid lighting bank
780 /*infostream<<"Bottom for sunlight-propagated block ("
781 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
784 // Bottom sunlight is not valid; get the block and loop to it
788 block = getBlockNoCreate(pos);
790 catch(InvalidPositionException &e)
801 Enable this to disable proper lighting for speeding up map
802 generation for testing or whatever
805 //if(g_settings->get(""))
807 core::map<v3s16, MapBlock*>::Iterator i;
808 i = blocks_to_update.getIterator();
809 for(; i.atEnd() == false; i++)
811 MapBlock *block = i.getNode()->getValue();
812 v3s16 p = block->getPos();
813 block->setLightingExpired(false);
821 //TimeTaker timer("unspreadLight");
822 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
827 u32 diff = modified_blocks.size() - count_was;
828 count_was = modified_blocks.size();
829 infostream<<"unspreadLight modified "<<diff<<std::endl;
833 //TimeTaker timer("spreadLight");
834 spreadLight(bank, light_sources, modified_blocks);
839 u32 diff = modified_blocks.size() - count_was;
840 count_was = modified_blocks.size();
841 infostream<<"spreadLight modified "<<diff<<std::endl;
847 //MapVoxelManipulator vmanip(this);
849 // Make a manual voxel manipulator and load all the blocks
850 // that touch the requested blocks
851 ManualMapVoxelManipulator vmanip(this);
854 //TimeTaker timer("initialEmerge");
856 core::map<v3s16, MapBlock*>::Iterator i;
857 i = blocks_to_update.getIterator();
858 for(; i.atEnd() == false; i++)
860 MapBlock *block = i.getNode()->getValue();
861 v3s16 p = block->getPos();
863 // Add all surrounding blocks
864 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
867 Add all surrounding blocks that have up-to-date lighting
868 NOTE: This doesn't quite do the job (not everything
869 appropriate is lighted)
871 /*for(s16 z=-1; z<=1; z++)
872 for(s16 y=-1; y<=1; y++)
873 for(s16 x=-1; x<=1; x++)
875 v3s16 p2 = p + v3s16(x,y,z);
876 MapBlock *block = getBlockNoCreateNoEx(p2);
881 if(block->getLightingExpired())
883 vmanip.initialEmerge(p2, p2);
886 // Lighting of block will be updated completely
887 block->setLightingExpired(false);
892 //TimeTaker timer("unSpreadLight");
893 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
896 //TimeTaker timer("spreadLight");
897 vmanip.spreadLight(bank, light_sources, nodemgr);
900 //TimeTaker timer("blitBack");
901 vmanip.blitBack(modified_blocks);
903 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
908 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
911 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
912 std::map<v3s16, MapBlock*> & modified_blocks)
914 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
915 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
918 Update information about whether day and night light differ
920 for(std::map<v3s16, MapBlock*>::iterator
921 i = modified_blocks.begin();
922 i != modified_blocks.end(); ++i)
924 MapBlock *block = i->second;
925 block->expireDayNightDiff();
931 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
932 std::map<v3s16, MapBlock*> &modified_blocks,
933 bool remove_metadata)
935 INodeDefManager *ndef = m_gamedef->ndef();
938 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
939 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
942 From this node to nodes underneath:
943 If lighting is sunlight (1.0), unlight neighbours and
948 v3s16 toppos = p + v3s16(0,1,0);
949 //v3s16 bottompos = p + v3s16(0,-1,0);
951 bool node_under_sunlight = true;
952 std::set<v3s16> light_sources;
955 Collect old node for rollback
957 RollbackNode rollback_oldnode(this, p, m_gamedef);
960 If there is a node at top and it doesn't have sunlight,
961 there has not been any sunlight going down.
963 Otherwise there probably is.
966 MapNode topnode = getNode(toppos);
968 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
969 node_under_sunlight = false;
971 catch(InvalidPositionException &e)
976 Remove all light that has come out of this node
979 enum LightBank banks[] =
984 for(s32 i=0; i<2; i++)
986 enum LightBank bank = banks[i];
988 u8 lightwas = getNode(p).getLight(bank, ndef);
990 // Add the block of the added node to modified_blocks
991 v3s16 blockpos = getNodeBlockPos(p);
992 MapBlock * block = getBlockNoCreate(blockpos);
993 assert(block != NULL);
994 modified_blocks[blockpos] = block;
996 assert(isValidPosition(p));
998 // Unlight neighbours of node.
999 // This means setting light of all consequent dimmer nodes
1001 // This also collects the nodes at the border which will spread
1002 // light again into this.
1003 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1005 n.setLight(bank, 0, ndef);
1009 If node lets sunlight through and is under sunlight, it has
1012 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1014 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1018 Remove node metadata
1020 if (remove_metadata) {
1021 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, bool remove_metadata)
1325 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1329 bool succeeded = true;
1331 std::map<v3s16, MapBlock*> modified_blocks;
1332 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
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);
2293 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2297 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2301 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2303 v3s16 blockpos = getNodeBlockPos(p);
2304 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2305 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2307 infostream<<"Map::setNodeMetadata(): Need to emerge "
2308 <<PP(blockpos)<<std::endl;
2309 block = emergeBlock(blockpos, false);
2312 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2316 block->m_node_metadata.set(p_rel, meta);
2320 void Map::removeNodeMetadata(v3s16 p)
2322 v3s16 blockpos = getNodeBlockPos(p);
2323 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2324 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2327 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2331 block->m_node_metadata.remove(p_rel);
2334 NodeTimer Map::getNodeTimer(v3s16 p)
2336 v3s16 blockpos = getNodeBlockPos(p);
2337 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2338 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2340 infostream<<"Map::getNodeTimer(): Need to emerge "
2341 <<PP(blockpos)<<std::endl;
2342 block = emergeBlock(blockpos, false);
2345 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2349 NodeTimer t = block->m_node_timers.get(p_rel);
2353 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2355 v3s16 blockpos = getNodeBlockPos(p);
2356 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2357 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2359 infostream<<"Map::setNodeTimer(): Need to emerge "
2360 <<PP(blockpos)<<std::endl;
2361 block = emergeBlock(blockpos, false);
2364 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2368 block->m_node_timers.set(p_rel, t);
2371 void Map::removeNodeTimer(v3s16 p)
2373 v3s16 blockpos = getNodeBlockPos(p);
2374 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2375 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2378 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2382 block->m_node_timers.remove(p_rel);
2385 s16 Map::getHeat(v3s16 p)
2387 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2391 //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2395 s16 Map::getHumidity(v3s16 p)
2397 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2399 return block->humidity;
2401 //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2408 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2409 Map(dout_server, gamedef),
2411 m_map_metadata_changed(true)
2413 verbosestream<<__FUNCTION_NAME<<std::endl;
2416 Try to load map; if not found, create a new one.
2419 // Determine which database backend to use
2420 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2422 bool succeeded = conf.readConfigFile(conf_path.c_str());
2423 if (!succeeded || !conf.exists("backend")) {
2424 // fall back to sqlite3
2425 dbase = new Database_SQLite3(this, savedir);
2426 conf.set("backend", "sqlite3");
2428 std::string backend = conf.get("backend");
2429 if (backend == "dummy")
2430 dbase = new Database_Dummy(this);
2431 else if (backend == "sqlite3")
2432 dbase = new Database_SQLite3(this, savedir);
2434 else if (backend == "leveldb")
2435 dbase = new Database_LevelDB(this, savedir);
2438 throw BaseException("Unknown map backend");
2441 m_savedir = savedir;
2442 m_map_saving_enabled = false;
2446 // If directory exists, check contents and load if possible
2447 if(fs::PathExists(m_savedir))
2449 // If directory is empty, it is safe to save into it.
2450 if(fs::GetDirListing(m_savedir).size() == 0)
2452 infostream<<"ServerMap: Empty save directory is valid."
2454 m_map_saving_enabled = true;
2459 // Load map metadata (seed, chunksize)
2462 catch(SettingNotFoundException &e){
2463 infostream<<"ServerMap: Some metadata not found."
2464 <<" Using default settings."<<std::endl;
2466 catch(FileNotGoodException &e){
2467 infostream<<"WARNING: Could not load map metadata"
2468 //<<" Disabling chunk-based generator."
2473 infostream<<"ServerMap: Successfully loaded map "
2474 <<"metadata from "<<savedir
2475 <<", assuming valid save directory."
2476 <<" seed="<< m_emerge->params.seed <<"."
2479 m_map_saving_enabled = true;
2480 // Map loaded, not creating new one
2484 // If directory doesn't exist, it is safe to save to it
2486 m_map_saving_enabled = true;
2489 catch(std::exception &e)
2491 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2492 <<", exception: "<<e.what()<<std::endl;
2493 infostream<<"Please remove the map or fix it."<<std::endl;
2494 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2497 infostream<<"Initializing new map."<<std::endl;
2499 // Create zero sector
2500 emergeSector(v2s16(0,0));
2502 // Initially write whole map
2503 save(MOD_STATE_CLEAN);
2506 ServerMap::~ServerMap()
2508 verbosestream<<__FUNCTION_NAME<<std::endl;
2512 if(m_map_saving_enabled)
2514 // Save only changed parts
2515 save(MOD_STATE_WRITE_AT_UNLOAD);
2516 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2520 infostream<<"ServerMap: Map not saved"<<std::endl;
2523 catch(std::exception &e)
2525 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2526 <<", exception: "<<e.what()<<std::endl;
2530 Close database if it was opened
2538 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2539 for(; i.atEnd() == false; i++)
2541 MapChunk *chunk = i.getNode()->getValue();
2547 u64 ServerMap::getSeed()
2549 return m_emerge->params.seed;
2552 s16 ServerMap::getWaterLevel()
2554 return m_emerge->params.water_level;
2557 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2559 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2560 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2562 s16 chunksize = m_emerge->params.chunksize;
2563 s16 coffset = -chunksize / 2;
2564 v3s16 chunk_offset(coffset, coffset, coffset);
2565 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2566 v3s16 blockpos_min = blockpos_div * chunksize;
2567 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2568 blockpos_min += chunk_offset;
2569 blockpos_max += chunk_offset;
2571 v3s16 extra_borders(1,1,1);
2573 // Do nothing if not inside limits (+-1 because of neighbors)
2574 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2575 blockpos_over_limit(blockpos_max + extra_borders))
2578 data->seed = m_emerge->params.seed;
2579 data->blockpos_min = blockpos_min;
2580 data->blockpos_max = blockpos_max;
2581 data->blockpos_requested = blockpos;
2582 data->nodedef = m_gamedef->ndef();
2585 Create the whole area of this and the neighboring blocks
2588 //TimeTaker timer("initBlockMake() create area");
2590 for(s16 x=blockpos_min.X-extra_borders.X;
2591 x<=blockpos_max.X+extra_borders.X; x++)
2592 for(s16 z=blockpos_min.Z-extra_borders.Z;
2593 z<=blockpos_max.Z+extra_borders.Z; z++)
2595 v2s16 sectorpos(x, z);
2596 // Sector metadata is loaded from disk if not already loaded.
2597 ServerMapSector *sector = createSector(sectorpos);
2600 for(s16 y=blockpos_min.Y-extra_borders.Y;
2601 y<=blockpos_max.Y+extra_borders.Y; y++)
2604 //MapBlock *block = createBlock(p);
2605 // 1) get from memory, 2) load from disk
2606 MapBlock *block = emergeBlock(p, false);
2607 // 3) create a blank one
2610 block = createBlock(p);
2613 Block gets sunlight if this is true.
2615 Refer to the map generator heuristics.
2617 bool ug = m_emerge->isBlockUnderground(p);
2618 block->setIsUnderground(ug);
2621 // Lighting will not be valid after make_chunk is called
2622 block->setLightingExpired(true);
2623 // Lighting will be calculated
2624 //block->setLightingExpired(false);
2630 Now we have a big empty area.
2632 Make a ManualMapVoxelManipulator that contains this and the
2636 // The area that contains this block and it's neighbors
2637 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2638 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2640 data->vmanip = new ManualMapVoxelManipulator(this);
2641 //data->vmanip->setMap(this);
2645 //TimeTaker timer("initBlockMake() initialEmerge");
2646 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2649 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2650 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2651 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2652 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2653 core::map<v3s16, u8>::Node *n;
2654 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2657 u8 flags = n->getValue();
2658 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2664 // Data is ready now.
2668 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2669 std::map<v3s16, MapBlock*> &changed_blocks)
2671 v3s16 blockpos_min = data->blockpos_min;
2672 v3s16 blockpos_max = data->blockpos_max;
2673 v3s16 blockpos_requested = data->blockpos_requested;
2674 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2675 <<blockpos_requested.Y<<","
2676 <<blockpos_requested.Z<<")"<<std::endl;*/
2678 v3s16 extra_borders(1,1,1);
2680 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2682 /*infostream<<"Resulting vmanip:"<<std::endl;
2683 data->vmanip.print(infostream);*/
2685 // Make sure affected blocks are loaded
2686 for(s16 x=blockpos_min.X-extra_borders.X;
2687 x<=blockpos_max.X+extra_borders.X; x++)
2688 for(s16 z=blockpos_min.Z-extra_borders.Z;
2689 z<=blockpos_max.Z+extra_borders.Z; z++)
2690 for(s16 y=blockpos_min.Y-extra_borders.Y;
2691 y<=blockpos_max.Y+extra_borders.Y; y++)
2694 // Load from disk if not already in memory
2695 emergeBlock(p, false);
2699 Blit generated stuff to map
2700 NOTE: blitBackAll adds nearly everything to changed_blocks
2704 //TimeTaker timer("finishBlockMake() blitBackAll");
2705 data->vmanip->blitBackAll(&changed_blocks);
2708 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2711 Copy transforming liquid information
2713 while(data->transforming_liquid.size() > 0)
2715 v3s16 p = data->transforming_liquid.pop_front();
2716 m_transforming_liquid.push_back(p);
2720 Do stuff in central blocks
2728 TimeTaker t("finishBlockMake lighting update");
2730 core::map<v3s16, MapBlock*> lighting_update_blocks;
2733 for(s16 x=blockpos_min.X-extra_borders.X;
2734 x<=blockpos_max.X+extra_borders.X; x++)
2735 for(s16 z=blockpos_min.Z-extra_borders.Z;
2736 z<=blockpos_max.Z+extra_borders.Z; z++)
2737 for(s16 y=blockpos_min.Y-extra_borders.Y;
2738 y<=blockpos_max.Y+extra_borders.Y; y++)
2741 MapBlock *block = getBlockNoCreateNoEx(p);
2743 lighting_update_blocks.insert(block->getPos(), block);
2746 updateLighting(lighting_update_blocks, changed_blocks);
2750 Set lighting to non-expired state in all of them.
2751 This is cheating, but it is not fast enough if all of them
2752 would actually be updated.
2754 for(s16 x=blockpos_min.X-extra_borders.X;
2755 x<=blockpos_max.X+extra_borders.X; x++)
2756 for(s16 z=blockpos_min.Z-extra_borders.Z;
2757 z<=blockpos_max.Z+extra_borders.Z; z++)
2758 for(s16 y=blockpos_min.Y-extra_borders.Y;
2759 y<=blockpos_max.Y+extra_borders.Y; y++)
2762 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2766 if(enable_mapgen_debug_info == false)
2767 t.stop(true); // Hide output
2772 Go through changed blocks
2774 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2775 i != changed_blocks.end(); ++i)
2777 MapBlock *block = i->second;
2780 Update day/night difference cache of the MapBlocks
2782 block->expireDayNightDiff();
2784 Set block as modified
2786 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2787 "finishBlockMake expireDayNightDiff");
2791 Set central blocks as generated
2793 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2794 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2795 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2798 MapBlock *block = getBlockNoCreateNoEx(p);
2800 block->setGenerated(true);
2804 Save changed parts of map
2805 NOTE: Will be saved later.
2807 //save(MOD_STATE_WRITE_AT_UNLOAD);
2809 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2810 <<","<<blockpos_requested.Y<<","
2811 <<blockpos_requested.Z<<")"<<std::endl;*/
2814 Update weather data in blocks
2816 ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
2817 for(s16 x=blockpos_min.X-extra_borders.X;
2818 x<=blockpos_max.X+extra_borders.X; x++)
2819 for(s16 z=blockpos_min.Z-extra_borders.Z;
2820 z<=blockpos_max.Z+extra_borders.Z; z++)
2821 for(s16 y=blockpos_min.Y-extra_borders.Y;
2822 y<=blockpos_max.Y+extra_borders.Y; y++)
2825 MapBlock *block = getBlockNoCreateNoEx(p);
2826 block->heat_last_update = 0;
2827 block->humidity_last_update = 0;
2828 if (senv->m_use_weather) {
2829 updateBlockHeat(senv, p * MAP_BLOCKSIZE, block);
2830 updateBlockHumidity(senv, p * MAP_BLOCKSIZE, block);
2832 block->heat = HEAT_UNDEFINED;
2833 block->humidity = HUMIDITY_UNDEFINED;
2838 if(enable_mapgen_debug_info)
2841 Analyze resulting blocks
2843 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2844 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2845 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2846 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2847 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2848 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2850 v3s16 p = v3s16(x,y,z);
2851 MapBlock *block = getBlockNoCreateNoEx(p);
2853 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2854 infostream<<"Generated "<<spos<<": "
2855 <<analyze_block(block)<<std::endl;
2860 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2866 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2868 DSTACKF("%s: p2d=(%d,%d)",
2873 Check if it exists already in memory
2875 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2880 Try to load it from disk (with blocks)
2882 //if(loadSectorFull(p2d) == true)
2885 Try to load metadata from disk
2888 if(loadSectorMeta(p2d) == true)
2890 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2893 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2894 throw InvalidPositionException("");
2900 Do not create over-limit
2902 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2903 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2904 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2905 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2906 throw InvalidPositionException("createSector(): pos. over limit");
2909 Generate blank sector
2912 sector = new ServerMapSector(this, p2d, m_gamedef);
2914 // Sector position on map in nodes
2915 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2920 m_sectors[p2d] = sector;
2927 This is a quick-hand function for calling makeBlock().
2929 MapBlock * ServerMap::generateBlock(
2931 std::map<v3s16, MapBlock*> &modified_blocks
2934 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2936 /*infostream<<"generateBlock(): "
2937 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2940 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2942 TimeTaker timer("generateBlock");
2944 //MapBlock *block = original_dummy;
2946 v2s16 p2d(p.X, p.Z);
2947 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2950 Do not generate over-limit
2952 if(blockpos_over_limit(p))
2954 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2955 throw InvalidPositionException("generateBlock(): pos. over limit");
2959 Create block make data
2962 initBlockMake(&data, p);
2968 TimeTaker t("mapgen::make_block()");
2969 mapgen->makeChunk(&data);
2970 //mapgen::make_block(&data);
2972 if(enable_mapgen_debug_info == false)
2973 t.stop(true); // Hide output
2977 Blit data back on map, update lighting, add mobs and whatever this does
2979 finishBlockMake(&data, modified_blocks);
2984 MapBlock *block = getBlockNoCreateNoEx(p);
2992 bool erroneus_content = false;
2993 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2994 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2995 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2998 MapNode n = block->getNode(p);
2999 if(n.getContent() == CONTENT_IGNORE)
3001 infostream<<"CONTENT_IGNORE at "
3002 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3004 erroneus_content = true;
3008 if(erroneus_content)
3017 Generate a completely empty block
3021 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3022 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3024 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3027 n.setContent(CONTENT_AIR);
3028 block->setNode(v3s16(x0,y0,z0), n);
3034 if(enable_mapgen_debug_info == false)
3035 timer.stop(true); // Hide output
3041 MapBlock * ServerMap::createBlock(v3s16 p)
3043 DSTACKF("%s: p=(%d,%d,%d)",
3044 __FUNCTION_NAME, p.X, p.Y, p.Z);
3047 Do not create over-limit
3049 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3050 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3051 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3052 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3053 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3054 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3055 throw InvalidPositionException("createBlock(): pos. over limit");
3057 v2s16 p2d(p.X, p.Z);
3060 This will create or load a sector if not found in memory.
3061 If block exists on disk, it will be loaded.
3063 NOTE: On old save formats, this will be slow, as it generates
3064 lighting on blocks for them.
3066 ServerMapSector *sector;
3068 sector = (ServerMapSector*)createSector(p2d);
3069 assert(sector->getId() == MAPSECTOR_SERVER);
3071 catch(InvalidPositionException &e)
3073 infostream<<"createBlock: createSector() failed"<<std::endl;
3077 NOTE: This should not be done, or at least the exception
3078 should not be passed on as std::exception, because it
3079 won't be catched at all.
3081 /*catch(std::exception &e)
3083 infostream<<"createBlock: createSector() failed: "
3084 <<e.what()<<std::endl;
3089 Try to get a block from the sector
3092 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3095 if(block->isDummy())
3100 block = sector->createBlankBlock(block_y);
3105 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3107 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3109 p.X, p.Y, p.Z, create_blank);
3112 MapBlock *block = getBlockNoCreateNoEx(p);
3113 if(block && block->isDummy() == false)
3118 MapBlock *block = loadBlock(p);
3124 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3125 MapBlock *block = sector->createBlankBlock(p.Y);
3133 std::map<v3s16, MapBlock*> modified_blocks;
3134 MapBlock *block = generateBlock(p, modified_blocks);
3138 event.type = MEET_OTHER;
3141 // Copy modified_blocks to event
3142 for(std::map<v3s16, MapBlock*>::iterator
3143 i = modified_blocks.begin();
3144 i != modified_blocks.end(); ++i)
3146 event.modified_blocks.insert(i->first);
3150 dispatchEvent(&event);
3160 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
3162 MapBlock *block = getBlockNoCreateNoEx(p3d);
3164 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
3169 void ServerMap::prepareBlock(MapBlock *block) {
3170 ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
3172 // Calculate weather conditions
3173 block->heat_last_update = 0;
3174 block->humidity_last_update = 0;
3175 if (senv->m_use_weather) {
3176 v3s16 p = block->getPos() * MAP_BLOCKSIZE;
3177 updateBlockHeat(senv, p, block);
3178 updateBlockHumidity(senv, p, block);
3180 block->heat = HEAT_UNDEFINED;
3181 block->humidity = HUMIDITY_UNDEFINED;
3185 s16 ServerMap::findGroundLevel(v2s16 p2d)
3189 Uh, just do something random...
3191 // Find existing map from top to down
3194 v3s16 p(p2d.X, max, p2d.Y);
3195 for(; p.Y>min; p.Y--)
3197 MapNode n = getNodeNoEx(p);
3198 if(n.getContent() != CONTENT_IGNORE)
3203 // If this node is not air, go to plan b
3204 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3206 // Search existing walkable and return it
3207 for(; p.Y>min; p.Y--)
3209 MapNode n = getNodeNoEx(p);
3210 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3219 Determine from map generator noise functions
3222 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3225 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3226 //return (s16)level;
3229 bool ServerMap::loadFromFolders() {
3230 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
3235 void ServerMap::createDirs(std::string path)
3237 if(fs::CreateAllDirs(path) == false)
3239 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3240 <<"\""<<path<<"\""<<std::endl;
3241 throw BaseException("ServerMap failed to create directory");
3245 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3251 snprintf(cc, 9, "%.4x%.4x",
3252 (unsigned int)pos.X&0xffff,
3253 (unsigned int)pos.Y&0xffff);
3255 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3257 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3258 (unsigned int)pos.X&0xfff,
3259 (unsigned int)pos.Y&0xfff);
3261 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3267 v2s16 ServerMap::getSectorPos(std::string dirname)
3271 std::string component;
3272 fs::RemoveLastPathComponent(dirname, &component, 1);
3273 if(component.size() == 8)
3276 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3278 else if(component.size() == 3)
3281 fs::RemoveLastPathComponent(dirname, &component, 2);
3282 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3283 // Sign-extend the 12 bit values up to 16 bits...
3284 if(x&0x800) x|=0xF000;
3285 if(y&0x800) y|=0xF000;
3292 v2s16 pos((s16)x, (s16)y);
3296 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3298 v2s16 p2d = getSectorPos(sectordir);
3300 if(blockfile.size() != 4){
3301 throw InvalidFilenameException("Invalid block filename");
3304 int r = sscanf(blockfile.c_str(), "%4x", &y);
3306 throw InvalidFilenameException("Invalid block filename");
3307 return v3s16(p2d.X, y, p2d.Y);
3310 std::string ServerMap::getBlockFilename(v3s16 p)
3313 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3317 void ServerMap::save(ModifiedState save_level)
3319 DSTACK(__FUNCTION_NAME);
3320 if(m_map_saving_enabled == false)
3322 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3326 if(save_level == MOD_STATE_CLEAN)
3327 infostream<<"ServerMap: Saving whole map, this can take time."
3330 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3335 // Profile modified reasons
3336 Profiler modprofiler;
3338 u32 sector_meta_count = 0;
3339 u32 block_count = 0;
3340 u32 block_count_all = 0; // Number of blocks in memory
3342 // Don't do anything with sqlite unless something is really saved
3343 bool save_started = false;
3345 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3346 i != m_sectors.end(); ++i)
3348 ServerMapSector *sector = (ServerMapSector*)i->second;
3349 assert(sector->getId() == MAPSECTOR_SERVER);
3351 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3353 saveSectorMeta(sector);
3354 sector_meta_count++;
3356 std::list<MapBlock*> blocks;
3357 sector->getBlocks(blocks);
3359 for(std::list<MapBlock*>::iterator j = blocks.begin();
3360 j != blocks.end(); ++j)
3362 MapBlock *block = *j;
3366 if(block->getModified() >= (u32)save_level)
3371 save_started = true;
3374 modprofiler.add(block->getModifiedReason(), 1);
3379 /*infostream<<"ServerMap: Written block ("
3380 <<block->getPos().X<<","
3381 <<block->getPos().Y<<","
3382 <<block->getPos().Z<<")"
3391 Only print if something happened or saved whole map
3393 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3394 || block_count != 0)
3396 infostream<<"ServerMap: Written: "
3397 <<sector_meta_count<<" sector metadata files, "
3398 <<block_count<<" block files"
3399 <<", "<<block_count_all<<" blocks in memory."
3401 PrintInfo(infostream); // ServerMap/ClientMap:
3402 infostream<<"Blocks modified by: "<<std::endl;
3403 modprofiler.print(infostream);
3407 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3409 if(loadFromFolders()){
3410 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3411 <<"all blocks that are stored in flat files"<<std::endl;
3413 dbase->listAllLoadableBlocks(dst);
3416 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3418 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3419 si != m_sectors.end(); ++si)
3421 MapSector *sector = si->second;
3423 std::list<MapBlock*> blocks;
3424 sector->getBlocks(blocks);
3426 for(std::list<MapBlock*>::iterator i = blocks.begin();
3427 i != blocks.end(); ++i)
3429 MapBlock *block = (*i);
3430 v3s16 p = block->getPos();
3436 void ServerMap::saveMapMeta()
3438 DSTACK(__FUNCTION_NAME);
3440 /*infostream<<"ServerMap::saveMapMeta(): "
3444 createDirs(m_savedir);
3446 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3447 std::ostringstream ss(std::ios_base::binary);
3451 m_emerge->saveParamsToSettings(¶ms);
3452 params.writeLines(ss);
3454 ss<<"[end_of_params]\n";
3456 if(!fs::safeWriteToFile(fullpath, ss.str()))
3458 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3459 <<"could not write "<<fullpath<<std::endl;
3460 throw FileNotGoodException("Cannot save chunk metadata");
3463 m_map_metadata_changed = false;
3466 void ServerMap::loadMapMeta()
3468 DSTACK(__FUNCTION_NAME);
3470 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3473 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3474 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3475 if(is.good() == false)
3477 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3478 <<"could not open"<<fullpath<<std::endl;
3479 throw FileNotGoodException("Cannot open map metadata");
3487 throw SerializationError
3488 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3490 std::getline(is, line);
3491 std::string trimmedline = trim(line);
3492 if(trimmedline == "[end_of_params]")
3494 params.parseConfigLine(line);
3497 m_emerge->loadParamsFromSettings(¶ms);
3499 verbosestream<<"ServerMap::loadMapMeta(): seed="
3500 << m_emerge->params.seed<<std::endl;
3503 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3505 DSTACK(__FUNCTION_NAME);
3506 // Format used for writing
3507 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3509 v2s16 pos = sector->getPos();
3510 std::string dir = getSectorDir(pos);
3513 std::string fullpath = dir + DIR_DELIM + "meta";
3514 std::ostringstream ss(std::ios_base::binary);
3516 sector->serialize(ss, version);
3518 if(!fs::safeWriteToFile(fullpath, ss.str()))
3519 throw FileNotGoodException("Cannot write sector metafile");
3521 sector->differs_from_disk = false;
3524 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3526 DSTACK(__FUNCTION_NAME);
3528 v2s16 p2d = getSectorPos(sectordir);
3530 ServerMapSector *sector = NULL;
3532 std::string fullpath = sectordir + DIR_DELIM + "meta";
3533 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3534 if(is.good() == false)
3536 // If the directory exists anyway, it probably is in some old
3537 // format. Just go ahead and create the sector.
3538 if(fs::PathExists(sectordir))
3540 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3541 <<fullpath<<" doesn't exist but directory does."
3542 <<" Continuing with a sector with no metadata."
3544 sector = new ServerMapSector(this, p2d, m_gamedef);
3545 m_sectors[p2d] = sector;
3549 throw FileNotGoodException("Cannot open sector metafile");
3554 sector = ServerMapSector::deSerialize
3555 (is, this, p2d, m_sectors, m_gamedef);
3557 saveSectorMeta(sector);
3560 sector->differs_from_disk = false;
3565 bool ServerMap::loadSectorMeta(v2s16 p2d)
3567 DSTACK(__FUNCTION_NAME);
3569 MapSector *sector = NULL;
3571 // The directory layout we're going to load from.
3572 // 1 - original sectors/xxxxzzzz/
3573 // 2 - new sectors2/xxx/zzz/
3574 // If we load from anything but the latest structure, we will
3575 // immediately save to the new one, and remove the old.
3577 std::string sectordir1 = getSectorDir(p2d, 1);
3578 std::string sectordir;
3579 if(fs::PathExists(sectordir1))
3581 sectordir = sectordir1;
3586 sectordir = getSectorDir(p2d, 2);
3590 sector = loadSectorMeta(sectordir, loadlayout != 2);
3592 catch(InvalidFilenameException &e)
3596 catch(FileNotGoodException &e)
3600 catch(std::exception &e)
3609 bool ServerMap::loadSectorFull(v2s16 p2d)
3611 DSTACK(__FUNCTION_NAME);
3613 MapSector *sector = NULL;
3615 // The directory layout we're going to load from.
3616 // 1 - original sectors/xxxxzzzz/
3617 // 2 - new sectors2/xxx/zzz/
3618 // If we load from anything but the latest structure, we will
3619 // immediately save to the new one, and remove the old.
3621 std::string sectordir1 = getSectorDir(p2d, 1);
3622 std::string sectordir;
3623 if(fs::PathExists(sectordir1))
3625 sectordir = sectordir1;
3630 sectordir = getSectorDir(p2d, 2);
3634 sector = loadSectorMeta(sectordir, loadlayout != 2);
3636 catch(InvalidFilenameException &e)
3640 catch(FileNotGoodException &e)
3644 catch(std::exception &e)
3652 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3654 std::vector<fs::DirListNode>::iterator i2;
3655 for(i2=list2.begin(); i2!=list2.end(); i2++)
3661 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3663 catch(InvalidFilenameException &e)
3665 // This catches unknown crap in directory
3671 infostream<<"Sector converted to new layout - deleting "<<
3672 sectordir1<<std::endl;
3673 fs::RecursiveDelete(sectordir1);
3680 void ServerMap::beginSave() {
3684 void ServerMap::endSave() {
3688 void ServerMap::saveBlock(MapBlock *block)
3690 dbase->saveBlock(block);
3693 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3695 DSTACK(__FUNCTION_NAME);
3697 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3700 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3701 if(is.good() == false)
3702 throw FileNotGoodException("Cannot open block file");
3704 v3s16 p3d = getBlockPos(sectordir, blockfile);
3705 v2s16 p2d(p3d.X, p3d.Z);
3707 assert(sector->getPos() == p2d);
3709 u8 version = SER_FMT_VER_INVALID;
3710 is.read((char*)&version, 1);
3713 throw SerializationError("ServerMap::loadBlock(): Failed"
3714 " to read MapBlock version");
3716 /*u32 block_size = MapBlock::serializedLength(version);
3717 SharedBuffer<u8> data(block_size);
3718 is.read((char*)*data, block_size);*/
3720 // This will always return a sector because we're the server
3721 //MapSector *sector = emergeSector(p2d);
3723 MapBlock *block = NULL;
3724 bool created_new = false;
3725 block = sector->getBlockNoCreateNoEx(p3d.Y);
3728 block = sector->createBlankBlockNoInsert(p3d.Y);
3733 block->deSerialize(is, version, true);
3735 // If it's a new block, insert it to the map
3737 sector->insertBlock(block);
3740 Save blocks loaded in old format in new format
3743 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3747 // Should be in database now, so delete the old file
3748 fs::RecursiveDelete(fullpath);
3751 // We just loaded it from the disk, so it's up-to-date.
3752 block->resetModified();
3755 catch(SerializationError &e)
3757 infostream<<"WARNING: Invalid block data on disk "
3758 <<"fullpath="<<fullpath
3759 <<" (SerializationError). "
3760 <<"what()="<<e.what()
3762 // Ignoring. A new one will be generated.
3765 // TODO: Backup file; name is in fullpath.
3769 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3771 DSTACK(__FUNCTION_NAME);
3774 std::istringstream is(*blob, std::ios_base::binary);
3776 u8 version = SER_FMT_VER_INVALID;
3777 is.read((char*)&version, 1);
3780 throw SerializationError("ServerMap::loadBlock(): Failed"
3781 " to read MapBlock version");
3783 /*u32 block_size = MapBlock::serializedLength(version);
3784 SharedBuffer<u8> data(block_size);
3785 is.read((char*)*data, block_size);*/
3787 // This will always return a sector because we're the server
3788 //MapSector *sector = emergeSector(p2d);
3790 MapBlock *block = NULL;
3791 bool created_new = false;
3792 block = sector->getBlockNoCreateNoEx(p3d.Y);
3795 block = sector->createBlankBlockNoInsert(p3d.Y);
3800 block->deSerialize(is, version, true);
3802 // If it's a new block, insert it to the map
3804 sector->insertBlock(block);
3807 Save blocks loaded in old format in new format
3810 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3811 // Only save if asked to; no need to update version
3815 // We just loaded it from, so it's up-to-date.
3816 block->resetModified();
3819 catch(SerializationError &e)
3821 errorstream<<"Invalid block data in database"
3822 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3823 <<" (SerializationError): "<<e.what()<<std::endl;
3825 // TODO: Block should be marked as invalid in memory so that it is
3826 // not touched but the game can run
3828 if(g_settings->getBool("ignore_world_load_errors")){
3829 errorstream<<"Ignoring block load error. Duck and cover! "
3830 <<"(ignore_world_load_errors)"<<std::endl;
3832 throw SerializationError("Invalid block data in database");
3838 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3840 DSTACK(__FUNCTION_NAME);
3842 v2s16 p2d(blockpos.X, blockpos.Z);
3846 ret = dbase->loadBlock(blockpos);
3847 if (ret) return (ret);
3848 // Not found in database, try the files
3850 // The directory layout we're going to load from.
3851 // 1 - original sectors/xxxxzzzz/
3852 // 2 - new sectors2/xxx/zzz/
3853 // If we load from anything but the latest structure, we will
3854 // immediately save to the new one, and remove the old.
3856 std::string sectordir1 = getSectorDir(p2d, 1);
3857 std::string sectordir;
3858 if(fs::PathExists(sectordir1))
3860 sectordir = sectordir1;
3865 sectordir = getSectorDir(p2d, 2);
3869 Make sure sector is loaded
3871 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3875 sector = loadSectorMeta(sectordir, loadlayout != 2);
3877 catch(InvalidFilenameException &e)
3881 catch(FileNotGoodException &e)
3885 catch(std::exception &e)
3892 Make sure file exists
3895 std::string blockfilename = getBlockFilename(blockpos);
3896 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3900 Load block and save it to the database
3902 loadBlock(sectordir, blockfilename, sector, true);
3903 return getBlockNoCreateNoEx(blockpos);
3906 void ServerMap::PrintInfo(std::ostream &out)
3911 s16 ServerMap::updateBlockHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
3913 u32 gametime = env->getGameTime();
3916 if (gametime - block->heat_last_update < 10)
3919 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3922 f32 heat = m_emerge->biomedef->calcBlockHeat(p, getSeed(),
3923 env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed());
3927 block->heat_last_update = gametime;
3932 s16 ServerMap::updateBlockHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
3934 u32 gametime = env->getGameTime();
3937 if (gametime - block->humidity_last_update < 10)
3938 return block->humidity;
3940 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3943 f32 humidity = m_emerge->biomedef->calcBlockHumidity(p, getSeed(),
3944 env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed());
3947 block->humidity = humidity;
3948 block->humidity_last_update = gametime;
3957 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3962 MapVoxelManipulator::~MapVoxelManipulator()
3964 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3968 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3970 TimeTaker timer1("emerge", &emerge_time);
3972 // Units of these are MapBlocks
3973 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3974 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3976 VoxelArea block_area_nodes
3977 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3979 addArea(block_area_nodes);
3981 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3982 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3983 for(s32 x=p_min.X; x<=p_max.X; x++)
3988 std::map<v3s16, u8>::iterator n;
3989 n = m_loaded_blocks.find(p);
3990 if(n != m_loaded_blocks.end())
3993 bool block_data_inexistent = false;
3996 TimeTaker timer1("emerge load", &emerge_load_time);
3998 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3999 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4001 a.print(infostream);
4002 infostream<<std::endl;*/
4004 block = m_map->getBlockNoCreate(p);
4005 if(block->isDummy())
4006 block_data_inexistent = true;
4008 block->copyTo(*this);
4010 catch(InvalidPositionException &e)
4012 block_data_inexistent = true;
4015 if(block_data_inexistent)
4017 flags |= VMANIP_BLOCK_DATA_INEXIST;
4019 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4020 // Fill with VOXELFLAG_INEXISTENT
4021 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4022 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4024 s32 i = m_area.index(a.MinEdge.X,y,z);
4025 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4028 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4030 // Mark that block was loaded as blank
4031 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4034 m_loaded_blocks[p] = flags;
4037 //infostream<<"emerge done"<<std::endl;
4041 SUGG: Add an option to only update eg. water and air nodes.
4042 This will make it interfere less with important stuff if
4045 void MapVoxelManipulator::blitBack
4046 (std::map<v3s16, MapBlock*> & modified_blocks)
4048 if(m_area.getExtent() == v3s16(0,0,0))
4051 //TimeTaker timer1("blitBack");
4053 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4054 <<m_loaded_blocks.size()<<std::endl;*/
4057 Initialize block cache
4059 v3s16 blockpos_last;
4060 MapBlock *block = NULL;
4061 bool block_checked_in_modified = false;
4063 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4064 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4065 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4069 u8 f = m_flags[m_area.index(p)];
4070 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4073 MapNode &n = m_data[m_area.index(p)];
4075 v3s16 blockpos = getNodeBlockPos(p);
4080 if(block == NULL || blockpos != blockpos_last){
4081 block = m_map->getBlockNoCreate(blockpos);
4082 blockpos_last = blockpos;
4083 block_checked_in_modified = false;
4086 // Calculate relative position in block
4087 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4089 // Don't continue if nothing has changed here
4090 if(block->getNode(relpos) == n)
4093 //m_map->setNode(m_area.MinEdge + p, n);
4094 block->setNode(relpos, n);
4097 Make sure block is in modified_blocks
4099 if(block_checked_in_modified == false)
4101 modified_blocks[blockpos] = block;
4102 block_checked_in_modified = true;
4105 catch(InvalidPositionException &e)
4111 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4112 MapVoxelManipulator(map),
4113 m_create_area(false)
4117 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4121 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4123 // Just create the area so that it can be pointed to
4124 VoxelManipulator::emerge(a, caller_id);
4127 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4128 v3s16 blockpos_max, bool load_if_inexistent)
4130 TimeTaker timer1("initialEmerge", &emerge_time);
4132 // Units of these are MapBlocks
4133 v3s16 p_min = blockpos_min;
4134 v3s16 p_max = blockpos_max;
4136 VoxelArea block_area_nodes
4137 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4139 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4142 infostream<<"initialEmerge: area: ";
4143 block_area_nodes.print(infostream);
4144 infostream<<" ("<<size_MB<<"MB)";
4145 infostream<<std::endl;
4148 addArea(block_area_nodes);
4150 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4151 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4152 for(s32 x=p_min.X; x<=p_max.X; x++)
4157 std::map<v3s16, u8>::iterator n;
4158 n = m_loaded_blocks.find(p);
4159 if(n != m_loaded_blocks.end())
4162 bool block_data_inexistent = false;
4165 TimeTaker timer1("emerge load", &emerge_load_time);
4167 block = m_map->getBlockNoCreate(p);
4168 if(block->isDummy())
4169 block_data_inexistent = true;
4171 block->copyTo(*this);
4173 catch(InvalidPositionException &e)
4175 block_data_inexistent = true;
4178 if(block_data_inexistent)
4181 if (load_if_inexistent) {
4182 ServerMap *svrmap = (ServerMap *)m_map;
4183 block = svrmap->emergeBlock(p, false);
4185 block = svrmap->createBlock(p);
4187 block->copyTo(*this);
4189 flags |= VMANIP_BLOCK_DATA_INEXIST;
4192 Mark area inexistent
4194 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4195 // Fill with VOXELFLAG_INEXISTENT
4196 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4197 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4199 s32 i = m_area.index(a.MinEdge.X,y,z);
4200 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4204 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4206 // Mark that block was loaded as blank
4207 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4210 m_loaded_blocks[p] = flags;
4214 void ManualMapVoxelManipulator::blitBackAll(
4215 std::map<v3s16, MapBlock*> * modified_blocks)
4217 if(m_area.getExtent() == v3s16(0,0,0))
4221 Copy data of all blocks
4223 for(std::map<v3s16, u8>::iterator
4224 i = m_loaded_blocks.begin();
4225 i != m_loaded_blocks.end(); ++i)
4228 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4229 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4230 if(existed == false)
4235 block->copyFrom(*this);
4238 (*modified_blocks)[p] = block;