3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "rollback_interface.h"
37 #include "mapgen_v6.h"
38 #include "mapgen_indev.h"
40 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
43 SQLite format specification:
44 - Initially only replaces sectors/ and sectors2/
46 If map.sqlite does not exist in the save dir
47 or the block was not found in the database
48 the map will try to load from sectors folder.
49 In either case, map.sqlite will be created
50 and all future saves will save there.
52 Structure of map.sqlite:
63 Map::Map(std::ostream &dout, IGameDef *gamedef):
68 /*m_sector_mutex.Init();
69 assert(m_sector_mutex.IsInitialized());*/
77 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
78 i != m_sectors.end(); ++i)
84 void Map::addEventReceiver(MapEventReceiver *event_receiver)
86 m_event_receivers.insert(event_receiver);
89 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
91 m_event_receivers.erase(event_receiver);
94 void Map::dispatchEvent(MapEditEvent *event)
96 for(std::set<MapEventReceiver*>::iterator
97 i = m_event_receivers.begin();
98 i != m_event_receivers.end(); ++i)
100 (*i)->onMapEditEvent(event);
104 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
106 if(m_sector_cache != NULL && p == m_sector_cache_p){
107 MapSector * sector = m_sector_cache;
111 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
113 if(n == m_sectors.end())
116 MapSector *sector = n->second;
118 // Cache the last result
119 m_sector_cache_p = p;
120 m_sector_cache = sector;
125 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
127 return getSectorNoGenerateNoExNoLock(p);
130 MapSector * Map::getSectorNoGenerate(v2s16 p)
132 MapSector *sector = getSectorNoGenerateNoEx(p);
134 throw InvalidPositionException();
139 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
141 v2s16 p2d(p3d.X, p3d.Z);
142 MapSector * sector = getSectorNoGenerateNoEx(p2d);
145 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
149 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
151 MapBlock *block = getBlockNoCreateNoEx(p3d);
153 throw InvalidPositionException();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
170 bool Map::isValidPosition(v3s16 p)
172 v3s16 blockpos = getNodeBlockPos(p);
173 MapBlock *block = getBlockNoCreate(blockpos);
174 return (block != NULL);
177 // Returns a CONTENT_IGNORE node if not found
178 MapNode Map::getNodeNoEx(v3s16 p)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreateNoEx(blockpos);
183 return MapNode(CONTENT_IGNORE);
184 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
185 return block->getNodeNoCheck(relpos);
188 // throws InvalidPositionException if not found
189 MapNode Map::getNode(v3s16 p)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 throw InvalidPositionException();
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 return block->getNodeNoCheck(relpos);
199 // throws InvalidPositionException if not found
200 void Map::setNode(v3s16 p, MapNode & n)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreate(blockpos);
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 // Never allow placing CONTENT_IGNORE, it fucks up stuff
206 if(n.getContent() == CONTENT_IGNORE){
207 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
208 <<" while trying to replace \""
209 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
210 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
211 debug_stacks_print_to(infostream);
214 block->setNodeNoCheck(relpos, n);
219 Goes recursively through the neighbours of the node.
221 Alters only transparent nodes.
223 If the lighting of the neighbour is lower than the lighting of
224 the node was (before changing it to 0 at the step before), the
225 lighting of the neighbour is set to 0 and then the same stuff
226 repeats for the neighbour.
228 The ending nodes of the routine are stored in light_sources.
229 This is useful when a light is removed. In such case, this
230 routine can be called for the light node and then again for
231 light_sources to re-light the area without the removed light.
233 values of from_nodes are lighting values.
235 void Map::unspreadLight(enum LightBank bank,
236 std::map<v3s16, u8> & from_nodes,
237 std::set<v3s16> & light_sources,
238 std::map<v3s16, MapBlock*> & modified_blocks)
240 INodeDefManager *nodemgr = m_gamedef->ndef();
243 v3s16(0,0,1), // back
245 v3s16(1,0,0), // right
246 v3s16(0,0,-1), // front
247 v3s16(0,-1,0), // bottom
248 v3s16(-1,0,0), // left
251 if(from_nodes.size() == 0)
254 u32 blockchangecount = 0;
256 std::map<v3s16, u8> unlighted_nodes;
259 Initialize block cache
262 MapBlock *block = NULL;
263 // Cache this a bit, too
264 bool block_checked_in_modified = false;
266 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
267 j != from_nodes.end(); ++j)
269 v3s16 pos = j->first;
270 v3s16 blockpos = getNodeBlockPos(pos);
272 // Only fetch a new block if the block position has changed
274 if(block == NULL || blockpos != blockpos_last){
275 block = getBlockNoCreate(blockpos);
276 blockpos_last = blockpos;
278 block_checked_in_modified = false;
282 catch(InvalidPositionException &e)
290 // Calculate relative position in block
291 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
293 // Get node straight from the block
294 //MapNode n = block->getNode(relpos);
296 u8 oldlight = j->second;
298 // Loop through 6 neighbors
299 for(u16 i=0; i<6; i++)
301 // Get the position of the neighbor node
302 v3s16 n2pos = pos + dirs[i];
304 // Get the block where the node is located
305 v3s16 blockpos = getNodeBlockPos(n2pos);
309 // Only fetch a new block if the block position has changed
311 if(block == NULL || blockpos != blockpos_last){
312 block = getBlockNoCreate(blockpos);
313 blockpos_last = blockpos;
315 block_checked_in_modified = false;
319 catch(InvalidPositionException &e)
324 // Calculate relative position in block
325 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
326 // Get node straight from the block
327 MapNode n2 = block->getNode(relpos);
329 bool changed = false;
331 //TODO: Optimize output by optimizing light_sources?
334 If the neighbor is dimmer than what was specified
335 as oldlight (the light of the previous node)
337 if(n2.getLight(bank, nodemgr) < oldlight)
340 And the neighbor is transparent and it has some light
342 if(nodemgr->get(n2).light_propagates
343 && n2.getLight(bank, nodemgr) != 0)
346 Set light to 0 and add to queue
349 u8 current_light = n2.getLight(bank, nodemgr);
350 n2.setLight(bank, 0, nodemgr);
351 block->setNode(relpos, n2);
353 unlighted_nodes[n2pos] = current_light;
357 Remove from light_sources if it is there
358 NOTE: This doesn't happen nearly at all
360 /*if(light_sources.find(n2pos))
362 infostream<<"Removed from light_sources"<<std::endl;
363 light_sources.remove(n2pos);
368 if(light_sources.find(n2pos) != NULL)
369 light_sources.remove(n2pos);*/
372 light_sources.insert(n2pos);
375 // Add to modified_blocks
376 if(changed == true && block_checked_in_modified == false)
378 // If the block is not found in modified_blocks, add.
379 if(modified_blocks.find(blockpos) == modified_blocks.end())
381 modified_blocks[blockpos] = block;
383 block_checked_in_modified = true;
386 catch(InvalidPositionException &e)
393 /*infostream<<"unspreadLight(): Changed block "
394 <<blockchangecount<<" times"
395 <<" for "<<from_nodes.size()<<" nodes"
398 if(unlighted_nodes.size() > 0)
399 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
403 A single-node wrapper of the above
405 void Map::unLightNeighbors(enum LightBank bank,
406 v3s16 pos, u8 lightwas,
407 std::set<v3s16> & light_sources,
408 std::map<v3s16, MapBlock*> & modified_blocks)
410 std::map<v3s16, u8> from_nodes;
411 from_nodes[pos] = lightwas;
413 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
417 Lights neighbors of from_nodes, collects all them and then
420 void Map::spreadLight(enum LightBank bank,
421 std::set<v3s16> & from_nodes,
422 std::map<v3s16, MapBlock*> & modified_blocks)
424 INodeDefManager *nodemgr = m_gamedef->ndef();
426 const v3s16 dirs[6] = {
427 v3s16(0,0,1), // back
429 v3s16(1,0,0), // right
430 v3s16(0,0,-1), // front
431 v3s16(0,-1,0), // bottom
432 v3s16(-1,0,0), // left
435 if(from_nodes.size() == 0)
438 u32 blockchangecount = 0;
440 std::set<v3s16> lighted_nodes;
443 Initialize block cache
446 MapBlock *block = NULL;
447 // Cache this a bit, too
448 bool block_checked_in_modified = false;
450 for(std::set<v3s16>::iterator j = from_nodes.begin();
451 j != from_nodes.end(); ++j)
454 v3s16 blockpos = getNodeBlockPos(pos);
456 // Only fetch a new block if the block position has changed
458 if(block == NULL || blockpos != blockpos_last){
459 block = getBlockNoCreate(blockpos);
460 blockpos_last = blockpos;
462 block_checked_in_modified = false;
466 catch(InvalidPositionException &e)
474 // Calculate relative position in block
475 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
477 // Get node straight from the block
478 MapNode n = block->getNode(relpos);
480 u8 oldlight = n.getLight(bank, nodemgr);
481 u8 newlight = diminish_light(oldlight);
483 // Loop through 6 neighbors
484 for(u16 i=0; i<6; i++){
485 // Get the position of the neighbor node
486 v3s16 n2pos = pos + dirs[i];
488 // Get the block where the node is located
489 v3s16 blockpos = getNodeBlockPos(n2pos);
493 // Only fetch a new block if the block position has changed
495 if(block == NULL || blockpos != blockpos_last){
496 block = getBlockNoCreate(blockpos);
497 blockpos_last = blockpos;
499 block_checked_in_modified = false;
503 catch(InvalidPositionException &e)
508 // Calculate relative position in block
509 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
510 // Get node straight from the block
511 MapNode n2 = block->getNode(relpos);
513 bool changed = false;
515 If the neighbor is brighter than the current node,
516 add to list (it will light up this node on its turn)
518 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
520 lighted_nodes.insert(n2pos);
524 If the neighbor is dimmer than how much light this node
525 would spread on it, add to list
527 if(n2.getLight(bank, nodemgr) < newlight)
529 if(nodemgr->get(n2).light_propagates)
531 n2.setLight(bank, newlight, nodemgr);
532 block->setNode(relpos, n2);
533 lighted_nodes.insert(n2pos);
538 // Add to modified_blocks
539 if(changed == true && block_checked_in_modified == false)
541 // If the block is not found in modified_blocks, add.
542 if(modified_blocks.find(blockpos) == modified_blocks.end())
544 modified_blocks[blockpos] = block;
546 block_checked_in_modified = true;
549 catch(InvalidPositionException &e)
556 /*infostream<<"spreadLight(): Changed block "
557 <<blockchangecount<<" times"
558 <<" for "<<from_nodes.size()<<" nodes"
561 if(lighted_nodes.size() > 0)
562 spreadLight(bank, lighted_nodes, modified_blocks);
566 A single-node source variation of the above.
568 void Map::lightNeighbors(enum LightBank bank,
570 std::map<v3s16, MapBlock*> & modified_blocks)
572 std::set<v3s16> from_nodes;
573 from_nodes.insert(pos);
574 spreadLight(bank, from_nodes, modified_blocks);
577 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
579 INodeDefManager *nodemgr = m_gamedef->ndef();
582 v3s16(0,0,1), // back
584 v3s16(1,0,0), // right
585 v3s16(0,0,-1), // front
586 v3s16(0,-1,0), // bottom
587 v3s16(-1,0,0), // left
590 u8 brightest_light = 0;
591 v3s16 brightest_pos(0,0,0);
592 bool found_something = false;
594 // Loop through 6 neighbors
595 for(u16 i=0; i<6; i++){
596 // Get the position of the neighbor node
597 v3s16 n2pos = p + dirs[i];
602 catch(InvalidPositionException &e)
606 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
607 brightest_light = n2.getLight(bank, nodemgr);
608 brightest_pos = n2pos;
609 found_something = true;
613 if(found_something == false)
614 throw InvalidPositionException();
616 return brightest_pos;
620 Propagates sunlight down from a node.
621 Starting point gets sunlight.
623 Returns the lowest y value of where the sunlight went.
625 Mud is turned into grass in where the sunlight stops.
627 s16 Map::propagateSunlight(v3s16 start,
628 std::map<v3s16, MapBlock*> & modified_blocks)
630 INodeDefManager *nodemgr = m_gamedef->ndef();
635 v3s16 pos(start.X, y, start.Z);
637 v3s16 blockpos = getNodeBlockPos(pos);
640 block = getBlockNoCreate(blockpos);
642 catch(InvalidPositionException &e)
647 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
648 MapNode n = block->getNode(relpos);
650 if(nodemgr->get(n).sunlight_propagates)
652 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
653 block->setNode(relpos, n);
655 modified_blocks[blockpos] = block;
659 // Sunlight goes no further
666 void Map::updateLighting(enum LightBank bank,
667 std::map<v3s16, MapBlock*> & a_blocks,
668 std::map<v3s16, MapBlock*> & modified_blocks)
670 INodeDefManager *nodemgr = m_gamedef->ndef();
672 /*m_dout<<DTIME<<"Map::updateLighting(): "
673 <<a_blocks.size()<<" blocks."<<std::endl;*/
675 //TimeTaker timer("updateLighting");
679 //u32 count_was = modified_blocks.size();
681 std::map<v3s16, MapBlock*> blocks_to_update;
683 std::set<v3s16> light_sources;
685 std::map<v3s16, u8> unlight_from;
687 int num_bottom_invalid = 0;
690 //TimeTaker t("first stuff");
692 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
693 i != a_blocks.end(); ++i)
695 MapBlock *block = i->second;
699 // Don't bother with dummy blocks.
703 v3s16 pos = block->getPos();
704 v3s16 posnodes = block->getPosRelative();
705 modified_blocks[pos] = block;
706 blocks_to_update[pos] = block;
709 Clear all light from block
711 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
712 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
713 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
718 MapNode n = block->getNode(p);
719 u8 oldlight = n.getLight(bank, nodemgr);
720 n.setLight(bank, 0, nodemgr);
721 block->setNode(p, n);
723 // If node sources light, add to list
724 u8 source = nodemgr->get(n).light_source;
726 light_sources.insert(p + posnodes);
728 // Collect borders for unlighting
729 if((x==0 || x == MAP_BLOCKSIZE-1
730 || y==0 || y == MAP_BLOCKSIZE-1
731 || z==0 || z == MAP_BLOCKSIZE-1)
734 v3s16 p_map = p + posnodes;
735 unlight_from[p_map] = oldlight;
738 catch(InvalidPositionException &e)
741 This would happen when dealing with a
745 infostream<<"updateLighting(): InvalidPositionException"
750 if(bank == LIGHTBANK_DAY)
752 bool bottom_valid = block->propagateSunlight(light_sources);
755 num_bottom_invalid++;
757 // If bottom is valid, we're done.
761 else if(bank == LIGHTBANK_NIGHT)
763 // For night lighting, sunlight is not propagated
768 // Invalid lighting bank
772 /*infostream<<"Bottom for sunlight-propagated block ("
773 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
776 // Bottom sunlight is not valid; get the block and loop to it
780 block = getBlockNoCreate(pos);
782 catch(InvalidPositionException &e)
793 Enable this to disable proper lighting for speeding up map
794 generation for testing or whatever
797 //if(g_settings->get(""))
799 core::map<v3s16, MapBlock*>::Iterator i;
800 i = blocks_to_update.getIterator();
801 for(; i.atEnd() == false; i++)
803 MapBlock *block = i.getNode()->getValue();
804 v3s16 p = block->getPos();
805 block->setLightingExpired(false);
813 //TimeTaker timer("unspreadLight");
814 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
819 u32 diff = modified_blocks.size() - count_was;
820 count_was = modified_blocks.size();
821 infostream<<"unspreadLight modified "<<diff<<std::endl;
825 //TimeTaker timer("spreadLight");
826 spreadLight(bank, light_sources, modified_blocks);
831 u32 diff = modified_blocks.size() - count_was;
832 count_was = modified_blocks.size();
833 infostream<<"spreadLight modified "<<diff<<std::endl;
839 //MapVoxelManipulator vmanip(this);
841 // Make a manual voxel manipulator and load all the blocks
842 // that touch the requested blocks
843 ManualMapVoxelManipulator vmanip(this);
846 //TimeTaker timer("initialEmerge");
848 core::map<v3s16, MapBlock*>::Iterator i;
849 i = blocks_to_update.getIterator();
850 for(; i.atEnd() == false; i++)
852 MapBlock *block = i.getNode()->getValue();
853 v3s16 p = block->getPos();
855 // Add all surrounding blocks
856 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
859 Add all surrounding blocks that have up-to-date lighting
860 NOTE: This doesn't quite do the job (not everything
861 appropriate is lighted)
863 /*for(s16 z=-1; z<=1; z++)
864 for(s16 y=-1; y<=1; y++)
865 for(s16 x=-1; x<=1; x++)
867 v3s16 p2 = p + v3s16(x,y,z);
868 MapBlock *block = getBlockNoCreateNoEx(p2);
873 if(block->getLightingExpired())
875 vmanip.initialEmerge(p2, p2);
878 // Lighting of block will be updated completely
879 block->setLightingExpired(false);
884 //TimeTaker timer("unSpreadLight");
885 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
888 //TimeTaker timer("spreadLight");
889 vmanip.spreadLight(bank, light_sources, nodemgr);
892 //TimeTaker timer("blitBack");
893 vmanip.blitBack(modified_blocks);
895 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
900 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
903 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
904 std::map<v3s16, MapBlock*> & modified_blocks)
906 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
907 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
910 Update information about whether day and night light differ
912 for(std::map<v3s16, MapBlock*>::iterator
913 i = modified_blocks.begin();
914 i != modified_blocks.end(); ++i)
916 MapBlock *block = i->second;
917 block->expireDayNightDiff();
923 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
924 std::map<v3s16, MapBlock*> &modified_blocks)
926 INodeDefManager *ndef = m_gamedef->ndef();
929 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
930 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
933 From this node to nodes underneath:
934 If lighting is sunlight (1.0), unlight neighbours and
939 v3s16 toppos = p + v3s16(0,1,0);
940 //v3s16 bottompos = p + v3s16(0,-1,0);
942 bool node_under_sunlight = true;
943 std::set<v3s16> light_sources;
946 Collect old node for rollback
948 RollbackNode rollback_oldnode(this, p, m_gamedef);
951 If there is a node at top and it doesn't have sunlight,
952 there has not been any sunlight going down.
954 Otherwise there probably is.
957 MapNode topnode = getNode(toppos);
959 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
960 node_under_sunlight = false;
962 catch(InvalidPositionException &e)
967 Remove all light that has come out of this node
970 enum LightBank banks[] =
975 for(s32 i=0; i<2; i++)
977 enum LightBank bank = banks[i];
979 u8 lightwas = getNode(p).getLight(bank, ndef);
981 // Add the block of the added node to modified_blocks
982 v3s16 blockpos = getNodeBlockPos(p);
983 MapBlock * block = getBlockNoCreate(blockpos);
984 assert(block != NULL);
985 modified_blocks[blockpos] = block;
987 assert(isValidPosition(p));
989 // Unlight neighbours of node.
990 // This means setting light of all consequent dimmer nodes
992 // This also collects the nodes at the border which will spread
993 // light again into this.
994 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
996 n.setLight(bank, 0, ndef);
1000 If node lets sunlight through and is under sunlight, it has
1003 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1005 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1009 Remove node metadata
1012 removeNodeMetadata(p);
1015 Set the node on the map
1021 If node is under sunlight and doesn't let sunlight through,
1022 take all sunlighted nodes under it and clear light from them
1023 and from where the light has been spread.
1024 TODO: This could be optimized by mass-unlighting instead
1027 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1031 //m_dout<<DTIME<<"y="<<y<<std::endl;
1032 v3s16 n2pos(p.X, y, p.Z);
1036 n2 = getNode(n2pos);
1038 catch(InvalidPositionException &e)
1043 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1045 unLightNeighbors(LIGHTBANK_DAY,
1046 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1047 light_sources, modified_blocks);
1048 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1056 for(s32 i=0; i<2; i++)
1058 enum LightBank bank = banks[i];
1061 Spread light from all nodes that might be capable of doing so
1063 spreadLight(bank, light_sources, modified_blocks);
1067 Update information about whether day and night light differ
1069 for(std::map<v3s16, MapBlock*>::iterator
1070 i = modified_blocks.begin();
1071 i != modified_blocks.end(); ++i)
1073 i->second->expireDayNightDiff();
1079 if(m_gamedef->rollback())
1081 RollbackNode rollback_newnode(this, p, m_gamedef);
1082 RollbackAction action;
1083 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1084 m_gamedef->rollback()->reportAction(action);
1088 Add neighboring liquid nodes and the node itself if it is
1089 liquid (=water node was added) to transform queue.
1092 v3s16(0,0,0), // self
1093 v3s16(0,0,1), // back
1094 v3s16(0,1,0), // top
1095 v3s16(1,0,0), // right
1096 v3s16(0,0,-1), // front
1097 v3s16(0,-1,0), // bottom
1098 v3s16(-1,0,0), // left
1100 for(u16 i=0; i<7; i++)
1105 v3s16 p2 = p + dirs[i];
1107 MapNode n2 = getNode(p2);
1108 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1110 m_transforming_liquid.push_back(p2);
1113 }catch(InvalidPositionException &e)
1121 void Map::removeNodeAndUpdate(v3s16 p,
1122 std::map<v3s16, MapBlock*> &modified_blocks)
1124 INodeDefManager *ndef = m_gamedef->ndef();
1126 /*PrintInfo(m_dout);
1127 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1128 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1130 bool node_under_sunlight = true;
1132 v3s16 toppos = p + v3s16(0,1,0);
1134 // Node will be replaced with this
1135 content_t replace_material = CONTENT_AIR;
1138 Collect old node for rollback
1140 RollbackNode rollback_oldnode(this, p, m_gamedef);
1143 If there is a node at top and it doesn't have sunlight,
1144 there will be no sunlight going down.
1147 MapNode topnode = getNode(toppos);
1149 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1150 node_under_sunlight = false;
1152 catch(InvalidPositionException &e)
1156 std::set<v3s16> light_sources;
1158 enum LightBank banks[] =
1163 for(s32 i=0; i<2; i++)
1165 enum LightBank bank = banks[i];
1168 Unlight neighbors (in case the node is a light source)
1170 unLightNeighbors(bank, p,
1171 getNode(p).getLight(bank, ndef),
1172 light_sources, modified_blocks);
1176 Remove node metadata
1179 removeNodeMetadata(p);
1183 This also clears the lighting.
1187 n.setContent(replace_material);
1190 for(s32 i=0; i<2; i++)
1192 enum LightBank bank = banks[i];
1195 Recalculate lighting
1197 spreadLight(bank, light_sources, modified_blocks);
1200 // Add the block of the removed node to modified_blocks
1201 v3s16 blockpos = getNodeBlockPos(p);
1202 MapBlock * block = getBlockNoCreate(blockpos);
1203 assert(block != NULL);
1204 modified_blocks[blockpos] = block;
1207 If the removed node was under sunlight, propagate the
1208 sunlight down from it and then light all neighbors
1209 of the propagated blocks.
1211 if(node_under_sunlight)
1213 s16 ybottom = propagateSunlight(p, modified_blocks);
1214 /*m_dout<<DTIME<<"Node was under sunlight. "
1215 "Propagating sunlight";
1216 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1218 for(; y >= ybottom; y--)
1220 v3s16 p2(p.X, y, p.Z);
1221 /*m_dout<<DTIME<<"lighting neighbors of node ("
1222 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1224 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1229 // Set the lighting of this node to 0
1230 // TODO: Is this needed? Lighting is cleared up there already.
1232 MapNode n = getNode(p);
1233 n.setLight(LIGHTBANK_DAY, 0, ndef);
1236 catch(InvalidPositionException &e)
1242 for(s32 i=0; i<2; i++)
1244 enum LightBank bank = banks[i];
1246 // Get the brightest neighbour node and propagate light from it
1247 v3s16 n2p = getBrightestNeighbour(bank, p);
1249 //MapNode n2 = getNode(n2p);
1250 lightNeighbors(bank, n2p, modified_blocks);
1252 catch(InvalidPositionException &e)
1258 Update information about whether day and night light differ
1260 for(std::map<v3s16, MapBlock*>::iterator
1261 i = modified_blocks.begin();
1262 i != modified_blocks.end(); ++i)
1264 i->second->expireDayNightDiff();
1270 if(m_gamedef->rollback())
1272 RollbackNode rollback_newnode(this, p, m_gamedef);
1273 RollbackAction action;
1274 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1275 m_gamedef->rollback()->reportAction(action);
1279 Add neighboring liquid nodes and this node to transform queue.
1280 (it's vital for the node itself to get updated last.)
1283 v3s16(0,0,1), // back
1284 v3s16(0,1,0), // top
1285 v3s16(1,0,0), // right
1286 v3s16(0,0,-1), // front
1287 v3s16(0,-1,0), // bottom
1288 v3s16(-1,0,0), // left
1289 v3s16(0,0,0), // self
1291 for(u16 i=0; i<7; i++)
1296 v3s16 p2 = p + dirs[i];
1298 MapNode n2 = getNode(p2);
1299 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1301 m_transforming_liquid.push_back(p2);
1304 }catch(InvalidPositionException &e)
1310 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1313 event.type = MEET_ADDNODE;
1317 bool succeeded = true;
1319 std::map<v3s16, MapBlock*> modified_blocks;
1320 addNodeAndUpdate(p, n, modified_blocks);
1322 // Copy modified_blocks to event
1323 for(std::map<v3s16, MapBlock*>::iterator
1324 i = modified_blocks.begin();
1325 i != modified_blocks.end(); ++i)
1327 event.modified_blocks.insert(i->first);
1330 catch(InvalidPositionException &e){
1334 dispatchEvent(&event);
1339 bool Map::removeNodeWithEvent(v3s16 p)
1342 event.type = MEET_REMOVENODE;
1345 bool succeeded = true;
1347 std::map<v3s16, MapBlock*> modified_blocks;
1348 removeNodeAndUpdate(p, modified_blocks);
1350 // Copy modified_blocks to event
1351 for(std::map<v3s16, MapBlock*>::iterator
1352 i = modified_blocks.begin();
1353 i != modified_blocks.end(); ++i)
1355 event.modified_blocks.insert(i->first);
1358 catch(InvalidPositionException &e){
1362 dispatchEvent(&event);
1367 bool Map::getDayNightDiff(v3s16 blockpos)
1370 v3s16 p = blockpos + v3s16(0,0,0);
1371 MapBlock *b = getBlockNoCreate(p);
1372 if(b->getDayNightDiff())
1375 catch(InvalidPositionException &e){}
1378 v3s16 p = blockpos + v3s16(-1,0,0);
1379 MapBlock *b = getBlockNoCreate(p);
1380 if(b->getDayNightDiff())
1383 catch(InvalidPositionException &e){}
1385 v3s16 p = blockpos + v3s16(0,-1,0);
1386 MapBlock *b = getBlockNoCreate(p);
1387 if(b->getDayNightDiff())
1390 catch(InvalidPositionException &e){}
1392 v3s16 p = blockpos + v3s16(0,0,-1);
1393 MapBlock *b = getBlockNoCreate(p);
1394 if(b->getDayNightDiff())
1397 catch(InvalidPositionException &e){}
1400 v3s16 p = blockpos + v3s16(1,0,0);
1401 MapBlock *b = getBlockNoCreate(p);
1402 if(b->getDayNightDiff())
1405 catch(InvalidPositionException &e){}
1407 v3s16 p = blockpos + v3s16(0,1,0);
1408 MapBlock *b = getBlockNoCreate(p);
1409 if(b->getDayNightDiff())
1412 catch(InvalidPositionException &e){}
1414 v3s16 p = blockpos + v3s16(0,0,1);
1415 MapBlock *b = getBlockNoCreate(p);
1416 if(b->getDayNightDiff())
1419 catch(InvalidPositionException &e){}
1425 Updates usage timers
1427 void Map::timerUpdate(float dtime, float unload_timeout,
1428 std::list<v3s16> *unloaded_blocks)
1430 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1432 // Profile modified reasons
1433 Profiler modprofiler;
1435 std::list<v2s16> sector_deletion_queue;
1436 u32 deleted_blocks_count = 0;
1437 u32 saved_blocks_count = 0;
1438 u32 block_count_all = 0;
1441 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1442 si != m_sectors.end(); ++si)
1444 MapSector *sector = si->second;
1446 bool all_blocks_deleted = true;
1448 std::list<MapBlock*> blocks;
1449 sector->getBlocks(blocks);
1451 for(std::list<MapBlock*>::iterator i = blocks.begin();
1452 i != blocks.end(); ++i)
1454 MapBlock *block = (*i);
1456 block->incrementUsageTimer(dtime);
1458 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1460 v3s16 p = block->getPos();
1463 if(block->getModified() != MOD_STATE_CLEAN
1464 && save_before_unloading)
1466 modprofiler.add(block->getModifiedReason(), 1);
1468 saved_blocks_count++;
1471 // Delete from memory
1472 sector->deleteBlock(block);
1475 unloaded_blocks->push_back(p);
1477 deleted_blocks_count++;
1481 all_blocks_deleted = false;
1486 if(all_blocks_deleted)
1488 sector_deletion_queue.push_back(si->first);
1493 // Finally delete the empty sectors
1494 deleteSectors(sector_deletion_queue);
1496 if(deleted_blocks_count != 0)
1498 PrintInfo(infostream); // ServerMap/ClientMap:
1499 infostream<<"Unloaded "<<deleted_blocks_count
1500 <<" blocks from memory";
1501 if(save_before_unloading)
1502 infostream<<", of which "<<saved_blocks_count<<" were written";
1503 infostream<<", "<<block_count_all<<" blocks in memory";
1504 infostream<<"."<<std::endl;
1505 if(saved_blocks_count != 0){
1506 PrintInfo(infostream); // ServerMap/ClientMap:
1507 infostream<<"Blocks modified by: "<<std::endl;
1508 modprofiler.print(infostream);
1513 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1515 timerUpdate(0.0, -1.0, unloaded_blocks);
1518 void Map::deleteSectors(std::list<v2s16> &list)
1520 for(std::list<v2s16>::iterator j = list.begin();
1521 j != list.end(); ++j)
1523 MapSector *sector = m_sectors[*j];
1524 // If sector is in sector cache, remove it from there
1525 if(m_sector_cache == sector)
1526 m_sector_cache = NULL;
1527 // Remove from map and delete
1528 m_sectors.erase(*j);
1534 void Map::unloadUnusedData(float timeout,
1535 core::list<v3s16> *deleted_blocks)
1537 core::list<v2s16> sector_deletion_queue;
1538 u32 deleted_blocks_count = 0;
1539 u32 saved_blocks_count = 0;
1541 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1542 for(; si.atEnd() == false; si++)
1544 MapSector *sector = si.getNode()->getValue();
1546 bool all_blocks_deleted = true;
1548 core::list<MapBlock*> blocks;
1549 sector->getBlocks(blocks);
1550 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1551 i != blocks.end(); i++)
1553 MapBlock *block = (*i);
1555 if(block->getUsageTimer() > timeout)
1558 if(block->getModified() != MOD_STATE_CLEAN)
1561 saved_blocks_count++;
1563 // Delete from memory
1564 sector->deleteBlock(block);
1565 deleted_blocks_count++;
1569 all_blocks_deleted = false;
1573 if(all_blocks_deleted)
1575 sector_deletion_queue.push_back(si.getNode()->getKey());
1579 deleteSectors(sector_deletion_queue);
1581 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1582 <<", of which "<<saved_blocks_count<<" were wr."
1585 //return sector_deletion_queue.getSize();
1586 //return deleted_blocks_count;
1590 void Map::PrintInfo(std::ostream &out)
1595 #define WATER_DROP_BOOST 4
1599 NEIGHBOR_SAME_LEVEL,
1602 struct NodeNeighbor {
1606 bool l; //can liquid
1610 void Map::transforming_liquid_add(v3s16 p) {
1611 m_transforming_liquid.push_back(p);
1614 s32 Map::transforming_liquid_size() {
1615 return m_transforming_liquid.size();
1618 const v3s16 g_7dirs[7] =
1620 // +right, +top, +back
1621 v3s16( 0,-1, 0), // bottom
1622 v3s16( 0, 0, 0), // self
1623 v3s16( 0, 0, 1), // back
1624 v3s16( 0, 0,-1), // front
1625 v3s16( 1, 0, 0), // right
1626 v3s16(-1, 0, 0), // left
1627 v3s16( 0, 1, 0) // top
1634 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1636 INodeDefManager *nodemgr = m_gamedef->ndef();
1638 DSTACK(__FUNCTION_NAME);
1639 //TimeTaker timer("transformLiquids()");
1642 u32 initial_size = m_transforming_liquid.size();
1644 u8 relax = g_settings->getS16("liquid_relax");
1645 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1646 int water_level = g_settings->getS16("water_level");
1648 // list of nodes that due to viscosity have not reached their max level height
1649 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1651 // List of MapBlocks that will require a lighting update (due to lava)
1652 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1654 u16 loop_max = g_settings->getU16("liquid_loop_max");
1656 while (m_transforming_liquid.size() > 0)
1658 // This should be done here so that it is done when continue is used
1659 if (loopcount >= initial_size || loopcount >= loop_max)
1663 Get a queued transforming liquid node
1665 v3s16 p0 = m_transforming_liquid.pop_front();
1666 u16 total_level = 0;
1667 // surrounding flowing liquid nodes
1668 NodeNeighbor neighbors[7];
1669 // current level of every block
1670 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1672 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1673 s8 can_liquid_same_level = 0;
1674 content_t liquid_kind = CONTENT_IGNORE;
1675 content_t liquid_kind_flowing = CONTENT_IGNORE;
1677 Collect information about the environment
1679 const v3s16 *dirs = g_7dirs;
1680 for (u16 i = 0; i < 7; i++) {
1681 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1684 nt = NEIGHBOR_UPPER;
1687 nt = NEIGHBOR_LOWER;
1690 v3s16 npos = p0 + dirs[i];
1692 neighbors[i].n = getNodeNoEx(npos);
1693 neighbors[i].t = nt;
1694 neighbors[i].p = npos;
1697 NodeNeighbor & nb = neighbors[i];
1699 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1701 if (nb.n.getContent() == CONTENT_AIR) {
1702 liquid_levels[i] = 0;
1707 // if this node is not (yet) of a liquid type,
1708 // choose the first liquid type we encounter
1709 if (liquid_kind_flowing == CONTENT_IGNORE)
1710 liquid_kind_flowing = nodemgr->getId(
1711 nodemgr->get(nb.n).liquid_alternative_flowing);
1712 if (liquid_kind == CONTENT_IGNORE)
1713 liquid_kind = nb.n.getContent();
1714 if (nb.n.getContent() == liquid_kind) {
1715 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1717 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1720 case LIQUID_FLOWING:
1721 // if this node is not (yet) of a liquid type,
1722 // choose the first liquid type we encounter
1723 if (liquid_kind_flowing == CONTENT_IGNORE)
1724 liquid_kind_flowing = nb.n.getContent();
1725 if (liquid_kind == CONTENT_IGNORE)
1726 liquid_kind = nodemgr->getId(
1727 nodemgr->get(nb.n).liquid_alternative_source);
1728 if (nb.n.getContent() == liquid_kind_flowing) {
1729 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1735 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1736 ++can_liquid_same_level;
1737 if (liquid_levels[i] > 0)
1738 total_level += liquid_levels[i];
1741 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1742 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1743 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1744 << nodemgr->get(nb.n.getContent()).liquid_type
1745 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1746 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1747 << " tlevel=" << (int)total_level << " cansame="
1748 << (int)can_liquid_same_level << std::endl;
1752 if (liquid_kind == CONTENT_IGNORE ||
1753 !neighbors[D_SELF].l ||
1757 // fill bottom block
1758 if (neighbors[D_BOTTOM].l) {
1759 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1760 LIQUID_LEVEL_SOURCE : total_level;
1761 total_level -= liquid_levels_want[D_BOTTOM];
1765 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1766 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1767 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1768 (can_liquid_same_level - relax) &&
1769 can_liquid_same_level >= relax + 1) {
1770 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1773 // prevent lakes in air above unloaded blocks
1774 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level || !fast_flood) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE) {
1778 // calculate self level 5 blocks
1780 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1781 ? LIQUID_LEVEL_SOURCE
1782 : total_level / can_liquid_same_level;
1783 total_level -= want_level * can_liquid_same_level;
1786 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1787 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1788 total_level <= (can_liquid_same_level - relax) &&
1789 can_liquid_same_level >= relax + 1) {
1793 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1794 if (!neighbors[ii].l)
1796 liquid_levels_want[ii] = want_level;
1797 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0
1798 && liquid_levels[ii] > liquid_levels_want[ii]
1800 ++liquid_levels_want[ii];
1805 for (u16 ii = 0; ii < 7; ++ii) {
1806 if (total_level < 1) break;
1807 if (liquid_levels_want[ii] >= 0 &&
1808 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1809 ++liquid_levels_want[ii];
1814 // fill top block if can
1815 if (neighbors[D_TOP].l) {
1816 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1817 LIQUID_LEVEL_SOURCE : total_level;
1818 total_level -= liquid_levels_want[D_TOP];
1821 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1822 if ( neighbors[ii].i ||
1823 (liquid_levels_want[ii] >= 0 &&
1824 (fast_flood && p0.Y < water_level &&
1825 (initial_size >= 1000
1827 && want_level >= LIQUID_LEVEL_SOURCE/4
1828 && can_liquid_same_level >= 5
1829 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1830 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1833 if (total_level > 0) //|| flowed != volume)
1834 infostream <<" AFTER level=" << (int)total_level
1835 //<< " flowed="<<flowed<< " volume=" << volume
1836 << " wantsame="<<(int)want_level<< " top="
1837 << (int)liquid_levels_want[D_TOP]<< " topwas="
1838 << (int)liquid_levels[D_TOP]<< " bot="
1839 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1843 for (u16 i = 0; i < 7; i++) {
1844 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1846 MapNode & n0 = neighbors[i].n;
1847 p0 = neighbors[i].p;
1849 decide on the type (and possibly level) of the current node
1851 content_t new_node_content;
1852 s8 new_node_level = -1;
1853 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1854 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1855 // amount to gain, limited by viscosity
1856 // must be at least 1 in absolute value
1857 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1858 if (level_inc < -viscosity || level_inc > viscosity)
1859 new_node_level = liquid_levels[i] + level_inc/viscosity;
1860 else if (level_inc < 0)
1861 new_node_level = liquid_levels[i] - 1;
1862 else if (level_inc > 0)
1863 new_node_level = liquid_levels[i] + 1;
1865 new_node_level = liquid_levels_want[i];
1868 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1869 new_node_content = liquid_kind;
1870 else if (new_node_level > 0)
1871 new_node_content = liquid_kind_flowing;
1873 new_node_content = CONTENT_AIR;
1875 // last level must flow down on stairs
1876 if (liquid_levels_want[i] != liquid_levels[i] &&
1877 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1878 new_node_level >= 1 && new_node_level <= 2) {
1879 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1880 if (neighbors[ii].l)
1881 must_reflow_second.push_back(p0 + dirs[ii]);
1886 check if anything has changed.
1887 if not, just continue with the next node.
1890 new_node_content == n0.getContent()
1891 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1892 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1893 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1894 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1897 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1898 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1899 LIQUID_INFINITY_MASK) == neighbors[i].i
1907 update the current node
1909 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1910 // set level to last 3 bits, flowing down bit to 4th bit
1911 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1912 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1913 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1914 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1917 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1918 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1919 <<(int)new_node_level<<std::endl;
1922 n0.setContent(new_node_content);
1923 // Find out whether there is a suspect for this action
1924 std::string suspect;
1925 if(m_gamedef->rollback()){
1926 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1929 if(!suspect.empty()){
1931 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1932 // Get old node for rollback
1933 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1937 RollbackNode rollback_newnode(this, p0, m_gamedef);
1938 RollbackAction action;
1939 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1940 m_gamedef->rollback()->reportAction(action);
1946 v3s16 blockpos = getNodeBlockPos(p0);
1947 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1949 modified_blocks[blockpos] = block;
1950 // If node emits light, MapBlock requires lighting update
1951 if(nodemgr->get(n0).light_source != 0)
1952 lighting_modified_blocks[block->getPos()] = block;
1954 must_reflow.push_back(neighbors[i].p);
1956 /* //for better relax only same level
1957 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1958 if (!neighbors[ii].l) continue;
1959 must_reflow.push_back(p0 + dirs[ii]);
1964 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1965 <<" reflow="<<must_reflow.size()
1966 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1968 while (must_reflow.size() > 0)
1969 m_transforming_liquid.push_back(must_reflow.pop_front());
1970 while (must_reflow_second.size() > 0)
1971 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1972 updateLighting(lighting_modified_blocks, modified_blocks);
1975 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1978 if (g_settings->getBool("liquid_finite"))
1979 return Map::transformLiquidsFinite(modified_blocks);
1981 INodeDefManager *nodemgr = m_gamedef->ndef();
1983 DSTACK(__FUNCTION_NAME);
1984 //TimeTaker timer("transformLiquids()");
1987 u32 initial_size = m_transforming_liquid.size();
1989 /*if(initial_size != 0)
1990 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1992 // list of nodes that due to viscosity have not reached their max level height
1993 UniqueQueue<v3s16> must_reflow;
1995 // List of MapBlocks that will require a lighting update (due to lava)
1996 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1998 u16 loop_max = g_settings->getU16("liquid_loop_max");
2000 while(m_transforming_liquid.size() != 0)
2002 // This should be done here so that it is done when continue is used
2003 if(loopcount >= initial_size || loopcount >= loop_max)
2008 Get a queued transforming liquid node
2010 v3s16 p0 = m_transforming_liquid.pop_front();
2012 MapNode n0 = getNodeNoEx(p0);
2015 Collect information about current node
2017 s8 liquid_level = -1;
2018 content_t liquid_kind = CONTENT_IGNORE;
2019 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2020 switch (liquid_type) {
2022 liquid_level = LIQUID_LEVEL_SOURCE;
2023 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2025 case LIQUID_FLOWING:
2026 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2027 liquid_kind = n0.getContent();
2030 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2031 // continue with the next node.
2032 if (n0.getContent() != CONTENT_AIR)
2034 liquid_kind = CONTENT_AIR;
2039 Collect information about the environment
2041 const v3s16 *dirs = g_6dirs;
2042 NodeNeighbor sources[6]; // surrounding sources
2043 int num_sources = 0;
2044 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2046 NodeNeighbor airs[6]; // surrounding air
2048 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2049 int num_neutrals = 0;
2050 bool flowing_down = false;
2051 for (u16 i = 0; i < 6; i++) {
2052 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2055 nt = NEIGHBOR_UPPER;
2058 nt = NEIGHBOR_LOWER;
2061 v3s16 npos = p0 + dirs[i];
2062 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2063 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2065 if (nb.n.getContent() == CONTENT_AIR) {
2066 airs[num_airs++] = nb;
2067 // if the current node is a water source the neighbor
2068 // should be enqueded for transformation regardless of whether the
2069 // current node changes or not.
2070 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2071 m_transforming_liquid.push_back(npos);
2072 // if the current node happens to be a flowing node, it will start to flow down here.
2073 if (nb.t == NEIGHBOR_LOWER) {
2074 flowing_down = true;
2077 neutrals[num_neutrals++] = nb;
2081 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2082 if (liquid_kind == CONTENT_AIR)
2083 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2084 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2085 neutrals[num_neutrals++] = nb;
2087 // Do not count bottom source, it will screw things up
2089 sources[num_sources++] = nb;
2092 case LIQUID_FLOWING:
2093 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2094 if (liquid_kind == CONTENT_AIR)
2095 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2096 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2097 neutrals[num_neutrals++] = nb;
2099 flows[num_flows++] = nb;
2100 if (nb.t == NEIGHBOR_LOWER)
2101 flowing_down = true;
2108 decide on the type (and possibly level) of the current node
2110 content_t new_node_content;
2111 s8 new_node_level = -1;
2112 s8 max_node_level = -1;
2113 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2114 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2115 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2116 // it's perfectly safe to use liquid_kind here to determine the new node content.
2117 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2118 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2119 // liquid_kind is set properly, see above
2120 new_node_content = liquid_kind;
2121 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2123 // no surrounding sources, so get the maximum level that can flow into this node
2124 for (u16 i = 0; i < num_flows; i++) {
2125 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2126 switch (flows[i].t) {
2127 case NEIGHBOR_UPPER:
2128 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2129 max_node_level = LIQUID_LEVEL_MAX;
2130 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2131 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2132 } else if (nb_liquid_level > max_node_level)
2133 max_node_level = nb_liquid_level;
2135 case NEIGHBOR_LOWER:
2137 case NEIGHBOR_SAME_LEVEL:
2138 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2139 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2140 max_node_level = nb_liquid_level - 1;
2146 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2147 if (viscosity > 1 && max_node_level != liquid_level) {
2148 // amount to gain, limited by viscosity
2149 // must be at least 1 in absolute value
2150 s8 level_inc = max_node_level - liquid_level;
2151 if (level_inc < -viscosity || level_inc > viscosity)
2152 new_node_level = liquid_level + level_inc/viscosity;
2153 else if (level_inc < 0)
2154 new_node_level = liquid_level - 1;
2155 else if (level_inc > 0)
2156 new_node_level = liquid_level + 1;
2157 if (new_node_level != max_node_level)
2158 must_reflow.push_back(p0);
2160 new_node_level = max_node_level;
2162 if (new_node_level >= 0)
2163 new_node_content = liquid_kind;
2165 new_node_content = CONTENT_AIR;
2170 check if anything has changed. if not, just continue with the next node.
2172 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2173 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2174 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2180 update the current node
2182 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2183 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2184 // set level to last 3 bits, flowing down bit to 4th bit
2185 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2187 // set the liquid level and flow bit to 0
2188 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2190 n0.setContent(new_node_content);
2192 // Find out whether there is a suspect for this action
2193 std::string suspect;
2194 if(m_gamedef->rollback()){
2195 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2198 if(!suspect.empty()){
2200 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2201 // Get old node for rollback
2202 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2206 RollbackNode rollback_newnode(this, p0, m_gamedef);
2207 RollbackAction action;
2208 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2209 m_gamedef->rollback()->reportAction(action);
2215 v3s16 blockpos = getNodeBlockPos(p0);
2216 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2218 modified_blocks[blockpos] = block;
2219 // If node emits light, MapBlock requires lighting update
2220 if(nodemgr->get(n0).light_source != 0)
2221 lighting_modified_blocks[block->getPos()] = block;
2225 enqueue neighbors for update if neccessary
2227 switch (nodemgr->get(n0.getContent()).liquid_type) {
2229 case LIQUID_FLOWING:
2230 // make sure source flows into all neighboring nodes
2231 for (u16 i = 0; i < num_flows; i++)
2232 if (flows[i].t != NEIGHBOR_UPPER)
2233 m_transforming_liquid.push_back(flows[i].p);
2234 for (u16 i = 0; i < num_airs; i++)
2235 if (airs[i].t != NEIGHBOR_UPPER)
2236 m_transforming_liquid.push_back(airs[i].p);
2239 // this flow has turned to air; neighboring flows might need to do the same
2240 for (u16 i = 0; i < num_flows; i++)
2241 m_transforming_liquid.push_back(flows[i].p);
2245 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2246 while (must_reflow.size() > 0)
2247 m_transforming_liquid.push_back(must_reflow.pop_front());
2248 updateLighting(lighting_modified_blocks, modified_blocks);
2251 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2253 v3s16 blockpos = getNodeBlockPos(p);
2254 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2255 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2257 infostream<<"Map::getNodeMetadata(): Need to emerge "
2258 <<PP(blockpos)<<std::endl;
2259 block = emergeBlock(blockpos, false);
2263 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2267 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2271 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2273 v3s16 blockpos = getNodeBlockPos(p);
2274 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2275 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2277 infostream<<"Map::setNodeMetadata(): Need to emerge "
2278 <<PP(blockpos)<<std::endl;
2279 block = emergeBlock(blockpos, false);
2283 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2287 block->m_node_metadata.set(p_rel, meta);
2290 void Map::removeNodeMetadata(v3s16 p)
2292 v3s16 blockpos = getNodeBlockPos(p);
2293 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2294 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2297 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2301 block->m_node_metadata.remove(p_rel);
2304 NodeTimer Map::getNodeTimer(v3s16 p)
2306 v3s16 blockpos = getNodeBlockPos(p);
2307 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2308 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2310 infostream<<"Map::getNodeTimer(): Need to emerge "
2311 <<PP(blockpos)<<std::endl;
2312 block = emergeBlock(blockpos, false);
2316 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2320 NodeTimer t = block->m_node_timers.get(p_rel);
2324 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2326 v3s16 blockpos = getNodeBlockPos(p);
2327 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2328 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2330 infostream<<"Map::setNodeTimer(): Need to emerge "
2331 <<PP(blockpos)<<std::endl;
2332 block = emergeBlock(blockpos, false);
2336 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2340 block->m_node_timers.set(p_rel, t);
2343 void Map::removeNodeTimer(v3s16 p)
2345 v3s16 blockpos = getNodeBlockPos(p);
2346 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2347 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2350 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2354 block->m_node_timers.remove(p_rel);
2360 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2361 Map(dout_server, gamedef),
2363 m_map_metadata_changed(true),
2365 m_database_read(NULL),
2366 m_database_write(NULL)
2368 verbosestream<<__FUNCTION_NAME<<std::endl;
2371 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2373 m_mgparams = new MapgenV6Params();
2375 m_seed = m_mgparams->seed;
2377 if (g_settings->get("fixed_map_seed").empty())
2379 m_seed = (((u64)(myrand() & 0xffff) << 0)
2380 | ((u64)(myrand() & 0xffff) << 16)
2381 | ((u64)(myrand() & 0xffff) << 32)
2382 | ((u64)(myrand() & 0xffff) << 48));
2383 m_mgparams->seed = m_seed;
2387 Experimental and debug stuff
2394 Try to load map; if not found, create a new one.
2397 m_savedir = savedir;
2398 m_map_saving_enabled = false;
2402 // If directory exists, check contents and load if possible
2403 if(fs::PathExists(m_savedir))
2405 // If directory is empty, it is safe to save into it.
2406 if(fs::GetDirListing(m_savedir).size() == 0)
2408 infostream<<"ServerMap: Empty save directory is valid."
2410 m_map_saving_enabled = true;
2415 // Load map metadata (seed, chunksize)
2418 catch(SettingNotFoundException &e){
2419 infostream<<"ServerMap: Some metadata not found."
2420 <<" Using default settings."<<std::endl;
2422 catch(FileNotGoodException &e){
2423 infostream<<"WARNING: Could not load map metadata"
2424 //<<" Disabling chunk-based generator."
2429 infostream<<"ServerMap: Successfully loaded map "
2430 <<"metadata from "<<savedir
2431 <<", assuming valid save directory."
2432 <<" seed="<<m_seed<<"."
2435 m_map_saving_enabled = true;
2436 // Map loaded, not creating new one
2440 // If directory doesn't exist, it is safe to save to it
2442 m_map_saving_enabled = true;
2445 catch(std::exception &e)
2447 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2448 <<", exception: "<<e.what()<<std::endl;
2449 infostream<<"Please remove the map or fix it."<<std::endl;
2450 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2453 infostream<<"Initializing new map."<<std::endl;
2455 // Create zero sector
2456 emergeSector(v2s16(0,0));
2458 // Initially write whole map
2459 save(MOD_STATE_CLEAN);
2462 ServerMap::~ServerMap()
2464 verbosestream<<__FUNCTION_NAME<<std::endl;
2468 if(m_map_saving_enabled)
2470 // Save only changed parts
2471 save(MOD_STATE_WRITE_AT_UNLOAD);
2472 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2476 infostream<<"ServerMap: Map not saved"<<std::endl;
2479 catch(std::exception &e)
2481 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2482 <<", exception: "<<e.what()<<std::endl;
2486 Close database if it was opened
2489 sqlite3_finalize(m_database_read);
2490 if(m_database_write)
2491 sqlite3_finalize(m_database_write);
2493 sqlite3_close(m_database);
2499 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2500 for(; i.atEnd() == false; i++)
2502 MapChunk *chunk = i.getNode()->getValue();
2510 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2512 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2513 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2515 s16 chunksize = m_mgparams->chunksize;
2516 s16 coffset = -chunksize / 2;
2517 v3s16 chunk_offset(coffset, coffset, coffset);
2518 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2519 v3s16 blockpos_min = blockpos_div * chunksize;
2520 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2521 blockpos_min += chunk_offset;
2522 blockpos_max += chunk_offset;
2524 v3s16 extra_borders(1,1,1);
2526 // Do nothing if not inside limits (+-1 because of neighbors)
2527 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2528 blockpos_over_limit(blockpos_max + extra_borders))
2531 data->seed = m_seed;
2532 data->blockpos_min = blockpos_min;
2533 data->blockpos_max = blockpos_max;
2534 data->blockpos_requested = blockpos;
2535 data->nodedef = m_gamedef->ndef();
2538 Create the whole area of this and the neighboring blocks
2541 //TimeTaker timer("initBlockMake() create area");
2543 for(s16 x=blockpos_min.X-extra_borders.X;
2544 x<=blockpos_max.X+extra_borders.X; x++)
2545 for(s16 z=blockpos_min.Z-extra_borders.Z;
2546 z<=blockpos_max.Z+extra_borders.Z; z++)
2548 v2s16 sectorpos(x, z);
2549 // Sector metadata is loaded from disk if not already loaded.
2550 ServerMapSector *sector = createSector(sectorpos);
2553 for(s16 y=blockpos_min.Y-extra_borders.Y;
2554 y<=blockpos_max.Y+extra_borders.Y; y++)
2557 //MapBlock *block = createBlock(p);
2558 // 1) get from memory, 2) load from disk
2559 MapBlock *block = emergeBlock(p, false);
2560 // 3) create a blank one
2563 block = createBlock(p);
2566 Block gets sunlight if this is true.
2568 Refer to the map generator heuristics.
2570 bool ug = m_emerge->isBlockUnderground(p);
2571 block->setIsUnderground(ug);
2574 // Lighting will not be valid after make_chunk is called
2575 block->setLightingExpired(true);
2576 // Lighting will be calculated
2577 //block->setLightingExpired(false);
2583 Now we have a big empty area.
2585 Make a ManualMapVoxelManipulator that contains this and the
2589 // The area that contains this block and it's neighbors
2590 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2591 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2593 data->vmanip = new ManualMapVoxelManipulator(this);
2594 //data->vmanip->setMap(this);
2598 //TimeTaker timer("initBlockMake() initialEmerge");
2599 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2602 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2603 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2604 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2605 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2606 core::map<v3s16, u8>::Node *n;
2607 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2610 u8 flags = n->getValue();
2611 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2617 // Data is ready now.
2621 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2622 std::map<v3s16, MapBlock*> &changed_blocks)
2624 v3s16 blockpos_min = data->blockpos_min;
2625 v3s16 blockpos_max = data->blockpos_max;
2626 v3s16 blockpos_requested = data->blockpos_requested;
2627 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2628 <<blockpos_requested.Y<<","
2629 <<blockpos_requested.Z<<")"<<std::endl;*/
2631 v3s16 extra_borders(1,1,1);
2633 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2635 /*infostream<<"Resulting vmanip:"<<std::endl;
2636 data->vmanip.print(infostream);*/
2638 // Make sure affected blocks are loaded
2639 for(s16 x=blockpos_min.X-extra_borders.X;
2640 x<=blockpos_max.X+extra_borders.X; x++)
2641 for(s16 z=blockpos_min.Z-extra_borders.Z;
2642 z<=blockpos_max.Z+extra_borders.Z; z++)
2643 for(s16 y=blockpos_min.Y-extra_borders.Y;
2644 y<=blockpos_max.Y+extra_borders.Y; y++)
2647 // Load from disk if not already in memory
2648 emergeBlock(p, false);
2652 Blit generated stuff to map
2653 NOTE: blitBackAll adds nearly everything to changed_blocks
2657 //TimeTaker timer("finishBlockMake() blitBackAll");
2658 data->vmanip->blitBackAll(&changed_blocks);
2661 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2664 Copy transforming liquid information
2666 while(data->transforming_liquid.size() > 0)
2668 v3s16 p = data->transforming_liquid.pop_front();
2669 m_transforming_liquid.push_back(p);
2673 Do stuff in central blocks
2681 TimeTaker t("finishBlockMake lighting update");
2683 core::map<v3s16, MapBlock*> lighting_update_blocks;
2686 for(s16 x=blockpos_min.X-extra_borders.X;
2687 x<=blockpos_max.X+extra_borders.X; x++)
2688 for(s16 z=blockpos_min.Z-extra_borders.Z;
2689 z<=blockpos_max.Z+extra_borders.Z; z++)
2690 for(s16 y=blockpos_min.Y-extra_borders.Y;
2691 y<=blockpos_max.Y+extra_borders.Y; y++)
2694 MapBlock *block = getBlockNoCreateNoEx(p);
2696 lighting_update_blocks.insert(block->getPos(), block);
2699 updateLighting(lighting_update_blocks, changed_blocks);
2703 Set lighting to non-expired state in all of them.
2704 This is cheating, but it is not fast enough if all of them
2705 would actually be updated.
2707 for(s16 x=blockpos_min.X-extra_borders.X;
2708 x<=blockpos_max.X+extra_borders.X; x++)
2709 for(s16 z=blockpos_min.Z-extra_borders.Z;
2710 z<=blockpos_max.Z+extra_borders.Z; z++)
2711 for(s16 y=blockpos_min.Y-extra_borders.Y;
2712 y<=blockpos_max.Y+extra_borders.Y; y++)
2715 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2719 if(enable_mapgen_debug_info == false)
2720 t.stop(true); // Hide output
2725 Go through changed blocks
2727 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2728 i != changed_blocks.end(); ++i)
2730 MapBlock *block = i->second;
2733 Update day/night difference cache of the MapBlocks
2735 block->expireDayNightDiff();
2737 Set block as modified
2739 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2740 "finishBlockMake expireDayNightDiff");
2744 Set central blocks as generated
2746 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2747 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2748 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2751 MapBlock *block = getBlockNoCreateNoEx(p);
2753 block->setGenerated(true);
2757 Save changed parts of map
2758 NOTE: Will be saved later.
2760 //save(MOD_STATE_WRITE_AT_UNLOAD);
2762 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2763 <<","<<blockpos_requested.Y<<","
2764 <<blockpos_requested.Z<<")"<<std::endl;*/
2766 if(enable_mapgen_debug_info)
2769 Analyze resulting blocks
2771 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2772 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2773 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2774 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2775 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2776 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2778 v3s16 p = v3s16(x,y,z);
2779 MapBlock *block = getBlockNoCreateNoEx(p);
2781 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2782 infostream<<"Generated "<<spos<<": "
2783 <<analyze_block(block)<<std::endl;
2788 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2794 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2796 DSTACKF("%s: p2d=(%d,%d)",
2801 Check if it exists already in memory
2803 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2808 Try to load it from disk (with blocks)
2810 //if(loadSectorFull(p2d) == true)
2813 Try to load metadata from disk
2816 if(loadSectorMeta(p2d) == true)
2818 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2821 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2822 throw InvalidPositionException("");
2828 Do not create over-limit
2830 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2831 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2832 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2833 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2834 throw InvalidPositionException("createSector(): pos. over limit");
2837 Generate blank sector
2840 sector = new ServerMapSector(this, p2d, m_gamedef);
2842 // Sector position on map in nodes
2843 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2848 m_sectors[p2d] = sector;
2855 This is a quick-hand function for calling makeBlock().
2857 MapBlock * ServerMap::generateBlock(
2859 std::map<v3s16, MapBlock*> &modified_blocks
2862 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2864 /*infostream<<"generateBlock(): "
2865 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2868 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2870 TimeTaker timer("generateBlock");
2872 //MapBlock *block = original_dummy;
2874 v2s16 p2d(p.X, p.Z);
2875 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2878 Do not generate over-limit
2880 if(blockpos_over_limit(p))
2882 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2883 throw InvalidPositionException("generateBlock(): pos. over limit");
2887 Create block make data
2890 initBlockMake(&data, p);
2896 TimeTaker t("mapgen::make_block()");
2897 mapgen->makeChunk(&data);
2898 //mapgen::make_block(&data);
2900 if(enable_mapgen_debug_info == false)
2901 t.stop(true); // Hide output
2905 Blit data back on map, update lighting, add mobs and whatever this does
2907 finishBlockMake(&data, modified_blocks);
2912 MapBlock *block = getBlockNoCreateNoEx(p);
2920 bool erroneus_content = false;
2921 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2922 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2923 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2926 MapNode n = block->getNode(p);
2927 if(n.getContent() == CONTENT_IGNORE)
2929 infostream<<"CONTENT_IGNORE at "
2930 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2932 erroneus_content = true;
2936 if(erroneus_content)
2945 Generate a completely empty block
2949 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2950 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2952 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2955 n.setContent(CONTENT_AIR);
2956 block->setNode(v3s16(x0,y0,z0), n);
2962 if(enable_mapgen_debug_info == false)
2963 timer.stop(true); // Hide output
2969 MapBlock * ServerMap::createBlock(v3s16 p)
2971 DSTACKF("%s: p=(%d,%d,%d)",
2972 __FUNCTION_NAME, p.X, p.Y, p.Z);
2975 Do not create over-limit
2977 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2978 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2979 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2980 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2981 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2982 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2983 throw InvalidPositionException("createBlock(): pos. over limit");
2985 v2s16 p2d(p.X, p.Z);
2988 This will create or load a sector if not found in memory.
2989 If block exists on disk, it will be loaded.
2991 NOTE: On old save formats, this will be slow, as it generates
2992 lighting on blocks for them.
2994 ServerMapSector *sector;
2996 sector = (ServerMapSector*)createSector(p2d);
2997 assert(sector->getId() == MAPSECTOR_SERVER);
2999 catch(InvalidPositionException &e)
3001 infostream<<"createBlock: createSector() failed"<<std::endl;
3005 NOTE: This should not be done, or at least the exception
3006 should not be passed on as std::exception, because it
3007 won't be catched at all.
3009 /*catch(std::exception &e)
3011 infostream<<"createBlock: createSector() failed: "
3012 <<e.what()<<std::endl;
3017 Try to get a block from the sector
3020 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3023 if(block->isDummy())
3028 block = sector->createBlankBlock(block_y);
3033 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3035 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3037 p.X, p.Y, p.Z, create_blank);
3040 MapBlock *block = getBlockNoCreateNoEx(p);
3041 if(block && block->isDummy() == false)
3046 MapBlock *block = loadBlock(p);
3052 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3053 MapBlock *block = sector->createBlankBlock(p.Y);
3057 /*if(allow_generate)
3059 std::map<v3s16, MapBlock*> modified_blocks;
3060 MapBlock *block = generateBlock(p, modified_blocks);
3064 event.type = MEET_OTHER;
3067 // Copy modified_blocks to event
3068 for(std::map<v3s16, MapBlock*>::iterator
3069 i = modified_blocks.begin();
3070 i != modified_blocks.end(); ++i)
3072 event.modified_blocks.insert(i->first);
3076 dispatchEvent(&event);
3085 s16 ServerMap::findGroundLevel(v2s16 p2d)
3089 Uh, just do something random...
3091 // Find existing map from top to down
3094 v3s16 p(p2d.X, max, p2d.Y);
3095 for(; p.Y>min; p.Y--)
3097 MapNode n = getNodeNoEx(p);
3098 if(n.getContent() != CONTENT_IGNORE)
3103 // If this node is not air, go to plan b
3104 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3106 // Search existing walkable and return it
3107 for(; p.Y>min; p.Y--)
3109 MapNode n = getNodeNoEx(p);
3110 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3119 Determine from map generator noise functions
3122 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3125 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3126 //return (s16)level;
3129 void ServerMap::createDatabase() {
3132 e = sqlite3_exec(m_database,
3133 "CREATE TABLE IF NOT EXISTS `blocks` ("
3134 "`pos` INT NOT NULL PRIMARY KEY,"
3137 , NULL, NULL, NULL);
3138 if(e == SQLITE_ABORT)
3139 throw FileNotGoodException("Could not create database structure");
3141 infostream<<"ServerMap: Database structure was created";
3144 void ServerMap::verifyDatabase() {
3149 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3150 bool needs_create = false;
3154 Open the database connection
3157 createDirs(m_savedir);
3159 if(!fs::PathExists(dbp))
3160 needs_create = true;
3162 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3163 if(d != SQLITE_OK) {
3164 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3165 throw FileNotGoodException("Cannot open database file");
3171 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3172 if(d != SQLITE_OK) {
3173 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3174 throw FileNotGoodException("Cannot prepare read statement");
3177 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3178 if(d != SQLITE_OK) {
3179 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3180 throw FileNotGoodException("Cannot prepare write statement");
3183 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3184 if(d != SQLITE_OK) {
3185 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3186 throw FileNotGoodException("Cannot prepare read statement");
3189 infostream<<"ServerMap: Database opened"<<std::endl;
3193 bool ServerMap::loadFromFolders() {
3194 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3199 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3200 return (sqlite3_int64)pos.Z*16777216 +
3201 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3204 void ServerMap::createDirs(std::string path)
3206 if(fs::CreateAllDirs(path) == false)
3208 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3209 <<"\""<<path<<"\""<<std::endl;
3210 throw BaseException("ServerMap failed to create directory");
3214 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3220 snprintf(cc, 9, "%.4x%.4x",
3221 (unsigned int)pos.X&0xffff,
3222 (unsigned int)pos.Y&0xffff);
3224 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3226 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3227 (unsigned int)pos.X&0xfff,
3228 (unsigned int)pos.Y&0xfff);
3230 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3236 v2s16 ServerMap::getSectorPos(std::string dirname)
3240 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
3241 assert(spos != std::string::npos);
3242 if(dirname.size() - spos == 8)
3245 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
3247 else if(dirname.size() - spos == 3)
3250 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3251 // Sign-extend the 12 bit values up to 16 bits...
3252 if(x&0x800) x|=0xF000;
3253 if(y&0x800) y|=0xF000;
3260 v2s16 pos((s16)x, (s16)y);
3264 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3266 v2s16 p2d = getSectorPos(sectordir);
3268 if(blockfile.size() != 4){
3269 throw InvalidFilenameException("Invalid block filename");
3272 int r = sscanf(blockfile.c_str(), "%4x", &y);
3274 throw InvalidFilenameException("Invalid block filename");
3275 return v3s16(p2d.X, y, p2d.Y);
3278 std::string ServerMap::getBlockFilename(v3s16 p)
3281 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3285 void ServerMap::save(ModifiedState save_level)
3287 DSTACK(__FUNCTION_NAME);
3288 if(m_map_saving_enabled == false)
3290 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3294 if(save_level == MOD_STATE_CLEAN)
3295 infostream<<"ServerMap: Saving whole map, this can take time."
3298 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3303 // Profile modified reasons
3304 Profiler modprofiler;
3306 u32 sector_meta_count = 0;
3307 u32 block_count = 0;
3308 u32 block_count_all = 0; // Number of blocks in memory
3310 // Don't do anything with sqlite unless something is really saved
3311 bool save_started = false;
3313 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3314 i != m_sectors.end(); ++i)
3316 ServerMapSector *sector = (ServerMapSector*)i->second;
3317 assert(sector->getId() == MAPSECTOR_SERVER);
3319 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3321 saveSectorMeta(sector);
3322 sector_meta_count++;
3324 std::list<MapBlock*> blocks;
3325 sector->getBlocks(blocks);
3327 for(std::list<MapBlock*>::iterator j = blocks.begin();
3328 j != blocks.end(); ++j)
3330 MapBlock *block = *j;
3334 if(block->getModified() >= (u32)save_level)
3339 save_started = true;
3342 modprofiler.add(block->getModifiedReason(), 1);
3347 /*infostream<<"ServerMap: Written block ("
3348 <<block->getPos().X<<","
3349 <<block->getPos().Y<<","
3350 <<block->getPos().Z<<")"
3359 Only print if something happened or saved whole map
3361 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3362 || block_count != 0)
3364 infostream<<"ServerMap: Written: "
3365 <<sector_meta_count<<" sector metadata files, "
3366 <<block_count<<" block files"
3367 <<", "<<block_count_all<<" blocks in memory."
3369 PrintInfo(infostream); // ServerMap/ClientMap:
3370 infostream<<"Blocks modified by: "<<std::endl;
3371 modprofiler.print(infostream);
3375 static s32 unsignedToSigned(s32 i, s32 max_positive)
3377 if(i < max_positive)
3380 return i - 2*max_positive;
3383 // modulo of a negative number does not work consistently in C
3384 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3388 return mod - ((-i) % mod);
3391 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3393 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3395 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3397 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3398 return v3s16(x,y,z);
3401 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3403 if(loadFromFolders()){
3404 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3405 <<"all blocks that are stored in flat files"<<std::endl;
3411 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3413 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3414 v3s16 p = getIntegerAsBlock(block_i);
3415 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3421 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3423 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3424 si != m_sectors.end(); ++si)
3426 MapSector *sector = si->second;
3428 std::list<MapBlock*> blocks;
3429 sector->getBlocks(blocks);
3431 for(std::list<MapBlock*>::iterator i = blocks.begin();
3432 i != blocks.end(); ++i)
3434 MapBlock *block = (*i);
3435 v3s16 p = block->getPos();
3441 void ServerMap::saveMapMeta()
3443 DSTACK(__FUNCTION_NAME);
3445 /*infostream<<"ServerMap::saveMapMeta(): "
3449 createDirs(m_savedir);
3451 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3452 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3453 if(os.good() == false)
3455 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3456 <<"could not open"<<fullpath<<std::endl;
3457 throw FileNotGoodException("Cannot open chunk metadata");
3462 m_emerge->setParamsToSettings(¶ms);
3463 params.writeLines(os);
3465 os<<"[end_of_params]\n";
3467 m_map_metadata_changed = false;
3470 void ServerMap::loadMapMeta()
3472 DSTACK(__FUNCTION_NAME);
3474 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3477 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3478 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3479 if(is.good() == false)
3481 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3482 <<"could not open"<<fullpath<<std::endl;
3483 throw FileNotGoodException("Cannot open map metadata");
3491 throw SerializationError
3492 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3494 std::getline(is, line);
3495 std::string trimmedline = trim(line);
3496 if(trimmedline == "[end_of_params]")
3498 params.parseConfigLine(line);
3501 MapgenParams *mgparams;
3503 mgparams = m_emerge->getParamsFromSettings(¶ms);
3504 } catch (SettingNotFoundException &e) {
3505 infostream << "Couldn't get a setting from map_meta.txt: "
3506 << e.what() << std::endl;
3513 m_mgparams = mgparams;
3514 m_seed = mgparams->seed;
3516 if (params.exists("seed")) {
3517 m_seed = params.getU64("seed");
3518 m_mgparams->seed = m_seed;
3522 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3525 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3527 DSTACK(__FUNCTION_NAME);
3528 // Format used for writing
3529 u8 version = SER_FMT_VER_HIGHEST;
3531 v2s16 pos = sector->getPos();
3532 std::string dir = getSectorDir(pos);
3535 std::string fullpath = dir + DIR_DELIM + "meta";
3536 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3537 if(o.good() == false)
3538 throw FileNotGoodException("Cannot open sector metafile");
3540 sector->serialize(o, version);
3542 sector->differs_from_disk = false;
3545 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3547 DSTACK(__FUNCTION_NAME);
3549 v2s16 p2d = getSectorPos(sectordir);
3551 ServerMapSector *sector = NULL;
3553 std::string fullpath = sectordir + DIR_DELIM + "meta";
3554 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3555 if(is.good() == false)
3557 // If the directory exists anyway, it probably is in some old
3558 // format. Just go ahead and create the sector.
3559 if(fs::PathExists(sectordir))
3561 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3562 <<fullpath<<" doesn't exist but directory does."
3563 <<" Continuing with a sector with no metadata."
3565 sector = new ServerMapSector(this, p2d, m_gamedef);
3566 m_sectors[p2d] = sector;
3570 throw FileNotGoodException("Cannot open sector metafile");
3575 sector = ServerMapSector::deSerialize
3576 (is, this, p2d, m_sectors, m_gamedef);
3578 saveSectorMeta(sector);
3581 sector->differs_from_disk = false;
3586 bool ServerMap::loadSectorMeta(v2s16 p2d)
3588 DSTACK(__FUNCTION_NAME);
3590 MapSector *sector = NULL;
3592 // The directory layout we're going to load from.
3593 // 1 - original sectors/xxxxzzzz/
3594 // 2 - new sectors2/xxx/zzz/
3595 // If we load from anything but the latest structure, we will
3596 // immediately save to the new one, and remove the old.
3598 std::string sectordir1 = getSectorDir(p2d, 1);
3599 std::string sectordir;
3600 if(fs::PathExists(sectordir1))
3602 sectordir = sectordir1;
3607 sectordir = getSectorDir(p2d, 2);
3611 sector = loadSectorMeta(sectordir, loadlayout != 2);
3613 catch(InvalidFilenameException &e)
3617 catch(FileNotGoodException &e)
3621 catch(std::exception &e)
3630 bool ServerMap::loadSectorFull(v2s16 p2d)
3632 DSTACK(__FUNCTION_NAME);
3634 MapSector *sector = NULL;
3636 // The directory layout we're going to load from.
3637 // 1 - original sectors/xxxxzzzz/
3638 // 2 - new sectors2/xxx/zzz/
3639 // If we load from anything but the latest structure, we will
3640 // immediately save to the new one, and remove the old.
3642 std::string sectordir1 = getSectorDir(p2d, 1);
3643 std::string sectordir;
3644 if(fs::PathExists(sectordir1))
3646 sectordir = sectordir1;
3651 sectordir = getSectorDir(p2d, 2);
3655 sector = loadSectorMeta(sectordir, loadlayout != 2);
3657 catch(InvalidFilenameException &e)
3661 catch(FileNotGoodException &e)
3665 catch(std::exception &e)
3673 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3675 std::vector<fs::DirListNode>::iterator i2;
3676 for(i2=list2.begin(); i2!=list2.end(); i2++)
3682 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3684 catch(InvalidFilenameException &e)
3686 // This catches unknown crap in directory
3692 infostream<<"Sector converted to new layout - deleting "<<
3693 sectordir1<<std::endl;
3694 fs::RecursiveDelete(sectordir1);
3701 void ServerMap::beginSave() {
3703 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3704 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3707 void ServerMap::endSave() {
3709 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3710 infostream<<"WARNING: endSave() failed, map might not have saved.";
3713 void ServerMap::saveBlock(MapBlock *block)
3715 DSTACK(__FUNCTION_NAME);
3717 Dummy blocks are not written
3719 if(block->isDummy())
3721 /*v3s16 p = block->getPos();
3722 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3723 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3727 // Format used for writing
3728 u8 version = SER_FMT_VER_HIGHEST;
3730 v3s16 p3d = block->getPos();
3734 v2s16 p2d(p3d.X, p3d.Z);
3735 std::string sectordir = getSectorDir(p2d);
3737 createDirs(sectordir);
3739 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3740 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3741 if(o.good() == false)
3742 throw FileNotGoodException("Cannot open block data");
3745 [0] u8 serialization version
3751 std::ostringstream o(std::ios_base::binary);
3753 o.write((char*)&version, 1);
3756 block->serialize(o, version, true);
3758 // Write block to database
3760 std::string tmp = o.str();
3761 const char *bytes = tmp.c_str();
3763 bool success = true;
3764 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3765 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3768 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3769 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3772 int written = sqlite3_step(m_database_write);
3773 if(written != SQLITE_DONE) {
3774 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3775 <<sqlite3_errmsg(m_database)<<std::endl;
3778 // Make ready for later reuse
3779 sqlite3_reset(m_database_write);
3781 // We just wrote it to the disk so clear modified flag
3783 block->resetModified();
3786 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3788 DSTACK(__FUNCTION_NAME);
3790 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3793 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3794 if(is.good() == false)
3795 throw FileNotGoodException("Cannot open block file");
3797 v3s16 p3d = getBlockPos(sectordir, blockfile);
3798 v2s16 p2d(p3d.X, p3d.Z);
3800 assert(sector->getPos() == p2d);
3802 u8 version = SER_FMT_VER_INVALID;
3803 is.read((char*)&version, 1);
3806 throw SerializationError("ServerMap::loadBlock(): Failed"
3807 " to read MapBlock version");
3809 /*u32 block_size = MapBlock::serializedLength(version);
3810 SharedBuffer<u8> data(block_size);
3811 is.read((char*)*data, block_size);*/
3813 // This will always return a sector because we're the server
3814 //MapSector *sector = emergeSector(p2d);
3816 MapBlock *block = NULL;
3817 bool created_new = false;
3818 block = sector->getBlockNoCreateNoEx(p3d.Y);
3821 block = sector->createBlankBlockNoInsert(p3d.Y);
3826 block->deSerialize(is, version, true);
3828 // If it's a new block, insert it to the map
3830 sector->insertBlock(block);
3833 Save blocks loaded in old format in new format
3836 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3840 // Should be in database now, so delete the old file
3841 fs::RecursiveDelete(fullpath);
3844 // We just loaded it from the disk, so it's up-to-date.
3845 block->resetModified();
3848 catch(SerializationError &e)
3850 infostream<<"WARNING: Invalid block data on disk "
3851 <<"fullpath="<<fullpath
3852 <<" (SerializationError). "
3853 <<"what()="<<e.what()
3855 //" Ignoring. A new one will be generated.
3858 // TODO: Backup file; name is in fullpath.
3862 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3864 DSTACK(__FUNCTION_NAME);
3867 std::istringstream is(*blob, std::ios_base::binary);
3869 u8 version = SER_FMT_VER_INVALID;
3870 is.read((char*)&version, 1);
3873 throw SerializationError("ServerMap::loadBlock(): Failed"
3874 " to read MapBlock version");
3876 /*u32 block_size = MapBlock::serializedLength(version);
3877 SharedBuffer<u8> data(block_size);
3878 is.read((char*)*data, block_size);*/
3880 // This will always return a sector because we're the server
3881 //MapSector *sector = emergeSector(p2d);
3883 MapBlock *block = NULL;
3884 bool created_new = false;
3885 block = sector->getBlockNoCreateNoEx(p3d.Y);
3888 block = sector->createBlankBlockNoInsert(p3d.Y);
3893 block->deSerialize(is, version, true);
3895 // If it's a new block, insert it to the map
3897 sector->insertBlock(block);
3900 Save blocks loaded in old format in new format
3903 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3904 // Only save if asked to; no need to update version
3908 // We just loaded it from, so it's up-to-date.
3909 block->resetModified();
3912 catch(SerializationError &e)
3914 errorstream<<"Invalid block data in database"
3915 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3916 <<" (SerializationError): "<<e.what()<<std::endl;
3918 // TODO: Block should be marked as invalid in memory so that it is
3919 // not touched but the game can run
3921 if(g_settings->getBool("ignore_world_load_errors")){
3922 errorstream<<"Ignoring block load error. Duck and cover! "
3923 <<"(ignore_world_load_errors)"<<std::endl;
3925 throw SerializationError("Invalid block data in database");
3931 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3933 DSTACK(__FUNCTION_NAME);
3935 v2s16 p2d(blockpos.X, blockpos.Z);
3937 if(!loadFromFolders()) {
3940 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3941 infostream<<"WARNING: Could not bind block position for load: "
3942 <<sqlite3_errmsg(m_database)<<std::endl;
3943 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3945 Make sure sector is loaded
3947 MapSector *sector = createSector(p2d);
3952 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3953 size_t len = sqlite3_column_bytes(m_database_read, 0);
3955 std::string datastr(data, len);
3957 loadBlock(&datastr, blockpos, sector, false);
3959 sqlite3_step(m_database_read);
3960 // We should never get more than 1 row, so ok to reset
3961 sqlite3_reset(m_database_read);
3963 return getBlockNoCreateNoEx(blockpos);
3965 sqlite3_reset(m_database_read);
3967 // Not found in database, try the files
3970 // The directory layout we're going to load from.
3971 // 1 - original sectors/xxxxzzzz/
3972 // 2 - new sectors2/xxx/zzz/
3973 // If we load from anything but the latest structure, we will
3974 // immediately save to the new one, and remove the old.
3976 std::string sectordir1 = getSectorDir(p2d, 1);
3977 std::string sectordir;
3978 if(fs::PathExists(sectordir1))
3980 sectordir = sectordir1;
3985 sectordir = getSectorDir(p2d, 2);
3989 Make sure sector is loaded
3991 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3995 sector = loadSectorMeta(sectordir, loadlayout != 2);
3997 catch(InvalidFilenameException &e)
4001 catch(FileNotGoodException &e)
4005 catch(std::exception &e)
4012 Make sure file exists
4015 std::string blockfilename = getBlockFilename(blockpos);
4016 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
4020 Load block and save it to the database
4022 loadBlock(sectordir, blockfilename, sector, true);
4023 return getBlockNoCreateNoEx(blockpos);
4026 void ServerMap::PrintInfo(std::ostream &out)
4035 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4040 MapVoxelManipulator::~MapVoxelManipulator()
4042 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4046 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4048 TimeTaker timer1("emerge", &emerge_time);
4050 // Units of these are MapBlocks
4051 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4052 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4054 VoxelArea block_area_nodes
4055 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4057 addArea(block_area_nodes);
4059 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4060 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4061 for(s32 x=p_min.X; x<=p_max.X; x++)
4066 std::map<v3s16, u8>::iterator n;
4067 n = m_loaded_blocks.find(p);
4068 if(n != m_loaded_blocks.end())
4071 bool block_data_inexistent = false;
4074 TimeTaker timer1("emerge load", &emerge_load_time);
4076 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4077 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4079 a.print(infostream);
4080 infostream<<std::endl;*/
4082 block = m_map->getBlockNoCreate(p);
4083 if(block->isDummy())
4084 block_data_inexistent = true;
4086 block->copyTo(*this);
4088 catch(InvalidPositionException &e)
4090 block_data_inexistent = true;
4093 if(block_data_inexistent)
4095 flags |= VMANIP_BLOCK_DATA_INEXIST;
4097 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4098 // Fill with VOXELFLAG_INEXISTENT
4099 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4100 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4102 s32 i = m_area.index(a.MinEdge.X,y,z);
4103 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4106 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4108 // Mark that block was loaded as blank
4109 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4112 m_loaded_blocks[p] = flags;
4115 //infostream<<"emerge done"<<std::endl;
4119 SUGG: Add an option to only update eg. water and air nodes.
4120 This will make it interfere less with important stuff if
4123 void MapVoxelManipulator::blitBack
4124 (std::map<v3s16, MapBlock*> & modified_blocks)
4126 if(m_area.getExtent() == v3s16(0,0,0))
4129 //TimeTaker timer1("blitBack");
4131 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4132 <<m_loaded_blocks.size()<<std::endl;*/
4135 Initialize block cache
4137 v3s16 blockpos_last;
4138 MapBlock *block = NULL;
4139 bool block_checked_in_modified = false;
4141 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4142 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4143 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4147 u8 f = m_flags[m_area.index(p)];
4148 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4151 MapNode &n = m_data[m_area.index(p)];
4153 v3s16 blockpos = getNodeBlockPos(p);
4158 if(block == NULL || blockpos != blockpos_last){
4159 block = m_map->getBlockNoCreate(blockpos);
4160 blockpos_last = blockpos;
4161 block_checked_in_modified = false;
4164 // Calculate relative position in block
4165 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4167 // Don't continue if nothing has changed here
4168 if(block->getNode(relpos) == n)
4171 //m_map->setNode(m_area.MinEdge + p, n);
4172 block->setNode(relpos, n);
4175 Make sure block is in modified_blocks
4177 if(block_checked_in_modified == false)
4179 modified_blocks[blockpos] = block;
4180 block_checked_in_modified = true;
4183 catch(InvalidPositionException &e)
4189 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4190 MapVoxelManipulator(map),
4191 m_create_area(false)
4195 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4199 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4201 // Just create the area so that it can be pointed to
4202 VoxelManipulator::emerge(a, caller_id);
4205 void ManualMapVoxelManipulator::initialEmerge(
4206 v3s16 blockpos_min, v3s16 blockpos_max)
4208 TimeTaker timer1("initialEmerge", &emerge_time);
4210 // Units of these are MapBlocks
4211 v3s16 p_min = blockpos_min;
4212 v3s16 p_max = blockpos_max;
4214 VoxelArea block_area_nodes
4215 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4217 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4220 infostream<<"initialEmerge: area: ";
4221 block_area_nodes.print(infostream);
4222 infostream<<" ("<<size_MB<<"MB)";
4223 infostream<<std::endl;
4226 addArea(block_area_nodes);
4228 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4229 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4230 for(s32 x=p_min.X; x<=p_max.X; x++)
4235 std::map<v3s16, u8>::iterator n;
4236 n = m_loaded_blocks.find(p);
4237 if(n != m_loaded_blocks.end())
4240 bool block_data_inexistent = false;
4243 TimeTaker timer1("emerge load", &emerge_load_time);
4245 block = m_map->getBlockNoCreate(p);
4246 if(block->isDummy())
4247 block_data_inexistent = true;
4249 block->copyTo(*this);
4251 catch(InvalidPositionException &e)
4253 block_data_inexistent = true;
4256 if(block_data_inexistent)
4258 flags |= VMANIP_BLOCK_DATA_INEXIST;
4261 Mark area inexistent
4263 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4264 // Fill with VOXELFLAG_INEXISTENT
4265 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4266 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4268 s32 i = m_area.index(a.MinEdge.X,y,z);
4269 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4272 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4274 // Mark that block was loaded as blank
4275 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4278 m_loaded_blocks[p] = flags;
4282 void ManualMapVoxelManipulator::blitBackAll(
4283 std::map<v3s16, MapBlock*> * modified_blocks)
4285 if(m_area.getExtent() == v3s16(0,0,0))
4289 Copy data of all blocks
4291 for(std::map<v3s16, u8>::iterator
4292 i = m_loaded_blocks.begin();
4293 i != m_loaded_blocks.end(); ++i)
4296 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4297 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4298 if(existed == false)
4303 block->copyFrom(*this);
4306 (*modified_blocks)[p] = block;