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"
42 #include "database-dummy.h"
43 #include "database-sqlite3.h"
45 #include "database-leveldb.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 SQLite format specification:
52 - Initially only replaces sectors/ and sectors2/
54 If map.sqlite does not exist in the save dir
55 or the block was not found in the database
56 the map will try to load from sectors folder.
57 In either case, map.sqlite will be created
58 and all future saves will save there.
60 Structure of map.sqlite:
71 Map::Map(std::ostream &dout, IGameDef *gamedef):
76 /*m_sector_mutex.Init();
77 assert(m_sector_mutex.IsInitialized());*/
85 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
86 i != m_sectors.end(); ++i)
92 void Map::addEventReceiver(MapEventReceiver *event_receiver)
94 m_event_receivers.insert(event_receiver);
97 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
99 m_event_receivers.erase(event_receiver);
102 void Map::dispatchEvent(MapEditEvent *event)
104 for(std::set<MapEventReceiver*>::iterator
105 i = m_event_receivers.begin();
106 i != m_event_receivers.end(); ++i)
108 (*i)->onMapEditEvent(event);
112 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
114 if(m_sector_cache != NULL && p == m_sector_cache_p){
115 MapSector * sector = m_sector_cache;
119 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
121 if(n == m_sectors.end())
124 MapSector *sector = n->second;
126 // Cache the last result
127 m_sector_cache_p = p;
128 m_sector_cache = sector;
133 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
135 return getSectorNoGenerateNoExNoLock(p);
138 MapSector * Map::getSectorNoGenerate(v2s16 p)
140 MapSector *sector = getSectorNoGenerateNoEx(p);
142 throw InvalidPositionException();
147 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
149 v2s16 p2d(p3d.X, p3d.Z);
150 MapSector * sector = getSectorNoGenerateNoEx(p2d);
153 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
157 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
159 MapBlock *block = getBlockNoCreateNoEx(p3d);
161 throw InvalidPositionException();
165 bool Map::isNodeUnderground(v3s16 p)
167 v3s16 blockpos = getNodeBlockPos(p);
169 MapBlock * block = getBlockNoCreate(blockpos);
170 return block->getIsUnderground();
172 catch(InvalidPositionException &e)
178 bool Map::isValidPosition(v3s16 p)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreate(blockpos);
182 return (block != NULL);
185 // Returns a CONTENT_IGNORE node if not found
186 MapNode Map::getNodeNoEx(v3s16 p)
188 v3s16 blockpos = getNodeBlockPos(p);
189 MapBlock *block = getBlockNoCreateNoEx(blockpos);
191 return MapNode(CONTENT_IGNORE);
192 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
193 return block->getNodeNoCheck(relpos);
196 // throws InvalidPositionException if not found
197 MapNode Map::getNode(v3s16 p)
199 v3s16 blockpos = getNodeBlockPos(p);
200 MapBlock *block = getBlockNoCreateNoEx(blockpos);
202 throw InvalidPositionException();
203 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
204 return block->getNodeNoCheck(relpos);
207 // throws InvalidPositionException if not found
208 void Map::setNode(v3s16 p, MapNode & n)
210 v3s16 blockpos = getNodeBlockPos(p);
211 MapBlock *block = getBlockNoCreate(blockpos);
212 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
213 // Never allow placing CONTENT_IGNORE, it fucks up stuff
214 if(n.getContent() == CONTENT_IGNORE){
215 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
216 <<" while trying to replace \""
217 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
218 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
219 debug_stacks_print_to(infostream);
222 block->setNodeNoCheck(relpos, n);
227 Goes recursively through the neighbours of the node.
229 Alters only transparent nodes.
231 If the lighting of the neighbour is lower than the lighting of
232 the node was (before changing it to 0 at the step before), the
233 lighting of the neighbour is set to 0 and then the same stuff
234 repeats for the neighbour.
236 The ending nodes of the routine are stored in light_sources.
237 This is useful when a light is removed. In such case, this
238 routine can be called for the light node and then again for
239 light_sources to re-light the area without the removed light.
241 values of from_nodes are lighting values.
243 void Map::unspreadLight(enum LightBank bank,
244 std::map<v3s16, u8> & from_nodes,
245 std::set<v3s16> & light_sources,
246 std::map<v3s16, MapBlock*> & modified_blocks)
248 INodeDefManager *nodemgr = m_gamedef->ndef();
251 v3s16(0,0,1), // back
253 v3s16(1,0,0), // right
254 v3s16(0,0,-1), // front
255 v3s16(0,-1,0), // bottom
256 v3s16(-1,0,0), // left
259 if(from_nodes.size() == 0)
262 u32 blockchangecount = 0;
264 std::map<v3s16, u8> unlighted_nodes;
267 Initialize block cache
270 MapBlock *block = NULL;
271 // Cache this a bit, too
272 bool block_checked_in_modified = false;
274 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
275 j != from_nodes.end(); ++j)
277 v3s16 pos = j->first;
278 v3s16 blockpos = getNodeBlockPos(pos);
280 // Only fetch a new block if the block position has changed
282 if(block == NULL || blockpos != blockpos_last){
283 block = getBlockNoCreate(blockpos);
284 blockpos_last = blockpos;
286 block_checked_in_modified = false;
290 catch(InvalidPositionException &e)
298 // Calculate relative position in block
299 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
301 // Get node straight from the block
302 //MapNode n = block->getNode(relpos);
304 u8 oldlight = j->second;
306 // Loop through 6 neighbors
307 for(u16 i=0; i<6; i++)
309 // Get the position of the neighbor node
310 v3s16 n2pos = pos + dirs[i];
312 // Get the block where the node is located
313 v3s16 blockpos = getNodeBlockPos(n2pos);
317 // Only fetch a new block if the block position has changed
319 if(block == NULL || blockpos != blockpos_last){
320 block = getBlockNoCreate(blockpos);
321 blockpos_last = blockpos;
323 block_checked_in_modified = false;
327 catch(InvalidPositionException &e)
332 // Calculate relative position in block
333 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
334 // Get node straight from the block
335 MapNode n2 = block->getNode(relpos);
337 bool changed = false;
339 //TODO: Optimize output by optimizing light_sources?
342 If the neighbor is dimmer than what was specified
343 as oldlight (the light of the previous node)
345 if(n2.getLight(bank, nodemgr) < oldlight)
348 And the neighbor is transparent and it has some light
350 if(nodemgr->get(n2).light_propagates
351 && n2.getLight(bank, nodemgr) != 0)
354 Set light to 0 and add to queue
357 u8 current_light = n2.getLight(bank, nodemgr);
358 n2.setLight(bank, 0, nodemgr);
359 block->setNode(relpos, n2);
361 unlighted_nodes[n2pos] = current_light;
365 Remove from light_sources if it is there
366 NOTE: This doesn't happen nearly at all
368 /*if(light_sources.find(n2pos))
370 infostream<<"Removed from light_sources"<<std::endl;
371 light_sources.remove(n2pos);
376 if(light_sources.find(n2pos) != NULL)
377 light_sources.remove(n2pos);*/
380 light_sources.insert(n2pos);
383 // Add to modified_blocks
384 if(changed == true && block_checked_in_modified == false)
386 // If the block is not found in modified_blocks, add.
387 if(modified_blocks.find(blockpos) == modified_blocks.end())
389 modified_blocks[blockpos] = block;
391 block_checked_in_modified = true;
394 catch(InvalidPositionException &e)
401 /*infostream<<"unspreadLight(): Changed block "
402 <<blockchangecount<<" times"
403 <<" for "<<from_nodes.size()<<" nodes"
406 if(unlighted_nodes.size() > 0)
407 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
411 A single-node wrapper of the above
413 void Map::unLightNeighbors(enum LightBank bank,
414 v3s16 pos, u8 lightwas,
415 std::set<v3s16> & light_sources,
416 std::map<v3s16, MapBlock*> & modified_blocks)
418 std::map<v3s16, u8> from_nodes;
419 from_nodes[pos] = lightwas;
421 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
425 Lights neighbors of from_nodes, collects all them and then
428 void Map::spreadLight(enum LightBank bank,
429 std::set<v3s16> & from_nodes,
430 std::map<v3s16, MapBlock*> & modified_blocks)
432 INodeDefManager *nodemgr = m_gamedef->ndef();
434 const v3s16 dirs[6] = {
435 v3s16(0,0,1), // back
437 v3s16(1,0,0), // right
438 v3s16(0,0,-1), // front
439 v3s16(0,-1,0), // bottom
440 v3s16(-1,0,0), // left
443 if(from_nodes.size() == 0)
446 u32 blockchangecount = 0;
448 std::set<v3s16> lighted_nodes;
451 Initialize block cache
454 MapBlock *block = NULL;
455 // Cache this a bit, too
456 bool block_checked_in_modified = false;
458 for(std::set<v3s16>::iterator j = from_nodes.begin();
459 j != from_nodes.end(); ++j)
462 v3s16 blockpos = getNodeBlockPos(pos);
464 // Only fetch a new block if the block position has changed
466 if(block == NULL || blockpos != blockpos_last){
467 block = getBlockNoCreate(blockpos);
468 blockpos_last = blockpos;
470 block_checked_in_modified = false;
474 catch(InvalidPositionException &e)
482 // Calculate relative position in block
483 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
485 // Get node straight from the block
486 MapNode n = block->getNode(relpos);
488 u8 oldlight = n.getLight(bank, nodemgr);
489 u8 newlight = diminish_light(oldlight);
491 // Loop through 6 neighbors
492 for(u16 i=0; i<6; i++){
493 // Get the position of the neighbor node
494 v3s16 n2pos = pos + dirs[i];
496 // Get the block where the node is located
497 v3s16 blockpos = getNodeBlockPos(n2pos);
501 // Only fetch a new block if the block position has changed
503 if(block == NULL || blockpos != blockpos_last){
504 block = getBlockNoCreate(blockpos);
505 blockpos_last = blockpos;
507 block_checked_in_modified = false;
511 catch(InvalidPositionException &e)
516 // Calculate relative position in block
517 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
518 // Get node straight from the block
519 MapNode n2 = block->getNode(relpos);
521 bool changed = false;
523 If the neighbor is brighter than the current node,
524 add to list (it will light up this node on its turn)
526 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
528 lighted_nodes.insert(n2pos);
532 If the neighbor is dimmer than how much light this node
533 would spread on it, add to list
535 if(n2.getLight(bank, nodemgr) < newlight)
537 if(nodemgr->get(n2).light_propagates)
539 n2.setLight(bank, newlight, nodemgr);
540 block->setNode(relpos, n2);
541 lighted_nodes.insert(n2pos);
546 // Add to modified_blocks
547 if(changed == true && block_checked_in_modified == false)
549 // If the block is not found in modified_blocks, add.
550 if(modified_blocks.find(blockpos) == modified_blocks.end())
552 modified_blocks[blockpos] = block;
554 block_checked_in_modified = true;
557 catch(InvalidPositionException &e)
564 /*infostream<<"spreadLight(): Changed block "
565 <<blockchangecount<<" times"
566 <<" for "<<from_nodes.size()<<" nodes"
569 if(lighted_nodes.size() > 0)
570 spreadLight(bank, lighted_nodes, modified_blocks);
574 A single-node source variation of the above.
576 void Map::lightNeighbors(enum LightBank bank,
578 std::map<v3s16, MapBlock*> & modified_blocks)
580 std::set<v3s16> from_nodes;
581 from_nodes.insert(pos);
582 spreadLight(bank, from_nodes, modified_blocks);
585 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
587 INodeDefManager *nodemgr = m_gamedef->ndef();
590 v3s16(0,0,1), // back
592 v3s16(1,0,0), // right
593 v3s16(0,0,-1), // front
594 v3s16(0,-1,0), // bottom
595 v3s16(-1,0,0), // left
598 u8 brightest_light = 0;
599 v3s16 brightest_pos(0,0,0);
600 bool found_something = false;
602 // Loop through 6 neighbors
603 for(u16 i=0; i<6; i++){
604 // Get the position of the neighbor node
605 v3s16 n2pos = p + dirs[i];
610 catch(InvalidPositionException &e)
614 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
615 brightest_light = n2.getLight(bank, nodemgr);
616 brightest_pos = n2pos;
617 found_something = true;
621 if(found_something == false)
622 throw InvalidPositionException();
624 return brightest_pos;
628 Propagates sunlight down from a node.
629 Starting point gets sunlight.
631 Returns the lowest y value of where the sunlight went.
633 Mud is turned into grass in where the sunlight stops.
635 s16 Map::propagateSunlight(v3s16 start,
636 std::map<v3s16, MapBlock*> & modified_blocks)
638 INodeDefManager *nodemgr = m_gamedef->ndef();
643 v3s16 pos(start.X, y, start.Z);
645 v3s16 blockpos = getNodeBlockPos(pos);
648 block = getBlockNoCreate(blockpos);
650 catch(InvalidPositionException &e)
655 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
656 MapNode n = block->getNode(relpos);
658 if(nodemgr->get(n).sunlight_propagates)
660 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
661 block->setNode(relpos, n);
663 modified_blocks[blockpos] = block;
667 // Sunlight goes no further
674 void Map::updateLighting(enum LightBank bank,
675 std::map<v3s16, MapBlock*> & a_blocks,
676 std::map<v3s16, MapBlock*> & modified_blocks)
678 INodeDefManager *nodemgr = m_gamedef->ndef();
680 /*m_dout<<DTIME<<"Map::updateLighting(): "
681 <<a_blocks.size()<<" blocks."<<std::endl;*/
683 //TimeTaker timer("updateLighting");
687 //u32 count_was = modified_blocks.size();
689 std::map<v3s16, MapBlock*> blocks_to_update;
691 std::set<v3s16> light_sources;
693 std::map<v3s16, u8> unlight_from;
695 int num_bottom_invalid = 0;
698 //TimeTaker t("first stuff");
700 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
701 i != a_blocks.end(); ++i)
703 MapBlock *block = i->second;
707 // Don't bother with dummy blocks.
711 v3s16 pos = block->getPos();
712 v3s16 posnodes = block->getPosRelative();
713 modified_blocks[pos] = block;
714 blocks_to_update[pos] = block;
717 Clear all light from block
719 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
720 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
721 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
726 MapNode n = block->getNode(p);
727 u8 oldlight = n.getLight(bank, nodemgr);
728 n.setLight(bank, 0, nodemgr);
729 block->setNode(p, n);
731 // If node sources light, add to list
732 u8 source = nodemgr->get(n).light_source;
734 light_sources.insert(p + posnodes);
736 // Collect borders for unlighting
737 if((x==0 || x == MAP_BLOCKSIZE-1
738 || y==0 || y == MAP_BLOCKSIZE-1
739 || z==0 || z == MAP_BLOCKSIZE-1)
742 v3s16 p_map = p + posnodes;
743 unlight_from[p_map] = oldlight;
746 catch(InvalidPositionException &e)
749 This would happen when dealing with a
753 infostream<<"updateLighting(): InvalidPositionException"
758 if(bank == LIGHTBANK_DAY)
760 bool bottom_valid = block->propagateSunlight(light_sources);
763 num_bottom_invalid++;
765 // If bottom is valid, we're done.
769 else if(bank == LIGHTBANK_NIGHT)
771 // For night lighting, sunlight is not propagated
776 // Invalid lighting bank
780 /*infostream<<"Bottom for sunlight-propagated block ("
781 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
784 // Bottom sunlight is not valid; get the block and loop to it
788 block = getBlockNoCreate(pos);
790 catch(InvalidPositionException &e)
801 Enable this to disable proper lighting for speeding up map
802 generation for testing or whatever
805 //if(g_settings->get(""))
807 core::map<v3s16, MapBlock*>::Iterator i;
808 i = blocks_to_update.getIterator();
809 for(; i.atEnd() == false; i++)
811 MapBlock *block = i.getNode()->getValue();
812 v3s16 p = block->getPos();
813 block->setLightingExpired(false);
821 //TimeTaker timer("unspreadLight");
822 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
827 u32 diff = modified_blocks.size() - count_was;
828 count_was = modified_blocks.size();
829 infostream<<"unspreadLight modified "<<diff<<std::endl;
833 //TimeTaker timer("spreadLight");
834 spreadLight(bank, light_sources, modified_blocks);
839 u32 diff = modified_blocks.size() - count_was;
840 count_was = modified_blocks.size();
841 infostream<<"spreadLight modified "<<diff<<std::endl;
847 //MapVoxelManipulator vmanip(this);
849 // Make a manual voxel manipulator and load all the blocks
850 // that touch the requested blocks
851 ManualMapVoxelManipulator vmanip(this);
854 //TimeTaker timer("initialEmerge");
856 core::map<v3s16, MapBlock*>::Iterator i;
857 i = blocks_to_update.getIterator();
858 for(; i.atEnd() == false; i++)
860 MapBlock *block = i.getNode()->getValue();
861 v3s16 p = block->getPos();
863 // Add all surrounding blocks
864 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
867 Add all surrounding blocks that have up-to-date lighting
868 NOTE: This doesn't quite do the job (not everything
869 appropriate is lighted)
871 /*for(s16 z=-1; z<=1; z++)
872 for(s16 y=-1; y<=1; y++)
873 for(s16 x=-1; x<=1; x++)
875 v3s16 p2 = p + v3s16(x,y,z);
876 MapBlock *block = getBlockNoCreateNoEx(p2);
881 if(block->getLightingExpired())
883 vmanip.initialEmerge(p2, p2);
886 // Lighting of block will be updated completely
887 block->setLightingExpired(false);
892 //TimeTaker timer("unSpreadLight");
893 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
896 //TimeTaker timer("spreadLight");
897 vmanip.spreadLight(bank, light_sources, nodemgr);
900 //TimeTaker timer("blitBack");
901 vmanip.blitBack(modified_blocks);
903 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
908 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
911 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
912 std::map<v3s16, MapBlock*> & modified_blocks)
914 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
915 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
918 Update information about whether day and night light differ
920 for(std::map<v3s16, MapBlock*>::iterator
921 i = modified_blocks.begin();
922 i != modified_blocks.end(); ++i)
924 MapBlock *block = i->second;
925 block->expireDayNightDiff();
931 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
932 std::map<v3s16, MapBlock*> &modified_blocks)
934 INodeDefManager *ndef = m_gamedef->ndef();
937 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
938 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
941 From this node to nodes underneath:
942 If lighting is sunlight (1.0), unlight neighbours and
947 v3s16 toppos = p + v3s16(0,1,0);
948 //v3s16 bottompos = p + v3s16(0,-1,0);
950 bool node_under_sunlight = true;
951 std::set<v3s16> light_sources;
954 Collect old node for rollback
956 RollbackNode rollback_oldnode(this, p, m_gamedef);
959 If there is a node at top and it doesn't have sunlight,
960 there has not been any sunlight going down.
962 Otherwise there probably is.
965 MapNode topnode = getNode(toppos);
967 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
968 node_under_sunlight = false;
970 catch(InvalidPositionException &e)
975 Remove all light that has come out of this node
978 enum LightBank banks[] =
983 for(s32 i=0; i<2; i++)
985 enum LightBank bank = banks[i];
987 u8 lightwas = getNode(p).getLight(bank, ndef);
989 // Add the block of the added node to modified_blocks
990 v3s16 blockpos = getNodeBlockPos(p);
991 MapBlock * block = getBlockNoCreate(blockpos);
992 assert(block != NULL);
993 modified_blocks[blockpos] = block;
995 assert(isValidPosition(p));
997 // Unlight neighbours of node.
998 // This means setting light of all consequent dimmer nodes
1000 // This also collects the nodes at the border which will spread
1001 // light again into this.
1002 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1004 n.setLight(bank, 0, ndef);
1008 If node lets sunlight through and is under sunlight, it has
1011 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1013 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1017 Remove node metadata
1020 removeNodeMetadata(p);
1023 Set the node on the map
1029 If node is under sunlight and doesn't let sunlight through,
1030 take all sunlighted nodes under it and clear light from them
1031 and from where the light has been spread.
1032 TODO: This could be optimized by mass-unlighting instead
1035 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1039 //m_dout<<DTIME<<"y="<<y<<std::endl;
1040 v3s16 n2pos(p.X, y, p.Z);
1044 n2 = getNode(n2pos);
1046 catch(InvalidPositionException &e)
1051 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1053 unLightNeighbors(LIGHTBANK_DAY,
1054 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1055 light_sources, modified_blocks);
1056 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1064 for(s32 i=0; i<2; i++)
1066 enum LightBank bank = banks[i];
1069 Spread light from all nodes that might be capable of doing so
1071 spreadLight(bank, light_sources, modified_blocks);
1075 Update information about whether day and night light differ
1077 for(std::map<v3s16, MapBlock*>::iterator
1078 i = modified_blocks.begin();
1079 i != modified_blocks.end(); ++i)
1081 i->second->expireDayNightDiff();
1087 if(m_gamedef->rollback())
1089 RollbackNode rollback_newnode(this, p, m_gamedef);
1090 RollbackAction action;
1091 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1092 m_gamedef->rollback()->reportAction(action);
1096 Add neighboring liquid nodes and the node itself if it is
1097 liquid (=water node was added) to transform queue.
1098 note: todo: for liquid_finite enough to add only self node
1101 v3s16(0,0,0), // self
1102 v3s16(0,0,1), // back
1103 v3s16(0,1,0), // top
1104 v3s16(1,0,0), // right
1105 v3s16(0,0,-1), // front
1106 v3s16(0,-1,0), // bottom
1107 v3s16(-1,0,0), // left
1109 for(u16 i=0; i<7; i++)
1114 v3s16 p2 = p + dirs[i];
1116 MapNode n2 = getNode(p2);
1117 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1119 m_transforming_liquid.push_back(p2);
1122 }catch(InvalidPositionException &e)
1130 void Map::removeNodeAndUpdate(v3s16 p,
1131 std::map<v3s16, MapBlock*> &modified_blocks)
1133 INodeDefManager *ndef = m_gamedef->ndef();
1135 /*PrintInfo(m_dout);
1136 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1137 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1139 bool node_under_sunlight = true;
1141 v3s16 toppos = p + v3s16(0,1,0);
1143 // Node will be replaced with this
1144 content_t replace_material = CONTENT_AIR;
1147 Collect old node for rollback
1149 RollbackNode rollback_oldnode(this, p, m_gamedef);
1152 If there is a node at top and it doesn't have sunlight,
1153 there will be no sunlight going down.
1156 MapNode topnode = getNode(toppos);
1158 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1159 node_under_sunlight = false;
1161 catch(InvalidPositionException &e)
1165 std::set<v3s16> light_sources;
1167 enum LightBank banks[] =
1172 for(s32 i=0; i<2; i++)
1174 enum LightBank bank = banks[i];
1177 Unlight neighbors (in case the node is a light source)
1179 unLightNeighbors(bank, p,
1180 getNode(p).getLight(bank, ndef),
1181 light_sources, modified_blocks);
1185 Remove node metadata
1188 removeNodeMetadata(p);
1192 This also clears the lighting.
1196 n.setContent(replace_material);
1199 for(s32 i=0; i<2; i++)
1201 enum LightBank bank = banks[i];
1204 Recalculate lighting
1206 spreadLight(bank, light_sources, modified_blocks);
1209 // Add the block of the removed node to modified_blocks
1210 v3s16 blockpos = getNodeBlockPos(p);
1211 MapBlock * block = getBlockNoCreate(blockpos);
1212 assert(block != NULL);
1213 modified_blocks[blockpos] = block;
1216 If the removed node was under sunlight, propagate the
1217 sunlight down from it and then light all neighbors
1218 of the propagated blocks.
1220 if(node_under_sunlight)
1222 s16 ybottom = propagateSunlight(p, modified_blocks);
1223 /*m_dout<<DTIME<<"Node was under sunlight. "
1224 "Propagating sunlight";
1225 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1227 for(; y >= ybottom; y--)
1229 v3s16 p2(p.X, y, p.Z);
1230 /*m_dout<<DTIME<<"lighting neighbors of node ("
1231 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1233 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1238 // Set the lighting of this node to 0
1239 // TODO: Is this needed? Lighting is cleared up there already.
1241 MapNode n = getNode(p);
1242 n.setLight(LIGHTBANK_DAY, 0, ndef);
1245 catch(InvalidPositionException &e)
1251 for(s32 i=0; i<2; i++)
1253 enum LightBank bank = banks[i];
1255 // Get the brightest neighbour node and propagate light from it
1256 v3s16 n2p = getBrightestNeighbour(bank, p);
1258 //MapNode n2 = getNode(n2p);
1259 lightNeighbors(bank, n2p, modified_blocks);
1261 catch(InvalidPositionException &e)
1267 Update information about whether day and night light differ
1269 for(std::map<v3s16, MapBlock*>::iterator
1270 i = modified_blocks.begin();
1271 i != modified_blocks.end(); ++i)
1273 i->second->expireDayNightDiff();
1279 if(m_gamedef->rollback())
1281 RollbackNode rollback_newnode(this, p, m_gamedef);
1282 RollbackAction action;
1283 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1284 m_gamedef->rollback()->reportAction(action);
1288 Add neighboring liquid nodes and this node to transform queue.
1289 (it's vital for the node itself to get updated last.)
1290 note: todo: for liquid_finite enough to add only self node
1293 v3s16(0,0,1), // back
1294 v3s16(0,1,0), // top
1295 v3s16(1,0,0), // right
1296 v3s16(0,0,-1), // front
1297 v3s16(0,-1,0), // bottom
1298 v3s16(-1,0,0), // left
1299 v3s16(0,0,0), // self
1301 for(u16 i=0; i<7; i++)
1306 v3s16 p2 = p + dirs[i];
1308 MapNode n2 = getNode(p2);
1309 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1311 m_transforming_liquid.push_back(p2);
1314 }catch(InvalidPositionException &e)
1320 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1323 event.type = MEET_ADDNODE;
1327 bool succeeded = true;
1329 std::map<v3s16, MapBlock*> modified_blocks;
1330 addNodeAndUpdate(p, n, modified_blocks);
1332 // Copy modified_blocks to event
1333 for(std::map<v3s16, MapBlock*>::iterator
1334 i = modified_blocks.begin();
1335 i != modified_blocks.end(); ++i)
1337 event.modified_blocks.insert(i->first);
1340 catch(InvalidPositionException &e){
1344 dispatchEvent(&event);
1349 bool Map::removeNodeWithEvent(v3s16 p)
1352 event.type = MEET_REMOVENODE;
1355 bool succeeded = true;
1357 std::map<v3s16, MapBlock*> modified_blocks;
1358 removeNodeAndUpdate(p, modified_blocks);
1360 // Copy modified_blocks to event
1361 for(std::map<v3s16, MapBlock*>::iterator
1362 i = modified_blocks.begin();
1363 i != modified_blocks.end(); ++i)
1365 event.modified_blocks.insert(i->first);
1368 catch(InvalidPositionException &e){
1372 dispatchEvent(&event);
1377 bool Map::getDayNightDiff(v3s16 blockpos)
1380 v3s16 p = blockpos + v3s16(0,0,0);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->getDayNightDiff())
1385 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(-1,0,0);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->getDayNightDiff())
1393 catch(InvalidPositionException &e){}
1395 v3s16 p = blockpos + v3s16(0,-1,0);
1396 MapBlock *b = getBlockNoCreate(p);
1397 if(b->getDayNightDiff())
1400 catch(InvalidPositionException &e){}
1402 v3s16 p = blockpos + v3s16(0,0,-1);
1403 MapBlock *b = getBlockNoCreate(p);
1404 if(b->getDayNightDiff())
1407 catch(InvalidPositionException &e){}
1410 v3s16 p = blockpos + v3s16(1,0,0);
1411 MapBlock *b = getBlockNoCreate(p);
1412 if(b->getDayNightDiff())
1415 catch(InvalidPositionException &e){}
1417 v3s16 p = blockpos + v3s16(0,1,0);
1418 MapBlock *b = getBlockNoCreate(p);
1419 if(b->getDayNightDiff())
1422 catch(InvalidPositionException &e){}
1424 v3s16 p = blockpos + v3s16(0,0,1);
1425 MapBlock *b = getBlockNoCreate(p);
1426 if(b->getDayNightDiff())
1429 catch(InvalidPositionException &e){}
1435 Updates usage timers
1437 void Map::timerUpdate(float dtime, float unload_timeout,
1438 std::list<v3s16> *unloaded_blocks)
1440 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1442 // Profile modified reasons
1443 Profiler modprofiler;
1445 std::list<v2s16> sector_deletion_queue;
1446 u32 deleted_blocks_count = 0;
1447 u32 saved_blocks_count = 0;
1448 u32 block_count_all = 0;
1451 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1452 si != m_sectors.end(); ++si)
1454 MapSector *sector = si->second;
1456 bool all_blocks_deleted = true;
1458 std::list<MapBlock*> blocks;
1459 sector->getBlocks(blocks);
1461 for(std::list<MapBlock*>::iterator i = blocks.begin();
1462 i != blocks.end(); ++i)
1464 MapBlock *block = (*i);
1466 block->incrementUsageTimer(dtime);
1468 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1470 v3s16 p = block->getPos();
1473 if(block->getModified() != MOD_STATE_CLEAN
1474 && save_before_unloading)
1476 modprofiler.add(block->getModifiedReason(), 1);
1478 saved_blocks_count++;
1481 // Delete from memory
1482 sector->deleteBlock(block);
1485 unloaded_blocks->push_back(p);
1487 deleted_blocks_count++;
1491 all_blocks_deleted = false;
1496 if(all_blocks_deleted)
1498 sector_deletion_queue.push_back(si->first);
1503 // Finally delete the empty sectors
1504 deleteSectors(sector_deletion_queue);
1506 if(deleted_blocks_count != 0)
1508 PrintInfo(infostream); // ServerMap/ClientMap:
1509 infostream<<"Unloaded "<<deleted_blocks_count
1510 <<" blocks from memory";
1511 if(save_before_unloading)
1512 infostream<<", of which "<<saved_blocks_count<<" were written";
1513 infostream<<", "<<block_count_all<<" blocks in memory";
1514 infostream<<"."<<std::endl;
1515 if(saved_blocks_count != 0){
1516 PrintInfo(infostream); // ServerMap/ClientMap:
1517 infostream<<"Blocks modified by: "<<std::endl;
1518 modprofiler.print(infostream);
1523 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1525 timerUpdate(0.0, -1.0, unloaded_blocks);
1528 void Map::deleteSectors(std::list<v2s16> &list)
1530 for(std::list<v2s16>::iterator j = list.begin();
1531 j != list.end(); ++j)
1533 MapSector *sector = m_sectors[*j];
1534 // If sector is in sector cache, remove it from there
1535 if(m_sector_cache == sector)
1536 m_sector_cache = NULL;
1537 // Remove from map and delete
1538 m_sectors.erase(*j);
1544 void Map::unloadUnusedData(float timeout,
1545 core::list<v3s16> *deleted_blocks)
1547 core::list<v2s16> sector_deletion_queue;
1548 u32 deleted_blocks_count = 0;
1549 u32 saved_blocks_count = 0;
1551 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1552 for(; si.atEnd() == false; si++)
1554 MapSector *sector = si.getNode()->getValue();
1556 bool all_blocks_deleted = true;
1558 core::list<MapBlock*> blocks;
1559 sector->getBlocks(blocks);
1560 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1561 i != blocks.end(); i++)
1563 MapBlock *block = (*i);
1565 if(block->getUsageTimer() > timeout)
1568 if(block->getModified() != MOD_STATE_CLEAN)
1571 saved_blocks_count++;
1573 // Delete from memory
1574 sector->deleteBlock(block);
1575 deleted_blocks_count++;
1579 all_blocks_deleted = false;
1583 if(all_blocks_deleted)
1585 sector_deletion_queue.push_back(si.getNode()->getKey());
1589 deleteSectors(sector_deletion_queue);
1591 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1592 <<", of which "<<saved_blocks_count<<" were wr."
1595 //return sector_deletion_queue.getSize();
1596 //return deleted_blocks_count;
1600 void Map::PrintInfo(std::ostream &out)
1605 #define WATER_DROP_BOOST 4
1609 NEIGHBOR_SAME_LEVEL,
1612 struct NodeNeighbor {
1616 bool l; //can liquid
1620 void Map::transforming_liquid_add(v3s16 p) {
1621 m_transforming_liquid.push_back(p);
1624 s32 Map::transforming_liquid_size() {
1625 return m_transforming_liquid.size();
1628 const v3s16 g_7dirs[7] =
1630 // +right, +top, +back
1631 v3s16( 0,-1, 0), // bottom
1632 v3s16( 0, 0, 0), // self
1633 v3s16( 0, 0, 1), // back
1634 v3s16( 0, 0,-1), // front
1635 v3s16( 1, 0, 0), // right
1636 v3s16(-1, 0, 0), // left
1637 v3s16( 0, 1, 0) // top
1644 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1646 INodeDefManager *nodemgr = m_gamedef->ndef();
1648 DSTACK(__FUNCTION_NAME);
1649 //TimeTaker timer("transformLiquids()");
1652 u32 initial_size = m_transforming_liquid.size();
1654 u8 relax = g_settings->getS16("liquid_relax");
1655 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1656 int water_level = g_settings->getS16("water_level");
1658 // list of nodes that due to viscosity have not reached their max level height
1659 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1661 // List of MapBlocks that will require a lighting update (due to lava)
1662 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1664 u16 loop_max = g_settings->getU16("liquid_loop_max");
1666 //if (m_transforming_liquid.size() > 0) errorstream << "Liquid queue size="<<m_transforming_liquid.size()<<std::endl;
1668 while (m_transforming_liquid.size() > 0)
1670 // This should be done here so that it is done when continue is used
1671 if (loopcount >= initial_size || loopcount >= loop_max)
1675 Get a queued transforming liquid node
1677 v3s16 p0 = m_transforming_liquid.pop_front();
1678 u16 total_level = 0;
1679 // surrounding flowing liquid nodes
1680 NodeNeighbor neighbors[7];
1681 // current level of every block
1682 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1684 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1685 s8 can_liquid_same_level = 0;
1686 content_t liquid_kind = CONTENT_IGNORE;
1687 content_t liquid_kind_flowing = CONTENT_IGNORE;
1689 Collect information about the environment
1691 const v3s16 *dirs = g_7dirs;
1692 for (u16 i = 0; i < 7; i++) {
1693 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1696 nt = NEIGHBOR_UPPER;
1699 nt = NEIGHBOR_LOWER;
1702 v3s16 npos = p0 + dirs[i];
1704 neighbors[i].n = getNodeNoEx(npos);
1705 neighbors[i].t = nt;
1706 neighbors[i].p = npos;
1709 NodeNeighbor & nb = neighbors[i];
1711 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1713 if (nb.n.getContent() == CONTENT_AIR) {
1714 liquid_levels[i] = 0;
1719 // if this node is not (yet) of a liquid type,
1720 // choose the first liquid type we encounter
1721 if (liquid_kind_flowing == CONTENT_IGNORE)
1722 liquid_kind_flowing = nodemgr->getId(
1723 nodemgr->get(nb.n).liquid_alternative_flowing);
1724 if (liquid_kind == CONTENT_IGNORE)
1725 liquid_kind = nb.n.getContent();
1726 if (nb.n.getContent() == liquid_kind) {
1727 liquid_levels[i] = nb.n.getLevel(nodemgr); //LIQUID_LEVEL_SOURCE;
1729 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1732 case LIQUID_FLOWING:
1733 // if this node is not (yet) of a liquid type,
1734 // choose the first liquid type we encounter
1735 if (liquid_kind_flowing == CONTENT_IGNORE)
1736 liquid_kind_flowing = nb.n.getContent();
1737 if (liquid_kind == CONTENT_IGNORE)
1738 liquid_kind = nodemgr->getId(
1739 nodemgr->get(nb.n).liquid_alternative_source);
1740 if (nb.n.getContent() == liquid_kind_flowing) {
1741 liquid_levels[i] = nb.n.getLevel(nodemgr); //(nb.n.param2 & LIQUID_LEVEL_MASK);
1747 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1748 ++can_liquid_same_level;
1749 if (liquid_levels[i] > 0)
1750 total_level += liquid_levels[i];
1753 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1754 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1755 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1756 << nodemgr->get(nb.n.getContent()).liquid_type
1757 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1758 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1759 << " tlevel=" << (int)total_level << " cansame="
1760 << (int)can_liquid_same_level << std::endl;
1764 if (liquid_kind == CONTENT_IGNORE ||
1765 !neighbors[D_SELF].l ||
1769 // fill bottom block
1770 if (neighbors[D_BOTTOM].l) {
1771 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1772 LIQUID_LEVEL_SOURCE : total_level;
1773 total_level -= liquid_levels_want[D_BOTTOM];
1777 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1778 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1779 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1780 (can_liquid_same_level - relax) &&
1781 can_liquid_same_level >= relax + 1) {
1782 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1785 // prevent lakes in air above unloaded blocks
1786 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE && !(loopcount % 3)) {
1790 // calculate self level 5 blocks
1792 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1793 ? LIQUID_LEVEL_SOURCE
1794 : total_level / can_liquid_same_level;
1795 total_level -= want_level * can_liquid_same_level;
1798 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1799 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1800 total_level <= (can_liquid_same_level - relax) &&
1801 can_liquid_same_level >= relax + 1) {
1805 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1806 if (!neighbors[ii].l)
1808 liquid_levels_want[ii] = want_level;
1809 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0) {
1810 if (loopcount % 3 || liquid_levels[ii] <= 0){
1811 if (liquid_levels[ii] > liquid_levels_want[ii]) {
1812 ++liquid_levels_want[ii];
1815 } else if (neighbors[ii].l > 0){
1816 ++liquid_levels_want[ii];
1822 for (u16 ii = 0; ii < 7; ++ii) {
1823 if (total_level < 1) break;
1824 if (liquid_levels_want[ii] >= 0 &&
1825 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1826 ++liquid_levels_want[ii];
1831 // fill top block if can
1832 if (neighbors[D_TOP].l) {
1833 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1834 LIQUID_LEVEL_SOURCE : total_level;
1835 total_level -= liquid_levels_want[D_TOP];
1838 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1839 if ( neighbors[ii].i ||
1840 (liquid_levels_want[ii] >= 0 &&
1841 (fast_flood && p0.Y < water_level &&
1842 (initial_size >= 1000
1844 && want_level >= LIQUID_LEVEL_SOURCE/4
1845 && can_liquid_same_level >= 5
1846 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1847 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1850 if (total_level > 0) //|| flowed != volume)
1851 infostream <<" AFTER level=" << (int)total_level
1852 //<< " flowed="<<flowed<< " volume=" << volume
1853 << " wantsame="<<(int)want_level<< " top="
1854 << (int)liquid_levels_want[D_TOP]<< " topwas="
1855 << (int)liquid_levels[D_TOP]<< " bot="
1856 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1860 for (u16 i = 0; i < 7; i++) {
1861 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1863 MapNode & n0 = neighbors[i].n;
1864 p0 = neighbors[i].p;
1866 decide on the type (and possibly level) of the current node
1868 content_t new_node_content;
1869 s8 new_node_level = -1;
1870 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1871 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1872 // amount to gain, limited by viscosity
1873 // must be at least 1 in absolute value
1874 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1875 if (level_inc < -viscosity || level_inc > viscosity)
1876 new_node_level = liquid_levels[i] + level_inc/viscosity;
1877 else if (level_inc < 0)
1878 new_node_level = liquid_levels[i] - 1;
1879 else if (level_inc > 0)
1880 new_node_level = liquid_levels[i] + 1;
1882 new_node_level = liquid_levels_want[i];
1885 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1886 new_node_content = liquid_kind;
1887 else if (new_node_level > 0)
1888 new_node_content = liquid_kind_flowing;
1890 new_node_content = CONTENT_AIR;
1892 // last level must flow down on stairs
1893 if (liquid_levels_want[i] != liquid_levels[i] &&
1894 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1895 new_node_level >= 1 && new_node_level <= 2) {
1896 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1897 if (neighbors[ii].l)
1898 must_reflow_second.push_back(p0 + dirs[ii]);
1903 check if anything has changed.
1904 if not, just continue with the next node.
1908 new_node_content == n0.getContent()
1909 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1910 (n0.getLevel(nodemgr) == (u8)new_node_level
1911 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1912 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1915 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1916 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1917 LIQUID_INFINITY_MASK) == neighbors[i].i
1920 if (liquid_levels[i] == new_node_level)
1928 update the current node
1931 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1932 // set level to last 3 bits, flowing down bit to 4th bit
1933 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1934 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1935 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1936 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1940 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1941 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1942 <<(int)new_node_level<<std::endl;
1945 n0.setContent(liquid_kind_flowing);
1946 n0.setLevel(nodemgr, new_node_level);
1947 // Find out whether there is a suspect for this action
1948 std::string suspect;
1949 if(m_gamedef->rollback()){
1950 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1953 if(!suspect.empty()){
1955 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1956 // Get old node for rollback
1957 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1961 RollbackNode rollback_newnode(this, p0, m_gamedef);
1962 RollbackAction action;
1963 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1964 m_gamedef->rollback()->reportAction(action);
1970 v3s16 blockpos = getNodeBlockPos(p0);
1971 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1973 modified_blocks[blockpos] = block;
1974 // If node emits light, MapBlock requires lighting update
1975 if(nodemgr->get(n0).light_source != 0)
1976 lighting_modified_blocks[block->getPos()] = block;
1978 must_reflow.push_back(neighbors[i].p);
1980 /* //for better relax only same level
1981 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1982 if (!neighbors[ii].l) continue;
1983 must_reflow.push_back(p0 + dirs[ii]);
1988 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1989 <<" reflow="<<must_reflow.size()
1990 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1992 while (must_reflow.size() > 0)
1993 m_transforming_liquid.push_back(must_reflow.pop_front());
1994 while (must_reflow_second.size() > 0)
1995 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1996 updateLighting(lighting_modified_blocks, modified_blocks);
1999 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
2002 if (g_settings->getBool("liquid_finite"))
2003 return Map::transformLiquidsFinite(modified_blocks);
2005 INodeDefManager *nodemgr = m_gamedef->ndef();
2007 DSTACK(__FUNCTION_NAME);
2008 //TimeTaker timer("transformLiquids()");
2011 u32 initial_size = m_transforming_liquid.size();
2013 /*if(initial_size != 0)
2014 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
2016 // list of nodes that due to viscosity have not reached their max level height
2017 UniqueQueue<v3s16> must_reflow;
2019 // List of MapBlocks that will require a lighting update (due to lava)
2020 std::map<v3s16, MapBlock*> lighting_modified_blocks;
2022 u16 loop_max = g_settings->getU16("liquid_loop_max");
2024 while(m_transforming_liquid.size() != 0)
2026 // This should be done here so that it is done when continue is used
2027 if(loopcount >= initial_size || loopcount >= loop_max)
2032 Get a queued transforming liquid node
2034 v3s16 p0 = m_transforming_liquid.pop_front();
2036 MapNode n0 = getNodeNoEx(p0);
2039 Collect information about current node
2041 s8 liquid_level = -1;
2042 content_t liquid_kind = CONTENT_IGNORE;
2043 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2044 switch (liquid_type) {
2046 liquid_level = LIQUID_LEVEL_SOURCE;
2047 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2049 case LIQUID_FLOWING:
2050 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2051 liquid_kind = n0.getContent();
2054 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2055 // continue with the next node.
2056 if (n0.getContent() != CONTENT_AIR)
2058 liquid_kind = CONTENT_AIR;
2063 Collect information about the environment
2065 const v3s16 *dirs = g_6dirs;
2066 NodeNeighbor sources[6]; // surrounding sources
2067 int num_sources = 0;
2068 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2070 NodeNeighbor airs[6]; // surrounding air
2072 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2073 int num_neutrals = 0;
2074 bool flowing_down = false;
2075 for (u16 i = 0; i < 6; i++) {
2076 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2079 nt = NEIGHBOR_UPPER;
2082 nt = NEIGHBOR_LOWER;
2085 v3s16 npos = p0 + dirs[i];
2086 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2087 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2089 if (nb.n.getContent() == CONTENT_AIR) {
2090 airs[num_airs++] = nb;
2091 // if the current node is a water source the neighbor
2092 // should be enqueded for transformation regardless of whether the
2093 // current node changes or not.
2094 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2095 m_transforming_liquid.push_back(npos);
2096 // if the current node happens to be a flowing node, it will start to flow down here.
2097 if (nb.t == NEIGHBOR_LOWER) {
2098 flowing_down = true;
2101 neutrals[num_neutrals++] = nb;
2105 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2106 if (liquid_kind == CONTENT_AIR)
2107 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2108 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2109 neutrals[num_neutrals++] = nb;
2111 // Do not count bottom source, it will screw things up
2113 sources[num_sources++] = nb;
2116 case LIQUID_FLOWING:
2117 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2118 if (liquid_kind == CONTENT_AIR)
2119 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2120 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2121 neutrals[num_neutrals++] = nb;
2123 flows[num_flows++] = nb;
2124 if (nb.t == NEIGHBOR_LOWER)
2125 flowing_down = true;
2132 decide on the type (and possibly level) of the current node
2134 content_t new_node_content;
2135 s8 new_node_level = -1;
2136 s8 max_node_level = -1;
2137 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2138 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2139 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2140 // it's perfectly safe to use liquid_kind here to determine the new node content.
2141 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2142 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2143 // liquid_kind is set properly, see above
2144 new_node_content = liquid_kind;
2145 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2147 // no surrounding sources, so get the maximum level that can flow into this node
2148 for (u16 i = 0; i < num_flows; i++) {
2149 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2150 switch (flows[i].t) {
2151 case NEIGHBOR_UPPER:
2152 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2153 max_node_level = LIQUID_LEVEL_MAX;
2154 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2155 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2156 } else if (nb_liquid_level > max_node_level)
2157 max_node_level = nb_liquid_level;
2159 case NEIGHBOR_LOWER:
2161 case NEIGHBOR_SAME_LEVEL:
2162 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2163 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2164 max_node_level = nb_liquid_level - 1;
2170 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2171 if (viscosity > 1 && max_node_level != liquid_level) {
2172 // amount to gain, limited by viscosity
2173 // must be at least 1 in absolute value
2174 s8 level_inc = max_node_level - liquid_level;
2175 if (level_inc < -viscosity || level_inc > viscosity)
2176 new_node_level = liquid_level + level_inc/viscosity;
2177 else if (level_inc < 0)
2178 new_node_level = liquid_level - 1;
2179 else if (level_inc > 0)
2180 new_node_level = liquid_level + 1;
2181 if (new_node_level != max_node_level)
2182 must_reflow.push_back(p0);
2184 new_node_level = max_node_level;
2186 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
2187 if (new_node_level >= (LIQUID_LEVEL_MAX+1-range))
2188 new_node_content = liquid_kind;
2190 new_node_content = CONTENT_AIR;
2195 check if anything has changed. if not, just continue with the next node.
2197 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2198 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2199 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2205 update the current node
2208 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2209 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2210 // set level to last 3 bits, flowing down bit to 4th bit
2211 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2213 // set the liquid level and flow bit to 0
2214 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2216 n0.setContent(new_node_content);
2218 // Find out whether there is a suspect for this action
2219 std::string suspect;
2220 if(m_gamedef->rollback()){
2221 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2224 if(!suspect.empty()){
2226 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2227 // Get old node for rollback
2228 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2232 RollbackNode rollback_newnode(this, p0, m_gamedef);
2233 RollbackAction action;
2234 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2235 m_gamedef->rollback()->reportAction(action);
2241 v3s16 blockpos = getNodeBlockPos(p0);
2242 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2244 modified_blocks[blockpos] = block;
2245 // If new or old node emits light, MapBlock requires lighting update
2246 if(nodemgr->get(n0).light_source != 0 ||
2247 nodemgr->get(n00).light_source != 0)
2248 lighting_modified_blocks[block->getPos()] = block;
2252 enqueue neighbors for update if neccessary
2254 switch (nodemgr->get(n0.getContent()).liquid_type) {
2256 case LIQUID_FLOWING:
2257 // make sure source flows into all neighboring nodes
2258 for (u16 i = 0; i < num_flows; i++)
2259 if (flows[i].t != NEIGHBOR_UPPER)
2260 m_transforming_liquid.push_back(flows[i].p);
2261 for (u16 i = 0; i < num_airs; i++)
2262 if (airs[i].t != NEIGHBOR_UPPER)
2263 m_transforming_liquid.push_back(airs[i].p);
2266 // this flow has turned to air; neighboring flows might need to do the same
2267 for (u16 i = 0; i < num_flows; i++)
2268 m_transforming_liquid.push_back(flows[i].p);
2272 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2273 while (must_reflow.size() > 0)
2274 m_transforming_liquid.push_back(must_reflow.pop_front());
2275 updateLighting(lighting_modified_blocks, modified_blocks);
2278 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2280 v3s16 blockpos = getNodeBlockPos(p);
2281 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2282 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2284 infostream<<"Map::getNodeMetadata(): Need to emerge "
2285 <<PP(blockpos)<<std::endl;
2286 block = emergeBlock(blockpos, false);
2290 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2294 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2298 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2300 v3s16 blockpos = getNodeBlockPos(p);
2301 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2302 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2304 infostream<<"Map::setNodeMetadata(): Need to emerge "
2305 <<PP(blockpos)<<std::endl;
2306 block = emergeBlock(blockpos, false);
2310 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2314 block->m_node_metadata.set(p_rel, meta);
2317 void Map::removeNodeMetadata(v3s16 p)
2319 v3s16 blockpos = getNodeBlockPos(p);
2320 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2321 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2324 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2328 block->m_node_metadata.remove(p_rel);
2331 NodeTimer Map::getNodeTimer(v3s16 p)
2333 v3s16 blockpos = getNodeBlockPos(p);
2334 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2335 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2337 infostream<<"Map::getNodeTimer(): Need to emerge "
2338 <<PP(blockpos)<<std::endl;
2339 block = emergeBlock(blockpos, false);
2343 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2347 NodeTimer t = block->m_node_timers.get(p_rel);
2351 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2353 v3s16 blockpos = getNodeBlockPos(p);
2354 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2355 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2357 infostream<<"Map::setNodeTimer(): Need to emerge "
2358 <<PP(blockpos)<<std::endl;
2359 block = emergeBlock(blockpos, false);
2363 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2367 block->m_node_timers.set(p_rel, t);
2370 void Map::removeNodeTimer(v3s16 p)
2372 v3s16 blockpos = getNodeBlockPos(p);
2373 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2374 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2377 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2381 block->m_node_timers.remove(p_rel);
2384 s16 Map::getHeat(v3s16 p)
2386 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2390 //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2394 s16 Map::getHumidity(v3s16 p)
2396 MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2398 return block->humidity;
2400 //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2407 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2408 Map(dout_server, gamedef),
2410 m_map_metadata_changed(true)
2412 verbosestream<<__FUNCTION_NAME<<std::endl;
2415 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2417 m_mgparams = new MapgenV6Params();
2419 m_seed = m_mgparams->seed;
2421 if (g_settings->get("fixed_map_seed").empty())
2423 m_seed = (((u64)(myrand() & 0xffff) << 0)
2424 | ((u64)(myrand() & 0xffff) << 16)
2425 | ((u64)(myrand() & 0xffff) << 32)
2426 | ((u64)(myrand() & 0xffff) << 48));
2427 m_mgparams->seed = m_seed;
2431 Experimental and debug stuff
2438 Try to load map; if not found, create a new one.
2441 // Determine which database backend to use
2442 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2444 bool succeeded = conf.readConfigFile(conf_path.c_str());
2445 if (!succeeded || !conf.exists("backend")) {
2446 // fall back to sqlite3
2447 dbase = new Database_SQLite3(this, savedir);
2448 conf.set("backend", "sqlite3");
2450 std::string backend = conf.get("backend");
2451 if (backend == "dummy")
2452 dbase = new Database_Dummy(this);
2453 else if (backend == "sqlite3")
2454 dbase = new Database_SQLite3(this, savedir);
2456 else if (backend == "leveldb")
2457 dbase = new Database_LevelDB(this, savedir);
2460 throw BaseException("Unknown map backend");
2463 m_savedir = savedir;
2464 m_map_saving_enabled = false;
2468 // If directory exists, check contents and load if possible
2469 if(fs::PathExists(m_savedir))
2471 // If directory is empty, it is safe to save into it.
2472 if(fs::GetDirListing(m_savedir).size() == 0)
2474 infostream<<"ServerMap: Empty save directory is valid."
2476 m_map_saving_enabled = true;
2481 // Load map metadata (seed, chunksize)
2484 catch(SettingNotFoundException &e){
2485 infostream<<"ServerMap: Some metadata not found."
2486 <<" Using default settings."<<std::endl;
2488 catch(FileNotGoodException &e){
2489 infostream<<"WARNING: Could not load map metadata"
2490 //<<" Disabling chunk-based generator."
2495 infostream<<"ServerMap: Successfully loaded map "
2496 <<"metadata from "<<savedir
2497 <<", assuming valid save directory."
2498 <<" seed="<<m_seed<<"."
2501 m_map_saving_enabled = true;
2502 // Map loaded, not creating new one
2506 // If directory doesn't exist, it is safe to save to it
2508 m_map_saving_enabled = true;
2511 catch(std::exception &e)
2513 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2514 <<", exception: "<<e.what()<<std::endl;
2515 infostream<<"Please remove the map or fix it."<<std::endl;
2516 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2519 infostream<<"Initializing new map."<<std::endl;
2521 // Create zero sector
2522 emergeSector(v2s16(0,0));
2524 // Initially write whole map
2525 save(MOD_STATE_CLEAN);
2528 ServerMap::~ServerMap()
2530 verbosestream<<__FUNCTION_NAME<<std::endl;
2534 if(m_map_saving_enabled)
2536 // Save only changed parts
2537 save(MOD_STATE_WRITE_AT_UNLOAD);
2538 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2542 infostream<<"ServerMap: Map not saved"<<std::endl;
2545 catch(std::exception &e)
2547 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2548 <<", exception: "<<e.what()<<std::endl;
2552 Close database if it was opened
2560 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2561 for(; i.atEnd() == false; i++)
2563 MapChunk *chunk = i.getNode()->getValue();
2571 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2573 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2574 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2576 s16 chunksize = m_mgparams->chunksize;
2577 s16 coffset = -chunksize / 2;
2578 v3s16 chunk_offset(coffset, coffset, coffset);
2579 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2580 v3s16 blockpos_min = blockpos_div * chunksize;
2581 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2582 blockpos_min += chunk_offset;
2583 blockpos_max += chunk_offset;
2585 v3s16 extra_borders(1,1,1);
2587 // Do nothing if not inside limits (+-1 because of neighbors)
2588 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2589 blockpos_over_limit(blockpos_max + extra_borders))
2592 data->seed = m_seed;
2593 data->blockpos_min = blockpos_min;
2594 data->blockpos_max = blockpos_max;
2595 data->blockpos_requested = blockpos;
2596 data->nodedef = m_gamedef->ndef();
2599 Create the whole area of this and the neighboring blocks
2602 //TimeTaker timer("initBlockMake() create area");
2604 for(s16 x=blockpos_min.X-extra_borders.X;
2605 x<=blockpos_max.X+extra_borders.X; x++)
2606 for(s16 z=blockpos_min.Z-extra_borders.Z;
2607 z<=blockpos_max.Z+extra_borders.Z; z++)
2609 v2s16 sectorpos(x, z);
2610 // Sector metadata is loaded from disk if not already loaded.
2611 ServerMapSector *sector = createSector(sectorpos);
2614 for(s16 y=blockpos_min.Y-extra_borders.Y;
2615 y<=blockpos_max.Y+extra_borders.Y; y++)
2618 //MapBlock *block = createBlock(p);
2619 // 1) get from memory, 2) load from disk
2620 MapBlock *block = emergeBlock(p, false);
2621 // 3) create a blank one
2624 block = createBlock(p);
2627 Block gets sunlight if this is true.
2629 Refer to the map generator heuristics.
2631 bool ug = m_emerge->isBlockUnderground(p);
2632 block->setIsUnderground(ug);
2635 // Lighting will not be valid after make_chunk is called
2636 block->setLightingExpired(true);
2637 // Lighting will be calculated
2638 //block->setLightingExpired(false);
2644 Now we have a big empty area.
2646 Make a ManualMapVoxelManipulator that contains this and the
2650 // The area that contains this block and it's neighbors
2651 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2652 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2654 data->vmanip = new ManualMapVoxelManipulator(this);
2655 //data->vmanip->setMap(this);
2659 //TimeTaker timer("initBlockMake() initialEmerge");
2660 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2663 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2664 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2665 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2666 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2667 core::map<v3s16, u8>::Node *n;
2668 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2671 u8 flags = n->getValue();
2672 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2678 // Data is ready now.
2682 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2683 std::map<v3s16, MapBlock*> &changed_blocks)
2685 v3s16 blockpos_min = data->blockpos_min;
2686 v3s16 blockpos_max = data->blockpos_max;
2687 v3s16 blockpos_requested = data->blockpos_requested;
2688 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2689 <<blockpos_requested.Y<<","
2690 <<blockpos_requested.Z<<")"<<std::endl;*/
2692 v3s16 extra_borders(1,1,1);
2694 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2696 /*infostream<<"Resulting vmanip:"<<std::endl;
2697 data->vmanip.print(infostream);*/
2699 // Make sure affected blocks are loaded
2700 for(s16 x=blockpos_min.X-extra_borders.X;
2701 x<=blockpos_max.X+extra_borders.X; x++)
2702 for(s16 z=blockpos_min.Z-extra_borders.Z;
2703 z<=blockpos_max.Z+extra_borders.Z; z++)
2704 for(s16 y=blockpos_min.Y-extra_borders.Y;
2705 y<=blockpos_max.Y+extra_borders.Y; y++)
2708 // Load from disk if not already in memory
2709 emergeBlock(p, false);
2713 Blit generated stuff to map
2714 NOTE: blitBackAll adds nearly everything to changed_blocks
2718 //TimeTaker timer("finishBlockMake() blitBackAll");
2719 data->vmanip->blitBackAll(&changed_blocks);
2722 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2725 Copy transforming liquid information
2727 while(data->transforming_liquid.size() > 0)
2729 v3s16 p = data->transforming_liquid.pop_front();
2730 m_transforming_liquid.push_back(p);
2734 Do stuff in central blocks
2742 TimeTaker t("finishBlockMake lighting update");
2744 core::map<v3s16, MapBlock*> lighting_update_blocks;
2747 for(s16 x=blockpos_min.X-extra_borders.X;
2748 x<=blockpos_max.X+extra_borders.X; x++)
2749 for(s16 z=blockpos_min.Z-extra_borders.Z;
2750 z<=blockpos_max.Z+extra_borders.Z; z++)
2751 for(s16 y=blockpos_min.Y-extra_borders.Y;
2752 y<=blockpos_max.Y+extra_borders.Y; y++)
2755 MapBlock *block = getBlockNoCreateNoEx(p);
2757 lighting_update_blocks.insert(block->getPos(), block);
2760 updateLighting(lighting_update_blocks, changed_blocks);
2764 Set lighting to non-expired state in all of them.
2765 This is cheating, but it is not fast enough if all of them
2766 would actually be updated.
2768 for(s16 x=blockpos_min.X-extra_borders.X;
2769 x<=blockpos_max.X+extra_borders.X; x++)
2770 for(s16 z=blockpos_min.Z-extra_borders.Z;
2771 z<=blockpos_max.Z+extra_borders.Z; z++)
2772 for(s16 y=blockpos_min.Y-extra_borders.Y;
2773 y<=blockpos_max.Y+extra_borders.Y; y++)
2776 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2780 if(enable_mapgen_debug_info == false)
2781 t.stop(true); // Hide output
2786 Go through changed blocks
2788 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2789 i != changed_blocks.end(); ++i)
2791 MapBlock *block = i->second;
2794 Update day/night difference cache of the MapBlocks
2796 block->expireDayNightDiff();
2798 Set block as modified
2800 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2801 "finishBlockMake expireDayNightDiff");
2805 Set central blocks as generated
2807 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2808 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2809 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2812 MapBlock *block = getBlockNoCreateNoEx(p);
2814 block->setGenerated(true);
2818 Save changed parts of map
2819 NOTE: Will be saved later.
2821 //save(MOD_STATE_WRITE_AT_UNLOAD);
2823 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2824 <<","<<blockpos_requested.Y<<","
2825 <<blockpos_requested.Z<<")"<<std::endl;*/
2827 if(enable_mapgen_debug_info)
2830 Analyze resulting blocks
2832 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2833 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2834 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2835 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2836 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2837 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2839 v3s16 p = v3s16(x,y,z);
2840 MapBlock *block = getBlockNoCreateNoEx(p);
2842 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2843 infostream<<"Generated "<<spos<<": "
2844 <<analyze_block(block)<<std::endl;
2849 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2855 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2857 DSTACKF("%s: p2d=(%d,%d)",
2862 Check if it exists already in memory
2864 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2869 Try to load it from disk (with blocks)
2871 //if(loadSectorFull(p2d) == true)
2874 Try to load metadata from disk
2877 if(loadSectorMeta(p2d) == true)
2879 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2882 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2883 throw InvalidPositionException("");
2889 Do not create over-limit
2891 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2892 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2893 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2894 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2895 throw InvalidPositionException("createSector(): pos. over limit");
2898 Generate blank sector
2901 sector = new ServerMapSector(this, p2d, m_gamedef);
2903 // Sector position on map in nodes
2904 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2909 m_sectors[p2d] = sector;
2916 This is a quick-hand function for calling makeBlock().
2918 MapBlock * ServerMap::generateBlock(
2920 std::map<v3s16, MapBlock*> &modified_blocks
2923 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2925 /*infostream<<"generateBlock(): "
2926 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2929 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2931 TimeTaker timer("generateBlock");
2933 //MapBlock *block = original_dummy;
2935 v2s16 p2d(p.X, p.Z);
2936 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2939 Do not generate over-limit
2941 if(blockpos_over_limit(p))
2943 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2944 throw InvalidPositionException("generateBlock(): pos. over limit");
2948 Create block make data
2951 initBlockMake(&data, p);
2957 TimeTaker t("mapgen::make_block()");
2958 mapgen->makeChunk(&data);
2959 //mapgen::make_block(&data);
2961 if(enable_mapgen_debug_info == false)
2962 t.stop(true); // Hide output
2966 Blit data back on map, update lighting, add mobs and whatever this does
2968 finishBlockMake(&data, modified_blocks);
2973 MapBlock *block = getBlockNoCreateNoEx(p);
2981 bool erroneus_content = false;
2982 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2983 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2984 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2987 MapNode n = block->getNode(p);
2988 if(n.getContent() == CONTENT_IGNORE)
2990 infostream<<"CONTENT_IGNORE at "
2991 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2993 erroneus_content = true;
2997 if(erroneus_content)
3006 Generate a completely empty block
3010 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3011 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3013 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3016 n.setContent(CONTENT_AIR);
3017 block->setNode(v3s16(x0,y0,z0), n);
3023 if(enable_mapgen_debug_info == false)
3024 timer.stop(true); // Hide output
3030 MapBlock * ServerMap::createBlock(v3s16 p)
3032 DSTACKF("%s: p=(%d,%d,%d)",
3033 __FUNCTION_NAME, p.X, p.Y, p.Z);
3036 Do not create over-limit
3038 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3039 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3040 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3041 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3042 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3043 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3044 throw InvalidPositionException("createBlock(): pos. over limit");
3046 v2s16 p2d(p.X, p.Z);
3049 This will create or load a sector if not found in memory.
3050 If block exists on disk, it will be loaded.
3052 NOTE: On old save formats, this will be slow, as it generates
3053 lighting on blocks for them.
3055 ServerMapSector *sector;
3057 sector = (ServerMapSector*)createSector(p2d);
3058 assert(sector->getId() == MAPSECTOR_SERVER);
3060 catch(InvalidPositionException &e)
3062 infostream<<"createBlock: createSector() failed"<<std::endl;
3066 NOTE: This should not be done, or at least the exception
3067 should not be passed on as std::exception, because it
3068 won't be catched at all.
3070 /*catch(std::exception &e)
3072 infostream<<"createBlock: createSector() failed: "
3073 <<e.what()<<std::endl;
3078 Try to get a block from the sector
3081 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3084 if(block->isDummy())
3089 block = sector->createBlankBlock(block_y);
3094 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3096 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3098 p.X, p.Y, p.Z, create_blank);
3101 MapBlock *block = getBlockNoCreateNoEx(p);
3102 if(block && block->isDummy() == false)
3107 MapBlock *block = loadBlock(p);
3113 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3114 MapBlock *block = sector->createBlankBlock(p.Y);
3118 /*if(allow_generate)
3120 std::map<v3s16, MapBlock*> modified_blocks;
3121 MapBlock *block = generateBlock(p, modified_blocks);
3125 event.type = MEET_OTHER;
3128 // Copy modified_blocks to event
3129 for(std::map<v3s16, MapBlock*>::iterator
3130 i = modified_blocks.begin();
3131 i != modified_blocks.end(); ++i)
3133 event.modified_blocks.insert(i->first);
3137 dispatchEvent(&event);
3146 s16 ServerMap::findGroundLevel(v2s16 p2d)
3150 Uh, just do something random...
3152 // Find existing map from top to down
3155 v3s16 p(p2d.X, max, p2d.Y);
3156 for(; p.Y>min; p.Y--)
3158 MapNode n = getNodeNoEx(p);
3159 if(n.getContent() != CONTENT_IGNORE)
3164 // If this node is not air, go to plan b
3165 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3167 // Search existing walkable and return it
3168 for(; p.Y>min; p.Y--)
3170 MapNode n = getNodeNoEx(p);
3171 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3180 Determine from map generator noise functions
3183 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3186 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3187 //return (s16)level;
3190 bool ServerMap::loadFromFolders() {
3191 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
3196 void ServerMap::createDirs(std::string path)
3198 if(fs::CreateAllDirs(path) == false)
3200 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3201 <<"\""<<path<<"\""<<std::endl;
3202 throw BaseException("ServerMap failed to create directory");
3206 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3212 snprintf(cc, 9, "%.4x%.4x",
3213 (unsigned int)pos.X&0xffff,
3214 (unsigned int)pos.Y&0xffff);
3216 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3218 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3219 (unsigned int)pos.X&0xfff,
3220 (unsigned int)pos.Y&0xfff);
3222 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3228 v2s16 ServerMap::getSectorPos(std::string dirname)
3232 std::string component;
3233 fs::RemoveLastPathComponent(dirname, &component, 1);
3234 if(component.size() == 8)
3237 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3239 else if(component.size() == 3)
3242 fs::RemoveLastPathComponent(dirname, &component, 2);
3243 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3244 // Sign-extend the 12 bit values up to 16 bits...
3245 if(x&0x800) x|=0xF000;
3246 if(y&0x800) y|=0xF000;
3253 v2s16 pos((s16)x, (s16)y);
3257 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3259 v2s16 p2d = getSectorPos(sectordir);
3261 if(blockfile.size() != 4){
3262 throw InvalidFilenameException("Invalid block filename");
3265 int r = sscanf(blockfile.c_str(), "%4x", &y);
3267 throw InvalidFilenameException("Invalid block filename");
3268 return v3s16(p2d.X, y, p2d.Y);
3271 std::string ServerMap::getBlockFilename(v3s16 p)
3274 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3278 void ServerMap::save(ModifiedState save_level)
3280 DSTACK(__FUNCTION_NAME);
3281 if(m_map_saving_enabled == false)
3283 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3287 if(save_level == MOD_STATE_CLEAN)
3288 infostream<<"ServerMap: Saving whole map, this can take time."
3291 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3296 // Profile modified reasons
3297 Profiler modprofiler;
3299 u32 sector_meta_count = 0;
3300 u32 block_count = 0;
3301 u32 block_count_all = 0; // Number of blocks in memory
3303 // Don't do anything with sqlite unless something is really saved
3304 bool save_started = false;
3306 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3307 i != m_sectors.end(); ++i)
3309 ServerMapSector *sector = (ServerMapSector*)i->second;
3310 assert(sector->getId() == MAPSECTOR_SERVER);
3312 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3314 saveSectorMeta(sector);
3315 sector_meta_count++;
3317 std::list<MapBlock*> blocks;
3318 sector->getBlocks(blocks);
3320 for(std::list<MapBlock*>::iterator j = blocks.begin();
3321 j != blocks.end(); ++j)
3323 MapBlock *block = *j;
3327 if(block->getModified() >= (u32)save_level)
3332 save_started = true;
3335 modprofiler.add(block->getModifiedReason(), 1);
3340 /*infostream<<"ServerMap: Written block ("
3341 <<block->getPos().X<<","
3342 <<block->getPos().Y<<","
3343 <<block->getPos().Z<<")"
3352 Only print if something happened or saved whole map
3354 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3355 || block_count != 0)
3357 infostream<<"ServerMap: Written: "
3358 <<sector_meta_count<<" sector metadata files, "
3359 <<block_count<<" block files"
3360 <<", "<<block_count_all<<" blocks in memory."
3362 PrintInfo(infostream); // ServerMap/ClientMap:
3363 infostream<<"Blocks modified by: "<<std::endl;
3364 modprofiler.print(infostream);
3368 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3370 if(loadFromFolders()){
3371 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3372 <<"all blocks that are stored in flat files"<<std::endl;
3374 dbase->listAllLoadableBlocks(dst);
3377 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3379 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3380 si != m_sectors.end(); ++si)
3382 MapSector *sector = si->second;
3384 std::list<MapBlock*> blocks;
3385 sector->getBlocks(blocks);
3387 for(std::list<MapBlock*>::iterator i = blocks.begin();
3388 i != blocks.end(); ++i)
3390 MapBlock *block = (*i);
3391 v3s16 p = block->getPos();
3397 void ServerMap::saveMapMeta()
3399 DSTACK(__FUNCTION_NAME);
3401 /*infostream<<"ServerMap::saveMapMeta(): "
3405 createDirs(m_savedir);
3407 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3408 std::ostringstream ss(std::ios_base::binary);
3412 m_emerge->setParamsToSettings(¶ms);
3413 params.writeLines(ss);
3415 ss<<"[end_of_params]\n";
3417 if(!fs::safeWriteToFile(fullpath, ss.str()))
3419 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3420 <<"could not write "<<fullpath<<std::endl;
3421 throw FileNotGoodException("Cannot save chunk metadata");
3424 m_map_metadata_changed = false;
3427 void ServerMap::loadMapMeta()
3429 DSTACK(__FUNCTION_NAME);
3431 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3434 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3435 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3436 if(is.good() == false)
3438 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3439 <<"could not open"<<fullpath<<std::endl;
3440 throw FileNotGoodException("Cannot open map metadata");
3448 throw SerializationError
3449 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3451 std::getline(is, line);
3452 std::string trimmedline = trim(line);
3453 if(trimmedline == "[end_of_params]")
3455 params.parseConfigLine(line);
3458 MapgenParams *mgparams;
3460 mgparams = m_emerge->getParamsFromSettings(¶ms);
3461 } catch (SettingNotFoundException &e) {
3462 infostream << "Couldn't get a setting from map_meta.txt: "
3463 << e.what() << std::endl;
3470 m_mgparams = mgparams;
3471 m_seed = mgparams->seed;
3473 if (params.exists("seed")) {
3474 m_seed = params.getU64("seed");
3475 m_mgparams->seed = m_seed;
3479 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3482 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3484 DSTACK(__FUNCTION_NAME);
3485 // Format used for writing
3486 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3488 v2s16 pos = sector->getPos();
3489 std::string dir = getSectorDir(pos);
3492 std::string fullpath = dir + DIR_DELIM + "meta";
3493 std::ostringstream ss(std::ios_base::binary);
3495 sector->serialize(ss, version);
3497 if(!fs::safeWriteToFile(fullpath, ss.str()))
3498 throw FileNotGoodException("Cannot write sector metafile");
3500 sector->differs_from_disk = false;
3503 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3505 DSTACK(__FUNCTION_NAME);
3507 v2s16 p2d = getSectorPos(sectordir);
3509 ServerMapSector *sector = NULL;
3511 std::string fullpath = sectordir + DIR_DELIM + "meta";
3512 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3513 if(is.good() == false)
3515 // If the directory exists anyway, it probably is in some old
3516 // format. Just go ahead and create the sector.
3517 if(fs::PathExists(sectordir))
3519 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3520 <<fullpath<<" doesn't exist but directory does."
3521 <<" Continuing with a sector with no metadata."
3523 sector = new ServerMapSector(this, p2d, m_gamedef);
3524 m_sectors[p2d] = sector;
3528 throw FileNotGoodException("Cannot open sector metafile");
3533 sector = ServerMapSector::deSerialize
3534 (is, this, p2d, m_sectors, m_gamedef);
3536 saveSectorMeta(sector);
3539 sector->differs_from_disk = false;
3544 bool ServerMap::loadSectorMeta(v2s16 p2d)
3546 DSTACK(__FUNCTION_NAME);
3548 MapSector *sector = NULL;
3550 // The directory layout we're going to load from.
3551 // 1 - original sectors/xxxxzzzz/
3552 // 2 - new sectors2/xxx/zzz/
3553 // If we load from anything but the latest structure, we will
3554 // immediately save to the new one, and remove the old.
3556 std::string sectordir1 = getSectorDir(p2d, 1);
3557 std::string sectordir;
3558 if(fs::PathExists(sectordir1))
3560 sectordir = sectordir1;
3565 sectordir = getSectorDir(p2d, 2);
3569 sector = loadSectorMeta(sectordir, loadlayout != 2);
3571 catch(InvalidFilenameException &e)
3575 catch(FileNotGoodException &e)
3579 catch(std::exception &e)
3588 bool ServerMap::loadSectorFull(v2s16 p2d)
3590 DSTACK(__FUNCTION_NAME);
3592 MapSector *sector = NULL;
3594 // The directory layout we're going to load from.
3595 // 1 - original sectors/xxxxzzzz/
3596 // 2 - new sectors2/xxx/zzz/
3597 // If we load from anything but the latest structure, we will
3598 // immediately save to the new one, and remove the old.
3600 std::string sectordir1 = getSectorDir(p2d, 1);
3601 std::string sectordir;
3602 if(fs::PathExists(sectordir1))
3604 sectordir = sectordir1;
3609 sectordir = getSectorDir(p2d, 2);
3613 sector = loadSectorMeta(sectordir, loadlayout != 2);
3615 catch(InvalidFilenameException &e)
3619 catch(FileNotGoodException &e)
3623 catch(std::exception &e)
3631 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3633 std::vector<fs::DirListNode>::iterator i2;
3634 for(i2=list2.begin(); i2!=list2.end(); i2++)
3640 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3642 catch(InvalidFilenameException &e)
3644 // This catches unknown crap in directory
3650 infostream<<"Sector converted to new layout - deleting "<<
3651 sectordir1<<std::endl;
3652 fs::RecursiveDelete(sectordir1);
3659 void ServerMap::beginSave() {
3663 void ServerMap::endSave() {
3667 void ServerMap::saveBlock(MapBlock *block)
3669 dbase->saveBlock(block);
3672 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3674 DSTACK(__FUNCTION_NAME);
3676 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3679 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3680 if(is.good() == false)
3681 throw FileNotGoodException("Cannot open block file");
3683 v3s16 p3d = getBlockPos(sectordir, blockfile);
3684 v2s16 p2d(p3d.X, p3d.Z);
3686 assert(sector->getPos() == p2d);
3688 u8 version = SER_FMT_VER_INVALID;
3689 is.read((char*)&version, 1);
3692 throw SerializationError("ServerMap::loadBlock(): Failed"
3693 " to read MapBlock version");
3695 /*u32 block_size = MapBlock::serializedLength(version);
3696 SharedBuffer<u8> data(block_size);
3697 is.read((char*)*data, block_size);*/
3699 // This will always return a sector because we're the server
3700 //MapSector *sector = emergeSector(p2d);
3702 MapBlock *block = NULL;
3703 bool created_new = false;
3704 block = sector->getBlockNoCreateNoEx(p3d.Y);
3707 block = sector->createBlankBlockNoInsert(p3d.Y);
3712 block->deSerialize(is, version, true);
3714 // If it's a new block, insert it to the map
3716 sector->insertBlock(block);
3719 Save blocks loaded in old format in new format
3722 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3726 // Should be in database now, so delete the old file
3727 fs::RecursiveDelete(fullpath);
3730 // We just loaded it from the disk, so it's up-to-date.
3731 block->resetModified();
3734 catch(SerializationError &e)
3736 infostream<<"WARNING: Invalid block data on disk "
3737 <<"fullpath="<<fullpath
3738 <<" (SerializationError). "
3739 <<"what()="<<e.what()
3741 // Ignoring. A new one will be generated.
3744 // TODO: Backup file; name is in fullpath.
3748 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3750 DSTACK(__FUNCTION_NAME);
3753 std::istringstream is(*blob, std::ios_base::binary);
3755 u8 version = SER_FMT_VER_INVALID;
3756 is.read((char*)&version, 1);
3759 throw SerializationError("ServerMap::loadBlock(): Failed"
3760 " to read MapBlock version");
3762 /*u32 block_size = MapBlock::serializedLength(version);
3763 SharedBuffer<u8> data(block_size);
3764 is.read((char*)*data, block_size);*/
3766 // This will always return a sector because we're the server
3767 //MapSector *sector = emergeSector(p2d);
3769 MapBlock *block = NULL;
3770 bool created_new = false;
3771 block = sector->getBlockNoCreateNoEx(p3d.Y);
3774 block = sector->createBlankBlockNoInsert(p3d.Y);
3779 block->deSerialize(is, version, true);
3781 // If it's a new block, insert it to the map
3783 sector->insertBlock(block);
3786 Save blocks loaded in old format in new format
3789 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3790 // Only save if asked to; no need to update version
3794 // We just loaded it from, so it's up-to-date.
3795 block->resetModified();
3798 catch(SerializationError &e)
3800 errorstream<<"Invalid block data in database"
3801 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3802 <<" (SerializationError): "<<e.what()<<std::endl;
3804 // TODO: Block should be marked as invalid in memory so that it is
3805 // not touched but the game can run
3807 if(g_settings->getBool("ignore_world_load_errors")){
3808 errorstream<<"Ignoring block load error. Duck and cover! "
3809 <<"(ignore_world_load_errors)"<<std::endl;
3811 throw SerializationError("Invalid block data in database");
3817 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3819 DSTACK(__FUNCTION_NAME);
3821 v2s16 p2d(blockpos.X, blockpos.Z);
3825 ret = dbase->loadBlock(blockpos);
3826 if (ret) return (ret);
3827 // Not found in database, try the files
3829 // The directory layout we're going to load from.
3830 // 1 - original sectors/xxxxzzzz/
3831 // 2 - new sectors2/xxx/zzz/
3832 // If we load from anything but the latest structure, we will
3833 // immediately save to the new one, and remove the old.
3835 std::string sectordir1 = getSectorDir(p2d, 1);
3836 std::string sectordir;
3837 if(fs::PathExists(sectordir1))
3839 sectordir = sectordir1;
3844 sectordir = getSectorDir(p2d, 2);
3848 Make sure sector is loaded
3850 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3854 sector = loadSectorMeta(sectordir, loadlayout != 2);
3856 catch(InvalidFilenameException &e)
3860 catch(FileNotGoodException &e)
3864 catch(std::exception &e)
3871 Make sure file exists
3874 std::string blockfilename = getBlockFilename(blockpos);
3875 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3879 Load block and save it to the database
3881 loadBlock(sectordir, blockfilename, sector, true);
3882 return getBlockNoCreateNoEx(blockpos);
3885 void ServerMap::PrintInfo(std::ostream &out)
3890 s16 ServerMap::getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
3893 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3895 if (env->getGameTime() - block->heat_time < 10)
3899 //variant 1: full random
3900 //f32 heat = NoisePerlin3D(m_emerge->biomedef->np_heat, p.X, env->getGameTime()/100, p.Z, m_emerge->params->seed);
3902 //variant 2: season change based on default heat map
3903 const f32 offset = 20; // = m_emerge->biomedef->np_heat->offset
3904 const f32 scale = 20; // = m_emerge->biomedef->np_heat->scale
3905 const f32 range = 20;
3906 f32 heat = NoisePerlin2D(m_emerge->biomedef->np_heat, p.X, p.Z,
3907 m_emerge->params->seed); // 0..50..100
3909 heat -= m_emerge->biomedef->np_heat->offset; // -50..0..+50
3911 // normalizing - todo REMOVE after fixed NoiseParams nparams_biome_def_heat = {50, 50, -> 20, 50,
3912 if(m_emerge->biomedef->np_heat->scale)
3913 heat /= m_emerge->biomedef->np_heat->scale / scale; // -20..0..+20
3915 f32 seasonv = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
3916 seasonv /= 86400 * g_settings->getS16("year_days"); // season change speed
3917 seasonv += (f32)p.X / 3000; // you can walk to area with other season
3918 seasonv = sin(seasonv * M_PI);
3919 heat += (range * (heat < 0 ? 2 : 0.5)) * seasonv; // -60..0..30
3921 heat += offset; // -40..0..50
3922 heat += p.Y / -333; // upper=colder, lower=hotter, 3c per 1000
3924 // daily change, hotter at sun +4, colder at night -4
3925 heat += 8 * (sin(cycle_shift(env->getTimeOfDayF(), -0.25) * M_PI) - 0.5); //-44..20..54
3929 block->heat_time = env->getGameTime();
3934 s16 ServerMap::getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
3937 block = getBlockNoCreateNoEx(getNodeBlockPos(p));
3939 if (env->getGameTime() - block->humidity_time < 10)
3940 return block->humidity;
3943 f32 humidity = NoisePerlin2D(m_emerge->biomedef->np_humidity, p.X, p.Z,
3944 m_emerge->params->seed);
3946 f32 seasonv = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
3947 seasonv /= 86400 * 2; // bad weather change speed (2 days)
3948 seasonv += (f32)p.Z / 300;
3949 humidity += 30 * sin(seasonv * M_PI);
3951 humidity += -12 * ( sin(cycle_shift(env->getTimeOfDayF(), -0.1) * M_PI) - 0.5);
3952 humidity = rangelim(humidity, 0, 100);
3955 block->humidity = humidity;
3956 block->humidity_time = env->getGameTime();
3965 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3970 MapVoxelManipulator::~MapVoxelManipulator()
3972 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3976 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3978 TimeTaker timer1("emerge", &emerge_time);
3980 // Units of these are MapBlocks
3981 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3982 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3984 VoxelArea block_area_nodes
3985 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3987 addArea(block_area_nodes);
3989 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3990 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3991 for(s32 x=p_min.X; x<=p_max.X; x++)
3996 std::map<v3s16, u8>::iterator n;
3997 n = m_loaded_blocks.find(p);
3998 if(n != m_loaded_blocks.end())
4001 bool block_data_inexistent = false;
4004 TimeTaker timer1("emerge load", &emerge_load_time);
4006 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4007 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4009 a.print(infostream);
4010 infostream<<std::endl;*/
4012 block = m_map->getBlockNoCreate(p);
4013 if(block->isDummy())
4014 block_data_inexistent = true;
4016 block->copyTo(*this);
4018 catch(InvalidPositionException &e)
4020 block_data_inexistent = true;
4023 if(block_data_inexistent)
4025 flags |= VMANIP_BLOCK_DATA_INEXIST;
4027 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4028 // Fill with VOXELFLAG_INEXISTENT
4029 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4030 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4032 s32 i = m_area.index(a.MinEdge.X,y,z);
4033 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4036 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4038 // Mark that block was loaded as blank
4039 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4042 m_loaded_blocks[p] = flags;
4045 //infostream<<"emerge done"<<std::endl;
4049 SUGG: Add an option to only update eg. water and air nodes.
4050 This will make it interfere less with important stuff if
4053 void MapVoxelManipulator::blitBack
4054 (std::map<v3s16, MapBlock*> & modified_blocks)
4056 if(m_area.getExtent() == v3s16(0,0,0))
4059 //TimeTaker timer1("blitBack");
4061 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4062 <<m_loaded_blocks.size()<<std::endl;*/
4065 Initialize block cache
4067 v3s16 blockpos_last;
4068 MapBlock *block = NULL;
4069 bool block_checked_in_modified = false;
4071 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4072 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4073 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4077 u8 f = m_flags[m_area.index(p)];
4078 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4081 MapNode &n = m_data[m_area.index(p)];
4083 v3s16 blockpos = getNodeBlockPos(p);
4088 if(block == NULL || blockpos != blockpos_last){
4089 block = m_map->getBlockNoCreate(blockpos);
4090 blockpos_last = blockpos;
4091 block_checked_in_modified = false;
4094 // Calculate relative position in block
4095 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4097 // Don't continue if nothing has changed here
4098 if(block->getNode(relpos) == n)
4101 //m_map->setNode(m_area.MinEdge + p, n);
4102 block->setNode(relpos, n);
4105 Make sure block is in modified_blocks
4107 if(block_checked_in_modified == false)
4109 modified_blocks[blockpos] = block;
4110 block_checked_in_modified = true;
4113 catch(InvalidPositionException &e)
4119 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4120 MapVoxelManipulator(map),
4121 m_create_area(false)
4125 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4129 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4131 // Just create the area so that it can be pointed to
4132 VoxelManipulator::emerge(a, caller_id);
4135 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4136 v3s16 blockpos_max, bool load_if_inexistent)
4138 TimeTaker timer1("initialEmerge", &emerge_time);
4140 // Units of these are MapBlocks
4141 v3s16 p_min = blockpos_min;
4142 v3s16 p_max = blockpos_max;
4144 VoxelArea block_area_nodes
4145 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4147 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4150 infostream<<"initialEmerge: area: ";
4151 block_area_nodes.print(infostream);
4152 infostream<<" ("<<size_MB<<"MB)";
4153 infostream<<std::endl;
4156 addArea(block_area_nodes);
4158 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4159 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4160 for(s32 x=p_min.X; x<=p_max.X; x++)
4165 std::map<v3s16, u8>::iterator n;
4166 n = m_loaded_blocks.find(p);
4167 if(n != m_loaded_blocks.end())
4170 bool block_data_inexistent = false;
4173 TimeTaker timer1("emerge load", &emerge_load_time);
4175 block = m_map->getBlockNoCreate(p);
4176 if(block->isDummy())
4177 block_data_inexistent = true;
4179 block->copyTo(*this);
4181 catch(InvalidPositionException &e)
4183 block_data_inexistent = true;
4186 if(block_data_inexistent)
4189 if (load_if_inexistent) {
4190 ServerMap *svrmap = (ServerMap *)m_map;
4191 block = svrmap->emergeBlock(p, false);
4193 block = svrmap->createBlock(p);
4195 block->copyTo(*this);
4197 flags |= VMANIP_BLOCK_DATA_INEXIST;
4200 Mark area inexistent
4202 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4203 // Fill with VOXELFLAG_INEXISTENT
4204 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4205 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4207 s32 i = m_area.index(a.MinEdge.X,y,z);
4208 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4212 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4214 // Mark that block was loaded as blank
4215 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4218 m_loaded_blocks[p] = flags;
4222 void ManualMapVoxelManipulator::blitBackAll(
4223 std::map<v3s16, MapBlock*> * modified_blocks)
4225 if(m_area.getExtent() == v3s16(0,0,0))
4229 Copy data of all blocks
4231 for(std::map<v3s16, u8>::iterator
4232 i = m_loaded_blocks.begin();
4233 i != m_loaded_blocks.end(); ++i)
4236 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4237 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4238 if(existed == false)
4243 block->copyFrom(*this);
4246 (*modified_blocks)[p] = block;