3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "rollback_interface.h"
37 #include "mapgen_v6.h"
38 #include "mapgen_indev.h"
41 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
44 SQLite format specification:
45 - Initially only replaces sectors/ and sectors2/
47 If map.sqlite does not exist in the save dir
48 or the block was not found in the database
49 the map will try to load from sectors folder.
50 In either case, map.sqlite will be created
51 and all future saves will save there.
53 Structure of map.sqlite:
64 Map::Map(std::ostream &dout, IGameDef *gamedef):
69 /*m_sector_mutex.Init();
70 assert(m_sector_mutex.IsInitialized());*/
78 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
79 i != m_sectors.end(); ++i)
85 void Map::addEventReceiver(MapEventReceiver *event_receiver)
87 m_event_receivers.insert(event_receiver);
90 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
92 m_event_receivers.erase(event_receiver);
95 void Map::dispatchEvent(MapEditEvent *event)
97 for(std::set<MapEventReceiver*>::iterator
98 i = m_event_receivers.begin();
99 i != m_event_receivers.end(); ++i)
101 (*i)->onMapEditEvent(event);
105 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
107 if(m_sector_cache != NULL && p == m_sector_cache_p){
108 MapSector * sector = m_sector_cache;
112 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
114 if(n == m_sectors.end())
117 MapSector *sector = n->second;
119 // Cache the last result
120 m_sector_cache_p = p;
121 m_sector_cache = sector;
126 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
128 return getSectorNoGenerateNoExNoLock(p);
131 MapSector * Map::getSectorNoGenerate(v2s16 p)
133 MapSector *sector = getSectorNoGenerateNoEx(p);
135 throw InvalidPositionException();
140 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
142 v2s16 p2d(p3d.X, p3d.Z);
143 MapSector * sector = getSectorNoGenerateNoEx(p2d);
146 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
150 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
152 MapBlock *block = getBlockNoCreateNoEx(p3d);
154 throw InvalidPositionException();
158 bool Map::isNodeUnderground(v3s16 p)
160 v3s16 blockpos = getNodeBlockPos(p);
162 MapBlock * block = getBlockNoCreate(blockpos);
163 return block->getIsUnderground();
165 catch(InvalidPositionException &e)
171 bool Map::isValidPosition(v3s16 p)
173 v3s16 blockpos = getNodeBlockPos(p);
174 MapBlock *block = getBlockNoCreate(blockpos);
175 return (block != NULL);
178 // Returns a CONTENT_IGNORE node if not found
179 MapNode Map::getNodeNoEx(v3s16 p)
181 v3s16 blockpos = getNodeBlockPos(p);
182 MapBlock *block = getBlockNoCreateNoEx(blockpos);
184 return MapNode(CONTENT_IGNORE);
185 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
186 return block->getNodeNoCheck(relpos);
189 // throws InvalidPositionException if not found
190 MapNode Map::getNode(v3s16 p)
192 v3s16 blockpos = getNodeBlockPos(p);
193 MapBlock *block = getBlockNoCreateNoEx(blockpos);
195 throw InvalidPositionException();
196 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
197 return block->getNodeNoCheck(relpos);
200 // throws InvalidPositionException if not found
201 void Map::setNode(v3s16 p, MapNode & n)
203 v3s16 blockpos = getNodeBlockPos(p);
204 MapBlock *block = getBlockNoCreate(blockpos);
205 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 // Never allow placing CONTENT_IGNORE, it fucks up stuff
207 if(n.getContent() == CONTENT_IGNORE){
208 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
209 <<" while trying to replace \""
210 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
211 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
212 debug_stacks_print_to(infostream);
215 block->setNodeNoCheck(relpos, n);
220 Goes recursively through the neighbours of the node.
222 Alters only transparent nodes.
224 If the lighting of the neighbour is lower than the lighting of
225 the node was (before changing it to 0 at the step before), the
226 lighting of the neighbour is set to 0 and then the same stuff
227 repeats for the neighbour.
229 The ending nodes of the routine are stored in light_sources.
230 This is useful when a light is removed. In such case, this
231 routine can be called for the light node and then again for
232 light_sources to re-light the area without the removed light.
234 values of from_nodes are lighting values.
236 void Map::unspreadLight(enum LightBank bank,
237 std::map<v3s16, u8> & from_nodes,
238 std::set<v3s16> & light_sources,
239 std::map<v3s16, MapBlock*> & modified_blocks)
241 INodeDefManager *nodemgr = m_gamedef->ndef();
244 v3s16(0,0,1), // back
246 v3s16(1,0,0), // right
247 v3s16(0,0,-1), // front
248 v3s16(0,-1,0), // bottom
249 v3s16(-1,0,0), // left
252 if(from_nodes.size() == 0)
255 u32 blockchangecount = 0;
257 std::map<v3s16, u8> unlighted_nodes;
260 Initialize block cache
263 MapBlock *block = NULL;
264 // Cache this a bit, too
265 bool block_checked_in_modified = false;
267 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
268 j != from_nodes.end(); ++j)
270 v3s16 pos = j->first;
271 v3s16 blockpos = getNodeBlockPos(pos);
273 // Only fetch a new block if the block position has changed
275 if(block == NULL || blockpos != blockpos_last){
276 block = getBlockNoCreate(blockpos);
277 blockpos_last = blockpos;
279 block_checked_in_modified = false;
283 catch(InvalidPositionException &e)
291 // Calculate relative position in block
292 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
294 // Get node straight from the block
295 //MapNode n = block->getNode(relpos);
297 u8 oldlight = j->second;
299 // Loop through 6 neighbors
300 for(u16 i=0; i<6; i++)
302 // Get the position of the neighbor node
303 v3s16 n2pos = pos + dirs[i];
305 // Get the block where the node is located
306 v3s16 blockpos = getNodeBlockPos(n2pos);
310 // Only fetch a new block if the block position has changed
312 if(block == NULL || blockpos != blockpos_last){
313 block = getBlockNoCreate(blockpos);
314 blockpos_last = blockpos;
316 block_checked_in_modified = false;
320 catch(InvalidPositionException &e)
325 // Calculate relative position in block
326 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
327 // Get node straight from the block
328 MapNode n2 = block->getNode(relpos);
330 bool changed = false;
332 //TODO: Optimize output by optimizing light_sources?
335 If the neighbor is dimmer than what was specified
336 as oldlight (the light of the previous node)
338 if(n2.getLight(bank, nodemgr) < oldlight)
341 And the neighbor is transparent and it has some light
343 if(nodemgr->get(n2).light_propagates
344 && n2.getLight(bank, nodemgr) != 0)
347 Set light to 0 and add to queue
350 u8 current_light = n2.getLight(bank, nodemgr);
351 n2.setLight(bank, 0, nodemgr);
352 block->setNode(relpos, n2);
354 unlighted_nodes[n2pos] = current_light;
358 Remove from light_sources if it is there
359 NOTE: This doesn't happen nearly at all
361 /*if(light_sources.find(n2pos))
363 infostream<<"Removed from light_sources"<<std::endl;
364 light_sources.remove(n2pos);
369 if(light_sources.find(n2pos) != NULL)
370 light_sources.remove(n2pos);*/
373 light_sources.insert(n2pos);
376 // Add to modified_blocks
377 if(changed == true && block_checked_in_modified == false)
379 // If the block is not found in modified_blocks, add.
380 if(modified_blocks.find(blockpos) == modified_blocks.end())
382 modified_blocks[blockpos] = block;
384 block_checked_in_modified = true;
387 catch(InvalidPositionException &e)
394 /*infostream<<"unspreadLight(): Changed block "
395 <<blockchangecount<<" times"
396 <<" for "<<from_nodes.size()<<" nodes"
399 if(unlighted_nodes.size() > 0)
400 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
404 A single-node wrapper of the above
406 void Map::unLightNeighbors(enum LightBank bank,
407 v3s16 pos, u8 lightwas,
408 std::set<v3s16> & light_sources,
409 std::map<v3s16, MapBlock*> & modified_blocks)
411 std::map<v3s16, u8> from_nodes;
412 from_nodes[pos] = lightwas;
414 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
418 Lights neighbors of from_nodes, collects all them and then
421 void Map::spreadLight(enum LightBank bank,
422 std::set<v3s16> & from_nodes,
423 std::map<v3s16, MapBlock*> & modified_blocks)
425 INodeDefManager *nodemgr = m_gamedef->ndef();
427 const v3s16 dirs[6] = {
428 v3s16(0,0,1), // back
430 v3s16(1,0,0), // right
431 v3s16(0,0,-1), // front
432 v3s16(0,-1,0), // bottom
433 v3s16(-1,0,0), // left
436 if(from_nodes.size() == 0)
439 u32 blockchangecount = 0;
441 std::set<v3s16> lighted_nodes;
444 Initialize block cache
447 MapBlock *block = NULL;
448 // Cache this a bit, too
449 bool block_checked_in_modified = false;
451 for(std::set<v3s16>::iterator j = from_nodes.begin();
452 j != from_nodes.end(); ++j)
455 v3s16 blockpos = getNodeBlockPos(pos);
457 // Only fetch a new block if the block position has changed
459 if(block == NULL || blockpos != blockpos_last){
460 block = getBlockNoCreate(blockpos);
461 blockpos_last = blockpos;
463 block_checked_in_modified = false;
467 catch(InvalidPositionException &e)
475 // Calculate relative position in block
476 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
478 // Get node straight from the block
479 MapNode n = block->getNode(relpos);
481 u8 oldlight = n.getLight(bank, nodemgr);
482 u8 newlight = diminish_light(oldlight);
484 // Loop through 6 neighbors
485 for(u16 i=0; i<6; i++){
486 // Get the position of the neighbor node
487 v3s16 n2pos = pos + dirs[i];
489 // Get the block where the node is located
490 v3s16 blockpos = getNodeBlockPos(n2pos);
494 // Only fetch a new block if the block position has changed
496 if(block == NULL || blockpos != blockpos_last){
497 block = getBlockNoCreate(blockpos);
498 blockpos_last = blockpos;
500 block_checked_in_modified = false;
504 catch(InvalidPositionException &e)
509 // Calculate relative position in block
510 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
511 // Get node straight from the block
512 MapNode n2 = block->getNode(relpos);
514 bool changed = false;
516 If the neighbor is brighter than the current node,
517 add to list (it will light up this node on its turn)
519 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
521 lighted_nodes.insert(n2pos);
525 If the neighbor is dimmer than how much light this node
526 would spread on it, add to list
528 if(n2.getLight(bank, nodemgr) < newlight)
530 if(nodemgr->get(n2).light_propagates)
532 n2.setLight(bank, newlight, nodemgr);
533 block->setNode(relpos, n2);
534 lighted_nodes.insert(n2pos);
539 // Add to modified_blocks
540 if(changed == true && block_checked_in_modified == false)
542 // If the block is not found in modified_blocks, add.
543 if(modified_blocks.find(blockpos) == modified_blocks.end())
545 modified_blocks[blockpos] = block;
547 block_checked_in_modified = true;
550 catch(InvalidPositionException &e)
557 /*infostream<<"spreadLight(): Changed block "
558 <<blockchangecount<<" times"
559 <<" for "<<from_nodes.size()<<" nodes"
562 if(lighted_nodes.size() > 0)
563 spreadLight(bank, lighted_nodes, modified_blocks);
567 A single-node source variation of the above.
569 void Map::lightNeighbors(enum LightBank bank,
571 std::map<v3s16, MapBlock*> & modified_blocks)
573 std::set<v3s16> from_nodes;
574 from_nodes.insert(pos);
575 spreadLight(bank, from_nodes, modified_blocks);
578 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
580 INodeDefManager *nodemgr = m_gamedef->ndef();
583 v3s16(0,0,1), // back
585 v3s16(1,0,0), // right
586 v3s16(0,0,-1), // front
587 v3s16(0,-1,0), // bottom
588 v3s16(-1,0,0), // left
591 u8 brightest_light = 0;
592 v3s16 brightest_pos(0,0,0);
593 bool found_something = false;
595 // Loop through 6 neighbors
596 for(u16 i=0; i<6; i++){
597 // Get the position of the neighbor node
598 v3s16 n2pos = p + dirs[i];
603 catch(InvalidPositionException &e)
607 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
608 brightest_light = n2.getLight(bank, nodemgr);
609 brightest_pos = n2pos;
610 found_something = true;
614 if(found_something == false)
615 throw InvalidPositionException();
617 return brightest_pos;
621 Propagates sunlight down from a node.
622 Starting point gets sunlight.
624 Returns the lowest y value of where the sunlight went.
626 Mud is turned into grass in where the sunlight stops.
628 s16 Map::propagateSunlight(v3s16 start,
629 std::map<v3s16, MapBlock*> & modified_blocks)
631 INodeDefManager *nodemgr = m_gamedef->ndef();
636 v3s16 pos(start.X, y, start.Z);
638 v3s16 blockpos = getNodeBlockPos(pos);
641 block = getBlockNoCreate(blockpos);
643 catch(InvalidPositionException &e)
648 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
649 MapNode n = block->getNode(relpos);
651 if(nodemgr->get(n).sunlight_propagates)
653 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
654 block->setNode(relpos, n);
656 modified_blocks[blockpos] = block;
660 // Sunlight goes no further
667 void Map::updateLighting(enum LightBank bank,
668 std::map<v3s16, MapBlock*> & a_blocks,
669 std::map<v3s16, MapBlock*> & modified_blocks)
671 INodeDefManager *nodemgr = m_gamedef->ndef();
673 /*m_dout<<DTIME<<"Map::updateLighting(): "
674 <<a_blocks.size()<<" blocks."<<std::endl;*/
676 //TimeTaker timer("updateLighting");
680 //u32 count_was = modified_blocks.size();
682 std::map<v3s16, MapBlock*> blocks_to_update;
684 std::set<v3s16> light_sources;
686 std::map<v3s16, u8> unlight_from;
688 int num_bottom_invalid = 0;
691 //TimeTaker t("first stuff");
693 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
694 i != a_blocks.end(); ++i)
696 MapBlock *block = i->second;
700 // Don't bother with dummy blocks.
704 v3s16 pos = block->getPos();
705 v3s16 posnodes = block->getPosRelative();
706 modified_blocks[pos] = block;
707 blocks_to_update[pos] = block;
710 Clear all light from block
712 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
713 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
714 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
719 MapNode n = block->getNode(p);
720 u8 oldlight = n.getLight(bank, nodemgr);
721 n.setLight(bank, 0, nodemgr);
722 block->setNode(p, n);
724 // If node sources light, add to list
725 u8 source = nodemgr->get(n).light_source;
727 light_sources.insert(p + posnodes);
729 // Collect borders for unlighting
730 if((x==0 || x == MAP_BLOCKSIZE-1
731 || y==0 || y == MAP_BLOCKSIZE-1
732 || z==0 || z == MAP_BLOCKSIZE-1)
735 v3s16 p_map = p + posnodes;
736 unlight_from[p_map] = oldlight;
739 catch(InvalidPositionException &e)
742 This would happen when dealing with a
746 infostream<<"updateLighting(): InvalidPositionException"
751 if(bank == LIGHTBANK_DAY)
753 bool bottom_valid = block->propagateSunlight(light_sources);
756 num_bottom_invalid++;
758 // If bottom is valid, we're done.
762 else if(bank == LIGHTBANK_NIGHT)
764 // For night lighting, sunlight is not propagated
769 // Invalid lighting bank
773 /*infostream<<"Bottom for sunlight-propagated block ("
774 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
777 // Bottom sunlight is not valid; get the block and loop to it
781 block = getBlockNoCreate(pos);
783 catch(InvalidPositionException &e)
794 Enable this to disable proper lighting for speeding up map
795 generation for testing or whatever
798 //if(g_settings->get(""))
800 core::map<v3s16, MapBlock*>::Iterator i;
801 i = blocks_to_update.getIterator();
802 for(; i.atEnd() == false; i++)
804 MapBlock *block = i.getNode()->getValue();
805 v3s16 p = block->getPos();
806 block->setLightingExpired(false);
814 //TimeTaker timer("unspreadLight");
815 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
820 u32 diff = modified_blocks.size() - count_was;
821 count_was = modified_blocks.size();
822 infostream<<"unspreadLight modified "<<diff<<std::endl;
826 //TimeTaker timer("spreadLight");
827 spreadLight(bank, light_sources, modified_blocks);
832 u32 diff = modified_blocks.size() - count_was;
833 count_was = modified_blocks.size();
834 infostream<<"spreadLight modified "<<diff<<std::endl;
840 //MapVoxelManipulator vmanip(this);
842 // Make a manual voxel manipulator and load all the blocks
843 // that touch the requested blocks
844 ManualMapVoxelManipulator vmanip(this);
847 //TimeTaker timer("initialEmerge");
849 core::map<v3s16, MapBlock*>::Iterator i;
850 i = blocks_to_update.getIterator();
851 for(; i.atEnd() == false; i++)
853 MapBlock *block = i.getNode()->getValue();
854 v3s16 p = block->getPos();
856 // Add all surrounding blocks
857 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
860 Add all surrounding blocks that have up-to-date lighting
861 NOTE: This doesn't quite do the job (not everything
862 appropriate is lighted)
864 /*for(s16 z=-1; z<=1; z++)
865 for(s16 y=-1; y<=1; y++)
866 for(s16 x=-1; x<=1; x++)
868 v3s16 p2 = p + v3s16(x,y,z);
869 MapBlock *block = getBlockNoCreateNoEx(p2);
874 if(block->getLightingExpired())
876 vmanip.initialEmerge(p2, p2);
879 // Lighting of block will be updated completely
880 block->setLightingExpired(false);
885 //TimeTaker timer("unSpreadLight");
886 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
889 //TimeTaker timer("spreadLight");
890 vmanip.spreadLight(bank, light_sources, nodemgr);
893 //TimeTaker timer("blitBack");
894 vmanip.blitBack(modified_blocks);
896 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
901 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
904 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
905 std::map<v3s16, MapBlock*> & modified_blocks)
907 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
908 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
911 Update information about whether day and night light differ
913 for(std::map<v3s16, MapBlock*>::iterator
914 i = modified_blocks.begin();
915 i != modified_blocks.end(); ++i)
917 MapBlock *block = i->second;
918 block->expireDayNightDiff();
924 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
925 std::map<v3s16, MapBlock*> &modified_blocks)
927 INodeDefManager *ndef = m_gamedef->ndef();
930 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
931 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
934 From this node to nodes underneath:
935 If lighting is sunlight (1.0), unlight neighbours and
940 v3s16 toppos = p + v3s16(0,1,0);
941 //v3s16 bottompos = p + v3s16(0,-1,0);
943 bool node_under_sunlight = true;
944 std::set<v3s16> light_sources;
947 Collect old node for rollback
949 RollbackNode rollback_oldnode(this, p, m_gamedef);
952 If there is a node at top and it doesn't have sunlight,
953 there has not been any sunlight going down.
955 Otherwise there probably is.
958 MapNode topnode = getNode(toppos);
960 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
961 node_under_sunlight = false;
963 catch(InvalidPositionException &e)
968 Remove all light that has come out of this node
971 enum LightBank banks[] =
976 for(s32 i=0; i<2; i++)
978 enum LightBank bank = banks[i];
980 u8 lightwas = getNode(p).getLight(bank, ndef);
982 // Add the block of the added node to modified_blocks
983 v3s16 blockpos = getNodeBlockPos(p);
984 MapBlock * block = getBlockNoCreate(blockpos);
985 assert(block != NULL);
986 modified_blocks[blockpos] = block;
988 assert(isValidPosition(p));
990 // Unlight neighbours of node.
991 // This means setting light of all consequent dimmer nodes
993 // This also collects the nodes at the border which will spread
994 // light again into this.
995 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
997 n.setLight(bank, 0, ndef);
1001 If node lets sunlight through and is under sunlight, it has
1004 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1006 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1010 Remove node metadata
1013 removeNodeMetadata(p);
1016 Set the node on the map
1022 If node is under sunlight and doesn't let sunlight through,
1023 take all sunlighted nodes under it and clear light from them
1024 and from where the light has been spread.
1025 TODO: This could be optimized by mass-unlighting instead
1028 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1032 //m_dout<<DTIME<<"y="<<y<<std::endl;
1033 v3s16 n2pos(p.X, y, p.Z);
1037 n2 = getNode(n2pos);
1039 catch(InvalidPositionException &e)
1044 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1046 unLightNeighbors(LIGHTBANK_DAY,
1047 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1048 light_sources, modified_blocks);
1049 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1057 for(s32 i=0; i<2; i++)
1059 enum LightBank bank = banks[i];
1062 Spread light from all nodes that might be capable of doing so
1064 spreadLight(bank, light_sources, modified_blocks);
1068 Update information about whether day and night light differ
1070 for(std::map<v3s16, MapBlock*>::iterator
1071 i = modified_blocks.begin();
1072 i != modified_blocks.end(); ++i)
1074 i->second->expireDayNightDiff();
1080 if(m_gamedef->rollback())
1082 RollbackNode rollback_newnode(this, p, m_gamedef);
1083 RollbackAction action;
1084 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1085 m_gamedef->rollback()->reportAction(action);
1089 Add neighboring liquid nodes and the node itself if it is
1090 liquid (=water node was added) to transform queue.
1091 note: todo: for liquid_finite enough to add only self node
1094 v3s16(0,0,0), // self
1095 v3s16(0,0,1), // back
1096 v3s16(0,1,0), // top
1097 v3s16(1,0,0), // right
1098 v3s16(0,0,-1), // front
1099 v3s16(0,-1,0), // bottom
1100 v3s16(-1,0,0), // left
1102 for(u16 i=0; i<7; i++)
1107 v3s16 p2 = p + dirs[i];
1109 MapNode n2 = getNode(p2);
1110 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1112 m_transforming_liquid.push_back(p2);
1115 }catch(InvalidPositionException &e)
1123 void Map::removeNodeAndUpdate(v3s16 p,
1124 std::map<v3s16, MapBlock*> &modified_blocks)
1126 INodeDefManager *ndef = m_gamedef->ndef();
1128 /*PrintInfo(m_dout);
1129 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1130 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1132 bool node_under_sunlight = true;
1134 v3s16 toppos = p + v3s16(0,1,0);
1136 // Node will be replaced with this
1137 content_t replace_material = CONTENT_AIR;
1140 Collect old node for rollback
1142 RollbackNode rollback_oldnode(this, p, m_gamedef);
1145 If there is a node at top and it doesn't have sunlight,
1146 there will be no sunlight going down.
1149 MapNode topnode = getNode(toppos);
1151 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1152 node_under_sunlight = false;
1154 catch(InvalidPositionException &e)
1158 std::set<v3s16> light_sources;
1160 enum LightBank banks[] =
1165 for(s32 i=0; i<2; i++)
1167 enum LightBank bank = banks[i];
1170 Unlight neighbors (in case the node is a light source)
1172 unLightNeighbors(bank, p,
1173 getNode(p).getLight(bank, ndef),
1174 light_sources, modified_blocks);
1178 Remove node metadata
1181 removeNodeMetadata(p);
1185 This also clears the lighting.
1189 n.setContent(replace_material);
1192 for(s32 i=0; i<2; i++)
1194 enum LightBank bank = banks[i];
1197 Recalculate lighting
1199 spreadLight(bank, light_sources, modified_blocks);
1202 // Add the block of the removed node to modified_blocks
1203 v3s16 blockpos = getNodeBlockPos(p);
1204 MapBlock * block = getBlockNoCreate(blockpos);
1205 assert(block != NULL);
1206 modified_blocks[blockpos] = block;
1209 If the removed node was under sunlight, propagate the
1210 sunlight down from it and then light all neighbors
1211 of the propagated blocks.
1213 if(node_under_sunlight)
1215 s16 ybottom = propagateSunlight(p, modified_blocks);
1216 /*m_dout<<DTIME<<"Node was under sunlight. "
1217 "Propagating sunlight";
1218 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1220 for(; y >= ybottom; y--)
1222 v3s16 p2(p.X, y, p.Z);
1223 /*m_dout<<DTIME<<"lighting neighbors of node ("
1224 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1226 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1231 // Set the lighting of this node to 0
1232 // TODO: Is this needed? Lighting is cleared up there already.
1234 MapNode n = getNode(p);
1235 n.setLight(LIGHTBANK_DAY, 0, ndef);
1238 catch(InvalidPositionException &e)
1244 for(s32 i=0; i<2; i++)
1246 enum LightBank bank = banks[i];
1248 // Get the brightest neighbour node and propagate light from it
1249 v3s16 n2p = getBrightestNeighbour(bank, p);
1251 //MapNode n2 = getNode(n2p);
1252 lightNeighbors(bank, n2p, modified_blocks);
1254 catch(InvalidPositionException &e)
1260 Update information about whether day and night light differ
1262 for(std::map<v3s16, MapBlock*>::iterator
1263 i = modified_blocks.begin();
1264 i != modified_blocks.end(); ++i)
1266 i->second->expireDayNightDiff();
1272 if(m_gamedef->rollback())
1274 RollbackNode rollback_newnode(this, p, m_gamedef);
1275 RollbackAction action;
1276 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1277 m_gamedef->rollback()->reportAction(action);
1281 Add neighboring liquid nodes and this node to transform queue.
1282 (it's vital for the node itself to get updated last.)
1283 note: todo: for liquid_finite enough to add only self node
1286 v3s16(0,0,1), // back
1287 v3s16(0,1,0), // top
1288 v3s16(1,0,0), // right
1289 v3s16(0,0,-1), // front
1290 v3s16(0,-1,0), // bottom
1291 v3s16(-1,0,0), // left
1292 v3s16(0,0,0), // self
1294 for(u16 i=0; i<7; i++)
1299 v3s16 p2 = p + dirs[i];
1301 MapNode n2 = getNode(p2);
1302 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1304 m_transforming_liquid.push_back(p2);
1307 }catch(InvalidPositionException &e)
1313 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1316 event.type = MEET_ADDNODE;
1320 bool succeeded = true;
1322 std::map<v3s16, MapBlock*> modified_blocks;
1323 addNodeAndUpdate(p, n, modified_blocks);
1325 // Copy modified_blocks to event
1326 for(std::map<v3s16, MapBlock*>::iterator
1327 i = modified_blocks.begin();
1328 i != modified_blocks.end(); ++i)
1330 event.modified_blocks.insert(i->first);
1333 catch(InvalidPositionException &e){
1337 dispatchEvent(&event);
1342 bool Map::removeNodeWithEvent(v3s16 p)
1345 event.type = MEET_REMOVENODE;
1348 bool succeeded = true;
1350 std::map<v3s16, MapBlock*> modified_blocks;
1351 removeNodeAndUpdate(p, modified_blocks);
1353 // Copy modified_blocks to event
1354 for(std::map<v3s16, MapBlock*>::iterator
1355 i = modified_blocks.begin();
1356 i != modified_blocks.end(); ++i)
1358 event.modified_blocks.insert(i->first);
1361 catch(InvalidPositionException &e){
1365 dispatchEvent(&event);
1370 bool Map::getDayNightDiff(v3s16 blockpos)
1373 v3s16 p = blockpos + v3s16(0,0,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->getDayNightDiff())
1378 catch(InvalidPositionException &e){}
1381 v3s16 p = blockpos + v3s16(-1,0,0);
1382 MapBlock *b = getBlockNoCreate(p);
1383 if(b->getDayNightDiff())
1386 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(0,-1,0);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->getDayNightDiff())
1393 catch(InvalidPositionException &e){}
1395 v3s16 p = blockpos + v3s16(0,0,-1);
1396 MapBlock *b = getBlockNoCreate(p);
1397 if(b->getDayNightDiff())
1400 catch(InvalidPositionException &e){}
1403 v3s16 p = blockpos + v3s16(1,0,0);
1404 MapBlock *b = getBlockNoCreate(p);
1405 if(b->getDayNightDiff())
1408 catch(InvalidPositionException &e){}
1410 v3s16 p = blockpos + v3s16(0,1,0);
1411 MapBlock *b = getBlockNoCreate(p);
1412 if(b->getDayNightDiff())
1415 catch(InvalidPositionException &e){}
1417 v3s16 p = blockpos + v3s16(0,0,1);
1418 MapBlock *b = getBlockNoCreate(p);
1419 if(b->getDayNightDiff())
1422 catch(InvalidPositionException &e){}
1428 Updates usage timers
1430 void Map::timerUpdate(float dtime, float unload_timeout,
1431 std::list<v3s16> *unloaded_blocks)
1433 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1435 // Profile modified reasons
1436 Profiler modprofiler;
1438 std::list<v2s16> sector_deletion_queue;
1439 u32 deleted_blocks_count = 0;
1440 u32 saved_blocks_count = 0;
1441 u32 block_count_all = 0;
1444 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1445 si != m_sectors.end(); ++si)
1447 MapSector *sector = si->second;
1449 bool all_blocks_deleted = true;
1451 std::list<MapBlock*> blocks;
1452 sector->getBlocks(blocks);
1454 for(std::list<MapBlock*>::iterator i = blocks.begin();
1455 i != blocks.end(); ++i)
1457 MapBlock *block = (*i);
1459 block->incrementUsageTimer(dtime);
1461 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1463 v3s16 p = block->getPos();
1466 if(block->getModified() != MOD_STATE_CLEAN
1467 && save_before_unloading)
1469 modprofiler.add(block->getModifiedReason(), 1);
1471 saved_blocks_count++;
1474 // Delete from memory
1475 sector->deleteBlock(block);
1478 unloaded_blocks->push_back(p);
1480 deleted_blocks_count++;
1484 all_blocks_deleted = false;
1489 if(all_blocks_deleted)
1491 sector_deletion_queue.push_back(si->first);
1496 // Finally delete the empty sectors
1497 deleteSectors(sector_deletion_queue);
1499 if(deleted_blocks_count != 0)
1501 PrintInfo(infostream); // ServerMap/ClientMap:
1502 infostream<<"Unloaded "<<deleted_blocks_count
1503 <<" blocks from memory";
1504 if(save_before_unloading)
1505 infostream<<", of which "<<saved_blocks_count<<" were written";
1506 infostream<<", "<<block_count_all<<" blocks in memory";
1507 infostream<<"."<<std::endl;
1508 if(saved_blocks_count != 0){
1509 PrintInfo(infostream); // ServerMap/ClientMap:
1510 infostream<<"Blocks modified by: "<<std::endl;
1511 modprofiler.print(infostream);
1516 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1518 timerUpdate(0.0, -1.0, unloaded_blocks);
1521 void Map::deleteSectors(std::list<v2s16> &list)
1523 for(std::list<v2s16>::iterator j = list.begin();
1524 j != list.end(); ++j)
1526 MapSector *sector = m_sectors[*j];
1527 // If sector is in sector cache, remove it from there
1528 if(m_sector_cache == sector)
1529 m_sector_cache = NULL;
1530 // Remove from map and delete
1531 m_sectors.erase(*j);
1537 void Map::unloadUnusedData(float timeout,
1538 core::list<v3s16> *deleted_blocks)
1540 core::list<v2s16> sector_deletion_queue;
1541 u32 deleted_blocks_count = 0;
1542 u32 saved_blocks_count = 0;
1544 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1545 for(; si.atEnd() == false; si++)
1547 MapSector *sector = si.getNode()->getValue();
1549 bool all_blocks_deleted = true;
1551 core::list<MapBlock*> blocks;
1552 sector->getBlocks(blocks);
1553 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1554 i != blocks.end(); i++)
1556 MapBlock *block = (*i);
1558 if(block->getUsageTimer() > timeout)
1561 if(block->getModified() != MOD_STATE_CLEAN)
1564 saved_blocks_count++;
1566 // Delete from memory
1567 sector->deleteBlock(block);
1568 deleted_blocks_count++;
1572 all_blocks_deleted = false;
1576 if(all_blocks_deleted)
1578 sector_deletion_queue.push_back(si.getNode()->getKey());
1582 deleteSectors(sector_deletion_queue);
1584 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1585 <<", of which "<<saved_blocks_count<<" were wr."
1588 //return sector_deletion_queue.getSize();
1589 //return deleted_blocks_count;
1593 void Map::PrintInfo(std::ostream &out)
1598 #define WATER_DROP_BOOST 4
1602 NEIGHBOR_SAME_LEVEL,
1605 struct NodeNeighbor {
1609 bool l; //can liquid
1613 void Map::transforming_liquid_add(v3s16 p) {
1614 m_transforming_liquid.push_back(p);
1617 s32 Map::transforming_liquid_size() {
1618 return m_transforming_liquid.size();
1621 const v3s16 g_7dirs[7] =
1623 // +right, +top, +back
1624 v3s16( 0,-1, 0), // bottom
1625 v3s16( 0, 0, 0), // self
1626 v3s16( 0, 0, 1), // back
1627 v3s16( 0, 0,-1), // front
1628 v3s16( 1, 0, 0), // right
1629 v3s16(-1, 0, 0), // left
1630 v3s16( 0, 1, 0) // top
1637 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1639 INodeDefManager *nodemgr = m_gamedef->ndef();
1641 DSTACK(__FUNCTION_NAME);
1642 //TimeTaker timer("transformLiquids()");
1645 u32 initial_size = m_transforming_liquid.size();
1647 u8 relax = g_settings->getS16("liquid_relax");
1648 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1649 int water_level = g_settings->getS16("water_level");
1651 // list of nodes that due to viscosity have not reached their max level height
1652 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1654 // List of MapBlocks that will require a lighting update (due to lava)
1655 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1657 u16 loop_max = g_settings->getU16("liquid_loop_max");
1659 //if (m_transforming_liquid.size() > 0) errorstream << "Liquid queue size="<<m_transforming_liquid.size()<<std::endl;
1661 while (m_transforming_liquid.size() > 0)
1663 // This should be done here so that it is done when continue is used
1664 if (loopcount >= initial_size || loopcount >= loop_max)
1668 Get a queued transforming liquid node
1670 v3s16 p0 = m_transforming_liquid.pop_front();
1671 u16 total_level = 0;
1672 // surrounding flowing liquid nodes
1673 NodeNeighbor neighbors[7];
1674 // current level of every block
1675 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1677 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1678 s8 can_liquid_same_level = 0;
1679 content_t liquid_kind = CONTENT_IGNORE;
1680 content_t liquid_kind_flowing = CONTENT_IGNORE;
1682 Collect information about the environment
1684 const v3s16 *dirs = g_7dirs;
1685 for (u16 i = 0; i < 7; i++) {
1686 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1689 nt = NEIGHBOR_UPPER;
1692 nt = NEIGHBOR_LOWER;
1695 v3s16 npos = p0 + dirs[i];
1697 neighbors[i].n = getNodeNoEx(npos);
1698 neighbors[i].t = nt;
1699 neighbors[i].p = npos;
1702 NodeNeighbor & nb = neighbors[i];
1704 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1706 if (nb.n.getContent() == CONTENT_AIR) {
1707 liquid_levels[i] = 0;
1712 // if this node is not (yet) of a liquid type,
1713 // choose the first liquid type we encounter
1714 if (liquid_kind_flowing == CONTENT_IGNORE)
1715 liquid_kind_flowing = nodemgr->getId(
1716 nodemgr->get(nb.n).liquid_alternative_flowing);
1717 if (liquid_kind == CONTENT_IGNORE)
1718 liquid_kind = nb.n.getContent();
1719 if (nb.n.getContent() == liquid_kind) {
1720 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1722 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1725 case LIQUID_FLOWING:
1726 // if this node is not (yet) of a liquid type,
1727 // choose the first liquid type we encounter
1728 if (liquid_kind_flowing == CONTENT_IGNORE)
1729 liquid_kind_flowing = nb.n.getContent();
1730 if (liquid_kind == CONTENT_IGNORE)
1731 liquid_kind = nodemgr->getId(
1732 nodemgr->get(nb.n).liquid_alternative_source);
1733 if (nb.n.getContent() == liquid_kind_flowing) {
1734 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1740 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1741 ++can_liquid_same_level;
1742 if (liquid_levels[i] > 0)
1743 total_level += liquid_levels[i];
1746 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1747 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1748 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1749 << nodemgr->get(nb.n.getContent()).liquid_type
1750 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1751 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1752 << " tlevel=" << (int)total_level << " cansame="
1753 << (int)can_liquid_same_level << std::endl;
1757 if (liquid_kind == CONTENT_IGNORE ||
1758 !neighbors[D_SELF].l ||
1762 // fill bottom block
1763 if (neighbors[D_BOTTOM].l) {
1764 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1765 LIQUID_LEVEL_SOURCE : total_level;
1766 total_level -= liquid_levels_want[D_BOTTOM];
1770 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1771 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1772 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1773 (can_liquid_same_level - relax) &&
1774 can_liquid_same_level >= relax + 1) {
1775 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1778 // prevent lakes in air above unloaded blocks
1779 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE && !(loopcount % 3)) {
1783 // calculate self level 5 blocks
1785 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1786 ? LIQUID_LEVEL_SOURCE
1787 : total_level / can_liquid_same_level;
1788 total_level -= want_level * can_liquid_same_level;
1791 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1792 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1793 total_level <= (can_liquid_same_level - relax) &&
1794 can_liquid_same_level >= relax + 1) {
1798 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1799 if (!neighbors[ii].l)
1801 liquid_levels_want[ii] = want_level;
1802 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0) {
1803 if (loopcount % 3 || liquid_levels[ii] <= 0){
1804 if (liquid_levels[ii] > liquid_levels_want[ii]) {
1805 ++liquid_levels_want[ii];
1808 } else if (neighbors[ii].l > 0){
1809 ++liquid_levels_want[ii];
1815 for (u16 ii = 0; ii < 7; ++ii) {
1816 if (total_level < 1) break;
1817 if (liquid_levels_want[ii] >= 0 &&
1818 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1819 ++liquid_levels_want[ii];
1824 // fill top block if can
1825 if (neighbors[D_TOP].l) {
1826 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1827 LIQUID_LEVEL_SOURCE : total_level;
1828 total_level -= liquid_levels_want[D_TOP];
1831 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1832 if ( neighbors[ii].i ||
1833 (liquid_levels_want[ii] >= 0 &&
1834 (fast_flood && p0.Y < water_level &&
1835 (initial_size >= 1000
1837 && want_level >= LIQUID_LEVEL_SOURCE/4
1838 && can_liquid_same_level >= 5
1839 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1840 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1843 if (total_level > 0) //|| flowed != volume)
1844 infostream <<" AFTER level=" << (int)total_level
1845 //<< " flowed="<<flowed<< " volume=" << volume
1846 << " wantsame="<<(int)want_level<< " top="
1847 << (int)liquid_levels_want[D_TOP]<< " topwas="
1848 << (int)liquid_levels[D_TOP]<< " bot="
1849 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1853 for (u16 i = 0; i < 7; i++) {
1854 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1856 MapNode & n0 = neighbors[i].n;
1857 p0 = neighbors[i].p;
1859 decide on the type (and possibly level) of the current node
1861 content_t new_node_content;
1862 s8 new_node_level = -1;
1863 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1864 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1865 // amount to gain, limited by viscosity
1866 // must be at least 1 in absolute value
1867 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1868 if (level_inc < -viscosity || level_inc > viscosity)
1869 new_node_level = liquid_levels[i] + level_inc/viscosity;
1870 else if (level_inc < 0)
1871 new_node_level = liquid_levels[i] - 1;
1872 else if (level_inc > 0)
1873 new_node_level = liquid_levels[i] + 1;
1875 new_node_level = liquid_levels_want[i];
1878 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1879 new_node_content = liquid_kind;
1880 else if (new_node_level > 0)
1881 new_node_content = liquid_kind_flowing;
1883 new_node_content = CONTENT_AIR;
1885 // last level must flow down on stairs
1886 if (liquid_levels_want[i] != liquid_levels[i] &&
1887 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1888 new_node_level >= 1 && new_node_level <= 2) {
1889 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1890 if (neighbors[ii].l)
1891 must_reflow_second.push_back(p0 + dirs[ii]);
1896 check if anything has changed.
1897 if not, just continue with the next node.
1900 new_node_content == n0.getContent()
1901 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1902 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1903 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1904 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1907 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1908 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1909 LIQUID_INFINITY_MASK) == neighbors[i].i
1917 update the current node
1919 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1920 // set level to last 3 bits, flowing down bit to 4th bit
1921 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1922 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1923 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1924 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1927 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1928 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1929 <<(int)new_node_level<<std::endl;
1932 n0.setContent(new_node_content);
1933 // Find out whether there is a suspect for this action
1934 std::string suspect;
1935 if(m_gamedef->rollback()){
1936 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1939 if(!suspect.empty()){
1941 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1942 // Get old node for rollback
1943 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1947 RollbackNode rollback_newnode(this, p0, m_gamedef);
1948 RollbackAction action;
1949 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1950 m_gamedef->rollback()->reportAction(action);
1956 v3s16 blockpos = getNodeBlockPos(p0);
1957 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1959 modified_blocks[blockpos] = block;
1960 // If node emits light, MapBlock requires lighting update
1961 if(nodemgr->get(n0).light_source != 0)
1962 lighting_modified_blocks[block->getPos()] = block;
1964 must_reflow.push_back(neighbors[i].p);
1966 /* //for better relax only same level
1967 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1968 if (!neighbors[ii].l) continue;
1969 must_reflow.push_back(p0 + dirs[ii]);
1974 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1975 <<" reflow="<<must_reflow.size()
1976 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1978 while (must_reflow.size() > 0)
1979 m_transforming_liquid.push_back(must_reflow.pop_front());
1980 while (must_reflow_second.size() > 0)
1981 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1982 updateLighting(lighting_modified_blocks, modified_blocks);
1985 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1988 if (g_settings->getBool("liquid_finite"))
1989 return Map::transformLiquidsFinite(modified_blocks);
1991 INodeDefManager *nodemgr = m_gamedef->ndef();
1993 DSTACK(__FUNCTION_NAME);
1994 //TimeTaker timer("transformLiquids()");
1997 u32 initial_size = m_transforming_liquid.size();
1999 /*if(initial_size != 0)
2000 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
2002 // list of nodes that due to viscosity have not reached their max level height
2003 UniqueQueue<v3s16> must_reflow;
2005 // List of MapBlocks that will require a lighting update (due to lava)
2006 std::map<v3s16, MapBlock*> lighting_modified_blocks;
2008 u16 loop_max = g_settings->getU16("liquid_loop_max");
2010 while(m_transforming_liquid.size() != 0)
2012 // This should be done here so that it is done when continue is used
2013 if(loopcount >= initial_size || loopcount >= loop_max)
2018 Get a queued transforming liquid node
2020 v3s16 p0 = m_transforming_liquid.pop_front();
2022 MapNode n0 = getNodeNoEx(p0);
2025 Collect information about current node
2027 s8 liquid_level = -1;
2028 content_t liquid_kind = CONTENT_IGNORE;
2029 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2030 switch (liquid_type) {
2032 liquid_level = LIQUID_LEVEL_SOURCE;
2033 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2035 case LIQUID_FLOWING:
2036 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2037 liquid_kind = n0.getContent();
2040 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2041 // continue with the next node.
2042 if (n0.getContent() != CONTENT_AIR)
2044 liquid_kind = CONTENT_AIR;
2049 Collect information about the environment
2051 const v3s16 *dirs = g_6dirs;
2052 NodeNeighbor sources[6]; // surrounding sources
2053 int num_sources = 0;
2054 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2056 NodeNeighbor airs[6]; // surrounding air
2058 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2059 int num_neutrals = 0;
2060 bool flowing_down = false;
2061 for (u16 i = 0; i < 6; i++) {
2062 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2065 nt = NEIGHBOR_UPPER;
2068 nt = NEIGHBOR_LOWER;
2071 v3s16 npos = p0 + dirs[i];
2072 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2073 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2075 if (nb.n.getContent() == CONTENT_AIR) {
2076 airs[num_airs++] = nb;
2077 // if the current node is a water source the neighbor
2078 // should be enqueded for transformation regardless of whether the
2079 // current node changes or not.
2080 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2081 m_transforming_liquid.push_back(npos);
2082 // if the current node happens to be a flowing node, it will start to flow down here.
2083 if (nb.t == NEIGHBOR_LOWER) {
2084 flowing_down = true;
2087 neutrals[num_neutrals++] = nb;
2091 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2092 if (liquid_kind == CONTENT_AIR)
2093 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2094 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2095 neutrals[num_neutrals++] = nb;
2097 // Do not count bottom source, it will screw things up
2099 sources[num_sources++] = nb;
2102 case LIQUID_FLOWING:
2103 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2104 if (liquid_kind == CONTENT_AIR)
2105 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2106 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2107 neutrals[num_neutrals++] = nb;
2109 flows[num_flows++] = nb;
2110 if (nb.t == NEIGHBOR_LOWER)
2111 flowing_down = true;
2118 decide on the type (and possibly level) of the current node
2120 content_t new_node_content;
2121 s8 new_node_level = -1;
2122 s8 max_node_level = -1;
2123 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2124 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2125 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2126 // it's perfectly safe to use liquid_kind here to determine the new node content.
2127 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2128 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2129 // liquid_kind is set properly, see above
2130 new_node_content = liquid_kind;
2131 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2133 // no surrounding sources, so get the maximum level that can flow into this node
2134 for (u16 i = 0; i < num_flows; i++) {
2135 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2136 switch (flows[i].t) {
2137 case NEIGHBOR_UPPER:
2138 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2139 max_node_level = LIQUID_LEVEL_MAX;
2140 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2141 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2142 } else if (nb_liquid_level > max_node_level)
2143 max_node_level = nb_liquid_level;
2145 case NEIGHBOR_LOWER:
2147 case NEIGHBOR_SAME_LEVEL:
2148 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2149 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2150 max_node_level = nb_liquid_level - 1;
2156 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2157 if (viscosity > 1 && max_node_level != liquid_level) {
2158 // amount to gain, limited by viscosity
2159 // must be at least 1 in absolute value
2160 s8 level_inc = max_node_level - liquid_level;
2161 if (level_inc < -viscosity || level_inc > viscosity)
2162 new_node_level = liquid_level + level_inc/viscosity;
2163 else if (level_inc < 0)
2164 new_node_level = liquid_level - 1;
2165 else if (level_inc > 0)
2166 new_node_level = liquid_level + 1;
2167 if (new_node_level != max_node_level)
2168 must_reflow.push_back(p0);
2170 new_node_level = max_node_level;
2172 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
2173 if (new_node_level >= (LIQUID_LEVEL_MAX+1-range))
2174 new_node_content = liquid_kind;
2176 new_node_content = CONTENT_AIR;
2181 check if anything has changed. if not, just continue with the next node.
2183 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2184 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2185 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2191 update the current node
2194 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2195 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2196 // set level to last 3 bits, flowing down bit to 4th bit
2197 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2199 // set the liquid level and flow bit to 0
2200 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2202 n0.setContent(new_node_content);
2204 // Find out whether there is a suspect for this action
2205 std::string suspect;
2206 if(m_gamedef->rollback()){
2207 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2210 if(!suspect.empty()){
2212 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2213 // Get old node for rollback
2214 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2218 RollbackNode rollback_newnode(this, p0, m_gamedef);
2219 RollbackAction action;
2220 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2221 m_gamedef->rollback()->reportAction(action);
2227 v3s16 blockpos = getNodeBlockPos(p0);
2228 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2230 modified_blocks[blockpos] = block;
2231 // If new or old node emits light, MapBlock requires lighting update
2232 if(nodemgr->get(n0).light_source != 0 ||
2233 nodemgr->get(n00).light_source != 0)
2234 lighting_modified_blocks[block->getPos()] = block;
2238 enqueue neighbors for update if neccessary
2240 switch (nodemgr->get(n0.getContent()).liquid_type) {
2242 case LIQUID_FLOWING:
2243 // make sure source flows into all neighboring nodes
2244 for (u16 i = 0; i < num_flows; i++)
2245 if (flows[i].t != NEIGHBOR_UPPER)
2246 m_transforming_liquid.push_back(flows[i].p);
2247 for (u16 i = 0; i < num_airs; i++)
2248 if (airs[i].t != NEIGHBOR_UPPER)
2249 m_transforming_liquid.push_back(airs[i].p);
2252 // this flow has turned to air; neighboring flows might need to do the same
2253 for (u16 i = 0; i < num_flows; i++)
2254 m_transforming_liquid.push_back(flows[i].p);
2258 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2259 while (must_reflow.size() > 0)
2260 m_transforming_liquid.push_back(must_reflow.pop_front());
2261 updateLighting(lighting_modified_blocks, modified_blocks);
2264 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2266 v3s16 blockpos = getNodeBlockPos(p);
2267 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2268 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2270 infostream<<"Map::getNodeMetadata(): Need to emerge "
2271 <<PP(blockpos)<<std::endl;
2272 block = emergeBlock(blockpos, false);
2276 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2280 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2284 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2286 v3s16 blockpos = getNodeBlockPos(p);
2287 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2288 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2290 infostream<<"Map::setNodeMetadata(): Need to emerge "
2291 <<PP(blockpos)<<std::endl;
2292 block = emergeBlock(blockpos, false);
2296 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2300 block->m_node_metadata.set(p_rel, meta);
2303 void Map::removeNodeMetadata(v3s16 p)
2305 v3s16 blockpos = getNodeBlockPos(p);
2306 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2307 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2310 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2314 block->m_node_metadata.remove(p_rel);
2317 NodeTimer Map::getNodeTimer(v3s16 p)
2319 v3s16 blockpos = getNodeBlockPos(p);
2320 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2321 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2323 infostream<<"Map::getNodeTimer(): Need to emerge "
2324 <<PP(blockpos)<<std::endl;
2325 block = emergeBlock(blockpos, false);
2329 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2333 NodeTimer t = block->m_node_timers.get(p_rel);
2337 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2339 v3s16 blockpos = getNodeBlockPos(p);
2340 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2341 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2343 infostream<<"Map::setNodeTimer(): Need to emerge "
2344 <<PP(blockpos)<<std::endl;
2345 block = emergeBlock(blockpos, false);
2349 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2353 block->m_node_timers.set(p_rel, t);
2356 void Map::removeNodeTimer(v3s16 p)
2358 v3s16 blockpos = getNodeBlockPos(p);
2359 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2360 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2363 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2367 block->m_node_timers.remove(p_rel);
2370 s16 Map::getHeat(v3s16 p)
2372 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2376 //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2380 s16 Map::getHumidity(v3s16 p)
2382 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2384 return block->humidity;
2386 //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2393 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2394 Map(dout_server, gamedef),
2396 m_map_metadata_changed(true),
2398 m_database_read(NULL),
2399 m_database_write(NULL)
2401 verbosestream<<__FUNCTION_NAME<<std::endl;
2404 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2406 m_mgparams = new MapgenV6Params();
2408 m_seed = m_mgparams->seed;
2410 if (g_settings->get("fixed_map_seed").empty())
2412 m_seed = (((u64)(myrand() & 0xffff) << 0)
2413 | ((u64)(myrand() & 0xffff) << 16)
2414 | ((u64)(myrand() & 0xffff) << 32)
2415 | ((u64)(myrand() & 0xffff) << 48));
2416 m_mgparams->seed = m_seed;
2420 Experimental and debug stuff
2427 Try to load map; if not found, create a new one.
2430 m_savedir = savedir;
2431 m_map_saving_enabled = false;
2435 // If directory exists, check contents and load if possible
2436 if(fs::PathExists(m_savedir))
2438 // If directory is empty, it is safe to save into it.
2439 if(fs::GetDirListing(m_savedir).size() == 0)
2441 infostream<<"ServerMap: Empty save directory is valid."
2443 m_map_saving_enabled = true;
2448 // Load map metadata (seed, chunksize)
2451 catch(SettingNotFoundException &e){
2452 infostream<<"ServerMap: Some metadata not found."
2453 <<" Using default settings."<<std::endl;
2455 catch(FileNotGoodException &e){
2456 infostream<<"WARNING: Could not load map metadata"
2457 //<<" Disabling chunk-based generator."
2462 infostream<<"ServerMap: Successfully loaded map "
2463 <<"metadata from "<<savedir
2464 <<", assuming valid save directory."
2465 <<" seed="<<m_seed<<"."
2468 m_map_saving_enabled = true;
2469 // Map loaded, not creating new one
2473 // If directory doesn't exist, it is safe to save to it
2475 m_map_saving_enabled = true;
2478 catch(std::exception &e)
2480 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2481 <<", exception: "<<e.what()<<std::endl;
2482 infostream<<"Please remove the map or fix it."<<std::endl;
2483 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2486 infostream<<"Initializing new map."<<std::endl;
2488 // Create zero sector
2489 emergeSector(v2s16(0,0));
2491 // Initially write whole map
2492 save(MOD_STATE_CLEAN);
2495 ServerMap::~ServerMap()
2497 verbosestream<<__FUNCTION_NAME<<std::endl;
2501 if(m_map_saving_enabled)
2503 // Save only changed parts
2504 save(MOD_STATE_WRITE_AT_UNLOAD);
2505 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2509 infostream<<"ServerMap: Map not saved"<<std::endl;
2512 catch(std::exception &e)
2514 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2515 <<", exception: "<<e.what()<<std::endl;
2519 Close database if it was opened
2522 sqlite3_finalize(m_database_read);
2523 if(m_database_write)
2524 sqlite3_finalize(m_database_write);
2526 sqlite3_close(m_database);
2532 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2533 for(; i.atEnd() == false; i++)
2535 MapChunk *chunk = i.getNode()->getValue();
2543 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2545 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2546 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2548 s16 chunksize = m_mgparams->chunksize;
2549 s16 coffset = -chunksize / 2;
2550 v3s16 chunk_offset(coffset, coffset, coffset);
2551 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2552 v3s16 blockpos_min = blockpos_div * chunksize;
2553 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2554 blockpos_min += chunk_offset;
2555 blockpos_max += chunk_offset;
2557 v3s16 extra_borders(1,1,1);
2559 // Do nothing if not inside limits (+-1 because of neighbors)
2560 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2561 blockpos_over_limit(blockpos_max + extra_borders))
2564 data->seed = m_seed;
2565 data->blockpos_min = blockpos_min;
2566 data->blockpos_max = blockpos_max;
2567 data->blockpos_requested = blockpos;
2568 data->nodedef = m_gamedef->ndef();
2571 Create the whole area of this and the neighboring blocks
2574 //TimeTaker timer("initBlockMake() create area");
2576 for(s16 x=blockpos_min.X-extra_borders.X;
2577 x<=blockpos_max.X+extra_borders.X; x++)
2578 for(s16 z=blockpos_min.Z-extra_borders.Z;
2579 z<=blockpos_max.Z+extra_borders.Z; z++)
2581 v2s16 sectorpos(x, z);
2582 // Sector metadata is loaded from disk if not already loaded.
2583 ServerMapSector *sector = createSector(sectorpos);
2586 for(s16 y=blockpos_min.Y-extra_borders.Y;
2587 y<=blockpos_max.Y+extra_borders.Y; y++)
2590 //MapBlock *block = createBlock(p);
2591 // 1) get from memory, 2) load from disk
2592 MapBlock *block = emergeBlock(p, false);
2593 // 3) create a blank one
2596 block = createBlock(p);
2599 Block gets sunlight if this is true.
2601 Refer to the map generator heuristics.
2603 bool ug = m_emerge->isBlockUnderground(p);
2604 block->setIsUnderground(ug);
2607 // Lighting will not be valid after make_chunk is called
2608 block->setLightingExpired(true);
2609 // Lighting will be calculated
2610 //block->setLightingExpired(false);
2616 Now we have a big empty area.
2618 Make a ManualMapVoxelManipulator that contains this and the
2622 // The area that contains this block and it's neighbors
2623 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2624 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2626 data->vmanip = new ManualMapVoxelManipulator(this);
2627 //data->vmanip->setMap(this);
2631 //TimeTaker timer("initBlockMake() initialEmerge");
2632 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2635 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2636 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2637 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2638 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2639 core::map<v3s16, u8>::Node *n;
2640 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2643 u8 flags = n->getValue();
2644 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2650 // Data is ready now.
2654 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2655 std::map<v3s16, MapBlock*> &changed_blocks)
2657 v3s16 blockpos_min = data->blockpos_min;
2658 v3s16 blockpos_max = data->blockpos_max;
2659 v3s16 blockpos_requested = data->blockpos_requested;
2660 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2661 <<blockpos_requested.Y<<","
2662 <<blockpos_requested.Z<<")"<<std::endl;*/
2664 v3s16 extra_borders(1,1,1);
2666 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2668 /*infostream<<"Resulting vmanip:"<<std::endl;
2669 data->vmanip.print(infostream);*/
2671 // Make sure affected blocks are loaded
2672 for(s16 x=blockpos_min.X-extra_borders.X;
2673 x<=blockpos_max.X+extra_borders.X; x++)
2674 for(s16 z=blockpos_min.Z-extra_borders.Z;
2675 z<=blockpos_max.Z+extra_borders.Z; z++)
2676 for(s16 y=blockpos_min.Y-extra_borders.Y;
2677 y<=blockpos_max.Y+extra_borders.Y; y++)
2680 // Load from disk if not already in memory
2681 emergeBlock(p, false);
2685 Blit generated stuff to map
2686 NOTE: blitBackAll adds nearly everything to changed_blocks
2690 //TimeTaker timer("finishBlockMake() blitBackAll");
2691 data->vmanip->blitBackAll(&changed_blocks);
2694 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2697 Copy transforming liquid information
2699 while(data->transforming_liquid.size() > 0)
2701 v3s16 p = data->transforming_liquid.pop_front();
2702 m_transforming_liquid.push_back(p);
2706 Do stuff in central blocks
2714 TimeTaker t("finishBlockMake lighting update");
2716 core::map<v3s16, MapBlock*> lighting_update_blocks;
2719 for(s16 x=blockpos_min.X-extra_borders.X;
2720 x<=blockpos_max.X+extra_borders.X; x++)
2721 for(s16 z=blockpos_min.Z-extra_borders.Z;
2722 z<=blockpos_max.Z+extra_borders.Z; z++)
2723 for(s16 y=blockpos_min.Y-extra_borders.Y;
2724 y<=blockpos_max.Y+extra_borders.Y; y++)
2727 MapBlock *block = getBlockNoCreateNoEx(p);
2729 lighting_update_blocks.insert(block->getPos(), block);
2732 updateLighting(lighting_update_blocks, changed_blocks);
2736 Set lighting to non-expired state in all of them.
2737 This is cheating, but it is not fast enough if all of them
2738 would actually be updated.
2740 for(s16 x=blockpos_min.X-extra_borders.X;
2741 x<=blockpos_max.X+extra_borders.X; x++)
2742 for(s16 z=blockpos_min.Z-extra_borders.Z;
2743 z<=blockpos_max.Z+extra_borders.Z; z++)
2744 for(s16 y=blockpos_min.Y-extra_borders.Y;
2745 y<=blockpos_max.Y+extra_borders.Y; y++)
2748 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2752 if(enable_mapgen_debug_info == false)
2753 t.stop(true); // Hide output
2758 Go through changed blocks
2760 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2761 i != changed_blocks.end(); ++i)
2763 MapBlock *block = i->second;
2766 Update day/night difference cache of the MapBlocks
2768 block->expireDayNightDiff();
2770 Set block as modified
2772 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2773 "finishBlockMake expireDayNightDiff");
2777 Set central blocks as generated
2779 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2780 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2781 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2784 MapBlock *block = getBlockNoCreateNoEx(p);
2786 block->setGenerated(true);
2790 Save changed parts of map
2791 NOTE: Will be saved later.
2793 //save(MOD_STATE_WRITE_AT_UNLOAD);
2795 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2796 <<","<<blockpos_requested.Y<<","
2797 <<blockpos_requested.Z<<")"<<std::endl;*/
2799 if(enable_mapgen_debug_info)
2802 Analyze resulting blocks
2804 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2805 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2806 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2807 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2808 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2809 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2811 v3s16 p = v3s16(x,y,z);
2812 MapBlock *block = getBlockNoCreateNoEx(p);
2814 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2815 infostream<<"Generated "<<spos<<": "
2816 <<analyze_block(block)<<std::endl;
2821 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2827 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2829 DSTACKF("%s: p2d=(%d,%d)",
2834 Check if it exists already in memory
2836 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2841 Try to load it from disk (with blocks)
2843 //if(loadSectorFull(p2d) == true)
2846 Try to load metadata from disk
2849 if(loadSectorMeta(p2d) == true)
2851 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2854 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2855 throw InvalidPositionException("");
2861 Do not create over-limit
2863 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2864 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2865 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2866 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2867 throw InvalidPositionException("createSector(): pos. over limit");
2870 Generate blank sector
2873 sector = new ServerMapSector(this, p2d, m_gamedef);
2875 // Sector position on map in nodes
2876 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2881 m_sectors[p2d] = sector;
2888 This is a quick-hand function for calling makeBlock().
2890 MapBlock * ServerMap::generateBlock(
2892 std::map<v3s16, MapBlock*> &modified_blocks
2895 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2897 /*infostream<<"generateBlock(): "
2898 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2901 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2903 TimeTaker timer("generateBlock");
2905 //MapBlock *block = original_dummy;
2907 v2s16 p2d(p.X, p.Z);
2908 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2911 Do not generate over-limit
2913 if(blockpos_over_limit(p))
2915 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2916 throw InvalidPositionException("generateBlock(): pos. over limit");
2920 Create block make data
2923 initBlockMake(&data, p);
2929 TimeTaker t("mapgen::make_block()");
2930 mapgen->makeChunk(&data);
2931 //mapgen::make_block(&data);
2933 if(enable_mapgen_debug_info == false)
2934 t.stop(true); // Hide output
2938 Blit data back on map, update lighting, add mobs and whatever this does
2940 finishBlockMake(&data, modified_blocks);
2945 MapBlock *block = getBlockNoCreateNoEx(p);
2953 bool erroneus_content = false;
2954 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2955 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2956 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2959 MapNode n = block->getNode(p);
2960 if(n.getContent() == CONTENT_IGNORE)
2962 infostream<<"CONTENT_IGNORE at "
2963 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2965 erroneus_content = true;
2969 if(erroneus_content)
2978 Generate a completely empty block
2982 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2983 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2985 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2988 n.setContent(CONTENT_AIR);
2989 block->setNode(v3s16(x0,y0,z0), n);
2995 if(enable_mapgen_debug_info == false)
2996 timer.stop(true); // Hide output
3002 MapBlock * ServerMap::createBlock(v3s16 p)
3004 DSTACKF("%s: p=(%d,%d,%d)",
3005 __FUNCTION_NAME, p.X, p.Y, p.Z);
3008 Do not create over-limit
3010 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3011 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3012 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3013 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3014 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3015 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3016 throw InvalidPositionException("createBlock(): pos. over limit");
3018 v2s16 p2d(p.X, p.Z);
3021 This will create or load a sector if not found in memory.
3022 If block exists on disk, it will be loaded.
3024 NOTE: On old save formats, this will be slow, as it generates
3025 lighting on blocks for them.
3027 ServerMapSector *sector;
3029 sector = (ServerMapSector*)createSector(p2d);
3030 assert(sector->getId() == MAPSECTOR_SERVER);
3032 catch(InvalidPositionException &e)
3034 infostream<<"createBlock: createSector() failed"<<std::endl;
3038 NOTE: This should not be done, or at least the exception
3039 should not be passed on as std::exception, because it
3040 won't be catched at all.
3042 /*catch(std::exception &e)
3044 infostream<<"createBlock: createSector() failed: "
3045 <<e.what()<<std::endl;
3050 Try to get a block from the sector
3053 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3056 if(block->isDummy())
3061 block = sector->createBlankBlock(block_y);
3066 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3068 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3070 p.X, p.Y, p.Z, create_blank);
3073 MapBlock *block = getBlockNoCreateNoEx(p);
3074 if(block && block->isDummy() == false)
3079 MapBlock *block = loadBlock(p);
3085 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3086 MapBlock *block = sector->createBlankBlock(p.Y);
3090 /*if(allow_generate)
3092 std::map<v3s16, MapBlock*> modified_blocks;
3093 MapBlock *block = generateBlock(p, modified_blocks);
3097 event.type = MEET_OTHER;
3100 // Copy modified_blocks to event
3101 for(std::map<v3s16, MapBlock*>::iterator
3102 i = modified_blocks.begin();
3103 i != modified_blocks.end(); ++i)
3105 event.modified_blocks.insert(i->first);
3109 dispatchEvent(&event);
3118 s16 ServerMap::findGroundLevel(v2s16 p2d)
3122 Uh, just do something random...
3124 // Find existing map from top to down
3127 v3s16 p(p2d.X, max, p2d.Y);
3128 for(; p.Y>min; p.Y--)
3130 MapNode n = getNodeNoEx(p);
3131 if(n.getContent() != CONTENT_IGNORE)
3136 // If this node is not air, go to plan b
3137 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3139 // Search existing walkable and return it
3140 for(; p.Y>min; p.Y--)
3142 MapNode n = getNodeNoEx(p);
3143 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3152 Determine from map generator noise functions
3155 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3158 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3159 //return (s16)level;
3162 void ServerMap::createDatabase() {
3165 e = sqlite3_exec(m_database,
3166 "CREATE TABLE IF NOT EXISTS `blocks` ("
3167 "`pos` INT NOT NULL PRIMARY KEY,"
3170 , NULL, NULL, NULL);
3171 if(e == SQLITE_ABORT)
3172 throw FileNotGoodException("Could not create database structure");
3174 infostream<<"ServerMap: Database structure was created";
3177 void ServerMap::verifyDatabase() {
3182 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3183 bool needs_create = false;
3187 Open the database connection
3190 createDirs(m_savedir);
3192 if(!fs::PathExists(dbp))
3193 needs_create = true;
3195 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3196 if(d != SQLITE_OK) {
3197 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3198 throw FileNotGoodException("Cannot open database file");
3204 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3205 if(d != SQLITE_OK) {
3206 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3207 throw FileNotGoodException("Cannot prepare read statement");
3210 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3211 if(d != SQLITE_OK) {
3212 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3213 throw FileNotGoodException("Cannot prepare write statement");
3216 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3217 if(d != SQLITE_OK) {
3218 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3219 throw FileNotGoodException("Cannot prepare read statement");
3222 infostream<<"ServerMap: Database opened"<<std::endl;
3226 bool ServerMap::loadFromFolders() {
3227 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3232 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3233 return (sqlite3_int64)pos.Z*16777216 +
3234 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3237 void ServerMap::createDirs(std::string path)
3239 if(fs::CreateAllDirs(path) == false)
3241 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3242 <<"\""<<path<<"\""<<std::endl;
3243 throw BaseException("ServerMap failed to create directory");
3247 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3253 snprintf(cc, 9, "%.4x%.4x",
3254 (unsigned int)pos.X&0xffff,
3255 (unsigned int)pos.Y&0xffff);
3257 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3259 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3260 (unsigned int)pos.X&0xfff,
3261 (unsigned int)pos.Y&0xfff);
3263 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3269 v2s16 ServerMap::getSectorPos(std::string dirname)
3273 std::string component;
3274 fs::RemoveLastPathComponent(dirname, &component, 1);
3275 if(component.size() == 8)
3278 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3280 else if(component.size() == 3)
3283 fs::RemoveLastPathComponent(dirname, &component, 2);
3284 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3285 // Sign-extend the 12 bit values up to 16 bits...
3286 if(x&0x800) x|=0xF000;
3287 if(y&0x800) y|=0xF000;
3294 v2s16 pos((s16)x, (s16)y);
3298 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3300 v2s16 p2d = getSectorPos(sectordir);
3302 if(blockfile.size() != 4){
3303 throw InvalidFilenameException("Invalid block filename");
3306 int r = sscanf(blockfile.c_str(), "%4x", &y);
3308 throw InvalidFilenameException("Invalid block filename");
3309 return v3s16(p2d.X, y, p2d.Y);
3312 std::string ServerMap::getBlockFilename(v3s16 p)
3315 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3319 void ServerMap::save(ModifiedState save_level)
3321 DSTACK(__FUNCTION_NAME);
3322 if(m_map_saving_enabled == false)
3324 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3328 if(save_level == MOD_STATE_CLEAN)
3329 infostream<<"ServerMap: Saving whole map, this can take time."
3332 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3337 // Profile modified reasons
3338 Profiler modprofiler;
3340 u32 sector_meta_count = 0;
3341 u32 block_count = 0;
3342 u32 block_count_all = 0; // Number of blocks in memory
3344 // Don't do anything with sqlite unless something is really saved
3345 bool save_started = false;
3347 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3348 i != m_sectors.end(); ++i)
3350 ServerMapSector *sector = (ServerMapSector*)i->second;
3351 assert(sector->getId() == MAPSECTOR_SERVER);
3353 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3355 saveSectorMeta(sector);
3356 sector_meta_count++;
3358 std::list<MapBlock*> blocks;
3359 sector->getBlocks(blocks);
3361 for(std::list<MapBlock*>::iterator j = blocks.begin();
3362 j != blocks.end(); ++j)
3364 MapBlock *block = *j;
3368 if(block->getModified() >= (u32)save_level)
3373 save_started = true;
3376 modprofiler.add(block->getModifiedReason(), 1);
3381 /*infostream<<"ServerMap: Written block ("
3382 <<block->getPos().X<<","
3383 <<block->getPos().Y<<","
3384 <<block->getPos().Z<<")"
3393 Only print if something happened or saved whole map
3395 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3396 || block_count != 0)
3398 infostream<<"ServerMap: Written: "
3399 <<sector_meta_count<<" sector metadata files, "
3400 <<block_count<<" block files"
3401 <<", "<<block_count_all<<" blocks in memory."
3403 PrintInfo(infostream); // ServerMap/ClientMap:
3404 infostream<<"Blocks modified by: "<<std::endl;
3405 modprofiler.print(infostream);
3409 static s32 unsignedToSigned(s32 i, s32 max_positive)
3411 if(i < max_positive)
3414 return i - 2*max_positive;
3417 // modulo of a negative number does not work consistently in C
3418 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3422 return mod - ((-i) % mod);
3425 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3427 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3429 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3431 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3432 return v3s16(x,y,z);
3435 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3437 if(loadFromFolders()){
3438 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3439 <<"all blocks that are stored in flat files"<<std::endl;
3445 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3447 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3448 v3s16 p = getIntegerAsBlock(block_i);
3449 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3455 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3457 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3458 si != m_sectors.end(); ++si)
3460 MapSector *sector = si->second;
3462 std::list<MapBlock*> blocks;
3463 sector->getBlocks(blocks);
3465 for(std::list<MapBlock*>::iterator i = blocks.begin();
3466 i != blocks.end(); ++i)
3468 MapBlock *block = (*i);
3469 v3s16 p = block->getPos();
3475 void ServerMap::saveMapMeta()
3477 DSTACK(__FUNCTION_NAME);
3479 /*infostream<<"ServerMap::saveMapMeta(): "
3483 createDirs(m_savedir);
3485 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3486 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3487 if(os.good() == false)
3489 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3490 <<"could not open"<<fullpath<<std::endl;
3491 throw FileNotGoodException("Cannot open chunk metadata");
3496 m_emerge->setParamsToSettings(¶ms);
3497 params.writeLines(os);
3499 os<<"[end_of_params]\n";
3501 m_map_metadata_changed = false;
3504 void ServerMap::loadMapMeta()
3506 DSTACK(__FUNCTION_NAME);
3508 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3511 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3512 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3513 if(is.good() == false)
3515 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3516 <<"could not open"<<fullpath<<std::endl;
3517 throw FileNotGoodException("Cannot open map metadata");
3525 throw SerializationError
3526 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3528 std::getline(is, line);
3529 std::string trimmedline = trim(line);
3530 if(trimmedline == "[end_of_params]")
3532 params.parseConfigLine(line);
3535 MapgenParams *mgparams;
3537 mgparams = m_emerge->getParamsFromSettings(¶ms);
3538 } catch (SettingNotFoundException &e) {
3539 infostream << "Couldn't get a setting from map_meta.txt: "
3540 << e.what() << std::endl;
3547 m_mgparams = mgparams;
3548 m_seed = mgparams->seed;
3550 if (params.exists("seed")) {
3551 m_seed = params.getU64("seed");
3552 m_mgparams->seed = m_seed;
3556 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3559 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3561 DSTACK(__FUNCTION_NAME);
3562 // Format used for writing
3563 u8 version = SER_FMT_VER_HIGHEST;
3565 v2s16 pos = sector->getPos();
3566 std::string dir = getSectorDir(pos);
3569 std::string fullpath = dir + DIR_DELIM + "meta";
3570 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3571 if(o.good() == false)
3572 throw FileNotGoodException("Cannot open sector metafile");
3574 sector->serialize(o, version);
3576 sector->differs_from_disk = false;
3579 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3581 DSTACK(__FUNCTION_NAME);
3583 v2s16 p2d = getSectorPos(sectordir);
3585 ServerMapSector *sector = NULL;
3587 std::string fullpath = sectordir + DIR_DELIM + "meta";
3588 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3589 if(is.good() == false)
3591 // If the directory exists anyway, it probably is in some old
3592 // format. Just go ahead and create the sector.
3593 if(fs::PathExists(sectordir))
3595 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3596 <<fullpath<<" doesn't exist but directory does."
3597 <<" Continuing with a sector with no metadata."
3599 sector = new ServerMapSector(this, p2d, m_gamedef);
3600 m_sectors[p2d] = sector;
3604 throw FileNotGoodException("Cannot open sector metafile");
3609 sector = ServerMapSector::deSerialize
3610 (is, this, p2d, m_sectors, m_gamedef);
3612 saveSectorMeta(sector);
3615 sector->differs_from_disk = false;
3620 bool ServerMap::loadSectorMeta(v2s16 p2d)
3622 DSTACK(__FUNCTION_NAME);
3624 MapSector *sector = NULL;
3626 // The directory layout we're going to load from.
3627 // 1 - original sectors/xxxxzzzz/
3628 // 2 - new sectors2/xxx/zzz/
3629 // If we load from anything but the latest structure, we will
3630 // immediately save to the new one, and remove the old.
3632 std::string sectordir1 = getSectorDir(p2d, 1);
3633 std::string sectordir;
3634 if(fs::PathExists(sectordir1))
3636 sectordir = sectordir1;
3641 sectordir = getSectorDir(p2d, 2);
3645 sector = loadSectorMeta(sectordir, loadlayout != 2);
3647 catch(InvalidFilenameException &e)
3651 catch(FileNotGoodException &e)
3655 catch(std::exception &e)
3664 bool ServerMap::loadSectorFull(v2s16 p2d)
3666 DSTACK(__FUNCTION_NAME);
3668 MapSector *sector = NULL;
3670 // The directory layout we're going to load from.
3671 // 1 - original sectors/xxxxzzzz/
3672 // 2 - new sectors2/xxx/zzz/
3673 // If we load from anything but the latest structure, we will
3674 // immediately save to the new one, and remove the old.
3676 std::string sectordir1 = getSectorDir(p2d, 1);
3677 std::string sectordir;
3678 if(fs::PathExists(sectordir1))
3680 sectordir = sectordir1;
3685 sectordir = getSectorDir(p2d, 2);
3689 sector = loadSectorMeta(sectordir, loadlayout != 2);
3691 catch(InvalidFilenameException &e)
3695 catch(FileNotGoodException &e)
3699 catch(std::exception &e)
3707 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3709 std::vector<fs::DirListNode>::iterator i2;
3710 for(i2=list2.begin(); i2!=list2.end(); i2++)
3716 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3718 catch(InvalidFilenameException &e)
3720 // This catches unknown crap in directory
3726 infostream<<"Sector converted to new layout - deleting "<<
3727 sectordir1<<std::endl;
3728 fs::RecursiveDelete(sectordir1);
3735 void ServerMap::beginSave() {
3737 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3738 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3741 void ServerMap::endSave() {
3743 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3744 infostream<<"WARNING: endSave() failed, map might not have saved.";
3747 void ServerMap::saveBlock(MapBlock *block)
3749 DSTACK(__FUNCTION_NAME);
3751 Dummy blocks are not written
3753 if(block->isDummy())
3755 /*v3s16 p = block->getPos();
3756 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3757 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3761 // Format used for writing
3762 u8 version = SER_FMT_VER_HIGHEST;
3764 v3s16 p3d = block->getPos();
3768 v2s16 p2d(p3d.X, p3d.Z);
3769 std::string sectordir = getSectorDir(p2d);
3771 createDirs(sectordir);
3773 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3774 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3775 if(o.good() == false)
3776 throw FileNotGoodException("Cannot open block data");
3779 [0] u8 serialization version
3785 std::ostringstream o(std::ios_base::binary);
3787 o.write((char*)&version, 1);
3790 block->serialize(o, version, true);
3792 // Write block to database
3794 std::string tmp = o.str();
3795 const char *bytes = tmp.c_str();
3797 bool success = true;
3798 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3799 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3802 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3803 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3806 int written = sqlite3_step(m_database_write);
3807 if(written != SQLITE_DONE) {
3808 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3809 <<sqlite3_errmsg(m_database)<<std::endl;
3812 // Make ready for later reuse
3813 sqlite3_reset(m_database_write);
3815 // We just wrote it to the disk so clear modified flag
3817 block->resetModified();
3820 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3822 DSTACK(__FUNCTION_NAME);
3824 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3827 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3828 if(is.good() == false)
3829 throw FileNotGoodException("Cannot open block file");
3831 v3s16 p3d = getBlockPos(sectordir, blockfile);
3832 v2s16 p2d(p3d.X, p3d.Z);
3834 assert(sector->getPos() == p2d);
3836 u8 version = SER_FMT_VER_INVALID;
3837 is.read((char*)&version, 1);
3840 throw SerializationError("ServerMap::loadBlock(): Failed"
3841 " to read MapBlock version");
3843 /*u32 block_size = MapBlock::serializedLength(version);
3844 SharedBuffer<u8> data(block_size);
3845 is.read((char*)*data, block_size);*/
3847 // This will always return a sector because we're the server
3848 //MapSector *sector = emergeSector(p2d);
3850 MapBlock *block = NULL;
3851 bool created_new = false;
3852 block = sector->getBlockNoCreateNoEx(p3d.Y);
3855 block = sector->createBlankBlockNoInsert(p3d.Y);
3860 block->deSerialize(is, version, true);
3862 // If it's a new block, insert it to the map
3864 sector->insertBlock(block);
3867 Save blocks loaded in old format in new format
3870 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3874 // Should be in database now, so delete the old file
3875 fs::RecursiveDelete(fullpath);
3878 // We just loaded it from the disk, so it's up-to-date.
3879 block->resetModified();
3882 catch(SerializationError &e)
3884 infostream<<"WARNING: Invalid block data on disk "
3885 <<"fullpath="<<fullpath
3886 <<" (SerializationError). "
3887 <<"what()="<<e.what()
3889 // Ignoring. A new one will be generated.
3892 // TODO: Backup file; name is in fullpath.
3896 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3898 DSTACK(__FUNCTION_NAME);
3901 std::istringstream is(*blob, std::ios_base::binary);
3903 u8 version = SER_FMT_VER_INVALID;
3904 is.read((char*)&version, 1);
3907 throw SerializationError("ServerMap::loadBlock(): Failed"
3908 " to read MapBlock version");
3910 /*u32 block_size = MapBlock::serializedLength(version);
3911 SharedBuffer<u8> data(block_size);
3912 is.read((char*)*data, block_size);*/
3914 // This will always return a sector because we're the server
3915 //MapSector *sector = emergeSector(p2d);
3917 MapBlock *block = NULL;
3918 bool created_new = false;
3919 block = sector->getBlockNoCreateNoEx(p3d.Y);
3922 block = sector->createBlankBlockNoInsert(p3d.Y);
3927 block->deSerialize(is, version, true);
3929 // If it's a new block, insert it to the map
3931 sector->insertBlock(block);
3934 Save blocks loaded in old format in new format
3937 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3938 // Only save if asked to; no need to update version
3942 // We just loaded it from, so it's up-to-date.
3943 block->resetModified();
3946 catch(SerializationError &e)
3948 errorstream<<"Invalid block data in database"
3949 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3950 <<" (SerializationError): "<<e.what()<<std::endl;
3952 // TODO: Block should be marked as invalid in memory so that it is
3953 // not touched but the game can run
3955 if(g_settings->getBool("ignore_world_load_errors")){
3956 errorstream<<"Ignoring block load error. Duck and cover! "
3957 <<"(ignore_world_load_errors)"<<std::endl;
3959 throw SerializationError("Invalid block data in database");
3965 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3967 DSTACK(__FUNCTION_NAME);
3969 v2s16 p2d(blockpos.X, blockpos.Z);
3971 if(!loadFromFolders()) {
3974 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3975 infostream<<"WARNING: Could not bind block position for load: "
3976 <<sqlite3_errmsg(m_database)<<std::endl;
3977 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3979 Make sure sector is loaded
3981 MapSector *sector = createSector(p2d);
3986 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3987 size_t len = sqlite3_column_bytes(m_database_read, 0);
3989 std::string datastr(data, len);
3991 loadBlock(&datastr, blockpos, sector, false);
3993 sqlite3_step(m_database_read);
3994 // We should never get more than 1 row, so ok to reset
3995 sqlite3_reset(m_database_read);
3997 return getBlockNoCreateNoEx(blockpos);
3999 sqlite3_reset(m_database_read);
4001 // Not found in database, try the files
4004 // The directory layout we're going to load from.
4005 // 1 - original sectors/xxxxzzzz/
4006 // 2 - new sectors2/xxx/zzz/
4007 // If we load from anything but the latest structure, we will
4008 // immediately save to the new one, and remove the old.
4010 std::string sectordir1 = getSectorDir(p2d, 1);
4011 std::string sectordir;
4012 if(fs::PathExists(sectordir1))
4014 sectordir = sectordir1;
4019 sectordir = getSectorDir(p2d, 2);
4023 Make sure sector is loaded
4025 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4029 sector = loadSectorMeta(sectordir, loadlayout != 2);
4031 catch(InvalidFilenameException &e)
4035 catch(FileNotGoodException &e)
4039 catch(std::exception &e)
4046 Make sure file exists
4049 std::string blockfilename = getBlockFilename(blockpos);
4050 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
4054 Load block and save it to the database
4056 loadBlock(sectordir, blockfilename, sector, true);
4057 return getBlockNoCreateNoEx(blockpos);
4060 void ServerMap::PrintInfo(std::ostream &out)
4065 s16 ServerMap::getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
4068 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
4070 if (env->getGameTime() - block->heat_time < 10)
4074 //variant 1: full random
4075 //f32 heat = NoisePerlin3D(m_emerge->biomedef->np_heat, p.X, env->getGameTime()/100, p.Z, m_emerge->params->seed);
4077 //variant 2: season change based on default heat map
4078 f32 heat = NoisePerlin2D(m_emerge->biomedef->np_heat, p.X, p.Z, m_emerge->params->seed);
4079 heat += -30; // -30 - todo REMOVE after fixed NoiseParams nparams_biome_def_heat = {50, 50, -> 20, 50,
4080 f32 base = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
4081 base /= ( 86400 * g_settings->getS16("year_days") );
4082 base += (f32)p.X / 3000;
4083 heat += 30 * sin(base * M_PI); // season
4085 heat += p.Y / -333; // upper=colder, lower=hotter
4087 // daily change, hotter at sun +4, colder at night -4
4088 heat += 8 * (sin(cycle_shift(env->getTimeOfDayF(), -0.25) * M_PI) - 0.5);
4092 block->heat_time = env->getGameTime();
4097 s16 ServerMap::getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
4100 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
4102 if (env->getGameTime() - block->humidity_time < 10)
4103 return block->humidity;
4106 f32 humidity = NoisePerlin3D( m_emerge->biomedef->np_humidity,
4107 p.X, env->getGameTime()/10, p.Z,
4108 m_emerge->params->seed);
4109 humidity += -12 * ( sin(cycle_shift(env->getTimeOfDayF(), -0.1) * M_PI) - 0.5);
4110 //todo like heat//humidity += 20 * ( sin(((f32)p.Z / 300) * M_PI) - 0.5);
4112 if (humidity > 100) humidity = 100;
4113 if (humidity < 0) humidity = 0;
4116 block->humidity = humidity;
4117 block->humidity_time = env->getGameTime();
4126 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4131 MapVoxelManipulator::~MapVoxelManipulator()
4133 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4137 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4139 TimeTaker timer1("emerge", &emerge_time);
4141 // Units of these are MapBlocks
4142 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4143 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4145 VoxelArea block_area_nodes
4146 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
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 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4168 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4170 a.print(infostream);
4171 infostream<<std::endl;*/
4173 block = m_map->getBlockNoCreate(p);
4174 if(block->isDummy())
4175 block_data_inexistent = true;
4177 block->copyTo(*this);
4179 catch(InvalidPositionException &e)
4181 block_data_inexistent = true;
4184 if(block_data_inexistent)
4186 flags |= VMANIP_BLOCK_DATA_INEXIST;
4188 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4189 // Fill with VOXELFLAG_INEXISTENT
4190 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4191 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4193 s32 i = m_area.index(a.MinEdge.X,y,z);
4194 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4197 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4199 // Mark that block was loaded as blank
4200 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4203 m_loaded_blocks[p] = flags;
4206 //infostream<<"emerge done"<<std::endl;
4210 SUGG: Add an option to only update eg. water and air nodes.
4211 This will make it interfere less with important stuff if
4214 void MapVoxelManipulator::blitBack
4215 (std::map<v3s16, MapBlock*> & modified_blocks)
4217 if(m_area.getExtent() == v3s16(0,0,0))
4220 //TimeTaker timer1("blitBack");
4222 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4223 <<m_loaded_blocks.size()<<std::endl;*/
4226 Initialize block cache
4228 v3s16 blockpos_last;
4229 MapBlock *block = NULL;
4230 bool block_checked_in_modified = false;
4232 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4233 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4234 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4238 u8 f = m_flags[m_area.index(p)];
4239 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4242 MapNode &n = m_data[m_area.index(p)];
4244 v3s16 blockpos = getNodeBlockPos(p);
4249 if(block == NULL || blockpos != blockpos_last){
4250 block = m_map->getBlockNoCreate(blockpos);
4251 blockpos_last = blockpos;
4252 block_checked_in_modified = false;
4255 // Calculate relative position in block
4256 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4258 // Don't continue if nothing has changed here
4259 if(block->getNode(relpos) == n)
4262 //m_map->setNode(m_area.MinEdge + p, n);
4263 block->setNode(relpos, n);
4266 Make sure block is in modified_blocks
4268 if(block_checked_in_modified == false)
4270 modified_blocks[blockpos] = block;
4271 block_checked_in_modified = true;
4274 catch(InvalidPositionException &e)
4280 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4281 MapVoxelManipulator(map),
4282 m_create_area(false)
4286 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4290 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4292 // Just create the area so that it can be pointed to
4293 VoxelManipulator::emerge(a, caller_id);
4296 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4297 v3s16 blockpos_max, bool load_if_inexistent)
4299 TimeTaker timer1("initialEmerge", &emerge_time);
4301 // Units of these are MapBlocks
4302 v3s16 p_min = blockpos_min;
4303 v3s16 p_max = blockpos_max;
4305 VoxelArea block_area_nodes
4306 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4308 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4311 infostream<<"initialEmerge: area: ";
4312 block_area_nodes.print(infostream);
4313 infostream<<" ("<<size_MB<<"MB)";
4314 infostream<<std::endl;
4317 addArea(block_area_nodes);
4319 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4320 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4321 for(s32 x=p_min.X; x<=p_max.X; x++)
4326 std::map<v3s16, u8>::iterator n;
4327 n = m_loaded_blocks.find(p);
4328 if(n != m_loaded_blocks.end())
4331 bool block_data_inexistent = false;
4334 TimeTaker timer1("emerge load", &emerge_load_time);
4336 block = m_map->getBlockNoCreate(p);
4337 if(block->isDummy())
4338 block_data_inexistent = true;
4340 block->copyTo(*this);
4342 catch(InvalidPositionException &e)
4344 block_data_inexistent = true;
4347 if(block_data_inexistent)
4350 if (load_if_inexistent) {
4351 ServerMap *svrmap = (ServerMap *)m_map;
4352 block = svrmap->emergeBlock(p, false);
4354 block = svrmap->createBlock(p);
4356 block->copyTo(*this);
4358 flags |= VMANIP_BLOCK_DATA_INEXIST;
4361 Mark area inexistent
4363 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4364 // Fill with VOXELFLAG_INEXISTENT
4365 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4366 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4368 s32 i = m_area.index(a.MinEdge.X,y,z);
4369 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4373 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4375 // Mark that block was loaded as blank
4376 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4379 m_loaded_blocks[p] = flags;
4383 void ManualMapVoxelManipulator::blitBackAll(
4384 std::map<v3s16, MapBlock*> * modified_blocks)
4386 if(m_area.getExtent() == v3s16(0,0,0))
4390 Copy data of all blocks
4392 for(std::map<v3s16, u8>::iterator
4393 i = m_loaded_blocks.begin();
4394 i != m_loaded_blocks.end(); ++i)
4397 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4398 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4399 if(existed == false)
4404 block->copyFrom(*this);
4407 (*modified_blocks)[p] = block;