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::deleteSectors(std::list<v2s16> &list)
1515 for(std::list<v2s16>::iterator j = list.begin();
1516 j != list.end(); ++j)
1518 MapSector *sector = m_sectors[*j];
1519 // If sector is in sector cache, remove it from there
1520 if(m_sector_cache == sector)
1521 m_sector_cache = NULL;
1522 // Remove from map and delete
1523 m_sectors.erase(*j);
1529 void Map::unloadUnusedData(float timeout,
1530 core::list<v3s16> *deleted_blocks)
1532 core::list<v2s16> sector_deletion_queue;
1533 u32 deleted_blocks_count = 0;
1534 u32 saved_blocks_count = 0;
1536 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1537 for(; si.atEnd() == false; si++)
1539 MapSector *sector = si.getNode()->getValue();
1541 bool all_blocks_deleted = true;
1543 core::list<MapBlock*> blocks;
1544 sector->getBlocks(blocks);
1545 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1546 i != blocks.end(); i++)
1548 MapBlock *block = (*i);
1550 if(block->getUsageTimer() > timeout)
1553 if(block->getModified() != MOD_STATE_CLEAN)
1556 saved_blocks_count++;
1558 // Delete from memory
1559 sector->deleteBlock(block);
1560 deleted_blocks_count++;
1564 all_blocks_deleted = false;
1568 if(all_blocks_deleted)
1570 sector_deletion_queue.push_back(si.getNode()->getKey());
1574 deleteSectors(sector_deletion_queue);
1576 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1577 <<", of which "<<saved_blocks_count<<" were wr."
1580 //return sector_deletion_queue.getSize();
1581 //return deleted_blocks_count;
1585 void Map::PrintInfo(std::ostream &out)
1590 #define WATER_DROP_BOOST 4
1594 NEIGHBOR_SAME_LEVEL,
1597 struct NodeNeighbor {
1601 bool l; //can liquid
1605 void Map::transforming_liquid_add(v3s16 p) {
1606 m_transforming_liquid.push_back(p);
1609 s32 Map::transforming_liquid_size() {
1610 return m_transforming_liquid.size();
1613 const v3s16 g_7dirs[7] =
1615 // +right, +top, +back
1616 v3s16( 0,-1, 0), // bottom
1617 v3s16( 0, 0, 0), // self
1618 v3s16( 0, 0, 1), // back
1619 v3s16( 0, 0,-1), // front
1620 v3s16( 1, 0, 0), // right
1621 v3s16(-1, 0, 0), // left
1622 v3s16( 0, 1, 0) // top
1629 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1631 INodeDefManager *nodemgr = m_gamedef->ndef();
1633 DSTACK(__FUNCTION_NAME);
1634 //TimeTaker timer("transformLiquids()");
1637 u32 initial_size = m_transforming_liquid.size();
1639 u8 relax = g_settings->getS16("liquid_relax");
1640 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1641 int water_level = g_settings->getS16("water_level");
1643 // list of nodes that due to viscosity have not reached their max level height
1644 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1646 // List of MapBlocks that will require a lighting update (due to lava)
1647 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1649 while (m_transforming_liquid.size() > 0)
1651 // This should be done here so that it is done when continue is used
1652 if (loopcount >= initial_size || loopcount >= 1000)
1656 Get a queued transforming liquid node
1658 v3s16 p0 = m_transforming_liquid.pop_front();
1659 u16 total_level = 0;
1660 // surrounding flowing liquid nodes
1661 NodeNeighbor neighbors[7];
1662 // current level of every block
1663 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1665 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1666 s8 can_liquid_same_level = 0;
1667 content_t liquid_kind = CONTENT_IGNORE;
1668 content_t liquid_kind_flowing = CONTENT_IGNORE;
1670 Collect information about the environment
1672 const v3s16 *dirs = g_7dirs;
1673 for (u16 i = 0; i < 7; i++) {
1674 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1677 nt = NEIGHBOR_UPPER;
1680 nt = NEIGHBOR_LOWER;
1683 v3s16 npos = p0 + dirs[i];
1685 neighbors[i].n = getNodeNoEx(npos);
1686 neighbors[i].t = nt;
1687 neighbors[i].p = npos;
1690 NodeNeighbor & nb = neighbors[i];
1692 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1694 if (nb.n.getContent() == CONTENT_AIR) {
1695 liquid_levels[i] = 0;
1700 // if this node is not (yet) of a liquid type,
1701 // choose the first liquid type we encounter
1702 if (liquid_kind_flowing == CONTENT_IGNORE)
1703 liquid_kind_flowing = nodemgr->getId(
1704 nodemgr->get(nb.n).liquid_alternative_flowing);
1705 if (liquid_kind == CONTENT_IGNORE)
1706 liquid_kind = nb.n.getContent();
1707 if (nb.n.getContent() == liquid_kind) {
1708 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1710 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1713 case LIQUID_FLOWING:
1714 // if this node is not (yet) of a liquid type,
1715 // choose the first liquid type we encounter
1716 if (liquid_kind_flowing == CONTENT_IGNORE)
1717 liquid_kind_flowing = nb.n.getContent();
1718 if (liquid_kind == CONTENT_IGNORE)
1719 liquid_kind = nodemgr->getId(
1720 nodemgr->get(nb.n).liquid_alternative_source);
1721 if (nb.n.getContent() == liquid_kind_flowing) {
1722 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1728 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1729 ++can_liquid_same_level;
1730 if (liquid_levels[i] > 0)
1731 total_level += liquid_levels[i];
1734 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1735 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1736 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1737 << nodemgr->get(nb.n.getContent()).liquid_type
1738 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1739 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1740 << " tlevel=" << (int)total_level << " cansame="
1741 << (int)can_liquid_same_level << std::endl;
1745 if (liquid_kind == CONTENT_IGNORE ||
1746 !neighbors[D_SELF].l ||
1750 // fill bottom block
1751 if (neighbors[D_BOTTOM].l) {
1752 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1753 LIQUID_LEVEL_SOURCE : total_level;
1754 total_level -= liquid_levels_want[D_BOTTOM];
1758 if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 &&
1759 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1760 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1761 (can_liquid_same_level - relax) &&
1762 can_liquid_same_level >= relax + 1) {
1763 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1766 // prevent lakes in air above unloaded blocks
1767 if (p0.Y > water_level && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE) {
1771 // calculate self level 5 blocks
1773 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1774 ? LIQUID_LEVEL_SOURCE
1775 : total_level / can_liquid_same_level;
1776 total_level -= want_level * can_liquid_same_level;
1779 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1780 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1781 total_level <= (can_liquid_same_level - relax) &&
1782 can_liquid_same_level >= relax + 1) {
1786 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1787 if (!neighbors[ii].l)
1789 liquid_levels_want[ii] = want_level;
1790 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0
1791 && liquid_levels[ii] > liquid_levels_want[ii]
1793 ++liquid_levels_want[ii];
1798 for (u16 ii = 0; ii < 7; ++ii) {
1799 if (total_level < 1) break;
1800 if (liquid_levels_want[ii] >= 0 &&
1801 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1802 ++liquid_levels_want[ii];
1807 // fill top block if can
1808 if (neighbors[D_TOP].l) {
1809 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1810 LIQUID_LEVEL_SOURCE : total_level;
1811 total_level -= liquid_levels_want[D_TOP];
1814 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1815 if ( neighbors[ii].i ||
1816 (liquid_levels_want[ii] >= 0 &&
1817 (fast_flood && p0.Y < water_level &&
1818 (initial_size >= 1000
1820 && want_level >= LIQUID_LEVEL_SOURCE/4
1821 && can_liquid_same_level >= 5
1822 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1823 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1826 if (total_level > 0) //|| flowed != volume)
1827 infostream <<" AFTER level=" << (int)total_level
1828 //<< " flowed="<<flowed<< " volume=" << volume
1829 << " wantsame="<<(int)want_level<< " top="
1830 << (int)liquid_levels_want[D_TOP]<< " topwas="
1831 << (int)liquid_levels[D_TOP]<< " bot="
1832 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1836 for (u16 i = 0; i < 7; i++) {
1837 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1839 MapNode & n0 = neighbors[i].n;
1840 p0 = neighbors[i].p;
1842 decide on the type (and possibly level) of the current node
1844 content_t new_node_content;
1845 s8 new_node_level = -1;
1846 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1847 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1848 // amount to gain, limited by viscosity
1849 // must be at least 1 in absolute value
1850 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1851 if (level_inc < -viscosity || level_inc > viscosity)
1852 new_node_level = liquid_levels[i] + level_inc/viscosity;
1853 else if (level_inc < 0)
1854 new_node_level = liquid_levels[i] - 1;
1855 else if (level_inc > 0)
1856 new_node_level = liquid_levels[i] + 1;
1858 new_node_level = liquid_levels_want[i];
1861 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1862 new_node_content = liquid_kind;
1863 else if (new_node_level > 0)
1864 new_node_content = liquid_kind_flowing;
1866 new_node_content = CONTENT_AIR;
1868 // last level must flow down on stairs
1869 if (liquid_levels_want[i] != liquid_levels[i] &&
1870 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1871 new_node_level >= 1 && new_node_level <= 2) {
1872 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1873 if (neighbors[ii].l)
1874 must_reflow_second.push_back(p0 + dirs[ii]);
1879 check if anything has changed.
1880 if not, just continue with the next node.
1883 new_node_content == n0.getContent()
1884 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1885 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1886 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1887 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1890 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1891 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1892 LIQUID_INFINITY_MASK) == neighbors[i].i
1900 update the current node
1902 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1903 // set level to last 3 bits, flowing down bit to 4th bit
1904 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1905 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1906 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1907 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1910 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1911 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1912 <<(int)new_node_level<<std::endl;
1915 n0.setContent(new_node_content);
1916 // Find out whether there is a suspect for this action
1917 std::string suspect;
1918 if(m_gamedef->rollback()){
1919 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1922 if(!suspect.empty()){
1924 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1925 // Get old node for rollback
1926 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1930 RollbackNode rollback_newnode(this, p0, m_gamedef);
1931 RollbackAction action;
1932 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1933 m_gamedef->rollback()->reportAction(action);
1939 v3s16 blockpos = getNodeBlockPos(p0);
1940 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1942 modified_blocks[blockpos] = block;
1943 // If node emits light, MapBlock requires lighting update
1944 if(nodemgr->get(n0).light_source != 0)
1945 lighting_modified_blocks[block->getPos()] = block;
1947 must_reflow.push_back(neighbors[i].p);
1949 /* //for better relax only same level
1950 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1951 if (!neighbors[ii].l) continue;
1952 must_reflow.push_back(p0 + dirs[ii]);
1957 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1958 <<" reflow="<<must_reflow.size()
1959 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1961 while (must_reflow.size() > 0)
1962 m_transforming_liquid.push_back(must_reflow.pop_front());
1963 while (must_reflow_second.size() > 0)
1964 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1965 updateLighting(lighting_modified_blocks, modified_blocks);
1968 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1971 if (g_settings->getBool("liquid_finite"))
1972 return Map::transformLiquidsFinite(modified_blocks);
1974 INodeDefManager *nodemgr = m_gamedef->ndef();
1976 DSTACK(__FUNCTION_NAME);
1977 //TimeTaker timer("transformLiquids()");
1980 u32 initial_size = m_transforming_liquid.size();
1982 /*if(initial_size != 0)
1983 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1985 // list of nodes that due to viscosity have not reached their max level height
1986 UniqueQueue<v3s16> must_reflow;
1988 // List of MapBlocks that will require a lighting update (due to lava)
1989 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1991 while(m_transforming_liquid.size() != 0)
1993 // This should be done here so that it is done when continue is used
1994 if(loopcount >= initial_size || loopcount >= 10000)
1999 Get a queued transforming liquid node
2001 v3s16 p0 = m_transforming_liquid.pop_front();
2003 MapNode n0 = getNodeNoEx(p0);
2006 Collect information about current node
2008 s8 liquid_level = -1;
2009 content_t liquid_kind = CONTENT_IGNORE;
2010 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2011 switch (liquid_type) {
2013 liquid_level = LIQUID_LEVEL_SOURCE;
2014 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2016 case LIQUID_FLOWING:
2017 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2018 liquid_kind = n0.getContent();
2021 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2022 // continue with the next node.
2023 if (n0.getContent() != CONTENT_AIR)
2025 liquid_kind = CONTENT_AIR;
2030 Collect information about the environment
2032 const v3s16 *dirs = g_6dirs;
2033 NodeNeighbor sources[6]; // surrounding sources
2034 int num_sources = 0;
2035 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2037 NodeNeighbor airs[6]; // surrounding air
2039 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2040 int num_neutrals = 0;
2041 bool flowing_down = false;
2042 for (u16 i = 0; i < 6; i++) {
2043 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2046 nt = NEIGHBOR_UPPER;
2049 nt = NEIGHBOR_LOWER;
2052 v3s16 npos = p0 + dirs[i];
2053 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2054 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2056 if (nb.n.getContent() == CONTENT_AIR) {
2057 airs[num_airs++] = nb;
2058 // if the current node is a water source the neighbor
2059 // should be enqueded for transformation regardless of whether the
2060 // current node changes or not.
2061 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2062 m_transforming_liquid.push_back(npos);
2063 // if the current node happens to be a flowing node, it will start to flow down here.
2064 if (nb.t == NEIGHBOR_LOWER) {
2065 flowing_down = true;
2068 neutrals[num_neutrals++] = nb;
2072 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2073 if (liquid_kind == CONTENT_AIR)
2074 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2075 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2076 neutrals[num_neutrals++] = nb;
2078 // Do not count bottom source, it will screw things up
2080 sources[num_sources++] = nb;
2083 case LIQUID_FLOWING:
2084 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2085 if (liquid_kind == CONTENT_AIR)
2086 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2087 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2088 neutrals[num_neutrals++] = nb;
2090 flows[num_flows++] = nb;
2091 if (nb.t == NEIGHBOR_LOWER)
2092 flowing_down = true;
2099 decide on the type (and possibly level) of the current node
2101 content_t new_node_content;
2102 s8 new_node_level = -1;
2103 s8 max_node_level = -1;
2104 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2105 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2106 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2107 // it's perfectly safe to use liquid_kind here to determine the new node content.
2108 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2109 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2110 // liquid_kind is set properly, see above
2111 new_node_content = liquid_kind;
2112 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2114 // no surrounding sources, so get the maximum level that can flow into this node
2115 for (u16 i = 0; i < num_flows; i++) {
2116 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2117 switch (flows[i].t) {
2118 case NEIGHBOR_UPPER:
2119 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2120 max_node_level = LIQUID_LEVEL_MAX;
2121 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2122 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2123 } else if (nb_liquid_level > max_node_level)
2124 max_node_level = nb_liquid_level;
2126 case NEIGHBOR_LOWER:
2128 case NEIGHBOR_SAME_LEVEL:
2129 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2130 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2131 max_node_level = nb_liquid_level - 1;
2137 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2138 if (viscosity > 1 && max_node_level != liquid_level) {
2139 // amount to gain, limited by viscosity
2140 // must be at least 1 in absolute value
2141 s8 level_inc = max_node_level - liquid_level;
2142 if (level_inc < -viscosity || level_inc > viscosity)
2143 new_node_level = liquid_level + level_inc/viscosity;
2144 else if (level_inc < 0)
2145 new_node_level = liquid_level - 1;
2146 else if (level_inc > 0)
2147 new_node_level = liquid_level + 1;
2148 if (new_node_level != max_node_level)
2149 must_reflow.push_back(p0);
2151 new_node_level = max_node_level;
2153 if (new_node_level >= 0)
2154 new_node_content = liquid_kind;
2156 new_node_content = CONTENT_AIR;
2161 check if anything has changed. if not, just continue with the next node.
2163 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2164 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2165 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2171 update the current node
2173 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2174 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2175 // set level to last 3 bits, flowing down bit to 4th bit
2176 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2178 // set the liquid level and flow bit to 0
2179 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2181 n0.setContent(new_node_content);
2183 // Find out whether there is a suspect for this action
2184 std::string suspect;
2185 if(m_gamedef->rollback()){
2186 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2189 if(!suspect.empty()){
2191 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2192 // Get old node for rollback
2193 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2197 RollbackNode rollback_newnode(this, p0, m_gamedef);
2198 RollbackAction action;
2199 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2200 m_gamedef->rollback()->reportAction(action);
2206 v3s16 blockpos = getNodeBlockPos(p0);
2207 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2209 modified_blocks[blockpos] = block;
2210 // If node emits light, MapBlock requires lighting update
2211 if(nodemgr->get(n0).light_source != 0)
2212 lighting_modified_blocks[block->getPos()] = block;
2216 enqueue neighbors for update if neccessary
2218 switch (nodemgr->get(n0.getContent()).liquid_type) {
2220 case LIQUID_FLOWING:
2221 // make sure source flows into all neighboring nodes
2222 for (u16 i = 0; i < num_flows; i++)
2223 if (flows[i].t != NEIGHBOR_UPPER)
2224 m_transforming_liquid.push_back(flows[i].p);
2225 for (u16 i = 0; i < num_airs; i++)
2226 if (airs[i].t != NEIGHBOR_UPPER)
2227 m_transforming_liquid.push_back(airs[i].p);
2230 // this flow has turned to air; neighboring flows might need to do the same
2231 for (u16 i = 0; i < num_flows; i++)
2232 m_transforming_liquid.push_back(flows[i].p);
2236 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2237 while (must_reflow.size() > 0)
2238 m_transforming_liquid.push_back(must_reflow.pop_front());
2239 updateLighting(lighting_modified_blocks, modified_blocks);
2242 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2244 v3s16 blockpos = getNodeBlockPos(p);
2245 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2246 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2248 infostream<<"Map::getNodeMetadata(): Need to emerge "
2249 <<PP(blockpos)<<std::endl;
2250 block = emergeBlock(blockpos, false);
2254 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2258 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2262 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2264 v3s16 blockpos = getNodeBlockPos(p);
2265 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2266 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2268 infostream<<"Map::setNodeMetadata(): Need to emerge "
2269 <<PP(blockpos)<<std::endl;
2270 block = emergeBlock(blockpos, false);
2274 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2278 block->m_node_metadata.set(p_rel, meta);
2281 void Map::removeNodeMetadata(v3s16 p)
2283 v3s16 blockpos = getNodeBlockPos(p);
2284 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2285 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2288 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2292 block->m_node_metadata.remove(p_rel);
2295 NodeTimer Map::getNodeTimer(v3s16 p)
2297 v3s16 blockpos = getNodeBlockPos(p);
2298 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2299 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2301 infostream<<"Map::getNodeTimer(): Need to emerge "
2302 <<PP(blockpos)<<std::endl;
2303 block = emergeBlock(blockpos, false);
2307 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2311 NodeTimer t = block->m_node_timers.get(p_rel);
2315 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2317 v3s16 blockpos = getNodeBlockPos(p);
2318 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2319 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2321 infostream<<"Map::setNodeTimer(): Need to emerge "
2322 <<PP(blockpos)<<std::endl;
2323 block = emergeBlock(blockpos, false);
2327 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2331 block->m_node_timers.set(p_rel, t);
2334 void Map::removeNodeTimer(v3s16 p)
2336 v3s16 blockpos = getNodeBlockPos(p);
2337 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2338 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2341 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2345 block->m_node_timers.remove(p_rel);
2351 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2352 Map(dout_server, gamedef),
2354 m_map_metadata_changed(true),
2356 m_database_read(NULL),
2357 m_database_write(NULL)
2359 verbosestream<<__FUNCTION_NAME<<std::endl;
2362 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2364 m_mgparams = new MapgenV6Params();
2366 m_seed = m_mgparams->seed;
2368 if (g_settings->get("fixed_map_seed").empty())
2370 m_seed = (((u64)(myrand() & 0xffff) << 0)
2371 | ((u64)(myrand() & 0xffff) << 16)
2372 | ((u64)(myrand() & 0xffff) << 32)
2373 | ((u64)(myrand() & 0xffff) << 48));
2374 m_mgparams->seed = m_seed;
2378 Experimental and debug stuff
2385 Try to load map; if not found, create a new one.
2388 m_savedir = savedir;
2389 m_map_saving_enabled = false;
2393 // If directory exists, check contents and load if possible
2394 if(fs::PathExists(m_savedir))
2396 // If directory is empty, it is safe to save into it.
2397 if(fs::GetDirListing(m_savedir).size() == 0)
2399 infostream<<"ServerMap: Empty save directory is valid."
2401 m_map_saving_enabled = true;
2406 // Load map metadata (seed, chunksize)
2409 catch(SettingNotFoundException &e){
2410 infostream<<"ServerMap: Some metadata not found."
2411 <<" Using default settings."<<std::endl;
2413 catch(FileNotGoodException &e){
2414 infostream<<"WARNING: Could not load map metadata"
2415 //<<" Disabling chunk-based generator."
2420 infostream<<"ServerMap: Successfully loaded map "
2421 <<"metadata from "<<savedir
2422 <<", assuming valid save directory."
2423 <<" seed="<<m_seed<<"."
2426 m_map_saving_enabled = true;
2427 // Map loaded, not creating new one
2431 // If directory doesn't exist, it is safe to save to it
2433 m_map_saving_enabled = true;
2436 catch(std::exception &e)
2438 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2439 <<", exception: "<<e.what()<<std::endl;
2440 infostream<<"Please remove the map or fix it."<<std::endl;
2441 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2444 infostream<<"Initializing new map."<<std::endl;
2446 // Create zero sector
2447 emergeSector(v2s16(0,0));
2449 // Initially write whole map
2450 save(MOD_STATE_CLEAN);
2453 ServerMap::~ServerMap()
2455 verbosestream<<__FUNCTION_NAME<<std::endl;
2459 if(m_map_saving_enabled)
2461 // Save only changed parts
2462 save(MOD_STATE_WRITE_AT_UNLOAD);
2463 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2467 infostream<<"ServerMap: Map not saved"<<std::endl;
2470 catch(std::exception &e)
2472 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2473 <<", exception: "<<e.what()<<std::endl;
2477 Close database if it was opened
2480 sqlite3_finalize(m_database_read);
2481 if(m_database_write)
2482 sqlite3_finalize(m_database_write);
2484 sqlite3_close(m_database);
2490 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2491 for(; i.atEnd() == false; i++)
2493 MapChunk *chunk = i.getNode()->getValue();
2501 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2503 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2504 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2506 s16 chunksize = m_mgparams->chunksize;
2507 s16 coffset = -chunksize / 2;
2508 v3s16 chunk_offset(coffset, coffset, coffset);
2509 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2510 v3s16 blockpos_min = blockpos_div * chunksize;
2511 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2512 blockpos_min += chunk_offset;
2513 blockpos_max += chunk_offset;
2515 v3s16 extra_borders(1,1,1);
2517 // Do nothing if not inside limits (+-1 because of neighbors)
2518 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2519 blockpos_over_limit(blockpos_max + extra_borders))
2522 data->seed = m_seed;
2523 data->blockpos_min = blockpos_min;
2524 data->blockpos_max = blockpos_max;
2525 data->blockpos_requested = blockpos;
2526 data->nodedef = m_gamedef->ndef();
2529 Create the whole area of this and the neighboring blocks
2532 //TimeTaker timer("initBlockMake() create area");
2534 for(s16 x=blockpos_min.X-extra_borders.X;
2535 x<=blockpos_max.X+extra_borders.X; x++)
2536 for(s16 z=blockpos_min.Z-extra_borders.Z;
2537 z<=blockpos_max.Z+extra_borders.Z; z++)
2539 v2s16 sectorpos(x, z);
2540 // Sector metadata is loaded from disk if not already loaded.
2541 ServerMapSector *sector = createSector(sectorpos);
2544 for(s16 y=blockpos_min.Y-extra_borders.Y;
2545 y<=blockpos_max.Y+extra_borders.Y; y++)
2548 //MapBlock *block = createBlock(p);
2549 // 1) get from memory, 2) load from disk
2550 MapBlock *block = emergeBlock(p, false);
2551 // 3) create a blank one
2554 block = createBlock(p);
2557 Block gets sunlight if this is true.
2559 Refer to the map generator heuristics.
2561 bool ug = m_emerge->isBlockUnderground(p);
2562 block->setIsUnderground(ug);
2565 // Lighting will not be valid after make_chunk is called
2566 block->setLightingExpired(true);
2567 // Lighting will be calculated
2568 //block->setLightingExpired(false);
2574 Now we have a big empty area.
2576 Make a ManualMapVoxelManipulator that contains this and the
2580 // The area that contains this block and it's neighbors
2581 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2582 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2584 data->vmanip = new ManualMapVoxelManipulator(this);
2585 //data->vmanip->setMap(this);
2589 //TimeTaker timer("initBlockMake() initialEmerge");
2590 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2593 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2594 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2595 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2596 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2597 core::map<v3s16, u8>::Node *n;
2598 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2601 u8 flags = n->getValue();
2602 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2608 // Data is ready now.
2612 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2613 std::map<v3s16, MapBlock*> &changed_blocks)
2615 v3s16 blockpos_min = data->blockpos_min;
2616 v3s16 blockpos_max = data->blockpos_max;
2617 v3s16 blockpos_requested = data->blockpos_requested;
2618 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2619 <<blockpos_requested.Y<<","
2620 <<blockpos_requested.Z<<")"<<std::endl;*/
2622 v3s16 extra_borders(1,1,1);
2624 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2626 /*infostream<<"Resulting vmanip:"<<std::endl;
2627 data->vmanip.print(infostream);*/
2629 // Make sure affected blocks are loaded
2630 for(s16 x=blockpos_min.X-extra_borders.X;
2631 x<=blockpos_max.X+extra_borders.X; x++)
2632 for(s16 z=blockpos_min.Z-extra_borders.Z;
2633 z<=blockpos_max.Z+extra_borders.Z; z++)
2634 for(s16 y=blockpos_min.Y-extra_borders.Y;
2635 y<=blockpos_max.Y+extra_borders.Y; y++)
2638 // Load from disk if not already in memory
2639 emergeBlock(p, false);
2643 Blit generated stuff to map
2644 NOTE: blitBackAll adds nearly everything to changed_blocks
2648 //TimeTaker timer("finishBlockMake() blitBackAll");
2649 data->vmanip->blitBackAll(&changed_blocks);
2652 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2655 Copy transforming liquid information
2657 while(data->transforming_liquid.size() > 0)
2659 v3s16 p = data->transforming_liquid.pop_front();
2660 m_transforming_liquid.push_back(p);
2664 Do stuff in central blocks
2672 TimeTaker t("finishBlockMake lighting update");
2674 core::map<v3s16, MapBlock*> lighting_update_blocks;
2677 for(s16 x=blockpos_min.X-extra_borders.X;
2678 x<=blockpos_max.X+extra_borders.X; x++)
2679 for(s16 z=blockpos_min.Z-extra_borders.Z;
2680 z<=blockpos_max.Z+extra_borders.Z; z++)
2681 for(s16 y=blockpos_min.Y-extra_borders.Y;
2682 y<=blockpos_max.Y+extra_borders.Y; y++)
2685 MapBlock *block = getBlockNoCreateNoEx(p);
2687 lighting_update_blocks.insert(block->getPos(), block);
2690 updateLighting(lighting_update_blocks, changed_blocks);
2694 Set lighting to non-expired state in all of them.
2695 This is cheating, but it is not fast enough if all of them
2696 would actually be updated.
2698 for(s16 x=blockpos_min.X-extra_borders.X;
2699 x<=blockpos_max.X+extra_borders.X; x++)
2700 for(s16 z=blockpos_min.Z-extra_borders.Z;
2701 z<=blockpos_max.Z+extra_borders.Z; z++)
2702 for(s16 y=blockpos_min.Y-extra_borders.Y;
2703 y<=blockpos_max.Y+extra_borders.Y; y++)
2706 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2710 if(enable_mapgen_debug_info == false)
2711 t.stop(true); // Hide output
2716 Go through changed blocks
2718 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2719 i != changed_blocks.end(); ++i)
2721 MapBlock *block = i->second;
2724 Update day/night difference cache of the MapBlocks
2726 block->expireDayNightDiff();
2728 Set block as modified
2730 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2731 "finishBlockMake expireDayNightDiff");
2735 Set central blocks as generated
2737 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2738 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2739 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2742 MapBlock *block = getBlockNoCreateNoEx(p);
2744 block->setGenerated(true);
2748 Save changed parts of map
2749 NOTE: Will be saved later.
2751 //save(MOD_STATE_WRITE_AT_UNLOAD);
2753 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2754 <<","<<blockpos_requested.Y<<","
2755 <<blockpos_requested.Z<<")"<<std::endl;*/
2757 if(enable_mapgen_debug_info)
2760 Analyze resulting blocks
2762 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2763 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2764 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2765 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2766 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2767 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2769 v3s16 p = v3s16(x,y,z);
2770 MapBlock *block = getBlockNoCreateNoEx(p);
2772 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2773 infostream<<"Generated "<<spos<<": "
2774 <<analyze_block(block)<<std::endl;
2779 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2785 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2787 DSTACKF("%s: p2d=(%d,%d)",
2792 Check if it exists already in memory
2794 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2799 Try to load it from disk (with blocks)
2801 //if(loadSectorFull(p2d) == true)
2804 Try to load metadata from disk
2807 if(loadSectorMeta(p2d) == true)
2809 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2812 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2813 throw InvalidPositionException("");
2819 Do not create over-limit
2821 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2822 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2823 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2824 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2825 throw InvalidPositionException("createSector(): pos. over limit");
2828 Generate blank sector
2831 sector = new ServerMapSector(this, p2d, m_gamedef);
2833 // Sector position on map in nodes
2834 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2839 m_sectors[p2d] = sector;
2846 This is a quick-hand function for calling makeBlock().
2848 MapBlock * ServerMap::generateBlock(
2850 std::map<v3s16, MapBlock*> &modified_blocks
2853 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2855 /*infostream<<"generateBlock(): "
2856 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2859 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2861 TimeTaker timer("generateBlock");
2863 //MapBlock *block = original_dummy;
2865 v2s16 p2d(p.X, p.Z);
2866 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2869 Do not generate over-limit
2871 if(blockpos_over_limit(p))
2873 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2874 throw InvalidPositionException("generateBlock(): pos. over limit");
2878 Create block make data
2881 initBlockMake(&data, p);
2887 TimeTaker t("mapgen::make_block()");
2888 mapgen->makeChunk(&data);
2889 //mapgen::make_block(&data);
2891 if(enable_mapgen_debug_info == false)
2892 t.stop(true); // Hide output
2896 Blit data back on map, update lighting, add mobs and whatever this does
2898 finishBlockMake(&data, modified_blocks);
2903 MapBlock *block = getBlockNoCreateNoEx(p);
2911 bool erroneus_content = false;
2912 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2913 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2914 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2917 MapNode n = block->getNode(p);
2918 if(n.getContent() == CONTENT_IGNORE)
2920 infostream<<"CONTENT_IGNORE at "
2921 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2923 erroneus_content = true;
2927 if(erroneus_content)
2936 Generate a completely empty block
2940 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2941 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2943 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2946 n.setContent(CONTENT_AIR);
2947 block->setNode(v3s16(x0,y0,z0), n);
2953 if(enable_mapgen_debug_info == false)
2954 timer.stop(true); // Hide output
2960 MapBlock * ServerMap::createBlock(v3s16 p)
2962 DSTACKF("%s: p=(%d,%d,%d)",
2963 __FUNCTION_NAME, p.X, p.Y, p.Z);
2966 Do not create over-limit
2968 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2969 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2970 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2971 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2972 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2973 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2974 throw InvalidPositionException("createBlock(): pos. over limit");
2976 v2s16 p2d(p.X, p.Z);
2979 This will create or load a sector if not found in memory.
2980 If block exists on disk, it will be loaded.
2982 NOTE: On old save formats, this will be slow, as it generates
2983 lighting on blocks for them.
2985 ServerMapSector *sector;
2987 sector = (ServerMapSector*)createSector(p2d);
2988 assert(sector->getId() == MAPSECTOR_SERVER);
2990 catch(InvalidPositionException &e)
2992 infostream<<"createBlock: createSector() failed"<<std::endl;
2996 NOTE: This should not be done, or at least the exception
2997 should not be passed on as std::exception, because it
2998 won't be catched at all.
3000 /*catch(std::exception &e)
3002 infostream<<"createBlock: createSector() failed: "
3003 <<e.what()<<std::endl;
3008 Try to get a block from the sector
3011 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3014 if(block->isDummy())
3019 block = sector->createBlankBlock(block_y);
3024 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3026 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3028 p.X, p.Y, p.Z, create_blank);
3031 MapBlock *block = getBlockNoCreateNoEx(p);
3032 if(block && block->isDummy() == false)
3037 MapBlock *block = loadBlock(p);
3043 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3044 MapBlock *block = sector->createBlankBlock(p.Y);
3048 /*if(allow_generate)
3050 std::map<v3s16, MapBlock*> modified_blocks;
3051 MapBlock *block = generateBlock(p, modified_blocks);
3055 event.type = MEET_OTHER;
3058 // Copy modified_blocks to event
3059 for(std::map<v3s16, MapBlock*>::iterator
3060 i = modified_blocks.begin();
3061 i != modified_blocks.end(); ++i)
3063 event.modified_blocks.insert(i->first);
3067 dispatchEvent(&event);
3076 s16 ServerMap::findGroundLevel(v2s16 p2d)
3080 Uh, just do something random...
3082 // Find existing map from top to down
3085 v3s16 p(p2d.X, max, p2d.Y);
3086 for(; p.Y>min; p.Y--)
3088 MapNode n = getNodeNoEx(p);
3089 if(n.getContent() != CONTENT_IGNORE)
3094 // If this node is not air, go to plan b
3095 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3097 // Search existing walkable and return it
3098 for(; p.Y>min; p.Y--)
3100 MapNode n = getNodeNoEx(p);
3101 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3110 Determine from map generator noise functions
3113 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3116 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3117 //return (s16)level;
3120 void ServerMap::createDatabase() {
3123 e = sqlite3_exec(m_database,
3124 "CREATE TABLE IF NOT EXISTS `blocks` ("
3125 "`pos` INT NOT NULL PRIMARY KEY,"
3128 , NULL, NULL, NULL);
3129 if(e == SQLITE_ABORT)
3130 throw FileNotGoodException("Could not create database structure");
3132 infostream<<"ServerMap: Database structure was created";
3135 void ServerMap::verifyDatabase() {
3140 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3141 bool needs_create = false;
3145 Open the database connection
3148 createDirs(m_savedir);
3150 if(!fs::PathExists(dbp))
3151 needs_create = true;
3153 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3154 if(d != SQLITE_OK) {
3155 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3156 throw FileNotGoodException("Cannot open database file");
3162 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3163 if(d != SQLITE_OK) {
3164 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3165 throw FileNotGoodException("Cannot prepare read statement");
3168 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3169 if(d != SQLITE_OK) {
3170 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3171 throw FileNotGoodException("Cannot prepare write statement");
3174 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3175 if(d != SQLITE_OK) {
3176 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3177 throw FileNotGoodException("Cannot prepare read statement");
3180 infostream<<"ServerMap: Database opened"<<std::endl;
3184 bool ServerMap::loadFromFolders() {
3185 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3190 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3191 return (sqlite3_int64)pos.Z*16777216 +
3192 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3195 void ServerMap::createDirs(std::string path)
3197 if(fs::CreateAllDirs(path) == false)
3199 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3200 <<"\""<<path<<"\""<<std::endl;
3201 throw BaseException("ServerMap failed to create directory");
3205 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3211 snprintf(cc, 9, "%.4x%.4x",
3212 (unsigned int)pos.X&0xffff,
3213 (unsigned int)pos.Y&0xffff);
3215 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3217 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3218 (unsigned int)pos.X&0xfff,
3219 (unsigned int)pos.Y&0xfff);
3221 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3227 v2s16 ServerMap::getSectorPos(std::string dirname)
3231 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
3232 assert(spos != std::string::npos);
3233 if(dirname.size() - spos == 8)
3236 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
3238 else if(dirname.size() - spos == 3)
3241 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3242 // Sign-extend the 12 bit values up to 16 bits...
3243 if(x&0x800) x|=0xF000;
3244 if(y&0x800) y|=0xF000;
3251 v2s16 pos((s16)x, (s16)y);
3255 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3257 v2s16 p2d = getSectorPos(sectordir);
3259 if(blockfile.size() != 4){
3260 throw InvalidFilenameException("Invalid block filename");
3263 int r = sscanf(blockfile.c_str(), "%4x", &y);
3265 throw InvalidFilenameException("Invalid block filename");
3266 return v3s16(p2d.X, y, p2d.Y);
3269 std::string ServerMap::getBlockFilename(v3s16 p)
3272 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3276 void ServerMap::save(ModifiedState save_level)
3278 DSTACK(__FUNCTION_NAME);
3279 if(m_map_saving_enabled == false)
3281 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3285 if(save_level == MOD_STATE_CLEAN)
3286 infostream<<"ServerMap: Saving whole map, this can take time."
3289 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3294 // Profile modified reasons
3295 Profiler modprofiler;
3297 u32 sector_meta_count = 0;
3298 u32 block_count = 0;
3299 u32 block_count_all = 0; // Number of blocks in memory
3301 // Don't do anything with sqlite unless something is really saved
3302 bool save_started = false;
3304 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3305 i != m_sectors.end(); ++i)
3307 ServerMapSector *sector = (ServerMapSector*)i->second;
3308 assert(sector->getId() == MAPSECTOR_SERVER);
3310 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3312 saveSectorMeta(sector);
3313 sector_meta_count++;
3315 std::list<MapBlock*> blocks;
3316 sector->getBlocks(blocks);
3318 for(std::list<MapBlock*>::iterator j = blocks.begin();
3319 j != blocks.end(); ++j)
3321 MapBlock *block = *j;
3325 if(block->getModified() >= (u32)save_level)
3330 save_started = true;
3333 modprofiler.add(block->getModifiedReason(), 1);
3338 /*infostream<<"ServerMap: Written block ("
3339 <<block->getPos().X<<","
3340 <<block->getPos().Y<<","
3341 <<block->getPos().Z<<")"
3350 Only print if something happened or saved whole map
3352 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3353 || block_count != 0)
3355 infostream<<"ServerMap: Written: "
3356 <<sector_meta_count<<" sector metadata files, "
3357 <<block_count<<" block files"
3358 <<", "<<block_count_all<<" blocks in memory."
3360 PrintInfo(infostream); // ServerMap/ClientMap:
3361 infostream<<"Blocks modified by: "<<std::endl;
3362 modprofiler.print(infostream);
3366 static s32 unsignedToSigned(s32 i, s32 max_positive)
3368 if(i < max_positive)
3371 return i - 2*max_positive;
3374 // modulo of a negative number does not work consistently in C
3375 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3379 return mod - ((-i) % mod);
3382 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3384 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3386 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3388 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3389 return v3s16(x,y,z);
3392 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3394 if(loadFromFolders()){
3395 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3396 <<"all blocks that are stored in flat files"<<std::endl;
3402 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3404 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3405 v3s16 p = getIntegerAsBlock(block_i);
3406 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3412 void ServerMap::saveMapMeta()
3414 DSTACK(__FUNCTION_NAME);
3416 /*infostream<<"ServerMap::saveMapMeta(): "
3420 createDirs(m_savedir);
3422 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3423 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3424 if(os.good() == false)
3426 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3427 <<"could not open"<<fullpath<<std::endl;
3428 throw FileNotGoodException("Cannot open chunk metadata");
3433 m_emerge->setParamsToSettings(¶ms);
3434 params.writeLines(os);
3436 os<<"[end_of_params]\n";
3438 m_map_metadata_changed = false;
3441 void ServerMap::loadMapMeta()
3443 DSTACK(__FUNCTION_NAME);
3445 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3448 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3449 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3450 if(is.good() == false)
3452 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3453 <<"could not open"<<fullpath<<std::endl;
3454 throw FileNotGoodException("Cannot open map metadata");
3462 throw SerializationError
3463 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3465 std::getline(is, line);
3466 std::string trimmedline = trim(line);
3467 if(trimmedline == "[end_of_params]")
3469 params.parseConfigLine(line);
3472 MapgenParams *mgparams;
3474 mgparams = m_emerge->getParamsFromSettings(¶ms);
3475 } catch (SettingNotFoundException &e) {
3476 infostream << "Couldn't get a setting from map_meta.txt: "
3477 << e.what() << std::endl;
3484 m_mgparams = mgparams;
3485 m_seed = mgparams->seed;
3487 if (params.exists("seed")) {
3488 m_seed = params.getU64("seed");
3489 m_mgparams->seed = m_seed;
3493 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3496 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3498 DSTACK(__FUNCTION_NAME);
3499 // Format used for writing
3500 u8 version = SER_FMT_VER_HIGHEST;
3502 v2s16 pos = sector->getPos();
3503 std::string dir = getSectorDir(pos);
3506 std::string fullpath = dir + DIR_DELIM + "meta";
3507 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3508 if(o.good() == false)
3509 throw FileNotGoodException("Cannot open sector metafile");
3511 sector->serialize(o, version);
3513 sector->differs_from_disk = false;
3516 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3518 DSTACK(__FUNCTION_NAME);
3520 v2s16 p2d = getSectorPos(sectordir);
3522 ServerMapSector *sector = NULL;
3524 std::string fullpath = sectordir + DIR_DELIM + "meta";
3525 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3526 if(is.good() == false)
3528 // If the directory exists anyway, it probably is in some old
3529 // format. Just go ahead and create the sector.
3530 if(fs::PathExists(sectordir))
3532 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3533 <<fullpath<<" doesn't exist but directory does."
3534 <<" Continuing with a sector with no metadata."
3536 sector = new ServerMapSector(this, p2d, m_gamedef);
3537 m_sectors[p2d] = sector;
3541 throw FileNotGoodException("Cannot open sector metafile");
3546 sector = ServerMapSector::deSerialize
3547 (is, this, p2d, m_sectors, m_gamedef);
3549 saveSectorMeta(sector);
3552 sector->differs_from_disk = false;
3557 bool ServerMap::loadSectorMeta(v2s16 p2d)
3559 DSTACK(__FUNCTION_NAME);
3561 MapSector *sector = NULL;
3563 // The directory layout we're going to load from.
3564 // 1 - original sectors/xxxxzzzz/
3565 // 2 - new sectors2/xxx/zzz/
3566 // If we load from anything but the latest structure, we will
3567 // immediately save to the new one, and remove the old.
3569 std::string sectordir1 = getSectorDir(p2d, 1);
3570 std::string sectordir;
3571 if(fs::PathExists(sectordir1))
3573 sectordir = sectordir1;
3578 sectordir = getSectorDir(p2d, 2);
3582 sector = loadSectorMeta(sectordir, loadlayout != 2);
3584 catch(InvalidFilenameException &e)
3588 catch(FileNotGoodException &e)
3592 catch(std::exception &e)
3601 bool ServerMap::loadSectorFull(v2s16 p2d)
3603 DSTACK(__FUNCTION_NAME);
3605 MapSector *sector = NULL;
3607 // The directory layout we're going to load from.
3608 // 1 - original sectors/xxxxzzzz/
3609 // 2 - new sectors2/xxx/zzz/
3610 // If we load from anything but the latest structure, we will
3611 // immediately save to the new one, and remove the old.
3613 std::string sectordir1 = getSectorDir(p2d, 1);
3614 std::string sectordir;
3615 if(fs::PathExists(sectordir1))
3617 sectordir = sectordir1;
3622 sectordir = getSectorDir(p2d, 2);
3626 sector = loadSectorMeta(sectordir, loadlayout != 2);
3628 catch(InvalidFilenameException &e)
3632 catch(FileNotGoodException &e)
3636 catch(std::exception &e)
3644 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3646 std::vector<fs::DirListNode>::iterator i2;
3647 for(i2=list2.begin(); i2!=list2.end(); i2++)
3653 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3655 catch(InvalidFilenameException &e)
3657 // This catches unknown crap in directory
3663 infostream<<"Sector converted to new layout - deleting "<<
3664 sectordir1<<std::endl;
3665 fs::RecursiveDelete(sectordir1);
3672 void ServerMap::beginSave() {
3674 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3675 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3678 void ServerMap::endSave() {
3680 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3681 infostream<<"WARNING: endSave() failed, map might not have saved.";
3684 void ServerMap::saveBlock(MapBlock *block)
3686 DSTACK(__FUNCTION_NAME);
3688 Dummy blocks are not written
3690 if(block->isDummy())
3692 /*v3s16 p = block->getPos();
3693 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3694 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3698 // Format used for writing
3699 u8 version = SER_FMT_VER_HIGHEST;
3701 v3s16 p3d = block->getPos();
3705 v2s16 p2d(p3d.X, p3d.Z);
3706 std::string sectordir = getSectorDir(p2d);
3708 createDirs(sectordir);
3710 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3711 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3712 if(o.good() == false)
3713 throw FileNotGoodException("Cannot open block data");
3716 [0] u8 serialization version
3722 std::ostringstream o(std::ios_base::binary);
3724 o.write((char*)&version, 1);
3727 block->serialize(o, version, true);
3729 // Write block to database
3731 std::string tmp = o.str();
3732 const char *bytes = tmp.c_str();
3734 bool success = true;
3735 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3736 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3739 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3740 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3743 int written = sqlite3_step(m_database_write);
3744 if(written != SQLITE_DONE) {
3745 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3746 <<sqlite3_errmsg(m_database)<<std::endl;
3749 // Make ready for later reuse
3750 sqlite3_reset(m_database_write);
3752 // We just wrote it to the disk so clear modified flag
3754 block->resetModified();
3757 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3759 DSTACK(__FUNCTION_NAME);
3761 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3764 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3765 if(is.good() == false)
3766 throw FileNotGoodException("Cannot open block file");
3768 v3s16 p3d = getBlockPos(sectordir, blockfile);
3769 v2s16 p2d(p3d.X, p3d.Z);
3771 assert(sector->getPos() == p2d);
3773 u8 version = SER_FMT_VER_INVALID;
3774 is.read((char*)&version, 1);
3777 throw SerializationError("ServerMap::loadBlock(): Failed"
3778 " to read MapBlock version");
3780 /*u32 block_size = MapBlock::serializedLength(version);
3781 SharedBuffer<u8> data(block_size);
3782 is.read((char*)*data, block_size);*/
3784 // This will always return a sector because we're the server
3785 //MapSector *sector = emergeSector(p2d);
3787 MapBlock *block = NULL;
3788 bool created_new = false;
3789 block = sector->getBlockNoCreateNoEx(p3d.Y);
3792 block = sector->createBlankBlockNoInsert(p3d.Y);
3797 block->deSerialize(is, version, true);
3799 // If it's a new block, insert it to the map
3801 sector->insertBlock(block);
3804 Save blocks loaded in old format in new format
3807 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3811 // Should be in database now, so delete the old file
3812 fs::RecursiveDelete(fullpath);
3815 // We just loaded it from the disk, so it's up-to-date.
3816 block->resetModified();
3819 catch(SerializationError &e)
3821 infostream<<"WARNING: Invalid block data on disk "
3822 <<"fullpath="<<fullpath
3823 <<" (SerializationError). "
3824 <<"what()="<<e.what()
3826 //" Ignoring. A new one will be generated.
3829 // TODO: Backup file; name is in fullpath.
3833 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3835 DSTACK(__FUNCTION_NAME);
3838 std::istringstream is(*blob, std::ios_base::binary);
3840 u8 version = SER_FMT_VER_INVALID;
3841 is.read((char*)&version, 1);
3844 throw SerializationError("ServerMap::loadBlock(): Failed"
3845 " to read MapBlock version");
3847 /*u32 block_size = MapBlock::serializedLength(version);
3848 SharedBuffer<u8> data(block_size);
3849 is.read((char*)*data, block_size);*/
3851 // This will always return a sector because we're the server
3852 //MapSector *sector = emergeSector(p2d);
3854 MapBlock *block = NULL;
3855 bool created_new = false;
3856 block = sector->getBlockNoCreateNoEx(p3d.Y);
3859 block = sector->createBlankBlockNoInsert(p3d.Y);
3864 block->deSerialize(is, version, true);
3866 // If it's a new block, insert it to the map
3868 sector->insertBlock(block);
3871 Save blocks loaded in old format in new format
3874 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3875 // Only save if asked to; no need to update version
3879 // We just loaded it from, so it's up-to-date.
3880 block->resetModified();
3883 catch(SerializationError &e)
3885 errorstream<<"Invalid block data in database"
3886 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3887 <<" (SerializationError): "<<e.what()<<std::endl;
3889 // TODO: Block should be marked as invalid in memory so that it is
3890 // not touched but the game can run
3892 if(g_settings->getBool("ignore_world_load_errors")){
3893 errorstream<<"Ignoring block load error. Duck and cover! "
3894 <<"(ignore_world_load_errors)"<<std::endl;
3896 throw SerializationError("Invalid block data in database");
3902 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3904 DSTACK(__FUNCTION_NAME);
3906 v2s16 p2d(blockpos.X, blockpos.Z);
3908 if(!loadFromFolders()) {
3911 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3912 infostream<<"WARNING: Could not bind block position for load: "
3913 <<sqlite3_errmsg(m_database)<<std::endl;
3914 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3916 Make sure sector is loaded
3918 MapSector *sector = createSector(p2d);
3923 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3924 size_t len = sqlite3_column_bytes(m_database_read, 0);
3926 std::string datastr(data, len);
3928 loadBlock(&datastr, blockpos, sector, false);
3930 sqlite3_step(m_database_read);
3931 // We should never get more than 1 row, so ok to reset
3932 sqlite3_reset(m_database_read);
3934 return getBlockNoCreateNoEx(blockpos);
3936 sqlite3_reset(m_database_read);
3938 // Not found in database, try the files
3941 // The directory layout we're going to load from.
3942 // 1 - original sectors/xxxxzzzz/
3943 // 2 - new sectors2/xxx/zzz/
3944 // If we load from anything but the latest structure, we will
3945 // immediately save to the new one, and remove the old.
3947 std::string sectordir1 = getSectorDir(p2d, 1);
3948 std::string sectordir;
3949 if(fs::PathExists(sectordir1))
3951 sectordir = sectordir1;
3956 sectordir = getSectorDir(p2d, 2);
3960 Make sure sector is loaded
3962 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3966 sector = loadSectorMeta(sectordir, loadlayout != 2);
3968 catch(InvalidFilenameException &e)
3972 catch(FileNotGoodException &e)
3976 catch(std::exception &e)
3983 Make sure file exists
3986 std::string blockfilename = getBlockFilename(blockpos);
3987 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3991 Load block and save it to the database
3993 loadBlock(sectordir, blockfilename, sector, true);
3994 return getBlockNoCreateNoEx(blockpos);
3997 void ServerMap::PrintInfo(std::ostream &out)
4006 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4011 MapVoxelManipulator::~MapVoxelManipulator()
4013 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4017 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4019 TimeTaker timer1("emerge", &emerge_time);
4021 // Units of these are MapBlocks
4022 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4023 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4025 VoxelArea block_area_nodes
4026 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4028 addArea(block_area_nodes);
4030 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4031 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4032 for(s32 x=p_min.X; x<=p_max.X; x++)
4037 std::map<v3s16, u8>::iterator n;
4038 n = m_loaded_blocks.find(p);
4039 if(n != m_loaded_blocks.end())
4042 bool block_data_inexistent = false;
4045 TimeTaker timer1("emerge load", &emerge_load_time);
4047 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4048 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4050 a.print(infostream);
4051 infostream<<std::endl;*/
4053 block = m_map->getBlockNoCreate(p);
4054 if(block->isDummy())
4055 block_data_inexistent = true;
4057 block->copyTo(*this);
4059 catch(InvalidPositionException &e)
4061 block_data_inexistent = true;
4064 if(block_data_inexistent)
4066 flags |= VMANIP_BLOCK_DATA_INEXIST;
4068 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4069 // Fill with VOXELFLAG_INEXISTENT
4070 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4071 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4073 s32 i = m_area.index(a.MinEdge.X,y,z);
4074 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4077 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4079 // Mark that block was loaded as blank
4080 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4083 m_loaded_blocks[p] = flags;
4086 //infostream<<"emerge done"<<std::endl;
4090 SUGG: Add an option to only update eg. water and air nodes.
4091 This will make it interfere less with important stuff if
4094 void MapVoxelManipulator::blitBack
4095 (std::map<v3s16, MapBlock*> & modified_blocks)
4097 if(m_area.getExtent() == v3s16(0,0,0))
4100 //TimeTaker timer1("blitBack");
4102 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4103 <<m_loaded_blocks.size()<<std::endl;*/
4106 Initialize block cache
4108 v3s16 blockpos_last;
4109 MapBlock *block = NULL;
4110 bool block_checked_in_modified = false;
4112 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4113 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4114 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4118 u8 f = m_flags[m_area.index(p)];
4119 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4122 MapNode &n = m_data[m_area.index(p)];
4124 v3s16 blockpos = getNodeBlockPos(p);
4129 if(block == NULL || blockpos != blockpos_last){
4130 block = m_map->getBlockNoCreate(blockpos);
4131 blockpos_last = blockpos;
4132 block_checked_in_modified = false;
4135 // Calculate relative position in block
4136 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4138 // Don't continue if nothing has changed here
4139 if(block->getNode(relpos) == n)
4142 //m_map->setNode(m_area.MinEdge + p, n);
4143 block->setNode(relpos, n);
4146 Make sure block is in modified_blocks
4148 if(block_checked_in_modified == false)
4150 modified_blocks[blockpos] = block;
4151 block_checked_in_modified = true;
4154 catch(InvalidPositionException &e)
4160 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4161 MapVoxelManipulator(map),
4162 m_create_area(false)
4166 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4170 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4172 // Just create the area so that it can be pointed to
4173 VoxelManipulator::emerge(a, caller_id);
4176 void ManualMapVoxelManipulator::initialEmerge(
4177 v3s16 blockpos_min, v3s16 blockpos_max)
4179 TimeTaker timer1("initialEmerge", &emerge_time);
4181 // Units of these are MapBlocks
4182 v3s16 p_min = blockpos_min;
4183 v3s16 p_max = blockpos_max;
4185 VoxelArea block_area_nodes
4186 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4188 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4191 infostream<<"initialEmerge: area: ";
4192 block_area_nodes.print(infostream);
4193 infostream<<" ("<<size_MB<<"MB)";
4194 infostream<<std::endl;
4197 addArea(block_area_nodes);
4199 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4200 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4201 for(s32 x=p_min.X; x<=p_max.X; x++)
4206 std::map<v3s16, u8>::iterator n;
4207 n = m_loaded_blocks.find(p);
4208 if(n != m_loaded_blocks.end())
4211 bool block_data_inexistent = false;
4214 TimeTaker timer1("emerge load", &emerge_load_time);
4216 block = m_map->getBlockNoCreate(p);
4217 if(block->isDummy())
4218 block_data_inexistent = true;
4220 block->copyTo(*this);
4222 catch(InvalidPositionException &e)
4224 block_data_inexistent = true;
4227 if(block_data_inexistent)
4229 flags |= VMANIP_BLOCK_DATA_INEXIST;
4232 Mark area inexistent
4234 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4235 // Fill with VOXELFLAG_INEXISTENT
4236 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4237 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4239 s32 i = m_area.index(a.MinEdge.X,y,z);
4240 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4243 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4245 // Mark that block was loaded as blank
4246 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4249 m_loaded_blocks[p] = flags;
4253 void ManualMapVoxelManipulator::blitBackAll(
4254 std::map<v3s16, MapBlock*> * modified_blocks)
4256 if(m_area.getExtent() == v3s16(0,0,0))
4260 Copy data of all blocks
4262 for(std::map<v3s16, u8>::iterator
4263 i = m_loaded_blocks.begin();
4264 i != m_loaded_blocks.end(); ++i)
4267 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4268 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4269 if(existed == false)
4274 block->copyFrom(*this);
4277 (*modified_blocks)[p] = block;