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 while (m_transforming_liquid.size() > 0)
1656 // This should be done here so that it is done when continue is used
1657 if (loopcount >= initial_size || loopcount >= 1000)
1661 Get a queued transforming liquid node
1663 v3s16 p0 = m_transforming_liquid.pop_front();
1664 u16 total_level = 0;
1665 // surrounding flowing liquid nodes
1666 NodeNeighbor neighbors[7];
1667 // current level of every block
1668 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1670 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1671 s8 can_liquid_same_level = 0;
1672 content_t liquid_kind = CONTENT_IGNORE;
1673 content_t liquid_kind_flowing = CONTENT_IGNORE;
1675 Collect information about the environment
1677 const v3s16 *dirs = g_7dirs;
1678 for (u16 i = 0; i < 7; i++) {
1679 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1682 nt = NEIGHBOR_UPPER;
1685 nt = NEIGHBOR_LOWER;
1688 v3s16 npos = p0 + dirs[i];
1690 neighbors[i].n = getNodeNoEx(npos);
1691 neighbors[i].t = nt;
1692 neighbors[i].p = npos;
1695 NodeNeighbor & nb = neighbors[i];
1697 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1699 if (nb.n.getContent() == CONTENT_AIR) {
1700 liquid_levels[i] = 0;
1705 // if this node is not (yet) of a liquid type,
1706 // choose the first liquid type we encounter
1707 if (liquid_kind_flowing == CONTENT_IGNORE)
1708 liquid_kind_flowing = nodemgr->getId(
1709 nodemgr->get(nb.n).liquid_alternative_flowing);
1710 if (liquid_kind == CONTENT_IGNORE)
1711 liquid_kind = nb.n.getContent();
1712 if (nb.n.getContent() == liquid_kind) {
1713 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1715 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1718 case LIQUID_FLOWING:
1719 // if this node is not (yet) of a liquid type,
1720 // choose the first liquid type we encounter
1721 if (liquid_kind_flowing == CONTENT_IGNORE)
1722 liquid_kind_flowing = nb.n.getContent();
1723 if (liquid_kind == CONTENT_IGNORE)
1724 liquid_kind = nodemgr->getId(
1725 nodemgr->get(nb.n).liquid_alternative_source);
1726 if (nb.n.getContent() == liquid_kind_flowing) {
1727 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1733 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1734 ++can_liquid_same_level;
1735 if (liquid_levels[i] > 0)
1736 total_level += liquid_levels[i];
1739 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1740 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1741 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1742 << nodemgr->get(nb.n.getContent()).liquid_type
1743 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1744 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1745 << " tlevel=" << (int)total_level << " cansame="
1746 << (int)can_liquid_same_level << std::endl;
1750 if (liquid_kind == CONTENT_IGNORE ||
1751 !neighbors[D_SELF].l ||
1755 // fill bottom block
1756 if (neighbors[D_BOTTOM].l) {
1757 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1758 LIQUID_LEVEL_SOURCE : total_level;
1759 total_level -= liquid_levels_want[D_BOTTOM];
1763 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1764 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1765 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1766 (can_liquid_same_level - relax) &&
1767 can_liquid_same_level >= relax + 1) {
1768 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1771 // prevent lakes in air above unloaded blocks
1772 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level || !fast_flood) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE) {
1776 // calculate self level 5 blocks
1778 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1779 ? LIQUID_LEVEL_SOURCE
1780 : total_level / can_liquid_same_level;
1781 total_level -= want_level * can_liquid_same_level;
1784 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1785 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1786 total_level <= (can_liquid_same_level - relax) &&
1787 can_liquid_same_level >= relax + 1) {
1791 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1792 if (!neighbors[ii].l)
1794 liquid_levels_want[ii] = want_level;
1795 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0
1796 && liquid_levels[ii] > liquid_levels_want[ii]
1798 ++liquid_levels_want[ii];
1803 for (u16 ii = 0; ii < 7; ++ii) {
1804 if (total_level < 1) break;
1805 if (liquid_levels_want[ii] >= 0 &&
1806 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1807 ++liquid_levels_want[ii];
1812 // fill top block if can
1813 if (neighbors[D_TOP].l) {
1814 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1815 LIQUID_LEVEL_SOURCE : total_level;
1816 total_level -= liquid_levels_want[D_TOP];
1819 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1820 if ( neighbors[ii].i ||
1821 (liquid_levels_want[ii] >= 0 &&
1822 (fast_flood && p0.Y < water_level &&
1823 (initial_size >= 1000
1825 && want_level >= LIQUID_LEVEL_SOURCE/4
1826 && can_liquid_same_level >= 5
1827 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1828 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1831 if (total_level > 0) //|| flowed != volume)
1832 infostream <<" AFTER level=" << (int)total_level
1833 //<< " flowed="<<flowed<< " volume=" << volume
1834 << " wantsame="<<(int)want_level<< " top="
1835 << (int)liquid_levels_want[D_TOP]<< " topwas="
1836 << (int)liquid_levels[D_TOP]<< " bot="
1837 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1841 for (u16 i = 0; i < 7; i++) {
1842 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1844 MapNode & n0 = neighbors[i].n;
1845 p0 = neighbors[i].p;
1847 decide on the type (and possibly level) of the current node
1849 content_t new_node_content;
1850 s8 new_node_level = -1;
1851 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1852 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1853 // amount to gain, limited by viscosity
1854 // must be at least 1 in absolute value
1855 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1856 if (level_inc < -viscosity || level_inc > viscosity)
1857 new_node_level = liquid_levels[i] + level_inc/viscosity;
1858 else if (level_inc < 0)
1859 new_node_level = liquid_levels[i] - 1;
1860 else if (level_inc > 0)
1861 new_node_level = liquid_levels[i] + 1;
1863 new_node_level = liquid_levels_want[i];
1866 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1867 new_node_content = liquid_kind;
1868 else if (new_node_level > 0)
1869 new_node_content = liquid_kind_flowing;
1871 new_node_content = CONTENT_AIR;
1873 // last level must flow down on stairs
1874 if (liquid_levels_want[i] != liquid_levels[i] &&
1875 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1876 new_node_level >= 1 && new_node_level <= 2) {
1877 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1878 if (neighbors[ii].l)
1879 must_reflow_second.push_back(p0 + dirs[ii]);
1884 check if anything has changed.
1885 if not, just continue with the next node.
1888 new_node_content == n0.getContent()
1889 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1890 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1891 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1892 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1895 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1896 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1897 LIQUID_INFINITY_MASK) == neighbors[i].i
1905 update the current node
1907 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1908 // set level to last 3 bits, flowing down bit to 4th bit
1909 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1910 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1911 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1912 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1915 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1916 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1917 <<(int)new_node_level<<std::endl;
1920 n0.setContent(new_node_content);
1921 // Find out whether there is a suspect for this action
1922 std::string suspect;
1923 if(m_gamedef->rollback()){
1924 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1927 if(!suspect.empty()){
1929 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1930 // Get old node for rollback
1931 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1935 RollbackNode rollback_newnode(this, p0, m_gamedef);
1936 RollbackAction action;
1937 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1938 m_gamedef->rollback()->reportAction(action);
1944 v3s16 blockpos = getNodeBlockPos(p0);
1945 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1947 modified_blocks[blockpos] = block;
1948 // If node emits light, MapBlock requires lighting update
1949 if(nodemgr->get(n0).light_source != 0)
1950 lighting_modified_blocks[block->getPos()] = block;
1952 must_reflow.push_back(neighbors[i].p);
1954 /* //for better relax only same level
1955 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1956 if (!neighbors[ii].l) continue;
1957 must_reflow.push_back(p0 + dirs[ii]);
1962 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1963 <<" reflow="<<must_reflow.size()
1964 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1966 while (must_reflow.size() > 0)
1967 m_transforming_liquid.push_back(must_reflow.pop_front());
1968 while (must_reflow_second.size() > 0)
1969 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1970 updateLighting(lighting_modified_blocks, modified_blocks);
1973 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1976 if (g_settings->getBool("liquid_finite"))
1977 return Map::transformLiquidsFinite(modified_blocks);
1979 INodeDefManager *nodemgr = m_gamedef->ndef();
1981 DSTACK(__FUNCTION_NAME);
1982 //TimeTaker timer("transformLiquids()");
1985 u32 initial_size = m_transforming_liquid.size();
1987 /*if(initial_size != 0)
1988 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1990 // list of nodes that due to viscosity have not reached their max level height
1991 UniqueQueue<v3s16> must_reflow;
1993 // List of MapBlocks that will require a lighting update (due to lava)
1994 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1996 while(m_transforming_liquid.size() != 0)
1998 // This should be done here so that it is done when continue is used
1999 if(loopcount >= initial_size || loopcount >= 10000)
2004 Get a queued transforming liquid node
2006 v3s16 p0 = m_transforming_liquid.pop_front();
2008 MapNode n0 = getNodeNoEx(p0);
2011 Collect information about current node
2013 s8 liquid_level = -1;
2014 content_t liquid_kind = CONTENT_IGNORE;
2015 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2016 switch (liquid_type) {
2018 liquid_level = LIQUID_LEVEL_SOURCE;
2019 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2021 case LIQUID_FLOWING:
2022 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2023 liquid_kind = n0.getContent();
2026 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2027 // continue with the next node.
2028 if (n0.getContent() != CONTENT_AIR)
2030 liquid_kind = CONTENT_AIR;
2035 Collect information about the environment
2037 const v3s16 *dirs = g_6dirs;
2038 NodeNeighbor sources[6]; // surrounding sources
2039 int num_sources = 0;
2040 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2042 NodeNeighbor airs[6]; // surrounding air
2044 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2045 int num_neutrals = 0;
2046 bool flowing_down = false;
2047 for (u16 i = 0; i < 6; i++) {
2048 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2051 nt = NEIGHBOR_UPPER;
2054 nt = NEIGHBOR_LOWER;
2057 v3s16 npos = p0 + dirs[i];
2058 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2059 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2061 if (nb.n.getContent() == CONTENT_AIR) {
2062 airs[num_airs++] = nb;
2063 // if the current node is a water source the neighbor
2064 // should be enqueded for transformation regardless of whether the
2065 // current node changes or not.
2066 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2067 m_transforming_liquid.push_back(npos);
2068 // if the current node happens to be a flowing node, it will start to flow down here.
2069 if (nb.t == NEIGHBOR_LOWER) {
2070 flowing_down = true;
2073 neutrals[num_neutrals++] = nb;
2077 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2078 if (liquid_kind == CONTENT_AIR)
2079 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2080 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2081 neutrals[num_neutrals++] = nb;
2083 // Do not count bottom source, it will screw things up
2085 sources[num_sources++] = nb;
2088 case LIQUID_FLOWING:
2089 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2090 if (liquid_kind == CONTENT_AIR)
2091 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2092 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2093 neutrals[num_neutrals++] = nb;
2095 flows[num_flows++] = nb;
2096 if (nb.t == NEIGHBOR_LOWER)
2097 flowing_down = true;
2104 decide on the type (and possibly level) of the current node
2106 content_t new_node_content;
2107 s8 new_node_level = -1;
2108 s8 max_node_level = -1;
2109 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2110 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2111 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2112 // it's perfectly safe to use liquid_kind here to determine the new node content.
2113 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2114 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2115 // liquid_kind is set properly, see above
2116 new_node_content = liquid_kind;
2117 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2119 // no surrounding sources, so get the maximum level that can flow into this node
2120 for (u16 i = 0; i < num_flows; i++) {
2121 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2122 switch (flows[i].t) {
2123 case NEIGHBOR_UPPER:
2124 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2125 max_node_level = LIQUID_LEVEL_MAX;
2126 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2127 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2128 } else if (nb_liquid_level > max_node_level)
2129 max_node_level = nb_liquid_level;
2131 case NEIGHBOR_LOWER:
2133 case NEIGHBOR_SAME_LEVEL:
2134 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2135 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2136 max_node_level = nb_liquid_level - 1;
2142 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2143 if (viscosity > 1 && max_node_level != liquid_level) {
2144 // amount to gain, limited by viscosity
2145 // must be at least 1 in absolute value
2146 s8 level_inc = max_node_level - liquid_level;
2147 if (level_inc < -viscosity || level_inc > viscosity)
2148 new_node_level = liquid_level + level_inc/viscosity;
2149 else if (level_inc < 0)
2150 new_node_level = liquid_level - 1;
2151 else if (level_inc > 0)
2152 new_node_level = liquid_level + 1;
2153 if (new_node_level != max_node_level)
2154 must_reflow.push_back(p0);
2156 new_node_level = max_node_level;
2158 if (new_node_level >= 0)
2159 new_node_content = liquid_kind;
2161 new_node_content = CONTENT_AIR;
2166 check if anything has changed. if not, just continue with the next node.
2168 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2169 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2170 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2176 update the current node
2178 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2179 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2180 // set level to last 3 bits, flowing down bit to 4th bit
2181 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2183 // set the liquid level and flow bit to 0
2184 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2186 n0.setContent(new_node_content);
2188 // Find out whether there is a suspect for this action
2189 std::string suspect;
2190 if(m_gamedef->rollback()){
2191 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2194 if(!suspect.empty()){
2196 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2197 // Get old node for rollback
2198 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2202 RollbackNode rollback_newnode(this, p0, m_gamedef);
2203 RollbackAction action;
2204 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2205 m_gamedef->rollback()->reportAction(action);
2211 v3s16 blockpos = getNodeBlockPos(p0);
2212 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2214 modified_blocks[blockpos] = block;
2215 // If node emits light, MapBlock requires lighting update
2216 if(nodemgr->get(n0).light_source != 0)
2217 lighting_modified_blocks[block->getPos()] = block;
2221 enqueue neighbors for update if neccessary
2223 switch (nodemgr->get(n0.getContent()).liquid_type) {
2225 case LIQUID_FLOWING:
2226 // make sure source flows into all neighboring nodes
2227 for (u16 i = 0; i < num_flows; i++)
2228 if (flows[i].t != NEIGHBOR_UPPER)
2229 m_transforming_liquid.push_back(flows[i].p);
2230 for (u16 i = 0; i < num_airs; i++)
2231 if (airs[i].t != NEIGHBOR_UPPER)
2232 m_transforming_liquid.push_back(airs[i].p);
2235 // this flow has turned to air; neighboring flows might need to do the same
2236 for (u16 i = 0; i < num_flows; i++)
2237 m_transforming_liquid.push_back(flows[i].p);
2241 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2242 while (must_reflow.size() > 0)
2243 m_transforming_liquid.push_back(must_reflow.pop_front());
2244 updateLighting(lighting_modified_blocks, modified_blocks);
2247 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2249 v3s16 blockpos = getNodeBlockPos(p);
2250 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2251 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2253 infostream<<"Map::getNodeMetadata(): Need to emerge "
2254 <<PP(blockpos)<<std::endl;
2255 block = emergeBlock(blockpos, false);
2259 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2263 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2267 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2269 v3s16 blockpos = getNodeBlockPos(p);
2270 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2271 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2273 infostream<<"Map::setNodeMetadata(): Need to emerge "
2274 <<PP(blockpos)<<std::endl;
2275 block = emergeBlock(blockpos, false);
2279 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2283 block->m_node_metadata.set(p_rel, meta);
2286 void Map::removeNodeMetadata(v3s16 p)
2288 v3s16 blockpos = getNodeBlockPos(p);
2289 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2290 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2293 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2297 block->m_node_metadata.remove(p_rel);
2300 NodeTimer Map::getNodeTimer(v3s16 p)
2302 v3s16 blockpos = getNodeBlockPos(p);
2303 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2304 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2306 infostream<<"Map::getNodeTimer(): Need to emerge "
2307 <<PP(blockpos)<<std::endl;
2308 block = emergeBlock(blockpos, false);
2312 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2316 NodeTimer t = block->m_node_timers.get(p_rel);
2320 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2322 v3s16 blockpos = getNodeBlockPos(p);
2323 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2324 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2326 infostream<<"Map::setNodeTimer(): Need to emerge "
2327 <<PP(blockpos)<<std::endl;
2328 block = emergeBlock(blockpos, false);
2332 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2336 block->m_node_timers.set(p_rel, t);
2339 void Map::removeNodeTimer(v3s16 p)
2341 v3s16 blockpos = getNodeBlockPos(p);
2342 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2343 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2346 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2350 block->m_node_timers.remove(p_rel);
2356 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2357 Map(dout_server, gamedef),
2359 m_map_metadata_changed(true),
2361 m_database_read(NULL),
2362 m_database_write(NULL)
2364 verbosestream<<__FUNCTION_NAME<<std::endl;
2367 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2369 m_mgparams = new MapgenV6Params();
2371 m_seed = m_mgparams->seed;
2373 if (g_settings->get("fixed_map_seed").empty())
2375 m_seed = (((u64)(myrand() & 0xffff) << 0)
2376 | ((u64)(myrand() & 0xffff) << 16)
2377 | ((u64)(myrand() & 0xffff) << 32)
2378 | ((u64)(myrand() & 0xffff) << 48));
2379 m_mgparams->seed = m_seed;
2383 Experimental and debug stuff
2390 Try to load map; if not found, create a new one.
2393 m_savedir = savedir;
2394 m_map_saving_enabled = false;
2398 // If directory exists, check contents and load if possible
2399 if(fs::PathExists(m_savedir))
2401 // If directory is empty, it is safe to save into it.
2402 if(fs::GetDirListing(m_savedir).size() == 0)
2404 infostream<<"ServerMap: Empty save directory is valid."
2406 m_map_saving_enabled = true;
2411 // Load map metadata (seed, chunksize)
2414 catch(SettingNotFoundException &e){
2415 infostream<<"ServerMap: Some metadata not found."
2416 <<" Using default settings."<<std::endl;
2418 catch(FileNotGoodException &e){
2419 infostream<<"WARNING: Could not load map metadata"
2420 //<<" Disabling chunk-based generator."
2425 infostream<<"ServerMap: Successfully loaded map "
2426 <<"metadata from "<<savedir
2427 <<", assuming valid save directory."
2428 <<" seed="<<m_seed<<"."
2431 m_map_saving_enabled = true;
2432 // Map loaded, not creating new one
2436 // If directory doesn't exist, it is safe to save to it
2438 m_map_saving_enabled = true;
2441 catch(std::exception &e)
2443 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2444 <<", exception: "<<e.what()<<std::endl;
2445 infostream<<"Please remove the map or fix it."<<std::endl;
2446 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2449 infostream<<"Initializing new map."<<std::endl;
2451 // Create zero sector
2452 emergeSector(v2s16(0,0));
2454 // Initially write whole map
2455 save(MOD_STATE_CLEAN);
2458 ServerMap::~ServerMap()
2460 verbosestream<<__FUNCTION_NAME<<std::endl;
2464 if(m_map_saving_enabled)
2466 // Save only changed parts
2467 save(MOD_STATE_WRITE_AT_UNLOAD);
2468 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2472 infostream<<"ServerMap: Map not saved"<<std::endl;
2475 catch(std::exception &e)
2477 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2478 <<", exception: "<<e.what()<<std::endl;
2482 Close database if it was opened
2485 sqlite3_finalize(m_database_read);
2486 if(m_database_write)
2487 sqlite3_finalize(m_database_write);
2489 sqlite3_close(m_database);
2495 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2496 for(; i.atEnd() == false; i++)
2498 MapChunk *chunk = i.getNode()->getValue();
2506 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2508 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2509 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2511 s16 chunksize = m_mgparams->chunksize;
2512 s16 coffset = -chunksize / 2;
2513 v3s16 chunk_offset(coffset, coffset, coffset);
2514 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2515 v3s16 blockpos_min = blockpos_div * chunksize;
2516 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2517 blockpos_min += chunk_offset;
2518 blockpos_max += chunk_offset;
2520 v3s16 extra_borders(1,1,1);
2522 // Do nothing if not inside limits (+-1 because of neighbors)
2523 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2524 blockpos_over_limit(blockpos_max + extra_borders))
2527 data->seed = m_seed;
2528 data->blockpos_min = blockpos_min;
2529 data->blockpos_max = blockpos_max;
2530 data->blockpos_requested = blockpos;
2531 data->nodedef = m_gamedef->ndef();
2534 Create the whole area of this and the neighboring blocks
2537 //TimeTaker timer("initBlockMake() create area");
2539 for(s16 x=blockpos_min.X-extra_borders.X;
2540 x<=blockpos_max.X+extra_borders.X; x++)
2541 for(s16 z=blockpos_min.Z-extra_borders.Z;
2542 z<=blockpos_max.Z+extra_borders.Z; z++)
2544 v2s16 sectorpos(x, z);
2545 // Sector metadata is loaded from disk if not already loaded.
2546 ServerMapSector *sector = createSector(sectorpos);
2549 for(s16 y=blockpos_min.Y-extra_borders.Y;
2550 y<=blockpos_max.Y+extra_borders.Y; y++)
2553 //MapBlock *block = createBlock(p);
2554 // 1) get from memory, 2) load from disk
2555 MapBlock *block = emergeBlock(p, false);
2556 // 3) create a blank one
2559 block = createBlock(p);
2562 Block gets sunlight if this is true.
2564 Refer to the map generator heuristics.
2566 bool ug = m_emerge->isBlockUnderground(p);
2567 block->setIsUnderground(ug);
2570 // Lighting will not be valid after make_chunk is called
2571 block->setLightingExpired(true);
2572 // Lighting will be calculated
2573 //block->setLightingExpired(false);
2579 Now we have a big empty area.
2581 Make a ManualMapVoxelManipulator that contains this and the
2585 // The area that contains this block and it's neighbors
2586 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2587 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2589 data->vmanip = new ManualMapVoxelManipulator(this);
2590 //data->vmanip->setMap(this);
2594 //TimeTaker timer("initBlockMake() initialEmerge");
2595 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2598 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2599 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2600 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2601 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2602 core::map<v3s16, u8>::Node *n;
2603 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2606 u8 flags = n->getValue();
2607 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2613 // Data is ready now.
2617 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2618 std::map<v3s16, MapBlock*> &changed_blocks)
2620 v3s16 blockpos_min = data->blockpos_min;
2621 v3s16 blockpos_max = data->blockpos_max;
2622 v3s16 blockpos_requested = data->blockpos_requested;
2623 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2624 <<blockpos_requested.Y<<","
2625 <<blockpos_requested.Z<<")"<<std::endl;*/
2627 v3s16 extra_borders(1,1,1);
2629 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2631 /*infostream<<"Resulting vmanip:"<<std::endl;
2632 data->vmanip.print(infostream);*/
2634 // Make sure affected blocks are loaded
2635 for(s16 x=blockpos_min.X-extra_borders.X;
2636 x<=blockpos_max.X+extra_borders.X; x++)
2637 for(s16 z=blockpos_min.Z-extra_borders.Z;
2638 z<=blockpos_max.Z+extra_borders.Z; z++)
2639 for(s16 y=blockpos_min.Y-extra_borders.Y;
2640 y<=blockpos_max.Y+extra_borders.Y; y++)
2643 // Load from disk if not already in memory
2644 emergeBlock(p, false);
2648 Blit generated stuff to map
2649 NOTE: blitBackAll adds nearly everything to changed_blocks
2653 //TimeTaker timer("finishBlockMake() blitBackAll");
2654 data->vmanip->blitBackAll(&changed_blocks);
2657 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2660 Copy transforming liquid information
2662 while(data->transforming_liquid.size() > 0)
2664 v3s16 p = data->transforming_liquid.pop_front();
2665 m_transforming_liquid.push_back(p);
2669 Do stuff in central blocks
2677 TimeTaker t("finishBlockMake lighting update");
2679 core::map<v3s16, MapBlock*> lighting_update_blocks;
2682 for(s16 x=blockpos_min.X-extra_borders.X;
2683 x<=blockpos_max.X+extra_borders.X; x++)
2684 for(s16 z=blockpos_min.Z-extra_borders.Z;
2685 z<=blockpos_max.Z+extra_borders.Z; z++)
2686 for(s16 y=blockpos_min.Y-extra_borders.Y;
2687 y<=blockpos_max.Y+extra_borders.Y; y++)
2690 MapBlock *block = getBlockNoCreateNoEx(p);
2692 lighting_update_blocks.insert(block->getPos(), block);
2695 updateLighting(lighting_update_blocks, changed_blocks);
2699 Set lighting to non-expired state in all of them.
2700 This is cheating, but it is not fast enough if all of them
2701 would actually be updated.
2703 for(s16 x=blockpos_min.X-extra_borders.X;
2704 x<=blockpos_max.X+extra_borders.X; x++)
2705 for(s16 z=blockpos_min.Z-extra_borders.Z;
2706 z<=blockpos_max.Z+extra_borders.Z; z++)
2707 for(s16 y=blockpos_min.Y-extra_borders.Y;
2708 y<=blockpos_max.Y+extra_borders.Y; y++)
2711 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2715 if(enable_mapgen_debug_info == false)
2716 t.stop(true); // Hide output
2721 Go through changed blocks
2723 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2724 i != changed_blocks.end(); ++i)
2726 MapBlock *block = i->second;
2729 Update day/night difference cache of the MapBlocks
2731 block->expireDayNightDiff();
2733 Set block as modified
2735 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2736 "finishBlockMake expireDayNightDiff");
2740 Set central blocks as generated
2742 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2743 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2744 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2747 MapBlock *block = getBlockNoCreateNoEx(p);
2749 block->setGenerated(true);
2753 Save changed parts of map
2754 NOTE: Will be saved later.
2756 //save(MOD_STATE_WRITE_AT_UNLOAD);
2758 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2759 <<","<<blockpos_requested.Y<<","
2760 <<blockpos_requested.Z<<")"<<std::endl;*/
2762 if(enable_mapgen_debug_info)
2765 Analyze resulting blocks
2767 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2768 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2769 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2770 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2771 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2772 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2774 v3s16 p = v3s16(x,y,z);
2775 MapBlock *block = getBlockNoCreateNoEx(p);
2777 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2778 infostream<<"Generated "<<spos<<": "
2779 <<analyze_block(block)<<std::endl;
2784 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2790 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2792 DSTACKF("%s: p2d=(%d,%d)",
2797 Check if it exists already in memory
2799 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2804 Try to load it from disk (with blocks)
2806 //if(loadSectorFull(p2d) == true)
2809 Try to load metadata from disk
2812 if(loadSectorMeta(p2d) == true)
2814 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2817 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2818 throw InvalidPositionException("");
2824 Do not create over-limit
2826 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2827 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2828 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2829 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2830 throw InvalidPositionException("createSector(): pos. over limit");
2833 Generate blank sector
2836 sector = new ServerMapSector(this, p2d, m_gamedef);
2838 // Sector position on map in nodes
2839 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2844 m_sectors[p2d] = sector;
2851 This is a quick-hand function for calling makeBlock().
2853 MapBlock * ServerMap::generateBlock(
2855 std::map<v3s16, MapBlock*> &modified_blocks
2858 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2860 /*infostream<<"generateBlock(): "
2861 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2864 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2866 TimeTaker timer("generateBlock");
2868 //MapBlock *block = original_dummy;
2870 v2s16 p2d(p.X, p.Z);
2871 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2874 Do not generate over-limit
2876 if(blockpos_over_limit(p))
2878 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2879 throw InvalidPositionException("generateBlock(): pos. over limit");
2883 Create block make data
2886 initBlockMake(&data, p);
2892 TimeTaker t("mapgen::make_block()");
2893 mapgen->makeChunk(&data);
2894 //mapgen::make_block(&data);
2896 if(enable_mapgen_debug_info == false)
2897 t.stop(true); // Hide output
2901 Blit data back on map, update lighting, add mobs and whatever this does
2903 finishBlockMake(&data, modified_blocks);
2908 MapBlock *block = getBlockNoCreateNoEx(p);
2916 bool erroneus_content = false;
2917 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2918 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2919 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2922 MapNode n = block->getNode(p);
2923 if(n.getContent() == CONTENT_IGNORE)
2925 infostream<<"CONTENT_IGNORE at "
2926 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2928 erroneus_content = true;
2932 if(erroneus_content)
2941 Generate a completely empty block
2945 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2946 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2948 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2951 n.setContent(CONTENT_AIR);
2952 block->setNode(v3s16(x0,y0,z0), n);
2958 if(enable_mapgen_debug_info == false)
2959 timer.stop(true); // Hide output
2965 MapBlock * ServerMap::createBlock(v3s16 p)
2967 DSTACKF("%s: p=(%d,%d,%d)",
2968 __FUNCTION_NAME, p.X, p.Y, p.Z);
2971 Do not create over-limit
2973 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2974 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2975 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2976 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2977 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2978 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2979 throw InvalidPositionException("createBlock(): pos. over limit");
2981 v2s16 p2d(p.X, p.Z);
2984 This will create or load a sector if not found in memory.
2985 If block exists on disk, it will be loaded.
2987 NOTE: On old save formats, this will be slow, as it generates
2988 lighting on blocks for them.
2990 ServerMapSector *sector;
2992 sector = (ServerMapSector*)createSector(p2d);
2993 assert(sector->getId() == MAPSECTOR_SERVER);
2995 catch(InvalidPositionException &e)
2997 infostream<<"createBlock: createSector() failed"<<std::endl;
3001 NOTE: This should not be done, or at least the exception
3002 should not be passed on as std::exception, because it
3003 won't be catched at all.
3005 /*catch(std::exception &e)
3007 infostream<<"createBlock: createSector() failed: "
3008 <<e.what()<<std::endl;
3013 Try to get a block from the sector
3016 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3019 if(block->isDummy())
3024 block = sector->createBlankBlock(block_y);
3029 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3031 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3033 p.X, p.Y, p.Z, create_blank);
3036 MapBlock *block = getBlockNoCreateNoEx(p);
3037 if(block && block->isDummy() == false)
3042 MapBlock *block = loadBlock(p);
3048 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3049 MapBlock *block = sector->createBlankBlock(p.Y);
3053 /*if(allow_generate)
3055 std::map<v3s16, MapBlock*> modified_blocks;
3056 MapBlock *block = generateBlock(p, modified_blocks);
3060 event.type = MEET_OTHER;
3063 // Copy modified_blocks to event
3064 for(std::map<v3s16, MapBlock*>::iterator
3065 i = modified_blocks.begin();
3066 i != modified_blocks.end(); ++i)
3068 event.modified_blocks.insert(i->first);
3072 dispatchEvent(&event);
3081 s16 ServerMap::findGroundLevel(v2s16 p2d)
3085 Uh, just do something random...
3087 // Find existing map from top to down
3090 v3s16 p(p2d.X, max, p2d.Y);
3091 for(; p.Y>min; p.Y--)
3093 MapNode n = getNodeNoEx(p);
3094 if(n.getContent() != CONTENT_IGNORE)
3099 // If this node is not air, go to plan b
3100 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3102 // Search existing walkable and return it
3103 for(; p.Y>min; p.Y--)
3105 MapNode n = getNodeNoEx(p);
3106 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3115 Determine from map generator noise functions
3118 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3121 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3122 //return (s16)level;
3125 void ServerMap::createDatabase() {
3128 e = sqlite3_exec(m_database,
3129 "CREATE TABLE IF NOT EXISTS `blocks` ("
3130 "`pos` INT NOT NULL PRIMARY KEY,"
3133 , NULL, NULL, NULL);
3134 if(e == SQLITE_ABORT)
3135 throw FileNotGoodException("Could not create database structure");
3137 infostream<<"ServerMap: Database structure was created";
3140 void ServerMap::verifyDatabase() {
3145 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3146 bool needs_create = false;
3150 Open the database connection
3153 createDirs(m_savedir);
3155 if(!fs::PathExists(dbp))
3156 needs_create = true;
3158 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3159 if(d != SQLITE_OK) {
3160 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3161 throw FileNotGoodException("Cannot open database file");
3167 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3168 if(d != SQLITE_OK) {
3169 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3170 throw FileNotGoodException("Cannot prepare read statement");
3173 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3174 if(d != SQLITE_OK) {
3175 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3176 throw FileNotGoodException("Cannot prepare write statement");
3179 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3180 if(d != SQLITE_OK) {
3181 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3182 throw FileNotGoodException("Cannot prepare read statement");
3185 infostream<<"ServerMap: Database opened"<<std::endl;
3189 bool ServerMap::loadFromFolders() {
3190 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3195 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3196 return (sqlite3_int64)pos.Z*16777216 +
3197 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3200 void ServerMap::createDirs(std::string path)
3202 if(fs::CreateAllDirs(path) == false)
3204 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3205 <<"\""<<path<<"\""<<std::endl;
3206 throw BaseException("ServerMap failed to create directory");
3210 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3216 snprintf(cc, 9, "%.4x%.4x",
3217 (unsigned int)pos.X&0xffff,
3218 (unsigned int)pos.Y&0xffff);
3220 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3222 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3223 (unsigned int)pos.X&0xfff,
3224 (unsigned int)pos.Y&0xfff);
3226 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3232 v2s16 ServerMap::getSectorPos(std::string dirname)
3236 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
3237 assert(spos != std::string::npos);
3238 if(dirname.size() - spos == 8)
3241 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
3243 else if(dirname.size() - spos == 3)
3246 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3247 // Sign-extend the 12 bit values up to 16 bits...
3248 if(x&0x800) x|=0xF000;
3249 if(y&0x800) y|=0xF000;
3256 v2s16 pos((s16)x, (s16)y);
3260 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3262 v2s16 p2d = getSectorPos(sectordir);
3264 if(blockfile.size() != 4){
3265 throw InvalidFilenameException("Invalid block filename");
3268 int r = sscanf(blockfile.c_str(), "%4x", &y);
3270 throw InvalidFilenameException("Invalid block filename");
3271 return v3s16(p2d.X, y, p2d.Y);
3274 std::string ServerMap::getBlockFilename(v3s16 p)
3277 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3281 void ServerMap::save(ModifiedState save_level)
3283 DSTACK(__FUNCTION_NAME);
3284 if(m_map_saving_enabled == false)
3286 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3290 if(save_level == MOD_STATE_CLEAN)
3291 infostream<<"ServerMap: Saving whole map, this can take time."
3294 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3299 // Profile modified reasons
3300 Profiler modprofiler;
3302 u32 sector_meta_count = 0;
3303 u32 block_count = 0;
3304 u32 block_count_all = 0; // Number of blocks in memory
3306 // Don't do anything with sqlite unless something is really saved
3307 bool save_started = false;
3309 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3310 i != m_sectors.end(); ++i)
3312 ServerMapSector *sector = (ServerMapSector*)i->second;
3313 assert(sector->getId() == MAPSECTOR_SERVER);
3315 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3317 saveSectorMeta(sector);
3318 sector_meta_count++;
3320 std::list<MapBlock*> blocks;
3321 sector->getBlocks(blocks);
3323 for(std::list<MapBlock*>::iterator j = blocks.begin();
3324 j != blocks.end(); ++j)
3326 MapBlock *block = *j;
3330 if(block->getModified() >= (u32)save_level)
3335 save_started = true;
3338 modprofiler.add(block->getModifiedReason(), 1);
3343 /*infostream<<"ServerMap: Written block ("
3344 <<block->getPos().X<<","
3345 <<block->getPos().Y<<","
3346 <<block->getPos().Z<<")"
3355 Only print if something happened or saved whole map
3357 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3358 || block_count != 0)
3360 infostream<<"ServerMap: Written: "
3361 <<sector_meta_count<<" sector metadata files, "
3362 <<block_count<<" block files"
3363 <<", "<<block_count_all<<" blocks in memory."
3365 PrintInfo(infostream); // ServerMap/ClientMap:
3366 infostream<<"Blocks modified by: "<<std::endl;
3367 modprofiler.print(infostream);
3371 static s32 unsignedToSigned(s32 i, s32 max_positive)
3373 if(i < max_positive)
3376 return i - 2*max_positive;
3379 // modulo of a negative number does not work consistently in C
3380 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3384 return mod - ((-i) % mod);
3387 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3389 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3391 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3393 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3394 return v3s16(x,y,z);
3397 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3399 if(loadFromFolders()){
3400 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3401 <<"all blocks that are stored in flat files"<<std::endl;
3407 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3409 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3410 v3s16 p = getIntegerAsBlock(block_i);
3411 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3417 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3419 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3420 si != m_sectors.end(); ++si)
3422 MapSector *sector = si->second;
3424 std::list<MapBlock*> blocks;
3425 sector->getBlocks(blocks);
3427 for(std::list<MapBlock*>::iterator i = blocks.begin();
3428 i != blocks.end(); ++i)
3430 MapBlock *block = (*i);
3431 v3s16 p = block->getPos();
3437 void ServerMap::saveMapMeta()
3439 DSTACK(__FUNCTION_NAME);
3441 /*infostream<<"ServerMap::saveMapMeta(): "
3445 createDirs(m_savedir);
3447 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3448 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3449 if(os.good() == false)
3451 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3452 <<"could not open"<<fullpath<<std::endl;
3453 throw FileNotGoodException("Cannot open chunk metadata");
3458 m_emerge->setParamsToSettings(¶ms);
3459 params.writeLines(os);
3461 os<<"[end_of_params]\n";
3463 m_map_metadata_changed = false;
3466 void ServerMap::loadMapMeta()
3468 DSTACK(__FUNCTION_NAME);
3470 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3473 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3474 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3475 if(is.good() == false)
3477 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3478 <<"could not open"<<fullpath<<std::endl;
3479 throw FileNotGoodException("Cannot open map metadata");
3487 throw SerializationError
3488 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3490 std::getline(is, line);
3491 std::string trimmedline = trim(line);
3492 if(trimmedline == "[end_of_params]")
3494 params.parseConfigLine(line);
3497 MapgenParams *mgparams;
3499 mgparams = m_emerge->getParamsFromSettings(¶ms);
3500 } catch (SettingNotFoundException &e) {
3501 infostream << "Couldn't get a setting from map_meta.txt: "
3502 << e.what() << std::endl;
3509 m_mgparams = mgparams;
3510 m_seed = mgparams->seed;
3512 if (params.exists("seed")) {
3513 m_seed = params.getU64("seed");
3514 m_mgparams->seed = m_seed;
3518 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3521 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3523 DSTACK(__FUNCTION_NAME);
3524 // Format used for writing
3525 u8 version = SER_FMT_VER_HIGHEST;
3527 v2s16 pos = sector->getPos();
3528 std::string dir = getSectorDir(pos);
3531 std::string fullpath = dir + DIR_DELIM + "meta";
3532 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3533 if(o.good() == false)
3534 throw FileNotGoodException("Cannot open sector metafile");
3536 sector->serialize(o, version);
3538 sector->differs_from_disk = false;
3541 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3543 DSTACK(__FUNCTION_NAME);
3545 v2s16 p2d = getSectorPos(sectordir);
3547 ServerMapSector *sector = NULL;
3549 std::string fullpath = sectordir + DIR_DELIM + "meta";
3550 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3551 if(is.good() == false)
3553 // If the directory exists anyway, it probably is in some old
3554 // format. Just go ahead and create the sector.
3555 if(fs::PathExists(sectordir))
3557 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3558 <<fullpath<<" doesn't exist but directory does."
3559 <<" Continuing with a sector with no metadata."
3561 sector = new ServerMapSector(this, p2d, m_gamedef);
3562 m_sectors[p2d] = sector;
3566 throw FileNotGoodException("Cannot open sector metafile");
3571 sector = ServerMapSector::deSerialize
3572 (is, this, p2d, m_sectors, m_gamedef);
3574 saveSectorMeta(sector);
3577 sector->differs_from_disk = false;
3582 bool ServerMap::loadSectorMeta(v2s16 p2d)
3584 DSTACK(__FUNCTION_NAME);
3586 MapSector *sector = NULL;
3588 // The directory layout we're going to load from.
3589 // 1 - original sectors/xxxxzzzz/
3590 // 2 - new sectors2/xxx/zzz/
3591 // If we load from anything but the latest structure, we will
3592 // immediately save to the new one, and remove the old.
3594 std::string sectordir1 = getSectorDir(p2d, 1);
3595 std::string sectordir;
3596 if(fs::PathExists(sectordir1))
3598 sectordir = sectordir1;
3603 sectordir = getSectorDir(p2d, 2);
3607 sector = loadSectorMeta(sectordir, loadlayout != 2);
3609 catch(InvalidFilenameException &e)
3613 catch(FileNotGoodException &e)
3617 catch(std::exception &e)
3626 bool ServerMap::loadSectorFull(v2s16 p2d)
3628 DSTACK(__FUNCTION_NAME);
3630 MapSector *sector = NULL;
3632 // The directory layout we're going to load from.
3633 // 1 - original sectors/xxxxzzzz/
3634 // 2 - new sectors2/xxx/zzz/
3635 // If we load from anything but the latest structure, we will
3636 // immediately save to the new one, and remove the old.
3638 std::string sectordir1 = getSectorDir(p2d, 1);
3639 std::string sectordir;
3640 if(fs::PathExists(sectordir1))
3642 sectordir = sectordir1;
3647 sectordir = getSectorDir(p2d, 2);
3651 sector = loadSectorMeta(sectordir, loadlayout != 2);
3653 catch(InvalidFilenameException &e)
3657 catch(FileNotGoodException &e)
3661 catch(std::exception &e)
3669 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3671 std::vector<fs::DirListNode>::iterator i2;
3672 for(i2=list2.begin(); i2!=list2.end(); i2++)
3678 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3680 catch(InvalidFilenameException &e)
3682 // This catches unknown crap in directory
3688 infostream<<"Sector converted to new layout - deleting "<<
3689 sectordir1<<std::endl;
3690 fs::RecursiveDelete(sectordir1);
3697 void ServerMap::beginSave() {
3699 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3700 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3703 void ServerMap::endSave() {
3705 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3706 infostream<<"WARNING: endSave() failed, map might not have saved.";
3709 void ServerMap::saveBlock(MapBlock *block)
3711 DSTACK(__FUNCTION_NAME);
3713 Dummy blocks are not written
3715 if(block->isDummy())
3717 /*v3s16 p = block->getPos();
3718 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3719 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3723 // Format used for writing
3724 u8 version = SER_FMT_VER_HIGHEST;
3726 v3s16 p3d = block->getPos();
3730 v2s16 p2d(p3d.X, p3d.Z);
3731 std::string sectordir = getSectorDir(p2d);
3733 createDirs(sectordir);
3735 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3736 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3737 if(o.good() == false)
3738 throw FileNotGoodException("Cannot open block data");
3741 [0] u8 serialization version
3747 std::ostringstream o(std::ios_base::binary);
3749 o.write((char*)&version, 1);
3752 block->serialize(o, version, true);
3754 // Write block to database
3756 std::string tmp = o.str();
3757 const char *bytes = tmp.c_str();
3759 bool success = true;
3760 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3761 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3764 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3765 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3768 int written = sqlite3_step(m_database_write);
3769 if(written != SQLITE_DONE) {
3770 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3771 <<sqlite3_errmsg(m_database)<<std::endl;
3774 // Make ready for later reuse
3775 sqlite3_reset(m_database_write);
3777 // We just wrote it to the disk so clear modified flag
3779 block->resetModified();
3782 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3784 DSTACK(__FUNCTION_NAME);
3786 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3789 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3790 if(is.good() == false)
3791 throw FileNotGoodException("Cannot open block file");
3793 v3s16 p3d = getBlockPos(sectordir, blockfile);
3794 v2s16 p2d(p3d.X, p3d.Z);
3796 assert(sector->getPos() == p2d);
3798 u8 version = SER_FMT_VER_INVALID;
3799 is.read((char*)&version, 1);
3802 throw SerializationError("ServerMap::loadBlock(): Failed"
3803 " to read MapBlock version");
3805 /*u32 block_size = MapBlock::serializedLength(version);
3806 SharedBuffer<u8> data(block_size);
3807 is.read((char*)*data, block_size);*/
3809 // This will always return a sector because we're the server
3810 //MapSector *sector = emergeSector(p2d);
3812 MapBlock *block = NULL;
3813 bool created_new = false;
3814 block = sector->getBlockNoCreateNoEx(p3d.Y);
3817 block = sector->createBlankBlockNoInsert(p3d.Y);
3822 block->deSerialize(is, version, true);
3824 // If it's a new block, insert it to the map
3826 sector->insertBlock(block);
3829 Save blocks loaded in old format in new format
3832 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3836 // Should be in database now, so delete the old file
3837 fs::RecursiveDelete(fullpath);
3840 // We just loaded it from the disk, so it's up-to-date.
3841 block->resetModified();
3844 catch(SerializationError &e)
3846 infostream<<"WARNING: Invalid block data on disk "
3847 <<"fullpath="<<fullpath
3848 <<" (SerializationError). "
3849 <<"what()="<<e.what()
3851 //" Ignoring. A new one will be generated.
3854 // TODO: Backup file; name is in fullpath.
3858 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3860 DSTACK(__FUNCTION_NAME);
3863 std::istringstream is(*blob, std::ios_base::binary);
3865 u8 version = SER_FMT_VER_INVALID;
3866 is.read((char*)&version, 1);
3869 throw SerializationError("ServerMap::loadBlock(): Failed"
3870 " to read MapBlock version");
3872 /*u32 block_size = MapBlock::serializedLength(version);
3873 SharedBuffer<u8> data(block_size);
3874 is.read((char*)*data, block_size);*/
3876 // This will always return a sector because we're the server
3877 //MapSector *sector = emergeSector(p2d);
3879 MapBlock *block = NULL;
3880 bool created_new = false;
3881 block = sector->getBlockNoCreateNoEx(p3d.Y);
3884 block = sector->createBlankBlockNoInsert(p3d.Y);
3889 block->deSerialize(is, version, true);
3891 // If it's a new block, insert it to the map
3893 sector->insertBlock(block);
3896 Save blocks loaded in old format in new format
3899 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3900 // Only save if asked to; no need to update version
3904 // We just loaded it from, so it's up-to-date.
3905 block->resetModified();
3908 catch(SerializationError &e)
3910 errorstream<<"Invalid block data in database"
3911 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3912 <<" (SerializationError): "<<e.what()<<std::endl;
3914 // TODO: Block should be marked as invalid in memory so that it is
3915 // not touched but the game can run
3917 if(g_settings->getBool("ignore_world_load_errors")){
3918 errorstream<<"Ignoring block load error. Duck and cover! "
3919 <<"(ignore_world_load_errors)"<<std::endl;
3921 throw SerializationError("Invalid block data in database");
3927 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3929 DSTACK(__FUNCTION_NAME);
3931 v2s16 p2d(blockpos.X, blockpos.Z);
3933 if(!loadFromFolders()) {
3936 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3937 infostream<<"WARNING: Could not bind block position for load: "
3938 <<sqlite3_errmsg(m_database)<<std::endl;
3939 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3941 Make sure sector is loaded
3943 MapSector *sector = createSector(p2d);
3948 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3949 size_t len = sqlite3_column_bytes(m_database_read, 0);
3951 std::string datastr(data, len);
3953 loadBlock(&datastr, blockpos, sector, false);
3955 sqlite3_step(m_database_read);
3956 // We should never get more than 1 row, so ok to reset
3957 sqlite3_reset(m_database_read);
3959 return getBlockNoCreateNoEx(blockpos);
3961 sqlite3_reset(m_database_read);
3963 // Not found in database, try the files
3966 // The directory layout we're going to load from.
3967 // 1 - original sectors/xxxxzzzz/
3968 // 2 - new sectors2/xxx/zzz/
3969 // If we load from anything but the latest structure, we will
3970 // immediately save to the new one, and remove the old.
3972 std::string sectordir1 = getSectorDir(p2d, 1);
3973 std::string sectordir;
3974 if(fs::PathExists(sectordir1))
3976 sectordir = sectordir1;
3981 sectordir = getSectorDir(p2d, 2);
3985 Make sure sector is loaded
3987 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3991 sector = loadSectorMeta(sectordir, loadlayout != 2);
3993 catch(InvalidFilenameException &e)
3997 catch(FileNotGoodException &e)
4001 catch(std::exception &e)
4008 Make sure file exists
4011 std::string blockfilename = getBlockFilename(blockpos);
4012 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
4016 Load block and save it to the database
4018 loadBlock(sectordir, blockfilename, sector, true);
4019 return getBlockNoCreateNoEx(blockpos);
4022 void ServerMap::PrintInfo(std::ostream &out)
4031 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4036 MapVoxelManipulator::~MapVoxelManipulator()
4038 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4042 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4044 TimeTaker timer1("emerge", &emerge_time);
4046 // Units of these are MapBlocks
4047 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4048 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4050 VoxelArea block_area_nodes
4051 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4053 addArea(block_area_nodes);
4055 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4056 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4057 for(s32 x=p_min.X; x<=p_max.X; x++)
4062 std::map<v3s16, u8>::iterator n;
4063 n = m_loaded_blocks.find(p);
4064 if(n != m_loaded_blocks.end())
4067 bool block_data_inexistent = false;
4070 TimeTaker timer1("emerge load", &emerge_load_time);
4072 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4073 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4075 a.print(infostream);
4076 infostream<<std::endl;*/
4078 block = m_map->getBlockNoCreate(p);
4079 if(block->isDummy())
4080 block_data_inexistent = true;
4082 block->copyTo(*this);
4084 catch(InvalidPositionException &e)
4086 block_data_inexistent = true;
4089 if(block_data_inexistent)
4091 flags |= VMANIP_BLOCK_DATA_INEXIST;
4093 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4094 // Fill with VOXELFLAG_INEXISTENT
4095 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4096 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4098 s32 i = m_area.index(a.MinEdge.X,y,z);
4099 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4102 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4104 // Mark that block was loaded as blank
4105 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4108 m_loaded_blocks[p] = flags;
4111 //infostream<<"emerge done"<<std::endl;
4115 SUGG: Add an option to only update eg. water and air nodes.
4116 This will make it interfere less with important stuff if
4119 void MapVoxelManipulator::blitBack
4120 (std::map<v3s16, MapBlock*> & modified_blocks)
4122 if(m_area.getExtent() == v3s16(0,0,0))
4125 //TimeTaker timer1("blitBack");
4127 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4128 <<m_loaded_blocks.size()<<std::endl;*/
4131 Initialize block cache
4133 v3s16 blockpos_last;
4134 MapBlock *block = NULL;
4135 bool block_checked_in_modified = false;
4137 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4138 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4139 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4143 u8 f = m_flags[m_area.index(p)];
4144 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4147 MapNode &n = m_data[m_area.index(p)];
4149 v3s16 blockpos = getNodeBlockPos(p);
4154 if(block == NULL || blockpos != blockpos_last){
4155 block = m_map->getBlockNoCreate(blockpos);
4156 blockpos_last = blockpos;
4157 block_checked_in_modified = false;
4160 // Calculate relative position in block
4161 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4163 // Don't continue if nothing has changed here
4164 if(block->getNode(relpos) == n)
4167 //m_map->setNode(m_area.MinEdge + p, n);
4168 block->setNode(relpos, n);
4171 Make sure block is in modified_blocks
4173 if(block_checked_in_modified == false)
4175 modified_blocks[blockpos] = block;
4176 block_checked_in_modified = true;
4179 catch(InvalidPositionException &e)
4185 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4186 MapVoxelManipulator(map),
4187 m_create_area(false)
4191 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4195 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4197 // Just create the area so that it can be pointed to
4198 VoxelManipulator::emerge(a, caller_id);
4201 void ManualMapVoxelManipulator::initialEmerge(
4202 v3s16 blockpos_min, v3s16 blockpos_max)
4204 TimeTaker timer1("initialEmerge", &emerge_time);
4206 // Units of these are MapBlocks
4207 v3s16 p_min = blockpos_min;
4208 v3s16 p_max = blockpos_max;
4210 VoxelArea block_area_nodes
4211 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4213 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4216 infostream<<"initialEmerge: area: ";
4217 block_area_nodes.print(infostream);
4218 infostream<<" ("<<size_MB<<"MB)";
4219 infostream<<std::endl;
4222 addArea(block_area_nodes);
4224 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4225 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4226 for(s32 x=p_min.X; x<=p_max.X; x++)
4231 std::map<v3s16, u8>::iterator n;
4232 n = m_loaded_blocks.find(p);
4233 if(n != m_loaded_blocks.end())
4236 bool block_data_inexistent = false;
4239 TimeTaker timer1("emerge load", &emerge_load_time);
4241 block = m_map->getBlockNoCreate(p);
4242 if(block->isDummy())
4243 block_data_inexistent = true;
4245 block->copyTo(*this);
4247 catch(InvalidPositionException &e)
4249 block_data_inexistent = true;
4252 if(block_data_inexistent)
4254 flags |= VMANIP_BLOCK_DATA_INEXIST;
4257 Mark area inexistent
4259 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4260 // Fill with VOXELFLAG_INEXISTENT
4261 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4262 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4264 s32 i = m_area.index(a.MinEdge.X,y,z);
4265 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4268 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4270 // Mark that block was loaded as blank
4271 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4274 m_loaded_blocks[p] = flags;
4278 void ManualMapVoxelManipulator::blitBackAll(
4279 std::map<v3s16, MapBlock*> * modified_blocks)
4281 if(m_area.getExtent() == v3s16(0,0,0))
4285 Copy data of all blocks
4287 for(std::map<v3s16, u8>::iterator
4288 i = m_loaded_blocks.begin();
4289 i != m_loaded_blocks.end(); ++i)
4292 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4293 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4294 if(existed == false)
4299 block->copyFrom(*this);
4302 (*modified_blocks)[p] = block;