3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/mathconstants.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
43 #include "database-dummy.h"
44 #include "database-sqlite3.h"
46 #include "database-leveldb.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
52 SQLite format specification:
53 - Initially only replaces sectors/ and sectors2/
55 If map.sqlite does not exist in the save dir
56 or the block was not found in the database
57 the map will try to load from sectors folder.
58 In either case, map.sqlite will be created
59 and all future saves will save there.
61 Structure of map.sqlite:
72 Map::Map(std::ostream &dout, IGameDef *gamedef):
77 /*m_sector_mutex.Init();
78 assert(m_sector_mutex.IsInitialized());*/
86 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
87 i != m_sectors.end(); ++i)
93 void Map::addEventReceiver(MapEventReceiver *event_receiver)
95 m_event_receivers.insert(event_receiver);
98 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
100 m_event_receivers.erase(event_receiver);
103 void Map::dispatchEvent(MapEditEvent *event)
105 for(std::set<MapEventReceiver*>::iterator
106 i = m_event_receivers.begin();
107 i != m_event_receivers.end(); ++i)
109 (*i)->onMapEditEvent(event);
113 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
115 if(m_sector_cache != NULL && p == m_sector_cache_p){
116 MapSector * sector = m_sector_cache;
120 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
122 if(n == m_sectors.end())
125 MapSector *sector = n->second;
127 // Cache the last result
128 m_sector_cache_p = p;
129 m_sector_cache = sector;
134 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
136 return getSectorNoGenerateNoExNoLock(p);
139 MapSector * Map::getSectorNoGenerate(v2s16 p)
141 MapSector *sector = getSectorNoGenerateNoEx(p);
143 throw InvalidPositionException();
148 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
150 v2s16 p2d(p3d.X, p3d.Z);
151 MapSector * sector = getSectorNoGenerateNoEx(p2d);
154 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
158 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
160 MapBlock *block = getBlockNoCreateNoEx(p3d);
162 throw InvalidPositionException();
166 bool Map::isNodeUnderground(v3s16 p)
168 v3s16 blockpos = getNodeBlockPos(p);
170 MapBlock * block = getBlockNoCreate(blockpos);
171 return block->getIsUnderground();
173 catch(InvalidPositionException &e)
179 bool Map::isValidPosition(v3s16 p)
181 v3s16 blockpos = getNodeBlockPos(p);
182 MapBlock *block = getBlockNoCreate(blockpos);
183 return (block != NULL);
186 // Returns a CONTENT_IGNORE node if not found
187 MapNode Map::getNodeNoEx(v3s16 p)
189 v3s16 blockpos = getNodeBlockPos(p);
190 MapBlock *block = getBlockNoCreateNoEx(blockpos);
192 return MapNode(CONTENT_IGNORE);
193 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
194 return block->getNodeNoCheck(relpos);
197 // throws InvalidPositionException if not found
198 MapNode Map::getNode(v3s16 p)
200 v3s16 blockpos = getNodeBlockPos(p);
201 MapBlock *block = getBlockNoCreateNoEx(blockpos);
203 throw InvalidPositionException();
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 return block->getNodeNoCheck(relpos);
208 // throws InvalidPositionException if not found
209 void Map::setNode(v3s16 p, MapNode & n)
211 v3s16 blockpos = getNodeBlockPos(p);
212 MapBlock *block = getBlockNoCreate(blockpos);
213 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
214 // Never allow placing CONTENT_IGNORE, it fucks up stuff
215 if(n.getContent() == CONTENT_IGNORE){
216 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
217 <<" while trying to replace \""
218 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
219 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
220 debug_stacks_print_to(infostream);
223 block->setNodeNoCheck(relpos, n);
228 Goes recursively through the neighbours of the node.
230 Alters only transparent nodes.
232 If the lighting of the neighbour is lower than the lighting of
233 the node was (before changing it to 0 at the step before), the
234 lighting of the neighbour is set to 0 and then the same stuff
235 repeats for the neighbour.
237 The ending nodes of the routine are stored in light_sources.
238 This is useful when a light is removed. In such case, this
239 routine can be called for the light node and then again for
240 light_sources to re-light the area without the removed light.
242 values of from_nodes are lighting values.
244 void Map::unspreadLight(enum LightBank bank,
245 std::map<v3s16, u8> & from_nodes,
246 std::set<v3s16> & light_sources,
247 std::map<v3s16, MapBlock*> & modified_blocks)
249 INodeDefManager *nodemgr = m_gamedef->ndef();
252 v3s16(0,0,1), // back
254 v3s16(1,0,0), // right
255 v3s16(0,0,-1), // front
256 v3s16(0,-1,0), // bottom
257 v3s16(-1,0,0), // left
260 if(from_nodes.size() == 0)
263 u32 blockchangecount = 0;
265 std::map<v3s16, u8> unlighted_nodes;
268 Initialize block cache
271 MapBlock *block = NULL;
272 // Cache this a bit, too
273 bool block_checked_in_modified = false;
275 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
276 j != from_nodes.end(); ++j)
278 v3s16 pos = j->first;
279 v3s16 blockpos = getNodeBlockPos(pos);
281 // Only fetch a new block if the block position has changed
283 if(block == NULL || blockpos != blockpos_last){
284 block = getBlockNoCreate(blockpos);
285 blockpos_last = blockpos;
287 block_checked_in_modified = false;
291 catch(InvalidPositionException &e)
299 // Calculate relative position in block
300 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
302 // Get node straight from the block
303 //MapNode n = block->getNode(relpos);
305 u8 oldlight = j->second;
307 // Loop through 6 neighbors
308 for(u16 i=0; i<6; i++)
310 // Get the position of the neighbor node
311 v3s16 n2pos = pos + dirs[i];
313 // Get the block where the node is located
314 v3s16 blockpos = getNodeBlockPos(n2pos);
318 // Only fetch a new block if the block position has changed
320 if(block == NULL || blockpos != blockpos_last){
321 block = getBlockNoCreate(blockpos);
322 blockpos_last = blockpos;
324 block_checked_in_modified = false;
328 catch(InvalidPositionException &e)
333 // Calculate relative position in block
334 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
335 // Get node straight from the block
336 MapNode n2 = block->getNode(relpos);
338 bool changed = false;
340 //TODO: Optimize output by optimizing light_sources?
343 If the neighbor is dimmer than what was specified
344 as oldlight (the light of the previous node)
346 if(n2.getLight(bank, nodemgr) < oldlight)
349 And the neighbor is transparent and it has some light
351 if(nodemgr->get(n2).light_propagates
352 && n2.getLight(bank, nodemgr) != 0)
355 Set light to 0 and add to queue
358 u8 current_light = n2.getLight(bank, nodemgr);
359 n2.setLight(bank, 0, nodemgr);
360 block->setNode(relpos, n2);
362 unlighted_nodes[n2pos] = current_light;
366 Remove from light_sources if it is there
367 NOTE: This doesn't happen nearly at all
369 /*if(light_sources.find(n2pos))
371 infostream<<"Removed from light_sources"<<std::endl;
372 light_sources.remove(n2pos);
377 if(light_sources.find(n2pos) != NULL)
378 light_sources.remove(n2pos);*/
381 light_sources.insert(n2pos);
384 // Add to modified_blocks
385 if(changed == true && block_checked_in_modified == false)
387 // If the block is not found in modified_blocks, add.
388 if(modified_blocks.find(blockpos) == modified_blocks.end())
390 modified_blocks[blockpos] = block;
392 block_checked_in_modified = true;
395 catch(InvalidPositionException &e)
402 /*infostream<<"unspreadLight(): Changed block "
403 <<blockchangecount<<" times"
404 <<" for "<<from_nodes.size()<<" nodes"
407 if(unlighted_nodes.size() > 0)
408 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
412 A single-node wrapper of the above
414 void Map::unLightNeighbors(enum LightBank bank,
415 v3s16 pos, u8 lightwas,
416 std::set<v3s16> & light_sources,
417 std::map<v3s16, MapBlock*> & modified_blocks)
419 std::map<v3s16, u8> from_nodes;
420 from_nodes[pos] = lightwas;
422 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
426 Lights neighbors of from_nodes, collects all them and then
429 void Map::spreadLight(enum LightBank bank,
430 std::set<v3s16> & from_nodes,
431 std::map<v3s16, MapBlock*> & modified_blocks)
433 INodeDefManager *nodemgr = m_gamedef->ndef();
435 const v3s16 dirs[6] = {
436 v3s16(0,0,1), // back
438 v3s16(1,0,0), // right
439 v3s16(0,0,-1), // front
440 v3s16(0,-1,0), // bottom
441 v3s16(-1,0,0), // left
444 if(from_nodes.size() == 0)
447 u32 blockchangecount = 0;
449 std::set<v3s16> lighted_nodes;
452 Initialize block cache
455 MapBlock *block = NULL;
456 // Cache this a bit, too
457 bool block_checked_in_modified = false;
459 for(std::set<v3s16>::iterator j = from_nodes.begin();
460 j != from_nodes.end(); ++j)
463 v3s16 blockpos = getNodeBlockPos(pos);
465 // Only fetch a new block if the block position has changed
467 if(block == NULL || blockpos != blockpos_last){
468 block = getBlockNoCreate(blockpos);
469 blockpos_last = blockpos;
471 block_checked_in_modified = false;
475 catch(InvalidPositionException &e)
483 // Calculate relative position in block
484 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
486 // Get node straight from the block
487 MapNode n = block->getNode(relpos);
489 u8 oldlight = n.getLight(bank, nodemgr);
490 u8 newlight = diminish_light(oldlight);
492 // Loop through 6 neighbors
493 for(u16 i=0; i<6; i++){
494 // Get the position of the neighbor node
495 v3s16 n2pos = pos + dirs[i];
497 // Get the block where the node is located
498 v3s16 blockpos = getNodeBlockPos(n2pos);
502 // Only fetch a new block if the block position has changed
504 if(block == NULL || blockpos != blockpos_last){
505 block = getBlockNoCreate(blockpos);
506 blockpos_last = blockpos;
508 block_checked_in_modified = false;
512 catch(InvalidPositionException &e)
517 // Calculate relative position in block
518 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
519 // Get node straight from the block
520 MapNode n2 = block->getNode(relpos);
522 bool changed = false;
524 If the neighbor is brighter than the current node,
525 add to list (it will light up this node on its turn)
527 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
529 lighted_nodes.insert(n2pos);
533 If the neighbor is dimmer than how much light this node
534 would spread on it, add to list
536 if(n2.getLight(bank, nodemgr) < newlight)
538 if(nodemgr->get(n2).light_propagates)
540 n2.setLight(bank, newlight, nodemgr);
541 block->setNode(relpos, n2);
542 lighted_nodes.insert(n2pos);
547 // Add to modified_blocks
548 if(changed == true && block_checked_in_modified == false)
550 // If the block is not found in modified_blocks, add.
551 if(modified_blocks.find(blockpos) == modified_blocks.end())
553 modified_blocks[blockpos] = block;
555 block_checked_in_modified = true;
558 catch(InvalidPositionException &e)
565 /*infostream<<"spreadLight(): Changed block "
566 <<blockchangecount<<" times"
567 <<" for "<<from_nodes.size()<<" nodes"
570 if(lighted_nodes.size() > 0)
571 spreadLight(bank, lighted_nodes, modified_blocks);
575 A single-node source variation of the above.
577 void Map::lightNeighbors(enum LightBank bank,
579 std::map<v3s16, MapBlock*> & modified_blocks)
581 std::set<v3s16> from_nodes;
582 from_nodes.insert(pos);
583 spreadLight(bank, from_nodes, modified_blocks);
586 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
588 INodeDefManager *nodemgr = m_gamedef->ndef();
591 v3s16(0,0,1), // back
593 v3s16(1,0,0), // right
594 v3s16(0,0,-1), // front
595 v3s16(0,-1,0), // bottom
596 v3s16(-1,0,0), // left
599 u8 brightest_light = 0;
600 v3s16 brightest_pos(0,0,0);
601 bool found_something = false;
603 // Loop through 6 neighbors
604 for(u16 i=0; i<6; i++){
605 // Get the position of the neighbor node
606 v3s16 n2pos = p + dirs[i];
611 catch(InvalidPositionException &e)
615 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
616 brightest_light = n2.getLight(bank, nodemgr);
617 brightest_pos = n2pos;
618 found_something = true;
622 if(found_something == false)
623 throw InvalidPositionException();
625 return brightest_pos;
629 Propagates sunlight down from a node.
630 Starting point gets sunlight.
632 Returns the lowest y value of where the sunlight went.
634 Mud is turned into grass in where the sunlight stops.
636 s16 Map::propagateSunlight(v3s16 start,
637 std::map<v3s16, MapBlock*> & modified_blocks)
639 INodeDefManager *nodemgr = m_gamedef->ndef();
644 v3s16 pos(start.X, y, start.Z);
646 v3s16 blockpos = getNodeBlockPos(pos);
649 block = getBlockNoCreate(blockpos);
651 catch(InvalidPositionException &e)
656 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
657 MapNode n = block->getNode(relpos);
659 if(nodemgr->get(n).sunlight_propagates)
661 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
662 block->setNode(relpos, n);
664 modified_blocks[blockpos] = block;
668 // Sunlight goes no further
675 void Map::updateLighting(enum LightBank bank,
676 std::map<v3s16, MapBlock*> & a_blocks,
677 std::map<v3s16, MapBlock*> & modified_blocks)
679 INodeDefManager *nodemgr = m_gamedef->ndef();
681 /*m_dout<<DTIME<<"Map::updateLighting(): "
682 <<a_blocks.size()<<" blocks."<<std::endl;*/
684 //TimeTaker timer("updateLighting");
688 //u32 count_was = modified_blocks.size();
690 std::map<v3s16, MapBlock*> blocks_to_update;
692 std::set<v3s16> light_sources;
694 std::map<v3s16, u8> unlight_from;
696 int num_bottom_invalid = 0;
699 //TimeTaker t("first stuff");
701 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
702 i != a_blocks.end(); ++i)
704 MapBlock *block = i->second;
708 // Don't bother with dummy blocks.
712 v3s16 pos = block->getPos();
713 v3s16 posnodes = block->getPosRelative();
714 modified_blocks[pos] = block;
715 blocks_to_update[pos] = block;
718 Clear all light from block
720 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
721 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
722 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
727 MapNode n = block->getNode(p);
728 u8 oldlight = n.getLight(bank, nodemgr);
729 n.setLight(bank, 0, nodemgr);
730 block->setNode(p, n);
732 // If node sources light, add to list
733 u8 source = nodemgr->get(n).light_source;
735 light_sources.insert(p + posnodes);
737 // Collect borders for unlighting
738 if((x==0 || x == MAP_BLOCKSIZE-1
739 || y==0 || y == MAP_BLOCKSIZE-1
740 || z==0 || z == MAP_BLOCKSIZE-1)
743 v3s16 p_map = p + posnodes;
744 unlight_from[p_map] = oldlight;
747 catch(InvalidPositionException &e)
750 This would happen when dealing with a
754 infostream<<"updateLighting(): InvalidPositionException"
759 if(bank == LIGHTBANK_DAY)
761 bool bottom_valid = block->propagateSunlight(light_sources);
764 num_bottom_invalid++;
766 // If bottom is valid, we're done.
770 else if(bank == LIGHTBANK_NIGHT)
772 // For night lighting, sunlight is not propagated
777 // Invalid lighting bank
781 /*infostream<<"Bottom for sunlight-propagated block ("
782 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
785 // Bottom sunlight is not valid; get the block and loop to it
789 block = getBlockNoCreate(pos);
791 catch(InvalidPositionException &e)
802 Enable this to disable proper lighting for speeding up map
803 generation for testing or whatever
806 //if(g_settings->get(""))
808 core::map<v3s16, MapBlock*>::Iterator i;
809 i = blocks_to_update.getIterator();
810 for(; i.atEnd() == false; i++)
812 MapBlock *block = i.getNode()->getValue();
813 v3s16 p = block->getPos();
814 block->setLightingExpired(false);
822 //TimeTaker timer("unspreadLight");
823 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
828 u32 diff = modified_blocks.size() - count_was;
829 count_was = modified_blocks.size();
830 infostream<<"unspreadLight modified "<<diff<<std::endl;
834 //TimeTaker timer("spreadLight");
835 spreadLight(bank, light_sources, modified_blocks);
840 u32 diff = modified_blocks.size() - count_was;
841 count_was = modified_blocks.size();
842 infostream<<"spreadLight modified "<<diff<<std::endl;
848 //MapVoxelManipulator vmanip(this);
850 // Make a manual voxel manipulator and load all the blocks
851 // that touch the requested blocks
852 ManualMapVoxelManipulator vmanip(this);
855 //TimeTaker timer("initialEmerge");
857 core::map<v3s16, MapBlock*>::Iterator i;
858 i = blocks_to_update.getIterator();
859 for(; i.atEnd() == false; i++)
861 MapBlock *block = i.getNode()->getValue();
862 v3s16 p = block->getPos();
864 // Add all surrounding blocks
865 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
868 Add all surrounding blocks that have up-to-date lighting
869 NOTE: This doesn't quite do the job (not everything
870 appropriate is lighted)
872 /*for(s16 z=-1; z<=1; z++)
873 for(s16 y=-1; y<=1; y++)
874 for(s16 x=-1; x<=1; x++)
876 v3s16 p2 = p + v3s16(x,y,z);
877 MapBlock *block = getBlockNoCreateNoEx(p2);
882 if(block->getLightingExpired())
884 vmanip.initialEmerge(p2, p2);
887 // Lighting of block will be updated completely
888 block->setLightingExpired(false);
893 //TimeTaker timer("unSpreadLight");
894 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
897 //TimeTaker timer("spreadLight");
898 vmanip.spreadLight(bank, light_sources, nodemgr);
901 //TimeTaker timer("blitBack");
902 vmanip.blitBack(modified_blocks);
904 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
909 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
912 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
913 std::map<v3s16, MapBlock*> & modified_blocks)
915 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
916 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
919 Update information about whether day and night light differ
921 for(std::map<v3s16, MapBlock*>::iterator
922 i = modified_blocks.begin();
923 i != modified_blocks.end(); ++i)
925 MapBlock *block = i->second;
926 block->expireDayNightDiff();
932 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
933 std::map<v3s16, MapBlock*> &modified_blocks)
935 INodeDefManager *ndef = m_gamedef->ndef();
938 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
939 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
942 From this node to nodes underneath:
943 If lighting is sunlight (1.0), unlight neighbours and
948 v3s16 toppos = p + v3s16(0,1,0);
949 //v3s16 bottompos = p + v3s16(0,-1,0);
951 bool node_under_sunlight = true;
952 std::set<v3s16> light_sources;
955 Collect old node for rollback
957 RollbackNode rollback_oldnode(this, p, m_gamedef);
960 If there is a node at top and it doesn't have sunlight,
961 there has not been any sunlight going down.
963 Otherwise there probably is.
966 MapNode topnode = getNode(toppos);
968 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
969 node_under_sunlight = false;
971 catch(InvalidPositionException &e)
976 Remove all light that has come out of this node
979 enum LightBank banks[] =
984 for(s32 i=0; i<2; i++)
986 enum LightBank bank = banks[i];
988 u8 lightwas = getNode(p).getLight(bank, ndef);
990 // Add the block of the added node to modified_blocks
991 v3s16 blockpos = getNodeBlockPos(p);
992 MapBlock * block = getBlockNoCreate(blockpos);
993 assert(block != NULL);
994 modified_blocks[blockpos] = block;
996 assert(isValidPosition(p));
998 // Unlight neighbours of node.
999 // This means setting light of all consequent dimmer nodes
1001 // This also collects the nodes at the border which will spread
1002 // light again into this.
1003 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1005 n.setLight(bank, 0, ndef);
1009 If node lets sunlight through and is under sunlight, it has
1012 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1014 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1018 Remove node metadata
1021 removeNodeMetadata(p);
1024 Set the node on the map
1030 If node is under sunlight and doesn't let sunlight through,
1031 take all sunlighted nodes under it and clear light from them
1032 and from where the light has been spread.
1033 TODO: This could be optimized by mass-unlighting instead
1036 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1040 //m_dout<<DTIME<<"y="<<y<<std::endl;
1041 v3s16 n2pos(p.X, y, p.Z);
1045 n2 = getNode(n2pos);
1047 catch(InvalidPositionException &e)
1052 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1054 unLightNeighbors(LIGHTBANK_DAY,
1055 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1056 light_sources, modified_blocks);
1057 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1065 for(s32 i=0; i<2; i++)
1067 enum LightBank bank = banks[i];
1070 Spread light from all nodes that might be capable of doing so
1072 spreadLight(bank, light_sources, modified_blocks);
1076 Update information about whether day and night light differ
1078 for(std::map<v3s16, MapBlock*>::iterator
1079 i = modified_blocks.begin();
1080 i != modified_blocks.end(); ++i)
1082 i->second->expireDayNightDiff();
1088 if(m_gamedef->rollback())
1090 RollbackNode rollback_newnode(this, p, m_gamedef);
1091 RollbackAction action;
1092 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1093 m_gamedef->rollback()->reportAction(action);
1097 Add neighboring liquid nodes and the node itself if it is
1098 liquid (=water node was added) to transform queue.
1099 note: todo: for liquid_finite enough to add only self node
1102 v3s16(0,0,0), // self
1103 v3s16(0,0,1), // back
1104 v3s16(0,1,0), // top
1105 v3s16(1,0,0), // right
1106 v3s16(0,0,-1), // front
1107 v3s16(0,-1,0), // bottom
1108 v3s16(-1,0,0), // left
1110 for(u16 i=0; i<7; i++)
1115 v3s16 p2 = p + dirs[i];
1117 MapNode n2 = getNode(p2);
1118 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1120 m_transforming_liquid.push_back(p2);
1123 }catch(InvalidPositionException &e)
1131 void Map::removeNodeAndUpdate(v3s16 p,
1132 std::map<v3s16, MapBlock*> &modified_blocks)
1134 INodeDefManager *ndef = m_gamedef->ndef();
1136 /*PrintInfo(m_dout);
1137 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1138 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1140 bool node_under_sunlight = true;
1142 v3s16 toppos = p + v3s16(0,1,0);
1144 // Node will be replaced with this
1145 content_t replace_material = CONTENT_AIR;
1148 Collect old node for rollback
1150 RollbackNode rollback_oldnode(this, p, m_gamedef);
1153 If there is a node at top and it doesn't have sunlight,
1154 there will be no sunlight going down.
1157 MapNode topnode = getNode(toppos);
1159 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1160 node_under_sunlight = false;
1162 catch(InvalidPositionException &e)
1166 std::set<v3s16> light_sources;
1168 enum LightBank banks[] =
1173 for(s32 i=0; i<2; i++)
1175 enum LightBank bank = banks[i];
1178 Unlight neighbors (in case the node is a light source)
1180 unLightNeighbors(bank, p,
1181 getNode(p).getLight(bank, ndef),
1182 light_sources, modified_blocks);
1186 Remove node metadata
1189 removeNodeMetadata(p);
1193 This also clears the lighting.
1197 n.setContent(replace_material);
1200 for(s32 i=0; i<2; i++)
1202 enum LightBank bank = banks[i];
1205 Recalculate lighting
1207 spreadLight(bank, light_sources, modified_blocks);
1210 // Add the block of the removed node to modified_blocks
1211 v3s16 blockpos = getNodeBlockPos(p);
1212 MapBlock * block = getBlockNoCreate(blockpos);
1213 assert(block != NULL);
1214 modified_blocks[blockpos] = block;
1217 If the removed node was under sunlight, propagate the
1218 sunlight down from it and then light all neighbors
1219 of the propagated blocks.
1221 if(node_under_sunlight)
1223 s16 ybottom = propagateSunlight(p, modified_blocks);
1224 /*m_dout<<DTIME<<"Node was under sunlight. "
1225 "Propagating sunlight";
1226 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1228 for(; y >= ybottom; y--)
1230 v3s16 p2(p.X, y, p.Z);
1231 /*m_dout<<DTIME<<"lighting neighbors of node ("
1232 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1234 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1239 // Set the lighting of this node to 0
1240 // TODO: Is this needed? Lighting is cleared up there already.
1242 MapNode n = getNode(p);
1243 n.setLight(LIGHTBANK_DAY, 0, ndef);
1246 catch(InvalidPositionException &e)
1252 for(s32 i=0; i<2; i++)
1254 enum LightBank bank = banks[i];
1256 // Get the brightest neighbour node and propagate light from it
1257 v3s16 n2p = getBrightestNeighbour(bank, p);
1259 //MapNode n2 = getNode(n2p);
1260 lightNeighbors(bank, n2p, modified_blocks);
1262 catch(InvalidPositionException &e)
1268 Update information about whether day and night light differ
1270 for(std::map<v3s16, MapBlock*>::iterator
1271 i = modified_blocks.begin();
1272 i != modified_blocks.end(); ++i)
1274 i->second->expireDayNightDiff();
1280 if(m_gamedef->rollback())
1282 RollbackNode rollback_newnode(this, p, m_gamedef);
1283 RollbackAction action;
1284 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1285 m_gamedef->rollback()->reportAction(action);
1289 Add neighboring liquid nodes and this node to transform queue.
1290 (it's vital for the node itself to get updated last.)
1291 note: todo: for liquid_finite enough to add only self node
1294 v3s16(0,0,1), // back
1295 v3s16(0,1,0), // top
1296 v3s16(1,0,0), // right
1297 v3s16(0,0,-1), // front
1298 v3s16(0,-1,0), // bottom
1299 v3s16(-1,0,0), // left
1300 v3s16(0,0,0), // self
1302 for(u16 i=0; i<7; i++)
1307 v3s16 p2 = p + dirs[i];
1309 MapNode n2 = getNode(p2);
1310 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1312 m_transforming_liquid.push_back(p2);
1315 }catch(InvalidPositionException &e)
1321 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1324 event.type = MEET_ADDNODE;
1328 bool succeeded = true;
1330 std::map<v3s16, MapBlock*> modified_blocks;
1331 addNodeAndUpdate(p, n, modified_blocks);
1333 // Copy modified_blocks to event
1334 for(std::map<v3s16, MapBlock*>::iterator
1335 i = modified_blocks.begin();
1336 i != modified_blocks.end(); ++i)
1338 event.modified_blocks.insert(i->first);
1341 catch(InvalidPositionException &e){
1345 dispatchEvent(&event);
1350 bool Map::removeNodeWithEvent(v3s16 p)
1353 event.type = MEET_REMOVENODE;
1356 bool succeeded = true;
1358 std::map<v3s16, MapBlock*> modified_blocks;
1359 removeNodeAndUpdate(p, modified_blocks);
1361 // Copy modified_blocks to event
1362 for(std::map<v3s16, MapBlock*>::iterator
1363 i = modified_blocks.begin();
1364 i != modified_blocks.end(); ++i)
1366 event.modified_blocks.insert(i->first);
1369 catch(InvalidPositionException &e){
1373 dispatchEvent(&event);
1378 bool Map::getDayNightDiff(v3s16 blockpos)
1381 v3s16 p = blockpos + v3s16(0,0,0);
1382 MapBlock *b = getBlockNoCreate(p);
1383 if(b->getDayNightDiff())
1386 catch(InvalidPositionException &e){}
1389 v3s16 p = blockpos + v3s16(-1,0,0);
1390 MapBlock *b = getBlockNoCreate(p);
1391 if(b->getDayNightDiff())
1394 catch(InvalidPositionException &e){}
1396 v3s16 p = blockpos + v3s16(0,-1,0);
1397 MapBlock *b = getBlockNoCreate(p);
1398 if(b->getDayNightDiff())
1401 catch(InvalidPositionException &e){}
1403 v3s16 p = blockpos + v3s16(0,0,-1);
1404 MapBlock *b = getBlockNoCreate(p);
1405 if(b->getDayNightDiff())
1408 catch(InvalidPositionException &e){}
1411 v3s16 p = blockpos + v3s16(1,0,0);
1412 MapBlock *b = getBlockNoCreate(p);
1413 if(b->getDayNightDiff())
1416 catch(InvalidPositionException &e){}
1418 v3s16 p = blockpos + v3s16(0,1,0);
1419 MapBlock *b = getBlockNoCreate(p);
1420 if(b->getDayNightDiff())
1423 catch(InvalidPositionException &e){}
1425 v3s16 p = blockpos + v3s16(0,0,1);
1426 MapBlock *b = getBlockNoCreate(p);
1427 if(b->getDayNightDiff())
1430 catch(InvalidPositionException &e){}
1436 Updates usage timers
1438 void Map::timerUpdate(float dtime, float unload_timeout,
1439 std::list<v3s16> *unloaded_blocks)
1441 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1443 // Profile modified reasons
1444 Profiler modprofiler;
1446 std::list<v2s16> sector_deletion_queue;
1447 u32 deleted_blocks_count = 0;
1448 u32 saved_blocks_count = 0;
1449 u32 block_count_all = 0;
1452 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1453 si != m_sectors.end(); ++si)
1455 MapSector *sector = si->second;
1457 bool all_blocks_deleted = true;
1459 std::list<MapBlock*> blocks;
1460 sector->getBlocks(blocks);
1462 for(std::list<MapBlock*>::iterator i = blocks.begin();
1463 i != blocks.end(); ++i)
1465 MapBlock *block = (*i);
1467 block->incrementUsageTimer(dtime);
1469 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1471 v3s16 p = block->getPos();
1474 if(block->getModified() != MOD_STATE_CLEAN
1475 && save_before_unloading)
1477 modprofiler.add(block->getModifiedReason(), 1);
1479 saved_blocks_count++;
1482 // Delete from memory
1483 sector->deleteBlock(block);
1486 unloaded_blocks->push_back(p);
1488 deleted_blocks_count++;
1492 all_blocks_deleted = false;
1497 if(all_blocks_deleted)
1499 sector_deletion_queue.push_back(si->first);
1504 // Finally delete the empty sectors
1505 deleteSectors(sector_deletion_queue);
1507 if(deleted_blocks_count != 0)
1509 PrintInfo(infostream); // ServerMap/ClientMap:
1510 infostream<<"Unloaded "<<deleted_blocks_count
1511 <<" blocks from memory";
1512 if(save_before_unloading)
1513 infostream<<", of which "<<saved_blocks_count<<" were written";
1514 infostream<<", "<<block_count_all<<" blocks in memory";
1515 infostream<<"."<<std::endl;
1516 if(saved_blocks_count != 0){
1517 PrintInfo(infostream); // ServerMap/ClientMap:
1518 infostream<<"Blocks modified by: "<<std::endl;
1519 modprofiler.print(infostream);
1524 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1526 timerUpdate(0.0, -1.0, unloaded_blocks);
1529 void Map::deleteSectors(std::list<v2s16> &list)
1531 for(std::list<v2s16>::iterator j = list.begin();
1532 j != list.end(); ++j)
1534 MapSector *sector = m_sectors[*j];
1535 // If sector is in sector cache, remove it from there
1536 if(m_sector_cache == sector)
1537 m_sector_cache = NULL;
1538 // Remove from map and delete
1539 m_sectors.erase(*j);
1545 void Map::unloadUnusedData(float timeout,
1546 core::list<v3s16> *deleted_blocks)
1548 core::list<v2s16> sector_deletion_queue;
1549 u32 deleted_blocks_count = 0;
1550 u32 saved_blocks_count = 0;
1552 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1553 for(; si.atEnd() == false; si++)
1555 MapSector *sector = si.getNode()->getValue();
1557 bool all_blocks_deleted = true;
1559 core::list<MapBlock*> blocks;
1560 sector->getBlocks(blocks);
1561 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1562 i != blocks.end(); i++)
1564 MapBlock *block = (*i);
1566 if(block->getUsageTimer() > timeout)
1569 if(block->getModified() != MOD_STATE_CLEAN)
1572 saved_blocks_count++;
1574 // Delete from memory
1575 sector->deleteBlock(block);
1576 deleted_blocks_count++;
1580 all_blocks_deleted = false;
1584 if(all_blocks_deleted)
1586 sector_deletion_queue.push_back(si.getNode()->getKey());
1590 deleteSectors(sector_deletion_queue);
1592 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1593 <<", of which "<<saved_blocks_count<<" were wr."
1596 //return sector_deletion_queue.getSize();
1597 //return deleted_blocks_count;
1601 void Map::PrintInfo(std::ostream &out)
1606 #define WATER_DROP_BOOST 4
1610 NEIGHBOR_SAME_LEVEL,
1613 struct NodeNeighbor {
1617 bool l; //can liquid
1621 void Map::transforming_liquid_add(v3s16 p) {
1622 m_transforming_liquid.push_back(p);
1625 s32 Map::transforming_liquid_size() {
1626 return m_transforming_liquid.size();
1629 const v3s16 g_7dirs[7] =
1631 // +right, +top, +back
1632 v3s16( 0,-1, 0), // bottom
1633 v3s16( 0, 0, 0), // self
1634 v3s16( 0, 0, 1), // back
1635 v3s16( 0, 0,-1), // front
1636 v3s16( 1, 0, 0), // right
1637 v3s16(-1, 0, 0), // left
1638 v3s16( 0, 1, 0) // top
1645 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1647 INodeDefManager *nodemgr = m_gamedef->ndef();
1649 DSTACK(__FUNCTION_NAME);
1650 //TimeTaker timer("transformLiquids()");
1653 u32 initial_size = m_transforming_liquid.size();
1655 u8 relax = g_settings->getS16("liquid_relax");
1656 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1657 int water_level = g_settings->getS16("water_level");
1659 // list of nodes that due to viscosity have not reached their max level height
1660 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1662 // List of MapBlocks that will require a lighting update (due to lava)
1663 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1665 u16 loop_max = g_settings->getU16("liquid_loop_max");
1667 //if (m_transforming_liquid.size() > 0) errorstream << "Liquid queue size="<<m_transforming_liquid.size()<<std::endl;
1669 while (m_transforming_liquid.size() > 0)
1671 // This should be done here so that it is done when continue is used
1672 if (loopcount >= initial_size || loopcount >= loop_max)
1676 Get a queued transforming liquid node
1678 v3s16 p0 = m_transforming_liquid.pop_front();
1679 u16 total_level = 0;
1680 // surrounding flowing liquid nodes
1681 NodeNeighbor neighbors[7];
1682 // current level of every block
1683 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1685 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1686 s8 can_liquid_same_level = 0;
1687 content_t liquid_kind = CONTENT_IGNORE;
1688 content_t liquid_kind_flowing = CONTENT_IGNORE;
1690 Collect information about the environment
1692 const v3s16 *dirs = g_7dirs;
1693 for (u16 i = 0; i < 7; i++) {
1694 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1697 nt = NEIGHBOR_UPPER;
1700 nt = NEIGHBOR_LOWER;
1703 v3s16 npos = p0 + dirs[i];
1705 neighbors[i].n = getNodeNoEx(npos);
1706 neighbors[i].t = nt;
1707 neighbors[i].p = npos;
1710 NodeNeighbor & nb = neighbors[i];
1712 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1714 if (nb.n.getContent() == CONTENT_AIR) {
1715 liquid_levels[i] = 0;
1720 // if this node is not (yet) of a liquid type,
1721 // choose the first liquid type we encounter
1722 if (liquid_kind_flowing == CONTENT_IGNORE)
1723 liquid_kind_flowing = nodemgr->getId(
1724 nodemgr->get(nb.n).liquid_alternative_flowing);
1725 if (liquid_kind == CONTENT_IGNORE)
1726 liquid_kind = nb.n.getContent();
1727 if (nb.n.getContent() == liquid_kind) {
1728 liquid_levels[i] = nb.n.getLevel(nodemgr); //LIQUID_LEVEL_SOURCE;
1730 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1733 case LIQUID_FLOWING:
1734 // if this node is not (yet) of a liquid type,
1735 // choose the first liquid type we encounter
1736 if (liquid_kind_flowing == CONTENT_IGNORE)
1737 liquid_kind_flowing = nb.n.getContent();
1738 if (liquid_kind == CONTENT_IGNORE)
1739 liquid_kind = nodemgr->getId(
1740 nodemgr->get(nb.n).liquid_alternative_source);
1741 if (nb.n.getContent() == liquid_kind_flowing) {
1742 liquid_levels[i] = nb.n.getLevel(nodemgr); //(nb.n.param2 & LIQUID_LEVEL_MASK);
1748 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1749 ++can_liquid_same_level;
1750 if (liquid_levels[i] > 0)
1751 total_level += liquid_levels[i];
1754 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1755 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1756 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1757 << nodemgr->get(nb.n.getContent()).liquid_type
1758 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1759 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1760 << " tlevel=" << (int)total_level << " cansame="
1761 << (int)can_liquid_same_level << std::endl;
1765 if (liquid_kind == CONTENT_IGNORE ||
1766 !neighbors[D_SELF].l ||
1770 // fill bottom block
1771 if (neighbors[D_BOTTOM].l) {
1772 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1773 LIQUID_LEVEL_SOURCE : total_level;
1774 total_level -= liquid_levels_want[D_BOTTOM];
1778 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1779 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1780 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1781 (can_liquid_same_level - relax) &&
1782 can_liquid_same_level >= relax + 1) {
1783 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1786 // prevent lakes in air above unloaded blocks
1787 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE && !(loopcount % 3)) {
1791 // calculate self level 5 blocks
1793 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1794 ? LIQUID_LEVEL_SOURCE
1795 : total_level / can_liquid_same_level;
1796 total_level -= want_level * can_liquid_same_level;
1799 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1800 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1801 total_level <= (can_liquid_same_level - relax) &&
1802 can_liquid_same_level >= relax + 1) {
1806 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1807 if (!neighbors[ii].l)
1809 liquid_levels_want[ii] = want_level;
1810 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0) {
1811 if (loopcount % 3 || liquid_levels[ii] <= 0){
1812 if (liquid_levels[ii] > liquid_levels_want[ii]) {
1813 ++liquid_levels_want[ii];
1816 } else if (neighbors[ii].l > 0){
1817 ++liquid_levels_want[ii];
1823 for (u16 ii = 0; ii < 7; ++ii) {
1824 if (total_level < 1) break;
1825 if (liquid_levels_want[ii] >= 0 &&
1826 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1827 ++liquid_levels_want[ii];
1832 // fill top block if can
1833 if (neighbors[D_TOP].l) {
1834 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1835 LIQUID_LEVEL_SOURCE : total_level;
1836 total_level -= liquid_levels_want[D_TOP];
1839 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1840 if ( neighbors[ii].i ||
1841 (liquid_levels_want[ii] >= 0 &&
1842 (fast_flood && p0.Y < water_level &&
1843 (initial_size >= 1000
1845 && want_level >= LIQUID_LEVEL_SOURCE/4
1846 && can_liquid_same_level >= 5
1847 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1848 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1851 if (total_level > 0) //|| flowed != volume)
1852 infostream <<" AFTER level=" << (int)total_level
1853 //<< " flowed="<<flowed<< " volume=" << volume
1854 << " wantsame="<<(int)want_level<< " top="
1855 << (int)liquid_levels_want[D_TOP]<< " topwas="
1856 << (int)liquid_levels[D_TOP]<< " bot="
1857 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1861 for (u16 i = 0; i < 7; i++) {
1862 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1864 MapNode & n0 = neighbors[i].n;
1865 p0 = neighbors[i].p;
1867 decide on the type (and possibly level) of the current node
1869 content_t new_node_content;
1870 s8 new_node_level = -1;
1871 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1872 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1873 // amount to gain, limited by viscosity
1874 // must be at least 1 in absolute value
1875 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1876 if (level_inc < -viscosity || level_inc > viscosity)
1877 new_node_level = liquid_levels[i] + level_inc/viscosity;
1878 else if (level_inc < 0)
1879 new_node_level = liquid_levels[i] - 1;
1880 else if (level_inc > 0)
1881 new_node_level = liquid_levels[i] + 1;
1883 new_node_level = liquid_levels_want[i];
1886 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1887 new_node_content = liquid_kind;
1888 else if (new_node_level > 0)
1889 new_node_content = liquid_kind_flowing;
1891 new_node_content = CONTENT_AIR;
1893 // last level must flow down on stairs
1894 if (liquid_levels_want[i] != liquid_levels[i] &&
1895 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1896 new_node_level >= 1 && new_node_level <= 2) {
1897 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1898 if (neighbors[ii].l)
1899 must_reflow_second.push_back(p0 + dirs[ii]);
1904 check if anything has changed.
1905 if not, just continue with the next node.
1909 new_node_content == n0.getContent()
1910 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1911 (n0.getLevel(nodemgr) == (u8)new_node_level
1912 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1913 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1916 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1917 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1918 LIQUID_INFINITY_MASK) == neighbors[i].i
1921 if (liquid_levels[i] == new_node_level)
1929 update the current node
1932 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1933 // set level to last 3 bits, flowing down bit to 4th bit
1934 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1935 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1936 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1937 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1941 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1942 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1943 <<(int)new_node_level<<std::endl;
1946 n0.setContent(liquid_kind_flowing);
1947 n0.setLevel(nodemgr, new_node_level);
1948 // Find out whether there is a suspect for this action
1949 std::string suspect;
1950 if(m_gamedef->rollback()){
1951 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1954 if(!suspect.empty()){
1956 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1957 // Get old node for rollback
1958 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1962 RollbackNode rollback_newnode(this, p0, m_gamedef);
1963 RollbackAction action;
1964 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1965 m_gamedef->rollback()->reportAction(action);
1971 v3s16 blockpos = getNodeBlockPos(p0);
1972 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1974 modified_blocks[blockpos] = block;
1975 // If node emits light, MapBlock requires lighting update
1976 if(nodemgr->get(n0).light_source != 0)
1977 lighting_modified_blocks[block->getPos()] = block;
1979 must_reflow.push_back(neighbors[i].p);
1981 /* //for better relax only same level
1982 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1983 if (!neighbors[ii].l) continue;
1984 must_reflow.push_back(p0 + dirs[ii]);
1989 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1990 <<" reflow="<<must_reflow.size()
1991 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1993 while (must_reflow.size() > 0)
1994 m_transforming_liquid.push_back(must_reflow.pop_front());
1995 while (must_reflow_second.size() > 0)
1996 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1997 updateLighting(lighting_modified_blocks, modified_blocks);
2000 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
2003 if (g_settings->getBool("liquid_finite"))
2004 return Map::transformLiquidsFinite(modified_blocks);
2006 INodeDefManager *nodemgr = m_gamedef->ndef();
2008 DSTACK(__FUNCTION_NAME);
2009 //TimeTaker timer("transformLiquids()");
2012 u32 initial_size = m_transforming_liquid.size();
2014 /*if(initial_size != 0)
2015 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
2017 // list of nodes that due to viscosity have not reached their max level height
2018 UniqueQueue<v3s16> must_reflow;
2020 // List of MapBlocks that will require a lighting update (due to lava)
2021 std::map<v3s16, MapBlock*> lighting_modified_blocks;
2023 u16 loop_max = g_settings->getU16("liquid_loop_max");
2025 while(m_transforming_liquid.size() != 0)
2027 // This should be done here so that it is done when continue is used
2028 if(loopcount >= initial_size || loopcount >= loop_max)
2033 Get a queued transforming liquid node
2035 v3s16 p0 = m_transforming_liquid.pop_front();
2037 MapNode n0 = getNodeNoEx(p0);
2040 Collect information about current node
2042 s8 liquid_level = -1;
2043 content_t liquid_kind = CONTENT_IGNORE;
2044 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2045 switch (liquid_type) {
2047 liquid_level = LIQUID_LEVEL_SOURCE;
2048 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2050 case LIQUID_FLOWING:
2051 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2052 liquid_kind = n0.getContent();
2055 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2056 // continue with the next node.
2057 if (n0.getContent() != CONTENT_AIR)
2059 liquid_kind = CONTENT_AIR;
2064 Collect information about the environment
2066 const v3s16 *dirs = g_6dirs;
2067 NodeNeighbor sources[6]; // surrounding sources
2068 int num_sources = 0;
2069 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2071 NodeNeighbor airs[6]; // surrounding air
2073 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2074 int num_neutrals = 0;
2075 bool flowing_down = false;
2076 for (u16 i = 0; i < 6; i++) {
2077 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2080 nt = NEIGHBOR_UPPER;
2083 nt = NEIGHBOR_LOWER;
2086 v3s16 npos = p0 + dirs[i];
2087 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2088 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2090 if (nb.n.getContent() == CONTENT_AIR) {
2091 airs[num_airs++] = nb;
2092 // if the current node is a water source the neighbor
2093 // should be enqueded for transformation regardless of whether the
2094 // current node changes or not.
2095 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2096 m_transforming_liquid.push_back(npos);
2097 // if the current node happens to be a flowing node, it will start to flow down here.
2098 if (nb.t == NEIGHBOR_LOWER) {
2099 flowing_down = true;
2102 neutrals[num_neutrals++] = nb;
2106 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2107 if (liquid_kind == CONTENT_AIR)
2108 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2109 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2110 neutrals[num_neutrals++] = nb;
2112 // Do not count bottom source, it will screw things up
2114 sources[num_sources++] = nb;
2117 case LIQUID_FLOWING:
2118 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2119 if (liquid_kind == CONTENT_AIR)
2120 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2121 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2122 neutrals[num_neutrals++] = nb;
2124 flows[num_flows++] = nb;
2125 if (nb.t == NEIGHBOR_LOWER)
2126 flowing_down = true;
2133 decide on the type (and possibly level) of the current node
2135 content_t new_node_content;
2136 s8 new_node_level = -1;
2137 s8 max_node_level = -1;
2138 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2139 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2140 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2141 // it's perfectly safe to use liquid_kind here to determine the new node content.
2142 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2143 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2144 // liquid_kind is set properly, see above
2145 new_node_content = liquid_kind;
2146 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2148 // no surrounding sources, so get the maximum level that can flow into this node
2149 for (u16 i = 0; i < num_flows; i++) {
2150 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2151 switch (flows[i].t) {
2152 case NEIGHBOR_UPPER:
2153 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2154 max_node_level = LIQUID_LEVEL_MAX;
2155 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2156 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2157 } else if (nb_liquid_level > max_node_level)
2158 max_node_level = nb_liquid_level;
2160 case NEIGHBOR_LOWER:
2162 case NEIGHBOR_SAME_LEVEL:
2163 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2164 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2165 max_node_level = nb_liquid_level - 1;
2171 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2172 if (viscosity > 1 && max_node_level != liquid_level) {
2173 // amount to gain, limited by viscosity
2174 // must be at least 1 in absolute value
2175 s8 level_inc = max_node_level - liquid_level;
2176 if (level_inc < -viscosity || level_inc > viscosity)
2177 new_node_level = liquid_level + level_inc/viscosity;
2178 else if (level_inc < 0)
2179 new_node_level = liquid_level - 1;
2180 else if (level_inc > 0)
2181 new_node_level = liquid_level + 1;
2182 if (new_node_level != max_node_level)
2183 must_reflow.push_back(p0);
2185 new_node_level = max_node_level;
2187 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
2188 if (new_node_level >= (LIQUID_LEVEL_MAX+1-range))
2189 new_node_content = liquid_kind;
2191 new_node_content = CONTENT_AIR;
2196 check if anything has changed. if not, just continue with the next node.
2198 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2199 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2200 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2206 update the current node
2209 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2210 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2211 // set level to last 3 bits, flowing down bit to 4th bit
2212 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2214 // set the liquid level and flow bit to 0
2215 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2217 n0.setContent(new_node_content);
2219 // Find out whether there is a suspect for this action
2220 std::string suspect;
2221 if(m_gamedef->rollback()){
2222 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2225 if(!suspect.empty()){
2227 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2228 // Get old node for rollback
2229 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2233 RollbackNode rollback_newnode(this, p0, m_gamedef);
2234 RollbackAction action;
2235 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2236 m_gamedef->rollback()->reportAction(action);
2242 v3s16 blockpos = getNodeBlockPos(p0);
2243 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2245 modified_blocks[blockpos] = block;
2246 // If new or old node emits light, MapBlock requires lighting update
2247 if(nodemgr->get(n0).light_source != 0 ||
2248 nodemgr->get(n00).light_source != 0)
2249 lighting_modified_blocks[block->getPos()] = block;
2253 enqueue neighbors for update if neccessary
2255 switch (nodemgr->get(n0.getContent()).liquid_type) {
2257 case LIQUID_FLOWING:
2258 // make sure source flows into all neighboring nodes
2259 for (u16 i = 0; i < num_flows; i++)
2260 if (flows[i].t != NEIGHBOR_UPPER)
2261 m_transforming_liquid.push_back(flows[i].p);
2262 for (u16 i = 0; i < num_airs; i++)
2263 if (airs[i].t != NEIGHBOR_UPPER)
2264 m_transforming_liquid.push_back(airs[i].p);
2267 // this flow has turned to air; neighboring flows might need to do the same
2268 for (u16 i = 0; i < num_flows; i++)
2269 m_transforming_liquid.push_back(flows[i].p);
2273 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2274 while (must_reflow.size() > 0)
2275 m_transforming_liquid.push_back(must_reflow.pop_front());
2276 updateLighting(lighting_modified_blocks, modified_blocks);
2279 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2281 v3s16 blockpos = getNodeBlockPos(p);
2282 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2283 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2285 infostream<<"Map::getNodeMetadata(): Need to emerge "
2286 <<PP(blockpos)<<std::endl;
2287 block = emergeBlock(blockpos, false);
2291 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2295 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2299 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2301 v3s16 blockpos = getNodeBlockPos(p);
2302 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2303 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2305 infostream<<"Map::setNodeMetadata(): Need to emerge "
2306 <<PP(blockpos)<<std::endl;
2307 block = emergeBlock(blockpos, false);
2311 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2315 block->m_node_metadata.set(p_rel, meta);
2318 void Map::removeNodeMetadata(v3s16 p)
2320 v3s16 blockpos = getNodeBlockPos(p);
2321 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2322 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2325 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2329 block->m_node_metadata.remove(p_rel);
2332 NodeTimer Map::getNodeTimer(v3s16 p)
2334 v3s16 blockpos = getNodeBlockPos(p);
2335 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2336 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2338 infostream<<"Map::getNodeTimer(): Need to emerge "
2339 <<PP(blockpos)<<std::endl;
2340 block = emergeBlock(blockpos, false);
2344 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2348 NodeTimer t = block->m_node_timers.get(p_rel);
2352 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2354 v3s16 blockpos = getNodeBlockPos(p);
2355 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2356 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2358 infostream<<"Map::setNodeTimer(): Need to emerge "
2359 <<PP(blockpos)<<std::endl;
2360 block = emergeBlock(blockpos, false);
2364 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2368 block->m_node_timers.set(p_rel, t);
2371 void Map::removeNodeTimer(v3s16 p)
2373 v3s16 blockpos = getNodeBlockPos(p);
2374 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2375 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2378 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2382 block->m_node_timers.remove(p_rel);
2385 s16 Map::getHeat(v3s16 p)
2387 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2391 //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2395 s16 Map::getHumidity(v3s16 p)
2397 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2399 return block->humidity;
2401 //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2408 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2409 Map(dout_server, gamedef),
2411 m_map_metadata_changed(true)
2413 verbosestream<<__FUNCTION_NAME<<std::endl;
2416 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2418 m_mgparams = new MapgenV6Params();
2420 m_seed = m_mgparams->seed;
2422 if (g_settings->get("fixed_map_seed").empty())
2424 m_seed = (((u64)(myrand() & 0xffff) << 0)
2425 | ((u64)(myrand() & 0xffff) << 16)
2426 | ((u64)(myrand() & 0xffff) << 32)
2427 | ((u64)(myrand() & 0xffff) << 48));
2428 m_mgparams->seed = m_seed;
2432 Experimental and debug stuff
2439 Try to load map; if not found, create a new one.
2442 // Determine which database backend to use
2443 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2445 bool succeeded = conf.readConfigFile(conf_path.c_str());
2446 if (!succeeded || !conf.exists("backend")) {
2447 // fall back to sqlite3
2448 dbase = new Database_SQLite3(this, savedir);
2449 conf.set("backend", "sqlite3");
2451 std::string backend = conf.get("backend");
2452 if (backend == "dummy")
2453 dbase = new Database_Dummy(this);
2454 else if (backend == "sqlite3")
2455 dbase = new Database_SQLite3(this, savedir);
2457 else if (backend == "leveldb")
2458 dbase = new Database_LevelDB(this, savedir);
2461 throw BaseException("Unknown map backend");
2464 m_savedir = savedir;
2465 m_map_saving_enabled = false;
2469 // If directory exists, check contents and load if possible
2470 if(fs::PathExists(m_savedir))
2472 // If directory is empty, it is safe to save into it.
2473 if(fs::GetDirListing(m_savedir).size() == 0)
2475 infostream<<"ServerMap: Empty save directory is valid."
2477 m_map_saving_enabled = true;
2482 // Load map metadata (seed, chunksize)
2485 catch(SettingNotFoundException &e){
2486 infostream<<"ServerMap: Some metadata not found."
2487 <<" Using default settings."<<std::endl;
2489 catch(FileNotGoodException &e){
2490 infostream<<"WARNING: Could not load map metadata"
2491 //<<" Disabling chunk-based generator."
2496 infostream<<"ServerMap: Successfully loaded map "
2497 <<"metadata from "<<savedir
2498 <<", assuming valid save directory."
2499 <<" seed="<<m_seed<<"."
2502 m_map_saving_enabled = true;
2503 // Map loaded, not creating new one
2507 // If directory doesn't exist, it is safe to save to it
2509 m_map_saving_enabled = true;
2512 catch(std::exception &e)
2514 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2515 <<", exception: "<<e.what()<<std::endl;
2516 infostream<<"Please remove the map or fix it."<<std::endl;
2517 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2520 infostream<<"Initializing new map."<<std::endl;
2522 // Create zero sector
2523 emergeSector(v2s16(0,0));
2525 // Initially write whole map
2526 save(MOD_STATE_CLEAN);
2529 ServerMap::~ServerMap()
2531 verbosestream<<__FUNCTION_NAME<<std::endl;
2535 if(m_map_saving_enabled)
2537 // Save only changed parts
2538 save(MOD_STATE_WRITE_AT_UNLOAD);
2539 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2543 infostream<<"ServerMap: Map not saved"<<std::endl;
2546 catch(std::exception &e)
2548 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2549 <<", exception: "<<e.what()<<std::endl;
2553 Close database if it was opened
2561 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2562 for(; i.atEnd() == false; i++)
2564 MapChunk *chunk = i.getNode()->getValue();
2572 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2574 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2575 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2577 s16 chunksize = m_mgparams->chunksize;
2578 s16 coffset = -chunksize / 2;
2579 v3s16 chunk_offset(coffset, coffset, coffset);
2580 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2581 v3s16 blockpos_min = blockpos_div * chunksize;
2582 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2583 blockpos_min += chunk_offset;
2584 blockpos_max += chunk_offset;
2586 v3s16 extra_borders(1,1,1);
2588 // Do nothing if not inside limits (+-1 because of neighbors)
2589 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2590 blockpos_over_limit(blockpos_max + extra_borders))
2593 data->seed = m_seed;
2594 data->blockpos_min = blockpos_min;
2595 data->blockpos_max = blockpos_max;
2596 data->blockpos_requested = blockpos;
2597 data->nodedef = m_gamedef->ndef();
2600 Create the whole area of this and the neighboring blocks
2603 //TimeTaker timer("initBlockMake() create area");
2605 for(s16 x=blockpos_min.X-extra_borders.X;
2606 x<=blockpos_max.X+extra_borders.X; x++)
2607 for(s16 z=blockpos_min.Z-extra_borders.Z;
2608 z<=blockpos_max.Z+extra_borders.Z; z++)
2610 v2s16 sectorpos(x, z);
2611 // Sector metadata is loaded from disk if not already loaded.
2612 ServerMapSector *sector = createSector(sectorpos);
2615 for(s16 y=blockpos_min.Y-extra_borders.Y;
2616 y<=blockpos_max.Y+extra_borders.Y; y++)
2619 //MapBlock *block = createBlock(p);
2620 // 1) get from memory, 2) load from disk
2621 MapBlock *block = emergeBlock(p, false);
2622 // 3) create a blank one
2625 block = createBlock(p);
2628 Block gets sunlight if this is true.
2630 Refer to the map generator heuristics.
2632 bool ug = m_emerge->isBlockUnderground(p);
2633 block->setIsUnderground(ug);
2636 // Lighting will not be valid after make_chunk is called
2637 block->setLightingExpired(true);
2638 // Lighting will be calculated
2639 //block->setLightingExpired(false);
2645 Now we have a big empty area.
2647 Make a ManualMapVoxelManipulator that contains this and the
2651 // The area that contains this block and it's neighbors
2652 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2653 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2655 data->vmanip = new ManualMapVoxelManipulator(this);
2656 //data->vmanip->setMap(this);
2660 //TimeTaker timer("initBlockMake() initialEmerge");
2661 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2664 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2665 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2666 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2667 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2668 core::map<v3s16, u8>::Node *n;
2669 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2672 u8 flags = n->getValue();
2673 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2679 // Data is ready now.
2683 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2684 std::map<v3s16, MapBlock*> &changed_blocks)
2686 v3s16 blockpos_min = data->blockpos_min;
2687 v3s16 blockpos_max = data->blockpos_max;
2688 v3s16 blockpos_requested = data->blockpos_requested;
2689 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2690 <<blockpos_requested.Y<<","
2691 <<blockpos_requested.Z<<")"<<std::endl;*/
2693 v3s16 extra_borders(1,1,1);
2695 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2697 /*infostream<<"Resulting vmanip:"<<std::endl;
2698 data->vmanip.print(infostream);*/
2700 // Make sure affected blocks are loaded
2701 for(s16 x=blockpos_min.X-extra_borders.X;
2702 x<=blockpos_max.X+extra_borders.X; x++)
2703 for(s16 z=blockpos_min.Z-extra_borders.Z;
2704 z<=blockpos_max.Z+extra_borders.Z; z++)
2705 for(s16 y=blockpos_min.Y-extra_borders.Y;
2706 y<=blockpos_max.Y+extra_borders.Y; y++)
2709 // Load from disk if not already in memory
2710 emergeBlock(p, false);
2714 Blit generated stuff to map
2715 NOTE: blitBackAll adds nearly everything to changed_blocks
2719 //TimeTaker timer("finishBlockMake() blitBackAll");
2720 data->vmanip->blitBackAll(&changed_blocks);
2723 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2726 Copy transforming liquid information
2728 while(data->transforming_liquid.size() > 0)
2730 v3s16 p = data->transforming_liquid.pop_front();
2731 m_transforming_liquid.push_back(p);
2735 Do stuff in central blocks
2743 TimeTaker t("finishBlockMake lighting update");
2745 core::map<v3s16, MapBlock*> lighting_update_blocks;
2748 for(s16 x=blockpos_min.X-extra_borders.X;
2749 x<=blockpos_max.X+extra_borders.X; x++)
2750 for(s16 z=blockpos_min.Z-extra_borders.Z;
2751 z<=blockpos_max.Z+extra_borders.Z; z++)
2752 for(s16 y=blockpos_min.Y-extra_borders.Y;
2753 y<=blockpos_max.Y+extra_borders.Y; y++)
2756 MapBlock *block = getBlockNoCreateNoEx(p);
2758 lighting_update_blocks.insert(block->getPos(), block);
2761 updateLighting(lighting_update_blocks, changed_blocks);
2765 Set lighting to non-expired state in all of them.
2766 This is cheating, but it is not fast enough if all of them
2767 would actually be updated.
2769 for(s16 x=blockpos_min.X-extra_borders.X;
2770 x<=blockpos_max.X+extra_borders.X; x++)
2771 for(s16 z=blockpos_min.Z-extra_borders.Z;
2772 z<=blockpos_max.Z+extra_borders.Z; z++)
2773 for(s16 y=blockpos_min.Y-extra_borders.Y;
2774 y<=blockpos_max.Y+extra_borders.Y; y++)
2777 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2781 if(enable_mapgen_debug_info == false)
2782 t.stop(true); // Hide output
2787 Go through changed blocks
2789 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2790 i != changed_blocks.end(); ++i)
2792 MapBlock *block = i->second;
2795 Update day/night difference cache of the MapBlocks
2797 block->expireDayNightDiff();
2799 Set block as modified
2801 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2802 "finishBlockMake expireDayNightDiff");
2806 Set central blocks as generated
2808 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2809 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2810 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2813 MapBlock *block = getBlockNoCreateNoEx(p);
2815 block->setGenerated(true);
2819 Save changed parts of map
2820 NOTE: Will be saved later.
2822 //save(MOD_STATE_WRITE_AT_UNLOAD);
2824 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2825 <<","<<blockpos_requested.Y<<","
2826 <<blockpos_requested.Z<<")"<<std::endl;*/
2828 if(enable_mapgen_debug_info)
2831 Analyze resulting blocks
2833 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2834 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2835 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2836 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2837 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2838 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2840 v3s16 p = v3s16(x,y,z);
2841 MapBlock *block = getBlockNoCreateNoEx(p);
2843 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2844 infostream<<"Generated "<<spos<<": "
2845 <<analyze_block(block)<<std::endl;
2850 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2856 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2858 DSTACKF("%s: p2d=(%d,%d)",
2863 Check if it exists already in memory
2865 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2870 Try to load it from disk (with blocks)
2872 //if(loadSectorFull(p2d) == true)
2875 Try to load metadata from disk
2878 if(loadSectorMeta(p2d) == true)
2880 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2883 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2884 throw InvalidPositionException("");
2890 Do not create over-limit
2892 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2893 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2894 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2895 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2896 throw InvalidPositionException("createSector(): pos. over limit");
2899 Generate blank sector
2902 sector = new ServerMapSector(this, p2d, m_gamedef);
2904 // Sector position on map in nodes
2905 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2910 m_sectors[p2d] = sector;
2917 This is a quick-hand function for calling makeBlock().
2919 MapBlock * ServerMap::generateBlock(
2921 std::map<v3s16, MapBlock*> &modified_blocks
2924 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2926 /*infostream<<"generateBlock(): "
2927 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2930 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2932 TimeTaker timer("generateBlock");
2934 //MapBlock *block = original_dummy;
2936 v2s16 p2d(p.X, p.Z);
2937 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2940 Do not generate over-limit
2942 if(blockpos_over_limit(p))
2944 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2945 throw InvalidPositionException("generateBlock(): pos. over limit");
2949 Create block make data
2952 initBlockMake(&data, p);
2958 TimeTaker t("mapgen::make_block()");
2959 mapgen->makeChunk(&data);
2960 //mapgen::make_block(&data);
2962 if(enable_mapgen_debug_info == false)
2963 t.stop(true); // Hide output
2967 Blit data back on map, update lighting, add mobs and whatever this does
2969 finishBlockMake(&data, modified_blocks);
2974 MapBlock *block = getBlockNoCreateNoEx(p);
2982 bool erroneus_content = false;
2983 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2984 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2985 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2988 MapNode n = block->getNode(p);
2989 if(n.getContent() == CONTENT_IGNORE)
2991 infostream<<"CONTENT_IGNORE at "
2992 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2994 erroneus_content = true;
2998 if(erroneus_content)
3007 Generate a completely empty block
3011 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3012 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3014 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3017 n.setContent(CONTENT_AIR);
3018 block->setNode(v3s16(x0,y0,z0), n);
3024 if(enable_mapgen_debug_info == false)
3025 timer.stop(true); // Hide output
3031 MapBlock * ServerMap::createBlock(v3s16 p)
3033 DSTACKF("%s: p=(%d,%d,%d)",
3034 __FUNCTION_NAME, p.X, p.Y, p.Z);
3037 Do not create over-limit
3039 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3040 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3041 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3042 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3043 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3044 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3045 throw InvalidPositionException("createBlock(): pos. over limit");
3047 v2s16 p2d(p.X, p.Z);
3050 This will create or load a sector if not found in memory.
3051 If block exists on disk, it will be loaded.
3053 NOTE: On old save formats, this will be slow, as it generates
3054 lighting on blocks for them.
3056 ServerMapSector *sector;
3058 sector = (ServerMapSector*)createSector(p2d);
3059 assert(sector->getId() == MAPSECTOR_SERVER);
3061 catch(InvalidPositionException &e)
3063 infostream<<"createBlock: createSector() failed"<<std::endl;
3067 NOTE: This should not be done, or at least the exception
3068 should not be passed on as std::exception, because it
3069 won't be catched at all.
3071 /*catch(std::exception &e)
3073 infostream<<"createBlock: createSector() failed: "
3074 <<e.what()<<std::endl;
3079 Try to get a block from the sector
3082 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3085 if(block->isDummy())
3090 block = sector->createBlankBlock(block_y);
3095 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3097 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3099 p.X, p.Y, p.Z, create_blank);
3102 MapBlock *block = getBlockNoCreateNoEx(p);
3103 if(block && block->isDummy() == false)
3108 MapBlock *block = loadBlock(p);
3114 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3115 MapBlock *block = sector->createBlankBlock(p.Y);
3119 /*if(allow_generate)
3121 std::map<v3s16, MapBlock*> modified_blocks;
3122 MapBlock *block = generateBlock(p, modified_blocks);
3126 event.type = MEET_OTHER;
3129 // Copy modified_blocks to event
3130 for(std::map<v3s16, MapBlock*>::iterator
3131 i = modified_blocks.begin();
3132 i != modified_blocks.end(); ++i)
3134 event.modified_blocks.insert(i->first);
3138 dispatchEvent(&event);
3147 s16 ServerMap::findGroundLevel(v2s16 p2d)
3151 Uh, just do something random...
3153 // Find existing map from top to down
3156 v3s16 p(p2d.X, max, p2d.Y);
3157 for(; p.Y>min; p.Y--)
3159 MapNode n = getNodeNoEx(p);
3160 if(n.getContent() != CONTENT_IGNORE)
3165 // If this node is not air, go to plan b
3166 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3168 // Search existing walkable and return it
3169 for(; p.Y>min; p.Y--)
3171 MapNode n = getNodeNoEx(p);
3172 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3181 Determine from map generator noise functions
3184 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3187 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3188 //return (s16)level;
3191 bool ServerMap::loadFromFolders() {
3192 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
3197 void ServerMap::createDirs(std::string path)
3199 if(fs::CreateAllDirs(path) == false)
3201 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3202 <<"\""<<path<<"\""<<std::endl;
3203 throw BaseException("ServerMap failed to create directory");
3207 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3213 snprintf(cc, 9, "%.4x%.4x",
3214 (unsigned int)pos.X&0xffff,
3215 (unsigned int)pos.Y&0xffff);
3217 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3219 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3220 (unsigned int)pos.X&0xfff,
3221 (unsigned int)pos.Y&0xfff);
3223 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3229 v2s16 ServerMap::getSectorPos(std::string dirname)
3233 std::string component;
3234 fs::RemoveLastPathComponent(dirname, &component, 1);
3235 if(component.size() == 8)
3238 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3240 else if(component.size() == 3)
3243 fs::RemoveLastPathComponent(dirname, &component, 2);
3244 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3245 // Sign-extend the 12 bit values up to 16 bits...
3246 if(x&0x800) x|=0xF000;
3247 if(y&0x800) y|=0xF000;
3254 v2s16 pos((s16)x, (s16)y);
3258 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3260 v2s16 p2d = getSectorPos(sectordir);
3262 if(blockfile.size() != 4){
3263 throw InvalidFilenameException("Invalid block filename");
3266 int r = sscanf(blockfile.c_str(), "%4x", &y);
3268 throw InvalidFilenameException("Invalid block filename");
3269 return v3s16(p2d.X, y, p2d.Y);
3272 std::string ServerMap::getBlockFilename(v3s16 p)
3275 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3279 void ServerMap::save(ModifiedState save_level)
3281 DSTACK(__FUNCTION_NAME);
3282 if(m_map_saving_enabled == false)
3284 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3288 if(save_level == MOD_STATE_CLEAN)
3289 infostream<<"ServerMap: Saving whole map, this can take time."
3292 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3297 // Profile modified reasons
3298 Profiler modprofiler;
3300 u32 sector_meta_count = 0;
3301 u32 block_count = 0;
3302 u32 block_count_all = 0; // Number of blocks in memory
3304 // Don't do anything with sqlite unless something is really saved
3305 bool save_started = false;
3307 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3308 i != m_sectors.end(); ++i)
3310 ServerMapSector *sector = (ServerMapSector*)i->second;
3311 assert(sector->getId() == MAPSECTOR_SERVER);
3313 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3315 saveSectorMeta(sector);
3316 sector_meta_count++;
3318 std::list<MapBlock*> blocks;
3319 sector->getBlocks(blocks);
3321 for(std::list<MapBlock*>::iterator j = blocks.begin();
3322 j != blocks.end(); ++j)
3324 MapBlock *block = *j;
3328 if(block->getModified() >= (u32)save_level)
3333 save_started = true;
3336 modprofiler.add(block->getModifiedReason(), 1);
3341 /*infostream<<"ServerMap: Written block ("
3342 <<block->getPos().X<<","
3343 <<block->getPos().Y<<","
3344 <<block->getPos().Z<<")"
3353 Only print if something happened or saved whole map
3355 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3356 || block_count != 0)
3358 infostream<<"ServerMap: Written: "
3359 <<sector_meta_count<<" sector metadata files, "
3360 <<block_count<<" block files"
3361 <<", "<<block_count_all<<" blocks in memory."
3363 PrintInfo(infostream); // ServerMap/ClientMap:
3364 infostream<<"Blocks modified by: "<<std::endl;
3365 modprofiler.print(infostream);
3369 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3371 if(loadFromFolders()){
3372 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3373 <<"all blocks that are stored in flat files"<<std::endl;
3375 dbase->listAllLoadableBlocks(dst);
3378 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3380 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3381 si != m_sectors.end(); ++si)
3383 MapSector *sector = si->second;
3385 std::list<MapBlock*> blocks;
3386 sector->getBlocks(blocks);
3388 for(std::list<MapBlock*>::iterator i = blocks.begin();
3389 i != blocks.end(); ++i)
3391 MapBlock *block = (*i);
3392 v3s16 p = block->getPos();
3398 void ServerMap::saveMapMeta()
3400 DSTACK(__FUNCTION_NAME);
3402 /*infostream<<"ServerMap::saveMapMeta(): "
3406 createDirs(m_savedir);
3408 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3409 std::ostringstream ss(std::ios_base::binary);
3413 m_emerge->setParamsToSettings(¶ms);
3414 params.writeLines(ss);
3416 ss<<"[end_of_params]\n";
3418 if(!fs::safeWriteToFile(fullpath, ss.str()))
3420 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3421 <<"could not write "<<fullpath<<std::endl;
3422 throw FileNotGoodException("Cannot save chunk metadata");
3425 m_map_metadata_changed = false;
3428 void ServerMap::loadMapMeta()
3430 DSTACK(__FUNCTION_NAME);
3432 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3435 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3436 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3437 if(is.good() == false)
3439 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3440 <<"could not open"<<fullpath<<std::endl;
3441 throw FileNotGoodException("Cannot open map metadata");
3449 throw SerializationError
3450 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3452 std::getline(is, line);
3453 std::string trimmedline = trim(line);
3454 if(trimmedline == "[end_of_params]")
3456 params.parseConfigLine(line);
3459 MapgenParams *mgparams;
3461 mgparams = m_emerge->getParamsFromSettings(¶ms);
3462 } catch (SettingNotFoundException &e) {
3463 infostream << "Couldn't get a setting from map_meta.txt: "
3464 << e.what() << std::endl;
3471 m_mgparams = mgparams;
3472 m_seed = mgparams->seed;
3474 if (params.exists("seed")) {
3475 m_seed = params.getU64("seed");
3476 m_mgparams->seed = m_seed;
3480 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3483 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3485 DSTACK(__FUNCTION_NAME);
3486 // Format used for writing
3487 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3489 v2s16 pos = sector->getPos();
3490 std::string dir = getSectorDir(pos);
3493 std::string fullpath = dir + DIR_DELIM + "meta";
3494 std::ostringstream ss(std::ios_base::binary);
3496 sector->serialize(ss, version);
3498 if(!fs::safeWriteToFile(fullpath, ss.str()))
3499 throw FileNotGoodException("Cannot write sector metafile");
3501 sector->differs_from_disk = false;
3504 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3506 DSTACK(__FUNCTION_NAME);
3508 v2s16 p2d = getSectorPos(sectordir);
3510 ServerMapSector *sector = NULL;
3512 std::string fullpath = sectordir + DIR_DELIM + "meta";
3513 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3514 if(is.good() == false)
3516 // If the directory exists anyway, it probably is in some old
3517 // format. Just go ahead and create the sector.
3518 if(fs::PathExists(sectordir))
3520 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3521 <<fullpath<<" doesn't exist but directory does."
3522 <<" Continuing with a sector with no metadata."
3524 sector = new ServerMapSector(this, p2d, m_gamedef);
3525 m_sectors[p2d] = sector;
3529 throw FileNotGoodException("Cannot open sector metafile");
3534 sector = ServerMapSector::deSerialize
3535 (is, this, p2d, m_sectors, m_gamedef);
3537 saveSectorMeta(sector);
3540 sector->differs_from_disk = false;
3545 bool ServerMap::loadSectorMeta(v2s16 p2d)
3547 DSTACK(__FUNCTION_NAME);
3549 MapSector *sector = NULL;
3551 // The directory layout we're going to load from.
3552 // 1 - original sectors/xxxxzzzz/
3553 // 2 - new sectors2/xxx/zzz/
3554 // If we load from anything but the latest structure, we will
3555 // immediately save to the new one, and remove the old.
3557 std::string sectordir1 = getSectorDir(p2d, 1);
3558 std::string sectordir;
3559 if(fs::PathExists(sectordir1))
3561 sectordir = sectordir1;
3566 sectordir = getSectorDir(p2d, 2);
3570 sector = loadSectorMeta(sectordir, loadlayout != 2);
3572 catch(InvalidFilenameException &e)
3576 catch(FileNotGoodException &e)
3580 catch(std::exception &e)
3589 bool ServerMap::loadSectorFull(v2s16 p2d)
3591 DSTACK(__FUNCTION_NAME);
3593 MapSector *sector = NULL;
3595 // The directory layout we're going to load from.
3596 // 1 - original sectors/xxxxzzzz/
3597 // 2 - new sectors2/xxx/zzz/
3598 // If we load from anything but the latest structure, we will
3599 // immediately save to the new one, and remove the old.
3601 std::string sectordir1 = getSectorDir(p2d, 1);
3602 std::string sectordir;
3603 if(fs::PathExists(sectordir1))
3605 sectordir = sectordir1;
3610 sectordir = getSectorDir(p2d, 2);
3614 sector = loadSectorMeta(sectordir, loadlayout != 2);
3616 catch(InvalidFilenameException &e)
3620 catch(FileNotGoodException &e)
3624 catch(std::exception &e)
3632 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3634 std::vector<fs::DirListNode>::iterator i2;
3635 for(i2=list2.begin(); i2!=list2.end(); i2++)
3641 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3643 catch(InvalidFilenameException &e)
3645 // This catches unknown crap in directory
3651 infostream<<"Sector converted to new layout - deleting "<<
3652 sectordir1<<std::endl;
3653 fs::RecursiveDelete(sectordir1);
3660 void ServerMap::beginSave() {
3664 void ServerMap::endSave() {
3668 void ServerMap::saveBlock(MapBlock *block)
3670 dbase->saveBlock(block);
3673 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3675 DSTACK(__FUNCTION_NAME);
3677 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3680 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3681 if(is.good() == false)
3682 throw FileNotGoodException("Cannot open block file");
3684 v3s16 p3d = getBlockPos(sectordir, blockfile);
3685 v2s16 p2d(p3d.X, p3d.Z);
3687 assert(sector->getPos() == p2d);
3689 u8 version = SER_FMT_VER_INVALID;
3690 is.read((char*)&version, 1);
3693 throw SerializationError("ServerMap::loadBlock(): Failed"
3694 " to read MapBlock version");
3696 /*u32 block_size = MapBlock::serializedLength(version);
3697 SharedBuffer<u8> data(block_size);
3698 is.read((char*)*data, block_size);*/
3700 // This will always return a sector because we're the server
3701 //MapSector *sector = emergeSector(p2d);
3703 MapBlock *block = NULL;
3704 bool created_new = false;
3705 block = sector->getBlockNoCreateNoEx(p3d.Y);
3708 block = sector->createBlankBlockNoInsert(p3d.Y);
3713 block->deSerialize(is, version, true);
3715 // If it's a new block, insert it to the map
3717 sector->insertBlock(block);
3720 Save blocks loaded in old format in new format
3723 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3727 // Should be in database now, so delete the old file
3728 fs::RecursiveDelete(fullpath);
3731 // We just loaded it from the disk, so it's up-to-date.
3732 block->resetModified();
3735 catch(SerializationError &e)
3737 infostream<<"WARNING: Invalid block data on disk "
3738 <<"fullpath="<<fullpath
3739 <<" (SerializationError). "
3740 <<"what()="<<e.what()
3742 // Ignoring. A new one will be generated.
3745 // TODO: Backup file; name is in fullpath.
3749 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3751 DSTACK(__FUNCTION_NAME);
3754 std::istringstream is(*blob, std::ios_base::binary);
3756 u8 version = SER_FMT_VER_INVALID;
3757 is.read((char*)&version, 1);
3760 throw SerializationError("ServerMap::loadBlock(): Failed"
3761 " to read MapBlock version");
3763 /*u32 block_size = MapBlock::serializedLength(version);
3764 SharedBuffer<u8> data(block_size);
3765 is.read((char*)*data, block_size);*/
3767 // This will always return a sector because we're the server
3768 //MapSector *sector = emergeSector(p2d);
3770 MapBlock *block = NULL;
3771 bool created_new = false;
3772 block = sector->getBlockNoCreateNoEx(p3d.Y);
3775 block = sector->createBlankBlockNoInsert(p3d.Y);
3780 block->deSerialize(is, version, true);
3782 // If it's a new block, insert it to the map
3784 sector->insertBlock(block);
3787 Save blocks loaded in old format in new format
3790 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3791 // Only save if asked to; no need to update version
3795 // We just loaded it from, so it's up-to-date.
3796 block->resetModified();
3799 catch(SerializationError &e)
3801 errorstream<<"Invalid block data in database"
3802 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3803 <<" (SerializationError): "<<e.what()<<std::endl;
3805 // TODO: Block should be marked as invalid in memory so that it is
3806 // not touched but the game can run
3808 if(g_settings->getBool("ignore_world_load_errors")){
3809 errorstream<<"Ignoring block load error. Duck and cover! "
3810 <<"(ignore_world_load_errors)"<<std::endl;
3812 throw SerializationError("Invalid block data in database");
3818 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3820 DSTACK(__FUNCTION_NAME);
3822 v2s16 p2d(blockpos.X, blockpos.Z);
3826 ret = dbase->loadBlock(blockpos);
3827 if (ret) return (ret);
3828 // Not found in database, try the files
3830 // The directory layout we're going to load from.
3831 // 1 - original sectors/xxxxzzzz/
3832 // 2 - new sectors2/xxx/zzz/
3833 // If we load from anything but the latest structure, we will
3834 // immediately save to the new one, and remove the old.
3836 std::string sectordir1 = getSectorDir(p2d, 1);
3837 std::string sectordir;
3838 if(fs::PathExists(sectordir1))
3840 sectordir = sectordir1;
3845 sectordir = getSectorDir(p2d, 2);
3849 Make sure sector is loaded
3851 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3855 sector = loadSectorMeta(sectordir, loadlayout != 2);
3857 catch(InvalidFilenameException &e)
3861 catch(FileNotGoodException &e)
3865 catch(std::exception &e)
3872 Make sure file exists
3875 std::string blockfilename = getBlockFilename(blockpos);
3876 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3880 Load block and save it to the database
3882 loadBlock(sectordir, blockfilename, sector, true);
3883 return getBlockNoCreateNoEx(blockpos);
3886 void ServerMap::PrintInfo(std::ostream &out)
3891 s16 ServerMap::getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
3894 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3896 if (env->getGameTime() - block->heat_time < 10)
3900 //variant 1: full random
3901 //f32 heat = NoisePerlin3D(m_emerge->biomedef->np_heat, p.X, env->getGameTime()/100, p.Z, m_emerge->params->seed);
3903 //variant 2: season change based on default heat map
3904 const f32 offset = 20; // = m_emerge->biomedef->np_heat->offset
3905 const f32 scale = 20; // = m_emerge->biomedef->np_heat->scale
3906 const f32 range = 20;
3907 f32 heat = NoisePerlin2D(m_emerge->biomedef->np_heat, p.X, p.Z,
3908 m_emerge->params->seed); // 0..50..100
3910 heat -= m_emerge->biomedef->np_heat->offset; // -50..0..+50
3912 // normalizing - todo REMOVE after fixed NoiseParams nparams_biome_def_heat = {50, 50, -> 20, 50,
3913 if(m_emerge->biomedef->np_heat->scale)
3914 heat /= m_emerge->biomedef->np_heat->scale / scale; // -20..0..+20
3916 f32 seasonv = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
3917 seasonv /= 86400 * g_settings->getS16("year_days"); // season change speed
3918 seasonv += (f32)p.X / 3000; // you can walk to area with other season
3919 seasonv = sin(seasonv * M_PI);
3920 heat += (range * (heat < 0 ? 2 : 0.5)) * seasonv; // -60..0..30
3922 heat += offset; // -40..0..50
3923 heat += p.Y / -333; // upper=colder, lower=hotter, 3c per 1000
3925 // daily change, hotter at sun +4, colder at night -4
3926 heat += 8 * (sin(cycle_shift(env->getTimeOfDayF(), -0.25) * M_PI) - 0.5); //-44..20..54
3930 block->heat_time = env->getGameTime();
3935 s16 ServerMap::getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
3938 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3940 if (env->getGameTime() - block->humidity_time < 10)
3941 return block->humidity;
3944 f32 humidity = NoisePerlin2D(m_emerge->biomedef->np_humidity, p.X, p.Z,
3945 m_emerge->params->seed);
3947 f32 seasonv = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
3948 seasonv /= 86400 * 2; // bad weather change speed (2 days)
3949 seasonv += (f32)p.Z / 300;
3950 humidity += 30 * sin(seasonv * M_PI);
3952 humidity += -12 * ( sin(cycle_shift(env->getTimeOfDayF(), -0.1) * M_PI) - 0.5);
3953 humidity = rangelim(humidity, 0, 100);
3956 block->humidity = humidity;
3957 block->humidity_time = env->getGameTime();
3966 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3971 MapVoxelManipulator::~MapVoxelManipulator()
3973 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3977 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3979 TimeTaker timer1("emerge", &emerge_time);
3981 // Units of these are MapBlocks
3982 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3983 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3985 VoxelArea block_area_nodes
3986 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3988 addArea(block_area_nodes);
3990 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3991 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3992 for(s32 x=p_min.X; x<=p_max.X; x++)
3997 std::map<v3s16, u8>::iterator n;
3998 n = m_loaded_blocks.find(p);
3999 if(n != m_loaded_blocks.end())
4002 bool block_data_inexistent = false;
4005 TimeTaker timer1("emerge load", &emerge_load_time);
4007 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4008 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4010 a.print(infostream);
4011 infostream<<std::endl;*/
4013 block = m_map->getBlockNoCreate(p);
4014 if(block->isDummy())
4015 block_data_inexistent = true;
4017 block->copyTo(*this);
4019 catch(InvalidPositionException &e)
4021 block_data_inexistent = true;
4024 if(block_data_inexistent)
4026 flags |= VMANIP_BLOCK_DATA_INEXIST;
4028 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4029 // Fill with VOXELFLAG_INEXISTENT
4030 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4031 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4033 s32 i = m_area.index(a.MinEdge.X,y,z);
4034 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4037 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4039 // Mark that block was loaded as blank
4040 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4043 m_loaded_blocks[p] = flags;
4046 //infostream<<"emerge done"<<std::endl;
4050 SUGG: Add an option to only update eg. water and air nodes.
4051 This will make it interfere less with important stuff if
4054 void MapVoxelManipulator::blitBack
4055 (std::map<v3s16, MapBlock*> & modified_blocks)
4057 if(m_area.getExtent() == v3s16(0,0,0))
4060 //TimeTaker timer1("blitBack");
4062 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4063 <<m_loaded_blocks.size()<<std::endl;*/
4066 Initialize block cache
4068 v3s16 blockpos_last;
4069 MapBlock *block = NULL;
4070 bool block_checked_in_modified = false;
4072 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4073 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4074 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4078 u8 f = m_flags[m_area.index(p)];
4079 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4082 MapNode &n = m_data[m_area.index(p)];
4084 v3s16 blockpos = getNodeBlockPos(p);
4089 if(block == NULL || blockpos != blockpos_last){
4090 block = m_map->getBlockNoCreate(blockpos);
4091 blockpos_last = blockpos;
4092 block_checked_in_modified = false;
4095 // Calculate relative position in block
4096 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4098 // Don't continue if nothing has changed here
4099 if(block->getNode(relpos) == n)
4102 //m_map->setNode(m_area.MinEdge + p, n);
4103 block->setNode(relpos, n);
4106 Make sure block is in modified_blocks
4108 if(block_checked_in_modified == false)
4110 modified_blocks[blockpos] = block;
4111 block_checked_in_modified = true;
4114 catch(InvalidPositionException &e)
4120 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4121 MapVoxelManipulator(map),
4122 m_create_area(false)
4126 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4130 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4132 // Just create the area so that it can be pointed to
4133 VoxelManipulator::emerge(a, caller_id);
4136 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4137 v3s16 blockpos_max, bool load_if_inexistent)
4139 TimeTaker timer1("initialEmerge", &emerge_time);
4141 // Units of these are MapBlocks
4142 v3s16 p_min = blockpos_min;
4143 v3s16 p_max = blockpos_max;
4145 VoxelArea block_area_nodes
4146 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4148 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4151 infostream<<"initialEmerge: area: ";
4152 block_area_nodes.print(infostream);
4153 infostream<<" ("<<size_MB<<"MB)";
4154 infostream<<std::endl;
4157 addArea(block_area_nodes);
4159 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4160 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4161 for(s32 x=p_min.X; x<=p_max.X; x++)
4166 std::map<v3s16, u8>::iterator n;
4167 n = m_loaded_blocks.find(p);
4168 if(n != m_loaded_blocks.end())
4171 bool block_data_inexistent = false;
4174 TimeTaker timer1("emerge load", &emerge_load_time);
4176 block = m_map->getBlockNoCreate(p);
4177 if(block->isDummy())
4178 block_data_inexistent = true;
4180 block->copyTo(*this);
4182 catch(InvalidPositionException &e)
4184 block_data_inexistent = true;
4187 if(block_data_inexistent)
4190 if (load_if_inexistent) {
4191 ServerMap *svrmap = (ServerMap *)m_map;
4192 block = svrmap->emergeBlock(p, false);
4194 block = svrmap->createBlock(p);
4196 block->copyTo(*this);
4198 flags |= VMANIP_BLOCK_DATA_INEXIST;
4201 Mark area inexistent
4203 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4204 // Fill with VOXELFLAG_INEXISTENT
4205 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4206 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4208 s32 i = m_area.index(a.MinEdge.X,y,z);
4209 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4213 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4215 // Mark that block was loaded as blank
4216 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4219 m_loaded_blocks[p] = flags;
4223 void ManualMapVoxelManipulator::blitBackAll(
4224 std::map<v3s16, MapBlock*> * modified_blocks)
4226 if(m_area.getExtent() == v3s16(0,0,0))
4230 Copy data of all blocks
4232 for(std::map<v3s16, u8>::iterator
4233 i = m_loaded_blocks.begin();
4234 i != m_loaded_blocks.end(); ++i)
4237 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4238 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4239 if(existed == false)
4244 block->copyFrom(*this);
4247 (*modified_blocks)[p] = block;