3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
27 #include "nodemetadata.h"
33 #include "util/directiontables.h"
34 #include "util/mathconstants.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] = nb.n.getLevel(nodemgr); //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.getLevel(nodemgr); //(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.
1901 new_node_content == n0.getContent()
1902 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1903 (n0.getLevel(nodemgr) == (u8)new_node_level
1904 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1905 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1908 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1909 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1910 LIQUID_INFINITY_MASK) == neighbors[i].i
1913 if (liquid_levels[i] == new_node_level)
1921 update the current node
1924 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1925 // set level to last 3 bits, flowing down bit to 4th bit
1926 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1927 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1928 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1929 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1933 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1934 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1935 <<(int)new_node_level<<std::endl;
1938 n0.setContent(liquid_kind_flowing);
1939 n0.setLevel(nodemgr, new_node_level);
1940 // Find out whether there is a suspect for this action
1941 std::string suspect;
1942 if(m_gamedef->rollback()){
1943 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1946 if(!suspect.empty()){
1948 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1949 // Get old node for rollback
1950 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1954 RollbackNode rollback_newnode(this, p0, m_gamedef);
1955 RollbackAction action;
1956 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1957 m_gamedef->rollback()->reportAction(action);
1963 v3s16 blockpos = getNodeBlockPos(p0);
1964 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1966 modified_blocks[blockpos] = block;
1967 // If node emits light, MapBlock requires lighting update
1968 if(nodemgr->get(n0).light_source != 0)
1969 lighting_modified_blocks[block->getPos()] = block;
1971 must_reflow.push_back(neighbors[i].p);
1973 /* //for better relax only same level
1974 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1975 if (!neighbors[ii].l) continue;
1976 must_reflow.push_back(p0 + dirs[ii]);
1981 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1982 <<" reflow="<<must_reflow.size()
1983 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1985 while (must_reflow.size() > 0)
1986 m_transforming_liquid.push_back(must_reflow.pop_front());
1987 while (must_reflow_second.size() > 0)
1988 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1989 updateLighting(lighting_modified_blocks, modified_blocks);
1992 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1995 if (g_settings->getBool("liquid_finite"))
1996 return Map::transformLiquidsFinite(modified_blocks);
1998 INodeDefManager *nodemgr = m_gamedef->ndef();
2000 DSTACK(__FUNCTION_NAME);
2001 //TimeTaker timer("transformLiquids()");
2004 u32 initial_size = m_transforming_liquid.size();
2006 /*if(initial_size != 0)
2007 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
2009 // list of nodes that due to viscosity have not reached their max level height
2010 UniqueQueue<v3s16> must_reflow;
2012 // List of MapBlocks that will require a lighting update (due to lava)
2013 std::map<v3s16, MapBlock*> lighting_modified_blocks;
2015 u16 loop_max = g_settings->getU16("liquid_loop_max");
2017 while(m_transforming_liquid.size() != 0)
2019 // This should be done here so that it is done when continue is used
2020 if(loopcount >= initial_size || loopcount >= loop_max)
2025 Get a queued transforming liquid node
2027 v3s16 p0 = m_transforming_liquid.pop_front();
2029 MapNode n0 = getNodeNoEx(p0);
2032 Collect information about current node
2034 s8 liquid_level = -1;
2035 content_t liquid_kind = CONTENT_IGNORE;
2036 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2037 switch (liquid_type) {
2039 liquid_level = LIQUID_LEVEL_SOURCE;
2040 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2042 case LIQUID_FLOWING:
2043 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2044 liquid_kind = n0.getContent();
2047 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2048 // continue with the next node.
2049 if (n0.getContent() != CONTENT_AIR)
2051 liquid_kind = CONTENT_AIR;
2056 Collect information about the environment
2058 const v3s16 *dirs = g_6dirs;
2059 NodeNeighbor sources[6]; // surrounding sources
2060 int num_sources = 0;
2061 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2063 NodeNeighbor airs[6]; // surrounding air
2065 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2066 int num_neutrals = 0;
2067 bool flowing_down = false;
2068 for (u16 i = 0; i < 6; i++) {
2069 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2072 nt = NEIGHBOR_UPPER;
2075 nt = NEIGHBOR_LOWER;
2078 v3s16 npos = p0 + dirs[i];
2079 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2080 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2082 if (nb.n.getContent() == CONTENT_AIR) {
2083 airs[num_airs++] = nb;
2084 // if the current node is a water source the neighbor
2085 // should be enqueded for transformation regardless of whether the
2086 // current node changes or not.
2087 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2088 m_transforming_liquid.push_back(npos);
2089 // if the current node happens to be a flowing node, it will start to flow down here.
2090 if (nb.t == NEIGHBOR_LOWER) {
2091 flowing_down = true;
2094 neutrals[num_neutrals++] = nb;
2098 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2099 if (liquid_kind == CONTENT_AIR)
2100 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2101 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2102 neutrals[num_neutrals++] = nb;
2104 // Do not count bottom source, it will screw things up
2106 sources[num_sources++] = nb;
2109 case LIQUID_FLOWING:
2110 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2111 if (liquid_kind == CONTENT_AIR)
2112 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2113 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2114 neutrals[num_neutrals++] = nb;
2116 flows[num_flows++] = nb;
2117 if (nb.t == NEIGHBOR_LOWER)
2118 flowing_down = true;
2125 decide on the type (and possibly level) of the current node
2127 content_t new_node_content;
2128 s8 new_node_level = -1;
2129 s8 max_node_level = -1;
2130 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2131 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2132 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2133 // it's perfectly safe to use liquid_kind here to determine the new node content.
2134 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2135 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2136 // liquid_kind is set properly, see above
2137 new_node_content = liquid_kind;
2138 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2140 // no surrounding sources, so get the maximum level that can flow into this node
2141 for (u16 i = 0; i < num_flows; i++) {
2142 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2143 switch (flows[i].t) {
2144 case NEIGHBOR_UPPER:
2145 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2146 max_node_level = LIQUID_LEVEL_MAX;
2147 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2148 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2149 } else if (nb_liquid_level > max_node_level)
2150 max_node_level = nb_liquid_level;
2152 case NEIGHBOR_LOWER:
2154 case NEIGHBOR_SAME_LEVEL:
2155 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2156 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2157 max_node_level = nb_liquid_level - 1;
2163 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2164 if (viscosity > 1 && max_node_level != liquid_level) {
2165 // amount to gain, limited by viscosity
2166 // must be at least 1 in absolute value
2167 s8 level_inc = max_node_level - liquid_level;
2168 if (level_inc < -viscosity || level_inc > viscosity)
2169 new_node_level = liquid_level + level_inc/viscosity;
2170 else if (level_inc < 0)
2171 new_node_level = liquid_level - 1;
2172 else if (level_inc > 0)
2173 new_node_level = liquid_level + 1;
2174 if (new_node_level != max_node_level)
2175 must_reflow.push_back(p0);
2177 new_node_level = max_node_level;
2179 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
2180 if (new_node_level >= (LIQUID_LEVEL_MAX+1-range))
2181 new_node_content = liquid_kind;
2183 new_node_content = CONTENT_AIR;
2188 check if anything has changed. if not, just continue with the next node.
2190 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2191 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2192 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2198 update the current node
2201 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2202 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2203 // set level to last 3 bits, flowing down bit to 4th bit
2204 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2206 // set the liquid level and flow bit to 0
2207 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2209 n0.setContent(new_node_content);
2211 // Find out whether there is a suspect for this action
2212 std::string suspect;
2213 if(m_gamedef->rollback()){
2214 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2217 if(!suspect.empty()){
2219 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2220 // Get old node for rollback
2221 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2225 RollbackNode rollback_newnode(this, p0, m_gamedef);
2226 RollbackAction action;
2227 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2228 m_gamedef->rollback()->reportAction(action);
2234 v3s16 blockpos = getNodeBlockPos(p0);
2235 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2237 modified_blocks[blockpos] = block;
2238 // If new or old node emits light, MapBlock requires lighting update
2239 if(nodemgr->get(n0).light_source != 0 ||
2240 nodemgr->get(n00).light_source != 0)
2241 lighting_modified_blocks[block->getPos()] = block;
2245 enqueue neighbors for update if neccessary
2247 switch (nodemgr->get(n0.getContent()).liquid_type) {
2249 case LIQUID_FLOWING:
2250 // make sure source flows into all neighboring nodes
2251 for (u16 i = 0; i < num_flows; i++)
2252 if (flows[i].t != NEIGHBOR_UPPER)
2253 m_transforming_liquid.push_back(flows[i].p);
2254 for (u16 i = 0; i < num_airs; i++)
2255 if (airs[i].t != NEIGHBOR_UPPER)
2256 m_transforming_liquid.push_back(airs[i].p);
2259 // this flow has turned to air; neighboring flows might need to do the same
2260 for (u16 i = 0; i < num_flows; i++)
2261 m_transforming_liquid.push_back(flows[i].p);
2265 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2266 while (must_reflow.size() > 0)
2267 m_transforming_liquid.push_back(must_reflow.pop_front());
2268 updateLighting(lighting_modified_blocks, modified_blocks);
2271 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2273 v3s16 blockpos = getNodeBlockPos(p);
2274 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2275 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2277 infostream<<"Map::getNodeMetadata(): Need to emerge "
2278 <<PP(blockpos)<<std::endl;
2279 block = emergeBlock(blockpos, false);
2283 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2287 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2291 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2293 v3s16 blockpos = getNodeBlockPos(p);
2294 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2295 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2297 infostream<<"Map::setNodeMetadata(): Need to emerge "
2298 <<PP(blockpos)<<std::endl;
2299 block = emergeBlock(blockpos, false);
2303 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2307 block->m_node_metadata.set(p_rel, meta);
2310 void Map::removeNodeMetadata(v3s16 p)
2312 v3s16 blockpos = getNodeBlockPos(p);
2313 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2314 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2317 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2321 block->m_node_metadata.remove(p_rel);
2324 NodeTimer Map::getNodeTimer(v3s16 p)
2326 v3s16 blockpos = getNodeBlockPos(p);
2327 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2328 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2330 infostream<<"Map::getNodeTimer(): Need to emerge "
2331 <<PP(blockpos)<<std::endl;
2332 block = emergeBlock(blockpos, false);
2336 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2340 NodeTimer t = block->m_node_timers.get(p_rel);
2344 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2346 v3s16 blockpos = getNodeBlockPos(p);
2347 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2348 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2350 infostream<<"Map::setNodeTimer(): Need to emerge "
2351 <<PP(blockpos)<<std::endl;
2352 block = emergeBlock(blockpos, false);
2356 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2360 block->m_node_timers.set(p_rel, t);
2363 void Map::removeNodeTimer(v3s16 p)
2365 v3s16 blockpos = getNodeBlockPos(p);
2366 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2367 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2370 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2374 block->m_node_timers.remove(p_rel);
2377 s16 Map::getHeat(v3s16 p)
2379 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2383 //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2387 s16 Map::getHumidity(v3s16 p)
2389 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2391 return block->humidity;
2393 //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2400 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2401 Map(dout_server, gamedef),
2403 m_map_metadata_changed(true),
2405 m_database_read(NULL),
2406 m_database_write(NULL)
2408 verbosestream<<__FUNCTION_NAME<<std::endl;
2411 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2413 m_mgparams = new MapgenV6Params();
2415 m_seed = m_mgparams->seed;
2417 if (g_settings->get("fixed_map_seed").empty())
2419 m_seed = (((u64)(myrand() & 0xffff) << 0)
2420 | ((u64)(myrand() & 0xffff) << 16)
2421 | ((u64)(myrand() & 0xffff) << 32)
2422 | ((u64)(myrand() & 0xffff) << 48));
2423 m_mgparams->seed = m_seed;
2427 Experimental and debug stuff
2434 Try to load map; if not found, create a new one.
2437 m_savedir = savedir;
2438 m_map_saving_enabled = false;
2442 // If directory exists, check contents and load if possible
2443 if(fs::PathExists(m_savedir))
2445 // If directory is empty, it is safe to save into it.
2446 if(fs::GetDirListing(m_savedir).size() == 0)
2448 infostream<<"ServerMap: Empty save directory is valid."
2450 m_map_saving_enabled = true;
2455 // Load map metadata (seed, chunksize)
2458 catch(SettingNotFoundException &e){
2459 infostream<<"ServerMap: Some metadata not found."
2460 <<" Using default settings."<<std::endl;
2462 catch(FileNotGoodException &e){
2463 infostream<<"WARNING: Could not load map metadata"
2464 //<<" Disabling chunk-based generator."
2469 infostream<<"ServerMap: Successfully loaded map "
2470 <<"metadata from "<<savedir
2471 <<", assuming valid save directory."
2472 <<" seed="<<m_seed<<"."
2475 m_map_saving_enabled = true;
2476 // Map loaded, not creating new one
2480 // If directory doesn't exist, it is safe to save to it
2482 m_map_saving_enabled = true;
2485 catch(std::exception &e)
2487 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2488 <<", exception: "<<e.what()<<std::endl;
2489 infostream<<"Please remove the map or fix it."<<std::endl;
2490 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2493 infostream<<"Initializing new map."<<std::endl;
2495 // Create zero sector
2496 emergeSector(v2s16(0,0));
2498 // Initially write whole map
2499 save(MOD_STATE_CLEAN);
2502 ServerMap::~ServerMap()
2504 verbosestream<<__FUNCTION_NAME<<std::endl;
2508 if(m_map_saving_enabled)
2510 // Save only changed parts
2511 save(MOD_STATE_WRITE_AT_UNLOAD);
2512 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2516 infostream<<"ServerMap: Map not saved"<<std::endl;
2519 catch(std::exception &e)
2521 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2522 <<", exception: "<<e.what()<<std::endl;
2526 Close database if it was opened
2529 sqlite3_finalize(m_database_read);
2530 if(m_database_write)
2531 sqlite3_finalize(m_database_write);
2533 sqlite3_close(m_database);
2539 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2540 for(; i.atEnd() == false; i++)
2542 MapChunk *chunk = i.getNode()->getValue();
2550 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2552 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2553 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2555 s16 chunksize = m_mgparams->chunksize;
2556 s16 coffset = -chunksize / 2;
2557 v3s16 chunk_offset(coffset, coffset, coffset);
2558 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2559 v3s16 blockpos_min = blockpos_div * chunksize;
2560 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2561 blockpos_min += chunk_offset;
2562 blockpos_max += chunk_offset;
2564 v3s16 extra_borders(1,1,1);
2566 // Do nothing if not inside limits (+-1 because of neighbors)
2567 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2568 blockpos_over_limit(blockpos_max + extra_borders))
2571 data->seed = m_seed;
2572 data->blockpos_min = blockpos_min;
2573 data->blockpos_max = blockpos_max;
2574 data->blockpos_requested = blockpos;
2575 data->nodedef = m_gamedef->ndef();
2578 Create the whole area of this and the neighboring blocks
2581 //TimeTaker timer("initBlockMake() create area");
2583 for(s16 x=blockpos_min.X-extra_borders.X;
2584 x<=blockpos_max.X+extra_borders.X; x++)
2585 for(s16 z=blockpos_min.Z-extra_borders.Z;
2586 z<=blockpos_max.Z+extra_borders.Z; z++)
2588 v2s16 sectorpos(x, z);
2589 // Sector metadata is loaded from disk if not already loaded.
2590 ServerMapSector *sector = createSector(sectorpos);
2593 for(s16 y=blockpos_min.Y-extra_borders.Y;
2594 y<=blockpos_max.Y+extra_borders.Y; y++)
2597 //MapBlock *block = createBlock(p);
2598 // 1) get from memory, 2) load from disk
2599 MapBlock *block = emergeBlock(p, false);
2600 // 3) create a blank one
2603 block = createBlock(p);
2606 Block gets sunlight if this is true.
2608 Refer to the map generator heuristics.
2610 bool ug = m_emerge->isBlockUnderground(p);
2611 block->setIsUnderground(ug);
2614 // Lighting will not be valid after make_chunk is called
2615 block->setLightingExpired(true);
2616 // Lighting will be calculated
2617 //block->setLightingExpired(false);
2623 Now we have a big empty area.
2625 Make a ManualMapVoxelManipulator that contains this and the
2629 // The area that contains this block and it's neighbors
2630 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2631 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2633 data->vmanip = new ManualMapVoxelManipulator(this);
2634 //data->vmanip->setMap(this);
2638 //TimeTaker timer("initBlockMake() initialEmerge");
2639 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2642 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2643 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2644 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2645 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2646 core::map<v3s16, u8>::Node *n;
2647 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2650 u8 flags = n->getValue();
2651 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2657 // Data is ready now.
2661 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2662 std::map<v3s16, MapBlock*> &changed_blocks)
2664 v3s16 blockpos_min = data->blockpos_min;
2665 v3s16 blockpos_max = data->blockpos_max;
2666 v3s16 blockpos_requested = data->blockpos_requested;
2667 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2668 <<blockpos_requested.Y<<","
2669 <<blockpos_requested.Z<<")"<<std::endl;*/
2671 v3s16 extra_borders(1,1,1);
2673 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2675 /*infostream<<"Resulting vmanip:"<<std::endl;
2676 data->vmanip.print(infostream);*/
2678 // Make sure affected blocks are loaded
2679 for(s16 x=blockpos_min.X-extra_borders.X;
2680 x<=blockpos_max.X+extra_borders.X; x++)
2681 for(s16 z=blockpos_min.Z-extra_borders.Z;
2682 z<=blockpos_max.Z+extra_borders.Z; z++)
2683 for(s16 y=blockpos_min.Y-extra_borders.Y;
2684 y<=blockpos_max.Y+extra_borders.Y; y++)
2687 // Load from disk if not already in memory
2688 emergeBlock(p, false);
2692 Blit generated stuff to map
2693 NOTE: blitBackAll adds nearly everything to changed_blocks
2697 //TimeTaker timer("finishBlockMake() blitBackAll");
2698 data->vmanip->blitBackAll(&changed_blocks);
2701 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2704 Copy transforming liquid information
2706 while(data->transforming_liquid.size() > 0)
2708 v3s16 p = data->transforming_liquid.pop_front();
2709 m_transforming_liquid.push_back(p);
2713 Do stuff in central blocks
2721 TimeTaker t("finishBlockMake lighting update");
2723 core::map<v3s16, MapBlock*> lighting_update_blocks;
2726 for(s16 x=blockpos_min.X-extra_borders.X;
2727 x<=blockpos_max.X+extra_borders.X; x++)
2728 for(s16 z=blockpos_min.Z-extra_borders.Z;
2729 z<=blockpos_max.Z+extra_borders.Z; z++)
2730 for(s16 y=blockpos_min.Y-extra_borders.Y;
2731 y<=blockpos_max.Y+extra_borders.Y; y++)
2734 MapBlock *block = getBlockNoCreateNoEx(p);
2736 lighting_update_blocks.insert(block->getPos(), block);
2739 updateLighting(lighting_update_blocks, changed_blocks);
2743 Set lighting to non-expired state in all of them.
2744 This is cheating, but it is not fast enough if all of them
2745 would actually be updated.
2747 for(s16 x=blockpos_min.X-extra_borders.X;
2748 x<=blockpos_max.X+extra_borders.X; x++)
2749 for(s16 z=blockpos_min.Z-extra_borders.Z;
2750 z<=blockpos_max.Z+extra_borders.Z; z++)
2751 for(s16 y=blockpos_min.Y-extra_borders.Y;
2752 y<=blockpos_max.Y+extra_borders.Y; y++)
2755 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2759 if(enable_mapgen_debug_info == false)
2760 t.stop(true); // Hide output
2765 Go through changed blocks
2767 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2768 i != changed_blocks.end(); ++i)
2770 MapBlock *block = i->second;
2773 Update day/night difference cache of the MapBlocks
2775 block->expireDayNightDiff();
2777 Set block as modified
2779 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2780 "finishBlockMake expireDayNightDiff");
2784 Set central blocks as generated
2786 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2787 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2788 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2791 MapBlock *block = getBlockNoCreateNoEx(p);
2793 block->setGenerated(true);
2797 Save changed parts of map
2798 NOTE: Will be saved later.
2800 //save(MOD_STATE_WRITE_AT_UNLOAD);
2802 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2803 <<","<<blockpos_requested.Y<<","
2804 <<blockpos_requested.Z<<")"<<std::endl;*/
2806 if(enable_mapgen_debug_info)
2809 Analyze resulting blocks
2811 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2812 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2813 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2814 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2815 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2816 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2818 v3s16 p = v3s16(x,y,z);
2819 MapBlock *block = getBlockNoCreateNoEx(p);
2821 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2822 infostream<<"Generated "<<spos<<": "
2823 <<analyze_block(block)<<std::endl;
2828 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2834 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2836 DSTACKF("%s: p2d=(%d,%d)",
2841 Check if it exists already in memory
2843 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2848 Try to load it from disk (with blocks)
2850 //if(loadSectorFull(p2d) == true)
2853 Try to load metadata from disk
2856 if(loadSectorMeta(p2d) == true)
2858 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2861 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2862 throw InvalidPositionException("");
2868 Do not create over-limit
2870 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2871 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2872 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2873 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2874 throw InvalidPositionException("createSector(): pos. over limit");
2877 Generate blank sector
2880 sector = new ServerMapSector(this, p2d, m_gamedef);
2882 // Sector position on map in nodes
2883 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2888 m_sectors[p2d] = sector;
2895 This is a quick-hand function for calling makeBlock().
2897 MapBlock * ServerMap::generateBlock(
2899 std::map<v3s16, MapBlock*> &modified_blocks
2902 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2904 /*infostream<<"generateBlock(): "
2905 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2908 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2910 TimeTaker timer("generateBlock");
2912 //MapBlock *block = original_dummy;
2914 v2s16 p2d(p.X, p.Z);
2915 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2918 Do not generate over-limit
2920 if(blockpos_over_limit(p))
2922 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2923 throw InvalidPositionException("generateBlock(): pos. over limit");
2927 Create block make data
2930 initBlockMake(&data, p);
2936 TimeTaker t("mapgen::make_block()");
2937 mapgen->makeChunk(&data);
2938 //mapgen::make_block(&data);
2940 if(enable_mapgen_debug_info == false)
2941 t.stop(true); // Hide output
2945 Blit data back on map, update lighting, add mobs and whatever this does
2947 finishBlockMake(&data, modified_blocks);
2952 MapBlock *block = getBlockNoCreateNoEx(p);
2960 bool erroneus_content = false;
2961 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2962 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2963 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2966 MapNode n = block->getNode(p);
2967 if(n.getContent() == CONTENT_IGNORE)
2969 infostream<<"CONTENT_IGNORE at "
2970 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2972 erroneus_content = true;
2976 if(erroneus_content)
2985 Generate a completely empty block
2989 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2990 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2992 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2995 n.setContent(CONTENT_AIR);
2996 block->setNode(v3s16(x0,y0,z0), n);
3002 if(enable_mapgen_debug_info == false)
3003 timer.stop(true); // Hide output
3009 MapBlock * ServerMap::createBlock(v3s16 p)
3011 DSTACKF("%s: p=(%d,%d,%d)",
3012 __FUNCTION_NAME, p.X, p.Y, p.Z);
3015 Do not create over-limit
3017 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3018 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3019 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3020 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3021 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3022 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3023 throw InvalidPositionException("createBlock(): pos. over limit");
3025 v2s16 p2d(p.X, p.Z);
3028 This will create or load a sector if not found in memory.
3029 If block exists on disk, it will be loaded.
3031 NOTE: On old save formats, this will be slow, as it generates
3032 lighting on blocks for them.
3034 ServerMapSector *sector;
3036 sector = (ServerMapSector*)createSector(p2d);
3037 assert(sector->getId() == MAPSECTOR_SERVER);
3039 catch(InvalidPositionException &e)
3041 infostream<<"createBlock: createSector() failed"<<std::endl;
3045 NOTE: This should not be done, or at least the exception
3046 should not be passed on as std::exception, because it
3047 won't be catched at all.
3049 /*catch(std::exception &e)
3051 infostream<<"createBlock: createSector() failed: "
3052 <<e.what()<<std::endl;
3057 Try to get a block from the sector
3060 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3063 if(block->isDummy())
3068 block = sector->createBlankBlock(block_y);
3073 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3075 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3077 p.X, p.Y, p.Z, create_blank);
3080 MapBlock *block = getBlockNoCreateNoEx(p);
3081 if(block && block->isDummy() == false)
3086 MapBlock *block = loadBlock(p);
3092 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3093 MapBlock *block = sector->createBlankBlock(p.Y);
3097 /*if(allow_generate)
3099 std::map<v3s16, MapBlock*> modified_blocks;
3100 MapBlock *block = generateBlock(p, modified_blocks);
3104 event.type = MEET_OTHER;
3107 // Copy modified_blocks to event
3108 for(std::map<v3s16, MapBlock*>::iterator
3109 i = modified_blocks.begin();
3110 i != modified_blocks.end(); ++i)
3112 event.modified_blocks.insert(i->first);
3116 dispatchEvent(&event);
3125 s16 ServerMap::findGroundLevel(v2s16 p2d)
3129 Uh, just do something random...
3131 // Find existing map from top to down
3134 v3s16 p(p2d.X, max, p2d.Y);
3135 for(; p.Y>min; p.Y--)
3137 MapNode n = getNodeNoEx(p);
3138 if(n.getContent() != CONTENT_IGNORE)
3143 // If this node is not air, go to plan b
3144 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3146 // Search existing walkable and return it
3147 for(; p.Y>min; p.Y--)
3149 MapNode n = getNodeNoEx(p);
3150 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3159 Determine from map generator noise functions
3162 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3165 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3166 //return (s16)level;
3169 void ServerMap::createDatabase() {
3172 e = sqlite3_exec(m_database,
3173 "CREATE TABLE IF NOT EXISTS `blocks` ("
3174 "`pos` INT NOT NULL PRIMARY KEY,"
3177 , NULL, NULL, NULL);
3178 if(e == SQLITE_ABORT)
3179 throw FileNotGoodException("Could not create database structure");
3181 infostream<<"ServerMap: Database structure was created";
3184 void ServerMap::verifyDatabase() {
3189 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3190 bool needs_create = false;
3194 Open the database connection
3197 createDirs(m_savedir);
3199 if(!fs::PathExists(dbp))
3200 needs_create = true;
3202 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3203 if(d != SQLITE_OK) {
3204 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3205 throw FileNotGoodException("Cannot open database file");
3211 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3212 if(d != SQLITE_OK) {
3213 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3214 throw FileNotGoodException("Cannot prepare read statement");
3217 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3218 if(d != SQLITE_OK) {
3219 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3220 throw FileNotGoodException("Cannot prepare write statement");
3223 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3224 if(d != SQLITE_OK) {
3225 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3226 throw FileNotGoodException("Cannot prepare read statement");
3229 infostream<<"ServerMap: Database opened"<<std::endl;
3233 bool ServerMap::loadFromFolders() {
3234 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3239 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3240 return (sqlite3_int64)pos.Z*16777216 +
3241 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3244 void ServerMap::createDirs(std::string path)
3246 if(fs::CreateAllDirs(path) == false)
3248 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3249 <<"\""<<path<<"\""<<std::endl;
3250 throw BaseException("ServerMap failed to create directory");
3254 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3260 snprintf(cc, 9, "%.4x%.4x",
3261 (unsigned int)pos.X&0xffff,
3262 (unsigned int)pos.Y&0xffff);
3264 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3266 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3267 (unsigned int)pos.X&0xfff,
3268 (unsigned int)pos.Y&0xfff);
3270 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3276 v2s16 ServerMap::getSectorPos(std::string dirname)
3280 std::string component;
3281 fs::RemoveLastPathComponent(dirname, &component, 1);
3282 if(component.size() == 8)
3285 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3287 else if(component.size() == 3)
3290 fs::RemoveLastPathComponent(dirname, &component, 2);
3291 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3292 // Sign-extend the 12 bit values up to 16 bits...
3293 if(x&0x800) x|=0xF000;
3294 if(y&0x800) y|=0xF000;
3301 v2s16 pos((s16)x, (s16)y);
3305 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3307 v2s16 p2d = getSectorPos(sectordir);
3309 if(blockfile.size() != 4){
3310 throw InvalidFilenameException("Invalid block filename");
3313 int r = sscanf(blockfile.c_str(), "%4x", &y);
3315 throw InvalidFilenameException("Invalid block filename");
3316 return v3s16(p2d.X, y, p2d.Y);
3319 std::string ServerMap::getBlockFilename(v3s16 p)
3322 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3326 void ServerMap::save(ModifiedState save_level)
3328 DSTACK(__FUNCTION_NAME);
3329 if(m_map_saving_enabled == false)
3331 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3335 if(save_level == MOD_STATE_CLEAN)
3336 infostream<<"ServerMap: Saving whole map, this can take time."
3339 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3344 // Profile modified reasons
3345 Profiler modprofiler;
3347 u32 sector_meta_count = 0;
3348 u32 block_count = 0;
3349 u32 block_count_all = 0; // Number of blocks in memory
3351 // Don't do anything with sqlite unless something is really saved
3352 bool save_started = false;
3354 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3355 i != m_sectors.end(); ++i)
3357 ServerMapSector *sector = (ServerMapSector*)i->second;
3358 assert(sector->getId() == MAPSECTOR_SERVER);
3360 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3362 saveSectorMeta(sector);
3363 sector_meta_count++;
3365 std::list<MapBlock*> blocks;
3366 sector->getBlocks(blocks);
3368 for(std::list<MapBlock*>::iterator j = blocks.begin();
3369 j != blocks.end(); ++j)
3371 MapBlock *block = *j;
3375 if(block->getModified() >= (u32)save_level)
3380 save_started = true;
3383 modprofiler.add(block->getModifiedReason(), 1);
3388 /*infostream<<"ServerMap: Written block ("
3389 <<block->getPos().X<<","
3390 <<block->getPos().Y<<","
3391 <<block->getPos().Z<<")"
3400 Only print if something happened or saved whole map
3402 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3403 || block_count != 0)
3405 infostream<<"ServerMap: Written: "
3406 <<sector_meta_count<<" sector metadata files, "
3407 <<block_count<<" block files"
3408 <<", "<<block_count_all<<" blocks in memory."
3410 PrintInfo(infostream); // ServerMap/ClientMap:
3411 infostream<<"Blocks modified by: "<<std::endl;
3412 modprofiler.print(infostream);
3416 static s32 unsignedToSigned(s32 i, s32 max_positive)
3418 if(i < max_positive)
3421 return i - 2*max_positive;
3424 // modulo of a negative number does not work consistently in C
3425 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3429 return mod - ((-i) % mod);
3432 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3434 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3436 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3438 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3439 return v3s16(x,y,z);
3442 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3444 if(loadFromFolders()){
3445 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3446 <<"all blocks that are stored in flat files"<<std::endl;
3452 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3454 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3455 v3s16 p = getIntegerAsBlock(block_i);
3456 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3462 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3464 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3465 si != m_sectors.end(); ++si)
3467 MapSector *sector = si->second;
3469 std::list<MapBlock*> blocks;
3470 sector->getBlocks(blocks);
3472 for(std::list<MapBlock*>::iterator i = blocks.begin();
3473 i != blocks.end(); ++i)
3475 MapBlock *block = (*i);
3476 v3s16 p = block->getPos();
3482 void ServerMap::saveMapMeta()
3484 DSTACK(__FUNCTION_NAME);
3486 /*infostream<<"ServerMap::saveMapMeta(): "
3490 createDirs(m_savedir);
3492 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3493 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3494 if(os.good() == false)
3496 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3497 <<"could not open"<<fullpath<<std::endl;
3498 throw FileNotGoodException("Cannot open chunk metadata");
3503 m_emerge->setParamsToSettings(¶ms);
3504 params.writeLines(os);
3506 os<<"[end_of_params]\n";
3508 m_map_metadata_changed = false;
3511 void ServerMap::loadMapMeta()
3513 DSTACK(__FUNCTION_NAME);
3515 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3518 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3519 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3520 if(is.good() == false)
3522 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3523 <<"could not open"<<fullpath<<std::endl;
3524 throw FileNotGoodException("Cannot open map metadata");
3532 throw SerializationError
3533 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3535 std::getline(is, line);
3536 std::string trimmedline = trim(line);
3537 if(trimmedline == "[end_of_params]")
3539 params.parseConfigLine(line);
3542 MapgenParams *mgparams;
3544 mgparams = m_emerge->getParamsFromSettings(¶ms);
3545 } catch (SettingNotFoundException &e) {
3546 infostream << "Couldn't get a setting from map_meta.txt: "
3547 << e.what() << std::endl;
3554 m_mgparams = mgparams;
3555 m_seed = mgparams->seed;
3557 if (params.exists("seed")) {
3558 m_seed = params.getU64("seed");
3559 m_mgparams->seed = m_seed;
3563 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3566 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3568 DSTACK(__FUNCTION_NAME);
3569 // Format used for writing
3570 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3572 v2s16 pos = sector->getPos();
3573 std::string dir = getSectorDir(pos);
3576 std::string fullpath = dir + DIR_DELIM + "meta";
3577 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3578 if(o.good() == false)
3579 throw FileNotGoodException("Cannot open sector metafile");
3581 sector->serialize(o, version);
3583 sector->differs_from_disk = false;
3586 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3588 DSTACK(__FUNCTION_NAME);
3590 v2s16 p2d = getSectorPos(sectordir);
3592 ServerMapSector *sector = NULL;
3594 std::string fullpath = sectordir + DIR_DELIM + "meta";
3595 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3596 if(is.good() == false)
3598 // If the directory exists anyway, it probably is in some old
3599 // format. Just go ahead and create the sector.
3600 if(fs::PathExists(sectordir))
3602 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3603 <<fullpath<<" doesn't exist but directory does."
3604 <<" Continuing with a sector with no metadata."
3606 sector = new ServerMapSector(this, p2d, m_gamedef);
3607 m_sectors[p2d] = sector;
3611 throw FileNotGoodException("Cannot open sector metafile");
3616 sector = ServerMapSector::deSerialize
3617 (is, this, p2d, m_sectors, m_gamedef);
3619 saveSectorMeta(sector);
3622 sector->differs_from_disk = false;
3627 bool ServerMap::loadSectorMeta(v2s16 p2d)
3629 DSTACK(__FUNCTION_NAME);
3631 MapSector *sector = NULL;
3633 // The directory layout we're going to load from.
3634 // 1 - original sectors/xxxxzzzz/
3635 // 2 - new sectors2/xxx/zzz/
3636 // If we load from anything but the latest structure, we will
3637 // immediately save to the new one, and remove the old.
3639 std::string sectordir1 = getSectorDir(p2d, 1);
3640 std::string sectordir;
3641 if(fs::PathExists(sectordir1))
3643 sectordir = sectordir1;
3648 sectordir = getSectorDir(p2d, 2);
3652 sector = loadSectorMeta(sectordir, loadlayout != 2);
3654 catch(InvalidFilenameException &e)
3658 catch(FileNotGoodException &e)
3662 catch(std::exception &e)
3671 bool ServerMap::loadSectorFull(v2s16 p2d)
3673 DSTACK(__FUNCTION_NAME);
3675 MapSector *sector = NULL;
3677 // The directory layout we're going to load from.
3678 // 1 - original sectors/xxxxzzzz/
3679 // 2 - new sectors2/xxx/zzz/
3680 // If we load from anything but the latest structure, we will
3681 // immediately save to the new one, and remove the old.
3683 std::string sectordir1 = getSectorDir(p2d, 1);
3684 std::string sectordir;
3685 if(fs::PathExists(sectordir1))
3687 sectordir = sectordir1;
3692 sectordir = getSectorDir(p2d, 2);
3696 sector = loadSectorMeta(sectordir, loadlayout != 2);
3698 catch(InvalidFilenameException &e)
3702 catch(FileNotGoodException &e)
3706 catch(std::exception &e)
3714 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3716 std::vector<fs::DirListNode>::iterator i2;
3717 for(i2=list2.begin(); i2!=list2.end(); i2++)
3723 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3725 catch(InvalidFilenameException &e)
3727 // This catches unknown crap in directory
3733 infostream<<"Sector converted to new layout - deleting "<<
3734 sectordir1<<std::endl;
3735 fs::RecursiveDelete(sectordir1);
3742 void ServerMap::beginSave() {
3744 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3745 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3748 void ServerMap::endSave() {
3750 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3751 infostream<<"WARNING: endSave() failed, map might not have saved.";
3754 void ServerMap::saveBlock(MapBlock *block)
3756 DSTACK(__FUNCTION_NAME);
3758 Dummy blocks are not written
3760 if(block->isDummy())
3762 /*v3s16 p = block->getPos();
3763 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3764 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3768 // Format used for writing
3769 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3771 v3s16 p3d = block->getPos();
3775 v2s16 p2d(p3d.X, p3d.Z);
3776 std::string sectordir = getSectorDir(p2d);
3778 createDirs(sectordir);
3780 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3781 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3782 if(o.good() == false)
3783 throw FileNotGoodException("Cannot open block data");
3786 [0] u8 serialization version
3792 std::ostringstream o(std::ios_base::binary);
3794 o.write((char*)&version, 1);
3797 block->serialize(o, version, true);
3799 // Write block to database
3801 std::string tmp = o.str();
3802 const char *bytes = tmp.c_str();
3804 bool success = true;
3805 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3806 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3809 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3810 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3813 int written = sqlite3_step(m_database_write);
3814 if(written != SQLITE_DONE) {
3815 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3816 <<sqlite3_errmsg(m_database)<<std::endl;
3819 // Make ready for later reuse
3820 sqlite3_reset(m_database_write);
3822 // We just wrote it to the disk so clear modified flag
3824 block->resetModified();
3827 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3829 DSTACK(__FUNCTION_NAME);
3831 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3834 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3835 if(is.good() == false)
3836 throw FileNotGoodException("Cannot open block file");
3838 v3s16 p3d = getBlockPos(sectordir, blockfile);
3839 v2s16 p2d(p3d.X, p3d.Z);
3841 assert(sector->getPos() == p2d);
3843 u8 version = SER_FMT_VER_INVALID;
3844 is.read((char*)&version, 1);
3847 throw SerializationError("ServerMap::loadBlock(): Failed"
3848 " to read MapBlock version");
3850 /*u32 block_size = MapBlock::serializedLength(version);
3851 SharedBuffer<u8> data(block_size);
3852 is.read((char*)*data, block_size);*/
3854 // This will always return a sector because we're the server
3855 //MapSector *sector = emergeSector(p2d);
3857 MapBlock *block = NULL;
3858 bool created_new = false;
3859 block = sector->getBlockNoCreateNoEx(p3d.Y);
3862 block = sector->createBlankBlockNoInsert(p3d.Y);
3867 block->deSerialize(is, version, true);
3869 // If it's a new block, insert it to the map
3871 sector->insertBlock(block);
3874 Save blocks loaded in old format in new format
3877 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3881 // Should be in database now, so delete the old file
3882 fs::RecursiveDelete(fullpath);
3885 // We just loaded it from the disk, so it's up-to-date.
3886 block->resetModified();
3889 catch(SerializationError &e)
3891 infostream<<"WARNING: Invalid block data on disk "
3892 <<"fullpath="<<fullpath
3893 <<" (SerializationError). "
3894 <<"what()="<<e.what()
3896 // Ignoring. A new one will be generated.
3899 // TODO: Backup file; name is in fullpath.
3903 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3905 DSTACK(__FUNCTION_NAME);
3908 std::istringstream is(*blob, std::ios_base::binary);
3910 u8 version = SER_FMT_VER_INVALID;
3911 is.read((char*)&version, 1);
3914 throw SerializationError("ServerMap::loadBlock(): Failed"
3915 " to read MapBlock version");
3917 /*u32 block_size = MapBlock::serializedLength(version);
3918 SharedBuffer<u8> data(block_size);
3919 is.read((char*)*data, block_size);*/
3921 // This will always return a sector because we're the server
3922 //MapSector *sector = emergeSector(p2d);
3924 MapBlock *block = NULL;
3925 bool created_new = false;
3926 block = sector->getBlockNoCreateNoEx(p3d.Y);
3929 block = sector->createBlankBlockNoInsert(p3d.Y);
3934 block->deSerialize(is, version, true);
3936 // If it's a new block, insert it to the map
3938 sector->insertBlock(block);
3941 Save blocks loaded in old format in new format
3944 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3945 // Only save if asked to; no need to update version
3949 // We just loaded it from, so it's up-to-date.
3950 block->resetModified();
3953 catch(SerializationError &e)
3955 errorstream<<"Invalid block data in database"
3956 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3957 <<" (SerializationError): "<<e.what()<<std::endl;
3959 // TODO: Block should be marked as invalid in memory so that it is
3960 // not touched but the game can run
3962 if(g_settings->getBool("ignore_world_load_errors")){
3963 errorstream<<"Ignoring block load error. Duck and cover! "
3964 <<"(ignore_world_load_errors)"<<std::endl;
3966 throw SerializationError("Invalid block data in database");
3972 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3974 DSTACK(__FUNCTION_NAME);
3976 v2s16 p2d(blockpos.X, blockpos.Z);
3978 if(!loadFromFolders()) {
3981 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3982 infostream<<"WARNING: Could not bind block position for load: "
3983 <<sqlite3_errmsg(m_database)<<std::endl;
3984 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3986 Make sure sector is loaded
3988 MapSector *sector = createSector(p2d);
3993 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3994 size_t len = sqlite3_column_bytes(m_database_read, 0);
3996 std::string datastr(data, len);
3998 loadBlock(&datastr, blockpos, sector, false);
4000 sqlite3_step(m_database_read);
4001 // We should never get more than 1 row, so ok to reset
4002 sqlite3_reset(m_database_read);
4004 return getBlockNoCreateNoEx(blockpos);
4006 sqlite3_reset(m_database_read);
4008 // Not found in database, try the files
4011 // The directory layout we're going to load from.
4012 // 1 - original sectors/xxxxzzzz/
4013 // 2 - new sectors2/xxx/zzz/
4014 // If we load from anything but the latest structure, we will
4015 // immediately save to the new one, and remove the old.
4017 std::string sectordir1 = getSectorDir(p2d, 1);
4018 std::string sectordir;
4019 if(fs::PathExists(sectordir1))
4021 sectordir = sectordir1;
4026 sectordir = getSectorDir(p2d, 2);
4030 Make sure sector is loaded
4032 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4036 sector = loadSectorMeta(sectordir, loadlayout != 2);
4038 catch(InvalidFilenameException &e)
4042 catch(FileNotGoodException &e)
4046 catch(std::exception &e)
4053 Make sure file exists
4056 std::string blockfilename = getBlockFilename(blockpos);
4057 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
4061 Load block and save it to the database
4063 loadBlock(sectordir, blockfilename, sector, true);
4064 return getBlockNoCreateNoEx(blockpos);
4067 void ServerMap::PrintInfo(std::ostream &out)
4072 s16 ServerMap::getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
4075 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
4077 if (env->getGameTime() - block->heat_time < 10)
4081 //variant 1: full random
4082 //f32 heat = NoisePerlin3D(m_emerge->biomedef->np_heat, p.X, env->getGameTime()/100, p.Z, m_emerge->params->seed);
4084 //variant 2: season change based on default heat map
4085 f32 heat = NoisePerlin2D(m_emerge->biomedef->np_heat, p.X, p.Z, m_emerge->params->seed);
4086 heat += -30; // -30 - todo REMOVE after fixed NoiseParams nparams_biome_def_heat = {50, 50, -> 20, 50,
4087 f32 base = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
4088 base /= ( 86400 * g_settings->getS16("year_days") );
4089 base += (f32)p.X / 3000;
4090 heat += 30 * sin(base * M_PI); // season
4092 heat += p.Y / -333; // upper=colder, lower=hotter
4094 // daily change, hotter at sun +4, colder at night -4
4095 heat += 8 * (sin(cycle_shift(env->getTimeOfDayF(), -0.25) * M_PI) - 0.5);
4099 block->heat_time = env->getGameTime();
4104 s16 ServerMap::getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
4107 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
4109 if (env->getGameTime() - block->humidity_time < 10)
4110 return block->humidity;
4113 f32 humidity = NoisePerlin3D( m_emerge->biomedef->np_humidity,
4114 p.X, env->getGameTime()/10, p.Z,
4115 m_emerge->params->seed);
4116 humidity += -12 * ( sin(cycle_shift(env->getTimeOfDayF(), -0.1) * M_PI) - 0.5);
4117 //todo like heat//humidity += 20 * ( sin(((f32)p.Z / 300) * M_PI) - 0.5);
4119 if (humidity > 100) humidity = 100;
4120 if (humidity < 0) humidity = 0;
4123 block->humidity = humidity;
4124 block->humidity_time = env->getGameTime();
4133 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4138 MapVoxelManipulator::~MapVoxelManipulator()
4140 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4144 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4146 TimeTaker timer1("emerge", &emerge_time);
4148 // Units of these are MapBlocks
4149 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4150 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4152 VoxelArea block_area_nodes
4153 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4155 addArea(block_area_nodes);
4157 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4158 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4159 for(s32 x=p_min.X; x<=p_max.X; x++)
4164 std::map<v3s16, u8>::iterator n;
4165 n = m_loaded_blocks.find(p);
4166 if(n != m_loaded_blocks.end())
4169 bool block_data_inexistent = false;
4172 TimeTaker timer1("emerge load", &emerge_load_time);
4174 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4175 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4177 a.print(infostream);
4178 infostream<<std::endl;*/
4180 block = m_map->getBlockNoCreate(p);
4181 if(block->isDummy())
4182 block_data_inexistent = true;
4184 block->copyTo(*this);
4186 catch(InvalidPositionException &e)
4188 block_data_inexistent = true;
4191 if(block_data_inexistent)
4193 flags |= VMANIP_BLOCK_DATA_INEXIST;
4195 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4196 // Fill with VOXELFLAG_INEXISTENT
4197 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4198 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4200 s32 i = m_area.index(a.MinEdge.X,y,z);
4201 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4204 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4206 // Mark that block was loaded as blank
4207 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4210 m_loaded_blocks[p] = flags;
4213 //infostream<<"emerge done"<<std::endl;
4217 SUGG: Add an option to only update eg. water and air nodes.
4218 This will make it interfere less with important stuff if
4221 void MapVoxelManipulator::blitBack
4222 (std::map<v3s16, MapBlock*> & modified_blocks)
4224 if(m_area.getExtent() == v3s16(0,0,0))
4227 //TimeTaker timer1("blitBack");
4229 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4230 <<m_loaded_blocks.size()<<std::endl;*/
4233 Initialize block cache
4235 v3s16 blockpos_last;
4236 MapBlock *block = NULL;
4237 bool block_checked_in_modified = false;
4239 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4240 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4241 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4245 u8 f = m_flags[m_area.index(p)];
4246 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4249 MapNode &n = m_data[m_area.index(p)];
4251 v3s16 blockpos = getNodeBlockPos(p);
4256 if(block == NULL || blockpos != blockpos_last){
4257 block = m_map->getBlockNoCreate(blockpos);
4258 blockpos_last = blockpos;
4259 block_checked_in_modified = false;
4262 // Calculate relative position in block
4263 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4265 // Don't continue if nothing has changed here
4266 if(block->getNode(relpos) == n)
4269 //m_map->setNode(m_area.MinEdge + p, n);
4270 block->setNode(relpos, n);
4273 Make sure block is in modified_blocks
4275 if(block_checked_in_modified == false)
4277 modified_blocks[blockpos] = block;
4278 block_checked_in_modified = true;
4281 catch(InvalidPositionException &e)
4287 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4288 MapVoxelManipulator(map),
4289 m_create_area(false)
4293 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4297 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4299 // Just create the area so that it can be pointed to
4300 VoxelManipulator::emerge(a, caller_id);
4303 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4304 v3s16 blockpos_max, bool load_if_inexistent)
4306 TimeTaker timer1("initialEmerge", &emerge_time);
4308 // Units of these are MapBlocks
4309 v3s16 p_min = blockpos_min;
4310 v3s16 p_max = blockpos_max;
4312 VoxelArea block_area_nodes
4313 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4315 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4318 infostream<<"initialEmerge: area: ";
4319 block_area_nodes.print(infostream);
4320 infostream<<" ("<<size_MB<<"MB)";
4321 infostream<<std::endl;
4324 addArea(block_area_nodes);
4326 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4327 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4328 for(s32 x=p_min.X; x<=p_max.X; x++)
4333 std::map<v3s16, u8>::iterator n;
4334 n = m_loaded_blocks.find(p);
4335 if(n != m_loaded_blocks.end())
4338 bool block_data_inexistent = false;
4341 TimeTaker timer1("emerge load", &emerge_load_time);
4343 block = m_map->getBlockNoCreate(p);
4344 if(block->isDummy())
4345 block_data_inexistent = true;
4347 block->copyTo(*this);
4349 catch(InvalidPositionException &e)
4351 block_data_inexistent = true;
4354 if(block_data_inexistent)
4357 if (load_if_inexistent) {
4358 ServerMap *svrmap = (ServerMap *)m_map;
4359 block = svrmap->emergeBlock(p, false);
4361 block = svrmap->createBlock(p);
4363 block->copyTo(*this);
4365 flags |= VMANIP_BLOCK_DATA_INEXIST;
4368 Mark area inexistent
4370 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4371 // Fill with VOXELFLAG_INEXISTENT
4372 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4373 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4375 s32 i = m_area.index(a.MinEdge.X,y,z);
4376 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4380 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4382 // Mark that block was loaded as blank
4383 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4386 m_loaded_blocks[p] = flags;
4390 void ManualMapVoxelManipulator::blitBackAll(
4391 std::map<v3s16, MapBlock*> * modified_blocks)
4393 if(m_area.getExtent() == v3s16(0,0,0))
4397 Copy data of all blocks
4399 for(std::map<v3s16, u8>::iterator
4400 i = m_loaded_blocks.begin();
4401 i != m_loaded_blocks.end(); ++i)
4404 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4405 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4406 if(existed == false)
4411 block->copyFrom(*this);
4414 (*modified_blocks)[p] = block;