3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "rollback_interface.h"
37 #include "mapgen_v6.h"
38 #include "mapgen_indev.h"
40 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
43 SQLite format specification:
44 - Initially only replaces sectors/ and sectors2/
46 If map.sqlite does not exist in the save dir
47 or the block was not found in the database
48 the map will try to load from sectors folder.
49 In either case, map.sqlite will be created
50 and all future saves will save there.
52 Structure of map.sqlite:
63 Map::Map(std::ostream &dout, IGameDef *gamedef):
68 /*m_sector_mutex.Init();
69 assert(m_sector_mutex.IsInitialized());*/
77 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
78 i != m_sectors.end(); ++i)
84 void Map::addEventReceiver(MapEventReceiver *event_receiver)
86 m_event_receivers.insert(event_receiver);
89 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
91 m_event_receivers.erase(event_receiver);
94 void Map::dispatchEvent(MapEditEvent *event)
96 for(std::set<MapEventReceiver*>::iterator
97 i = m_event_receivers.begin();
98 i != m_event_receivers.end(); ++i)
100 (*i)->onMapEditEvent(event);
104 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
106 if(m_sector_cache != NULL && p == m_sector_cache_p){
107 MapSector * sector = m_sector_cache;
111 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
113 if(n == m_sectors.end())
116 MapSector *sector = n->second;
118 // Cache the last result
119 m_sector_cache_p = p;
120 m_sector_cache = sector;
125 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
127 return getSectorNoGenerateNoExNoLock(p);
130 MapSector * Map::getSectorNoGenerate(v2s16 p)
132 MapSector *sector = getSectorNoGenerateNoEx(p);
134 throw InvalidPositionException();
139 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
141 v2s16 p2d(p3d.X, p3d.Z);
142 MapSector * sector = getSectorNoGenerateNoEx(p2d);
145 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
149 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
151 MapBlock *block = getBlockNoCreateNoEx(p3d);
153 throw InvalidPositionException();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
170 bool Map::isValidPosition(v3s16 p)
172 v3s16 blockpos = getNodeBlockPos(p);
173 MapBlock *block = getBlockNoCreate(blockpos);
174 return (block != NULL);
177 // Returns a CONTENT_IGNORE node if not found
178 MapNode Map::getNodeNoEx(v3s16 p)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreateNoEx(blockpos);
183 return MapNode(CONTENT_IGNORE);
184 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
185 return block->getNodeNoCheck(relpos);
188 // throws InvalidPositionException if not found
189 MapNode Map::getNode(v3s16 p)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 throw InvalidPositionException();
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 return block->getNodeNoCheck(relpos);
199 // throws InvalidPositionException if not found
200 void Map::setNode(v3s16 p, MapNode & n)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreate(blockpos);
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 // Never allow placing CONTENT_IGNORE, it fucks up stuff
206 if(n.getContent() == CONTENT_IGNORE){
207 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
208 <<" while trying to replace \""
209 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
210 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
211 debug_stacks_print_to(infostream);
214 block->setNodeNoCheck(relpos, n);
219 Goes recursively through the neighbours of the node.
221 Alters only transparent nodes.
223 If the lighting of the neighbour is lower than the lighting of
224 the node was (before changing it to 0 at the step before), the
225 lighting of the neighbour is set to 0 and then the same stuff
226 repeats for the neighbour.
228 The ending nodes of the routine are stored in light_sources.
229 This is useful when a light is removed. In such case, this
230 routine can be called for the light node and then again for
231 light_sources to re-light the area without the removed light.
233 values of from_nodes are lighting values.
235 void Map::unspreadLight(enum LightBank bank,
236 std::map<v3s16, u8> & from_nodes,
237 std::set<v3s16> & light_sources,
238 std::map<v3s16, MapBlock*> & modified_blocks)
240 INodeDefManager *nodemgr = m_gamedef->ndef();
243 v3s16(0,0,1), // back
245 v3s16(1,0,0), // right
246 v3s16(0,0,-1), // front
247 v3s16(0,-1,0), // bottom
248 v3s16(-1,0,0), // left
251 if(from_nodes.size() == 0)
254 u32 blockchangecount = 0;
256 std::map<v3s16, u8> unlighted_nodes;
259 Initialize block cache
262 MapBlock *block = NULL;
263 // Cache this a bit, too
264 bool block_checked_in_modified = false;
266 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
267 j != from_nodes.end(); ++j)
269 v3s16 pos = j->first;
270 v3s16 blockpos = getNodeBlockPos(pos);
272 // Only fetch a new block if the block position has changed
274 if(block == NULL || blockpos != blockpos_last){
275 block = getBlockNoCreate(blockpos);
276 blockpos_last = blockpos;
278 block_checked_in_modified = false;
282 catch(InvalidPositionException &e)
290 // Calculate relative position in block
291 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
293 // Get node straight from the block
294 //MapNode n = block->getNode(relpos);
296 u8 oldlight = j->second;
298 // Loop through 6 neighbors
299 for(u16 i=0; i<6; i++)
301 // Get the position of the neighbor node
302 v3s16 n2pos = pos + dirs[i];
304 // Get the block where the node is located
305 v3s16 blockpos = getNodeBlockPos(n2pos);
309 // Only fetch a new block if the block position has changed
311 if(block == NULL || blockpos != blockpos_last){
312 block = getBlockNoCreate(blockpos);
313 blockpos_last = blockpos;
315 block_checked_in_modified = false;
319 catch(InvalidPositionException &e)
324 // Calculate relative position in block
325 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
326 // Get node straight from the block
327 MapNode n2 = block->getNode(relpos);
329 bool changed = false;
331 //TODO: Optimize output by optimizing light_sources?
334 If the neighbor is dimmer than what was specified
335 as oldlight (the light of the previous node)
337 if(n2.getLight(bank, nodemgr) < oldlight)
340 And the neighbor is transparent and it has some light
342 if(nodemgr->get(n2).light_propagates
343 && n2.getLight(bank, nodemgr) != 0)
346 Set light to 0 and add to queue
349 u8 current_light = n2.getLight(bank, nodemgr);
350 n2.setLight(bank, 0, nodemgr);
351 block->setNode(relpos, n2);
353 unlighted_nodes[n2pos] = current_light;
357 Remove from light_sources if it is there
358 NOTE: This doesn't happen nearly at all
360 /*if(light_sources.find(n2pos))
362 infostream<<"Removed from light_sources"<<std::endl;
363 light_sources.remove(n2pos);
368 if(light_sources.find(n2pos) != NULL)
369 light_sources.remove(n2pos);*/
372 light_sources.insert(n2pos);
375 // Add to modified_blocks
376 if(changed == true && block_checked_in_modified == false)
378 // If the block is not found in modified_blocks, add.
379 if(modified_blocks.find(blockpos) == modified_blocks.end())
381 modified_blocks[blockpos] = block;
383 block_checked_in_modified = true;
386 catch(InvalidPositionException &e)
393 /*infostream<<"unspreadLight(): Changed block "
394 <<blockchangecount<<" times"
395 <<" for "<<from_nodes.size()<<" nodes"
398 if(unlighted_nodes.size() > 0)
399 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
403 A single-node wrapper of the above
405 void Map::unLightNeighbors(enum LightBank bank,
406 v3s16 pos, u8 lightwas,
407 std::set<v3s16> & light_sources,
408 std::map<v3s16, MapBlock*> & modified_blocks)
410 std::map<v3s16, u8> from_nodes;
411 from_nodes[pos] = lightwas;
413 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
417 Lights neighbors of from_nodes, collects all them and then
420 void Map::spreadLight(enum LightBank bank,
421 std::set<v3s16> & from_nodes,
422 std::map<v3s16, MapBlock*> & modified_blocks)
424 INodeDefManager *nodemgr = m_gamedef->ndef();
426 const v3s16 dirs[6] = {
427 v3s16(0,0,1), // back
429 v3s16(1,0,0), // right
430 v3s16(0,0,-1), // front
431 v3s16(0,-1,0), // bottom
432 v3s16(-1,0,0), // left
435 if(from_nodes.size() == 0)
438 u32 blockchangecount = 0;
440 std::set<v3s16> lighted_nodes;
443 Initialize block cache
446 MapBlock *block = NULL;
447 // Cache this a bit, too
448 bool block_checked_in_modified = false;
450 for(std::set<v3s16>::iterator j = from_nodes.begin();
451 j != from_nodes.end(); ++j)
454 v3s16 blockpos = getNodeBlockPos(pos);
456 // Only fetch a new block if the block position has changed
458 if(block == NULL || blockpos != blockpos_last){
459 block = getBlockNoCreate(blockpos);
460 blockpos_last = blockpos;
462 block_checked_in_modified = false;
466 catch(InvalidPositionException &e)
474 // Calculate relative position in block
475 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
477 // Get node straight from the block
478 MapNode n = block->getNode(relpos);
480 u8 oldlight = n.getLight(bank, nodemgr);
481 u8 newlight = diminish_light(oldlight);
483 // Loop through 6 neighbors
484 for(u16 i=0; i<6; i++){
485 // Get the position of the neighbor node
486 v3s16 n2pos = pos + dirs[i];
488 // Get the block where the node is located
489 v3s16 blockpos = getNodeBlockPos(n2pos);
493 // Only fetch a new block if the block position has changed
495 if(block == NULL || blockpos != blockpos_last){
496 block = getBlockNoCreate(blockpos);
497 blockpos_last = blockpos;
499 block_checked_in_modified = false;
503 catch(InvalidPositionException &e)
508 // Calculate relative position in block
509 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
510 // Get node straight from the block
511 MapNode n2 = block->getNode(relpos);
513 bool changed = false;
515 If the neighbor is brighter than the current node,
516 add to list (it will light up this node on its turn)
518 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
520 lighted_nodes.insert(n2pos);
524 If the neighbor is dimmer than how much light this node
525 would spread on it, add to list
527 if(n2.getLight(bank, nodemgr) < newlight)
529 if(nodemgr->get(n2).light_propagates)
531 n2.setLight(bank, newlight, nodemgr);
532 block->setNode(relpos, n2);
533 lighted_nodes.insert(n2pos);
538 // Add to modified_blocks
539 if(changed == true && block_checked_in_modified == false)
541 // If the block is not found in modified_blocks, add.
542 if(modified_blocks.find(blockpos) == modified_blocks.end())
544 modified_blocks[blockpos] = block;
546 block_checked_in_modified = true;
549 catch(InvalidPositionException &e)
556 /*infostream<<"spreadLight(): Changed block "
557 <<blockchangecount<<" times"
558 <<" for "<<from_nodes.size()<<" nodes"
561 if(lighted_nodes.size() > 0)
562 spreadLight(bank, lighted_nodes, modified_blocks);
566 A single-node source variation of the above.
568 void Map::lightNeighbors(enum LightBank bank,
570 std::map<v3s16, MapBlock*> & modified_blocks)
572 std::set<v3s16> from_nodes;
573 from_nodes.insert(pos);
574 spreadLight(bank, from_nodes, modified_blocks);
577 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
579 INodeDefManager *nodemgr = m_gamedef->ndef();
582 v3s16(0,0,1), // back
584 v3s16(1,0,0), // right
585 v3s16(0,0,-1), // front
586 v3s16(0,-1,0), // bottom
587 v3s16(-1,0,0), // left
590 u8 brightest_light = 0;
591 v3s16 brightest_pos(0,0,0);
592 bool found_something = false;
594 // Loop through 6 neighbors
595 for(u16 i=0; i<6; i++){
596 // Get the position of the neighbor node
597 v3s16 n2pos = p + dirs[i];
602 catch(InvalidPositionException &e)
606 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
607 brightest_light = n2.getLight(bank, nodemgr);
608 brightest_pos = n2pos;
609 found_something = true;
613 if(found_something == false)
614 throw InvalidPositionException();
616 return brightest_pos;
620 Propagates sunlight down from a node.
621 Starting point gets sunlight.
623 Returns the lowest y value of where the sunlight went.
625 Mud is turned into grass in where the sunlight stops.
627 s16 Map::propagateSunlight(v3s16 start,
628 std::map<v3s16, MapBlock*> & modified_blocks)
630 INodeDefManager *nodemgr = m_gamedef->ndef();
635 v3s16 pos(start.X, y, start.Z);
637 v3s16 blockpos = getNodeBlockPos(pos);
640 block = getBlockNoCreate(blockpos);
642 catch(InvalidPositionException &e)
647 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
648 MapNode n = block->getNode(relpos);
650 if(nodemgr->get(n).sunlight_propagates)
652 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
653 block->setNode(relpos, n);
655 modified_blocks[blockpos] = block;
659 // Sunlight goes no further
666 void Map::updateLighting(enum LightBank bank,
667 std::map<v3s16, MapBlock*> & a_blocks,
668 std::map<v3s16, MapBlock*> & modified_blocks)
670 INodeDefManager *nodemgr = m_gamedef->ndef();
672 /*m_dout<<DTIME<<"Map::updateLighting(): "
673 <<a_blocks.size()<<" blocks."<<std::endl;*/
675 //TimeTaker timer("updateLighting");
679 //u32 count_was = modified_blocks.size();
681 std::map<v3s16, MapBlock*> blocks_to_update;
683 std::set<v3s16> light_sources;
685 std::map<v3s16, u8> unlight_from;
687 int num_bottom_invalid = 0;
690 //TimeTaker t("first stuff");
692 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
693 i != a_blocks.end(); ++i)
695 MapBlock *block = i->second;
699 // Don't bother with dummy blocks.
703 v3s16 pos = block->getPos();
704 v3s16 posnodes = block->getPosRelative();
705 modified_blocks[pos] = block;
706 blocks_to_update[pos] = block;
709 Clear all light from block
711 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
712 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
713 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
718 MapNode n = block->getNode(p);
719 u8 oldlight = n.getLight(bank, nodemgr);
720 n.setLight(bank, 0, nodemgr);
721 block->setNode(p, n);
723 // If node sources light, add to list
724 u8 source = nodemgr->get(n).light_source;
726 light_sources.insert(p + posnodes);
728 // Collect borders for unlighting
729 if((x==0 || x == MAP_BLOCKSIZE-1
730 || y==0 || y == MAP_BLOCKSIZE-1
731 || z==0 || z == MAP_BLOCKSIZE-1)
734 v3s16 p_map = p + posnodes;
735 unlight_from[p_map] = oldlight;
738 catch(InvalidPositionException &e)
741 This would happen when dealing with a
745 infostream<<"updateLighting(): InvalidPositionException"
750 if(bank == LIGHTBANK_DAY)
752 bool bottom_valid = block->propagateSunlight(light_sources);
755 num_bottom_invalid++;
757 // If bottom is valid, we're done.
761 else if(bank == LIGHTBANK_NIGHT)
763 // For night lighting, sunlight is not propagated
768 // Invalid lighting bank
772 /*infostream<<"Bottom for sunlight-propagated block ("
773 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
776 // Bottom sunlight is not valid; get the block and loop to it
780 block = getBlockNoCreate(pos);
782 catch(InvalidPositionException &e)
793 Enable this to disable proper lighting for speeding up map
794 generation for testing or whatever
797 //if(g_settings->get(""))
799 core::map<v3s16, MapBlock*>::Iterator i;
800 i = blocks_to_update.getIterator();
801 for(; i.atEnd() == false; i++)
803 MapBlock *block = i.getNode()->getValue();
804 v3s16 p = block->getPos();
805 block->setLightingExpired(false);
813 //TimeTaker timer("unspreadLight");
814 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
819 u32 diff = modified_blocks.size() - count_was;
820 count_was = modified_blocks.size();
821 infostream<<"unspreadLight modified "<<diff<<std::endl;
825 //TimeTaker timer("spreadLight");
826 spreadLight(bank, light_sources, modified_blocks);
831 u32 diff = modified_blocks.size() - count_was;
832 count_was = modified_blocks.size();
833 infostream<<"spreadLight modified "<<diff<<std::endl;
839 //MapVoxelManipulator vmanip(this);
841 // Make a manual voxel manipulator and load all the blocks
842 // that touch the requested blocks
843 ManualMapVoxelManipulator vmanip(this);
846 //TimeTaker timer("initialEmerge");
848 core::map<v3s16, MapBlock*>::Iterator i;
849 i = blocks_to_update.getIterator();
850 for(; i.atEnd() == false; i++)
852 MapBlock *block = i.getNode()->getValue();
853 v3s16 p = block->getPos();
855 // Add all surrounding blocks
856 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
859 Add all surrounding blocks that have up-to-date lighting
860 NOTE: This doesn't quite do the job (not everything
861 appropriate is lighted)
863 /*for(s16 z=-1; z<=1; z++)
864 for(s16 y=-1; y<=1; y++)
865 for(s16 x=-1; x<=1; x++)
867 v3s16 p2 = p + v3s16(x,y,z);
868 MapBlock *block = getBlockNoCreateNoEx(p2);
873 if(block->getLightingExpired())
875 vmanip.initialEmerge(p2, p2);
878 // Lighting of block will be updated completely
879 block->setLightingExpired(false);
884 //TimeTaker timer("unSpreadLight");
885 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
888 //TimeTaker timer("spreadLight");
889 vmanip.spreadLight(bank, light_sources, nodemgr);
892 //TimeTaker timer("blitBack");
893 vmanip.blitBack(modified_blocks);
895 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
900 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
903 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
904 std::map<v3s16, MapBlock*> & modified_blocks)
906 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
907 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
910 Update information about whether day and night light differ
912 for(std::map<v3s16, MapBlock*>::iterator
913 i = modified_blocks.begin();
914 i != modified_blocks.end(); ++i)
916 MapBlock *block = i->second;
917 block->expireDayNightDiff();
923 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
924 std::map<v3s16, MapBlock*> &modified_blocks)
926 INodeDefManager *ndef = m_gamedef->ndef();
929 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
930 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
933 From this node to nodes underneath:
934 If lighting is sunlight (1.0), unlight neighbours and
939 v3s16 toppos = p + v3s16(0,1,0);
940 //v3s16 bottompos = p + v3s16(0,-1,0);
942 bool node_under_sunlight = true;
943 std::set<v3s16> light_sources;
946 Collect old node for rollback
948 RollbackNode rollback_oldnode(this, p, m_gamedef);
951 If there is a node at top and it doesn't have sunlight,
952 there has not been any sunlight going down.
954 Otherwise there probably is.
957 MapNode topnode = getNode(toppos);
959 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
960 node_under_sunlight = false;
962 catch(InvalidPositionException &e)
967 Remove all light that has come out of this node
970 enum LightBank banks[] =
975 for(s32 i=0; i<2; i++)
977 enum LightBank bank = banks[i];
979 u8 lightwas = getNode(p).getLight(bank, ndef);
981 // Add the block of the added node to modified_blocks
982 v3s16 blockpos = getNodeBlockPos(p);
983 MapBlock * block = getBlockNoCreate(blockpos);
984 assert(block != NULL);
985 modified_blocks[blockpos] = block;
987 assert(isValidPosition(p));
989 // Unlight neighbours of node.
990 // This means setting light of all consequent dimmer nodes
992 // This also collects the nodes at the border which will spread
993 // light again into this.
994 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
996 n.setLight(bank, 0, ndef);
1000 If node lets sunlight through and is under sunlight, it has
1003 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1005 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1009 Remove node metadata
1012 removeNodeMetadata(p);
1015 Set the node on the map
1021 If node is under sunlight and doesn't let sunlight through,
1022 take all sunlighted nodes under it and clear light from them
1023 and from where the light has been spread.
1024 TODO: This could be optimized by mass-unlighting instead
1027 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1031 //m_dout<<DTIME<<"y="<<y<<std::endl;
1032 v3s16 n2pos(p.X, y, p.Z);
1036 n2 = getNode(n2pos);
1038 catch(InvalidPositionException &e)
1043 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1045 unLightNeighbors(LIGHTBANK_DAY,
1046 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1047 light_sources, modified_blocks);
1048 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1056 for(s32 i=0; i<2; i++)
1058 enum LightBank bank = banks[i];
1061 Spread light from all nodes that might be capable of doing so
1063 spreadLight(bank, light_sources, modified_blocks);
1067 Update information about whether day and night light differ
1069 for(std::map<v3s16, MapBlock*>::iterator
1070 i = modified_blocks.begin();
1071 i != modified_blocks.end(); ++i)
1073 i->second->expireDayNightDiff();
1079 if(m_gamedef->rollback())
1081 RollbackNode rollback_newnode(this, p, m_gamedef);
1082 RollbackAction action;
1083 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1084 m_gamedef->rollback()->reportAction(action);
1088 Add neighboring liquid nodes and the node itself if it is
1089 liquid (=water node was added) to transform queue.
1092 v3s16(0,0,0), // self
1093 v3s16(0,0,1), // back
1094 v3s16(0,1,0), // top
1095 v3s16(1,0,0), // right
1096 v3s16(0,0,-1), // front
1097 v3s16(0,-1,0), // bottom
1098 v3s16(-1,0,0), // left
1100 for(u16 i=0; i<7; i++)
1105 v3s16 p2 = p + dirs[i];
1107 MapNode n2 = getNode(p2);
1108 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1110 m_transforming_liquid.push_back(p2);
1113 }catch(InvalidPositionException &e)
1121 void Map::removeNodeAndUpdate(v3s16 p,
1122 std::map<v3s16, MapBlock*> &modified_blocks)
1124 INodeDefManager *ndef = m_gamedef->ndef();
1126 /*PrintInfo(m_dout);
1127 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1128 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1130 bool node_under_sunlight = true;
1132 v3s16 toppos = p + v3s16(0,1,0);
1134 // Node will be replaced with this
1135 content_t replace_material = CONTENT_AIR;
1138 Collect old node for rollback
1140 RollbackNode rollback_oldnode(this, p, m_gamedef);
1143 If there is a node at top and it doesn't have sunlight,
1144 there will be no sunlight going down.
1147 MapNode topnode = getNode(toppos);
1149 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1150 node_under_sunlight = false;
1152 catch(InvalidPositionException &e)
1156 std::set<v3s16> light_sources;
1158 enum LightBank banks[] =
1163 for(s32 i=0; i<2; i++)
1165 enum LightBank bank = banks[i];
1168 Unlight neighbors (in case the node is a light source)
1170 unLightNeighbors(bank, p,
1171 getNode(p).getLight(bank, ndef),
1172 light_sources, modified_blocks);
1176 Remove node metadata
1179 removeNodeMetadata(p);
1183 This also clears the lighting.
1187 n.setContent(replace_material);
1190 for(s32 i=0; i<2; i++)
1192 enum LightBank bank = banks[i];
1195 Recalculate lighting
1197 spreadLight(bank, light_sources, modified_blocks);
1200 // Add the block of the removed node to modified_blocks
1201 v3s16 blockpos = getNodeBlockPos(p);
1202 MapBlock * block = getBlockNoCreate(blockpos);
1203 assert(block != NULL);
1204 modified_blocks[blockpos] = block;
1207 If the removed node was under sunlight, propagate the
1208 sunlight down from it and then light all neighbors
1209 of the propagated blocks.
1211 if(node_under_sunlight)
1213 s16 ybottom = propagateSunlight(p, modified_blocks);
1214 /*m_dout<<DTIME<<"Node was under sunlight. "
1215 "Propagating sunlight";
1216 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1218 for(; y >= ybottom; y--)
1220 v3s16 p2(p.X, y, p.Z);
1221 /*m_dout<<DTIME<<"lighting neighbors of node ("
1222 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1224 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1229 // Set the lighting of this node to 0
1230 // TODO: Is this needed? Lighting is cleared up there already.
1232 MapNode n = getNode(p);
1233 n.setLight(LIGHTBANK_DAY, 0, ndef);
1236 catch(InvalidPositionException &e)
1242 for(s32 i=0; i<2; i++)
1244 enum LightBank bank = banks[i];
1246 // Get the brightest neighbour node and propagate light from it
1247 v3s16 n2p = getBrightestNeighbour(bank, p);
1249 //MapNode n2 = getNode(n2p);
1250 lightNeighbors(bank, n2p, modified_blocks);
1252 catch(InvalidPositionException &e)
1258 Update information about whether day and night light differ
1260 for(std::map<v3s16, MapBlock*>::iterator
1261 i = modified_blocks.begin();
1262 i != modified_blocks.end(); ++i)
1264 i->second->expireDayNightDiff();
1270 if(m_gamedef->rollback())
1272 RollbackNode rollback_newnode(this, p, m_gamedef);
1273 RollbackAction action;
1274 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1275 m_gamedef->rollback()->reportAction(action);
1279 Add neighboring liquid nodes and this node to transform queue.
1280 (it's vital for the node itself to get updated last.)
1283 v3s16(0,0,1), // back
1284 v3s16(0,1,0), // top
1285 v3s16(1,0,0), // right
1286 v3s16(0,0,-1), // front
1287 v3s16(0,-1,0), // bottom
1288 v3s16(-1,0,0), // left
1289 v3s16(0,0,0), // self
1291 for(u16 i=0; i<7; i++)
1296 v3s16 p2 = p + dirs[i];
1298 MapNode n2 = getNode(p2);
1299 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1301 m_transforming_liquid.push_back(p2);
1304 }catch(InvalidPositionException &e)
1310 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1313 event.type = MEET_ADDNODE;
1317 bool succeeded = true;
1319 std::map<v3s16, MapBlock*> modified_blocks;
1320 addNodeAndUpdate(p, n, modified_blocks);
1322 // Copy modified_blocks to event
1323 for(std::map<v3s16, MapBlock*>::iterator
1324 i = modified_blocks.begin();
1325 i != modified_blocks.end(); ++i)
1327 event.modified_blocks.insert(i->first);
1330 catch(InvalidPositionException &e){
1334 dispatchEvent(&event);
1339 bool Map::removeNodeWithEvent(v3s16 p)
1342 event.type = MEET_REMOVENODE;
1345 bool succeeded = true;
1347 std::map<v3s16, MapBlock*> modified_blocks;
1348 removeNodeAndUpdate(p, modified_blocks);
1350 // Copy modified_blocks to event
1351 for(std::map<v3s16, MapBlock*>::iterator
1352 i = modified_blocks.begin();
1353 i != modified_blocks.end(); ++i)
1355 event.modified_blocks.insert(i->first);
1358 catch(InvalidPositionException &e){
1362 dispatchEvent(&event);
1367 bool Map::getDayNightDiff(v3s16 blockpos)
1370 v3s16 p = blockpos + v3s16(0,0,0);
1371 MapBlock *b = getBlockNoCreate(p);
1372 if(b->getDayNightDiff())
1375 catch(InvalidPositionException &e){}
1378 v3s16 p = blockpos + v3s16(-1,0,0);
1379 MapBlock *b = getBlockNoCreate(p);
1380 if(b->getDayNightDiff())
1383 catch(InvalidPositionException &e){}
1385 v3s16 p = blockpos + v3s16(0,-1,0);
1386 MapBlock *b = getBlockNoCreate(p);
1387 if(b->getDayNightDiff())
1390 catch(InvalidPositionException &e){}
1392 v3s16 p = blockpos + v3s16(0,0,-1);
1393 MapBlock *b = getBlockNoCreate(p);
1394 if(b->getDayNightDiff())
1397 catch(InvalidPositionException &e){}
1400 v3s16 p = blockpos + v3s16(1,0,0);
1401 MapBlock *b = getBlockNoCreate(p);
1402 if(b->getDayNightDiff())
1405 catch(InvalidPositionException &e){}
1407 v3s16 p = blockpos + v3s16(0,1,0);
1408 MapBlock *b = getBlockNoCreate(p);
1409 if(b->getDayNightDiff())
1412 catch(InvalidPositionException &e){}
1414 v3s16 p = blockpos + v3s16(0,0,1);
1415 MapBlock *b = getBlockNoCreate(p);
1416 if(b->getDayNightDiff())
1419 catch(InvalidPositionException &e){}
1425 Updates usage timers
1427 void Map::timerUpdate(float dtime, float unload_timeout,
1428 std::list<v3s16> *unloaded_blocks)
1430 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1432 // Profile modified reasons
1433 Profiler modprofiler;
1435 std::list<v2s16> sector_deletion_queue;
1436 u32 deleted_blocks_count = 0;
1437 u32 saved_blocks_count = 0;
1438 u32 block_count_all = 0;
1441 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1442 si != m_sectors.end(); ++si)
1444 MapSector *sector = si->second;
1446 bool all_blocks_deleted = true;
1448 std::list<MapBlock*> blocks;
1449 sector->getBlocks(blocks);
1451 for(std::list<MapBlock*>::iterator i = blocks.begin();
1452 i != blocks.end(); ++i)
1454 MapBlock *block = (*i);
1456 block->incrementUsageTimer(dtime);
1458 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1460 v3s16 p = block->getPos();
1463 if(block->getModified() != MOD_STATE_CLEAN
1464 && save_before_unloading)
1466 modprofiler.add(block->getModifiedReason(), 1);
1468 saved_blocks_count++;
1471 // Delete from memory
1472 sector->deleteBlock(block);
1475 unloaded_blocks->push_back(p);
1477 deleted_blocks_count++;
1481 all_blocks_deleted = false;
1486 if(all_blocks_deleted)
1488 sector_deletion_queue.push_back(si->first);
1493 // Finally delete the empty sectors
1494 deleteSectors(sector_deletion_queue);
1496 if(deleted_blocks_count != 0)
1498 PrintInfo(infostream); // ServerMap/ClientMap:
1499 infostream<<"Unloaded "<<deleted_blocks_count
1500 <<" blocks from memory";
1501 if(save_before_unloading)
1502 infostream<<", of which "<<saved_blocks_count<<" were written";
1503 infostream<<", "<<block_count_all<<" blocks in memory";
1504 infostream<<"."<<std::endl;
1505 if(saved_blocks_count != 0){
1506 PrintInfo(infostream); // ServerMap/ClientMap:
1507 infostream<<"Blocks modified by: "<<std::endl;
1508 modprofiler.print(infostream);
1513 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1515 timerUpdate(0.0, -1.0, unloaded_blocks);
1518 void Map::deleteSectors(std::list<v2s16> &list)
1520 for(std::list<v2s16>::iterator j = list.begin();
1521 j != list.end(); ++j)
1523 MapSector *sector = m_sectors[*j];
1524 // If sector is in sector cache, remove it from there
1525 if(m_sector_cache == sector)
1526 m_sector_cache = NULL;
1527 // Remove from map and delete
1528 m_sectors.erase(*j);
1534 void Map::unloadUnusedData(float timeout,
1535 core::list<v3s16> *deleted_blocks)
1537 core::list<v2s16> sector_deletion_queue;
1538 u32 deleted_blocks_count = 0;
1539 u32 saved_blocks_count = 0;
1541 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1542 for(; si.atEnd() == false; si++)
1544 MapSector *sector = si.getNode()->getValue();
1546 bool all_blocks_deleted = true;
1548 core::list<MapBlock*> blocks;
1549 sector->getBlocks(blocks);
1550 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1551 i != blocks.end(); i++)
1553 MapBlock *block = (*i);
1555 if(block->getUsageTimer() > timeout)
1558 if(block->getModified() != MOD_STATE_CLEAN)
1561 saved_blocks_count++;
1563 // Delete from memory
1564 sector->deleteBlock(block);
1565 deleted_blocks_count++;
1569 all_blocks_deleted = false;
1573 if(all_blocks_deleted)
1575 sector_deletion_queue.push_back(si.getNode()->getKey());
1579 deleteSectors(sector_deletion_queue);
1581 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1582 <<", of which "<<saved_blocks_count<<" were wr."
1585 //return sector_deletion_queue.getSize();
1586 //return deleted_blocks_count;
1590 void Map::PrintInfo(std::ostream &out)
1595 #define WATER_DROP_BOOST 4
1599 NEIGHBOR_SAME_LEVEL,
1602 struct NodeNeighbor {
1606 bool l; //can liquid
1610 void Map::transforming_liquid_add(v3s16 p) {
1611 m_transforming_liquid.push_back(p);
1614 s32 Map::transforming_liquid_size() {
1615 return m_transforming_liquid.size();
1618 const v3s16 g_7dirs[7] =
1620 // +right, +top, +back
1621 v3s16( 0,-1, 0), // bottom
1622 v3s16( 0, 0, 0), // self
1623 v3s16( 0, 0, 1), // back
1624 v3s16( 0, 0,-1), // front
1625 v3s16( 1, 0, 0), // right
1626 v3s16(-1, 0, 0), // left
1627 v3s16( 0, 1, 0) // top
1634 void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks)
1636 INodeDefManager *nodemgr = m_gamedef->ndef();
1638 DSTACK(__FUNCTION_NAME);
1639 //TimeTaker timer("transformLiquids()");
1642 u32 initial_size = m_transforming_liquid.size();
1644 u8 relax = g_settings->getS16("liquid_relax");
1645 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1646 int water_level = g_settings->getS16("water_level");
1648 // list of nodes that due to viscosity have not reached their max level height
1649 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1651 // List of MapBlocks that will require a lighting update (due to lava)
1652 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1654 u16 loop_max = g_settings->getU16("liquid_loop_max");
1656 //if (m_transforming_liquid.size() > 0) errorstream << "Liquid queue size="<<m_transforming_liquid.size()<<std::endl;
1658 while (m_transforming_liquid.size() > 0)
1660 // This should be done here so that it is done when continue is used
1661 if (loopcount >= initial_size || loopcount >= loop_max)
1665 Get a queued transforming liquid node
1667 v3s16 p0 = m_transforming_liquid.pop_front();
1668 u16 total_level = 0;
1669 // surrounding flowing liquid nodes
1670 NodeNeighbor neighbors[7];
1671 // current level of every block
1672 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1674 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1675 s8 can_liquid_same_level = 0;
1676 content_t liquid_kind = CONTENT_IGNORE;
1677 content_t liquid_kind_flowing = CONTENT_IGNORE;
1679 Collect information about the environment
1681 const v3s16 *dirs = g_7dirs;
1682 for (u16 i = 0; i < 7; i++) {
1683 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1686 nt = NEIGHBOR_UPPER;
1689 nt = NEIGHBOR_LOWER;
1692 v3s16 npos = p0 + dirs[i];
1694 neighbors[i].n = getNodeNoEx(npos);
1695 neighbors[i].t = nt;
1696 neighbors[i].p = npos;
1699 NodeNeighbor & nb = neighbors[i];
1701 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1703 if (nb.n.getContent() == CONTENT_AIR) {
1704 liquid_levels[i] = 0;
1709 // if this node is not (yet) of a liquid type,
1710 // choose the first liquid type we encounter
1711 if (liquid_kind_flowing == CONTENT_IGNORE)
1712 liquid_kind_flowing = nodemgr->getId(
1713 nodemgr->get(nb.n).liquid_alternative_flowing);
1714 if (liquid_kind == CONTENT_IGNORE)
1715 liquid_kind = nb.n.getContent();
1716 if (nb.n.getContent() == liquid_kind) {
1717 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1719 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1722 case LIQUID_FLOWING:
1723 // if this node is not (yet) of a liquid type,
1724 // choose the first liquid type we encounter
1725 if (liquid_kind_flowing == CONTENT_IGNORE)
1726 liquid_kind_flowing = nb.n.getContent();
1727 if (liquid_kind == CONTENT_IGNORE)
1728 liquid_kind = nodemgr->getId(
1729 nodemgr->get(nb.n).liquid_alternative_source);
1730 if (nb.n.getContent() == liquid_kind_flowing) {
1731 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1737 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1738 ++can_liquid_same_level;
1739 if (liquid_levels[i] > 0)
1740 total_level += liquid_levels[i];
1743 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
1744 << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
1745 << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1746 << nodemgr->get(nb.n.getContent()).liquid_type
1747 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1748 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1749 << " tlevel=" << (int)total_level << " cansame="
1750 << (int)can_liquid_same_level << std::endl;
1754 if (liquid_kind == CONTENT_IGNORE ||
1755 !neighbors[D_SELF].l ||
1759 // fill bottom block
1760 if (neighbors[D_BOTTOM].l) {
1761 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
1762 LIQUID_LEVEL_SOURCE : total_level;
1763 total_level -= liquid_levels_want[D_BOTTOM];
1767 if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 &&
1768 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
1769 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
1770 (can_liquid_same_level - relax) &&
1771 can_liquid_same_level >= relax + 1) {
1772 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1775 // prevent lakes in air above unloaded blocks
1776 if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE && !(loopcount % 3)) {
1780 // calculate self level 5 blocks
1782 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1783 ? LIQUID_LEVEL_SOURCE
1784 : total_level / can_liquid_same_level;
1785 total_level -= want_level * can_liquid_same_level;
1788 if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
1789 liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
1790 total_level <= (can_liquid_same_level - relax) &&
1791 can_liquid_same_level >= relax + 1) {
1795 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1796 if (!neighbors[ii].l)
1798 liquid_levels_want[ii] = want_level;
1799 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0) {
1800 if (loopcount % 3 || liquid_levels[ii] <= 0){
1801 if (liquid_levels[ii] > liquid_levels_want[ii]) {
1802 ++liquid_levels_want[ii];
1805 } else if (neighbors[ii].l > 0){
1806 ++liquid_levels_want[ii];
1812 for (u16 ii = 0; ii < 7; ++ii) {
1813 if (total_level < 1) break;
1814 if (liquid_levels_want[ii] >= 0 &&
1815 liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1816 ++liquid_levels_want[ii];
1821 // fill top block if can
1822 if (neighbors[D_TOP].l) {
1823 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
1824 LIQUID_LEVEL_SOURCE : total_level;
1825 total_level -= liquid_levels_want[D_TOP];
1828 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1829 if ( neighbors[ii].i ||
1830 (liquid_levels_want[ii] >= 0 &&
1831 (fast_flood && p0.Y < water_level &&
1832 (initial_size >= 1000
1834 && want_level >= LIQUID_LEVEL_SOURCE/4
1835 && can_liquid_same_level >= 5
1836 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1837 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1840 if (total_level > 0) //|| flowed != volume)
1841 infostream <<" AFTER level=" << (int)total_level
1842 //<< " flowed="<<flowed<< " volume=" << volume
1843 << " wantsame="<<(int)want_level<< " top="
1844 << (int)liquid_levels_want[D_TOP]<< " topwas="
1845 << (int)liquid_levels[D_TOP]<< " bot="
1846 << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1850 for (u16 i = 0; i < 7; i++) {
1851 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1853 MapNode & n0 = neighbors[i].n;
1854 p0 = neighbors[i].p;
1856 decide on the type (and possibly level) of the current node
1858 content_t new_node_content;
1859 s8 new_node_level = -1;
1860 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1861 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1862 // amount to gain, limited by viscosity
1863 // must be at least 1 in absolute value
1864 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1865 if (level_inc < -viscosity || level_inc > viscosity)
1866 new_node_level = liquid_levels[i] + level_inc/viscosity;
1867 else if (level_inc < 0)
1868 new_node_level = liquid_levels[i] - 1;
1869 else if (level_inc > 0)
1870 new_node_level = liquid_levels[i] + 1;
1872 new_node_level = liquid_levels_want[i];
1875 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1876 new_node_content = liquid_kind;
1877 else if (new_node_level > 0)
1878 new_node_content = liquid_kind_flowing;
1880 new_node_content = CONTENT_AIR;
1882 // last level must flow down on stairs
1883 if (liquid_levels_want[i] != liquid_levels[i] &&
1884 liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
1885 new_node_level >= 1 && new_node_level <= 2) {
1886 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1887 if (neighbors[ii].l)
1888 must_reflow_second.push_back(p0 + dirs[ii]);
1893 check if anything has changed.
1894 if not, just continue with the next node.
1897 new_node_content == n0.getContent()
1898 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1899 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1900 //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
1901 //LIQUID_FLOW_DOWN_MASK) == flowing_down
1904 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1905 (((n0.param2 & LIQUID_INFINITY_MASK) ==
1906 LIQUID_INFINITY_MASK) == neighbors[i].i
1914 update the current node
1916 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1917 // set level to last 3 bits, flowing down bit to 4th bit
1918 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1919 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1920 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1921 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1924 infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
1925 <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
1926 <<(int)new_node_level<<std::endl;
1929 n0.setContent(new_node_content);
1930 // Find out whether there is a suspect for this action
1931 std::string suspect;
1932 if(m_gamedef->rollback()){
1933 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1936 if(!suspect.empty()){
1938 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1939 // Get old node for rollback
1940 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1944 RollbackNode rollback_newnode(this, p0, m_gamedef);
1945 RollbackAction action;
1946 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1947 m_gamedef->rollback()->reportAction(action);
1953 v3s16 blockpos = getNodeBlockPos(p0);
1954 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1956 modified_blocks[blockpos] = block;
1957 // If node emits light, MapBlock requires lighting update
1958 if(nodemgr->get(n0).light_source != 0)
1959 lighting_modified_blocks[block->getPos()] = block;
1961 must_reflow.push_back(neighbors[i].p);
1963 /* //for better relax only same level
1964 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
1965 if (!neighbors[ii].l) continue;
1966 must_reflow.push_back(p0 + dirs[ii]);
1971 infostream<<"Map::transformLiquids(): loopcount="<<loopcount
1972 <<" reflow="<<must_reflow.size()
1973 <<" queue="<< m_transforming_liquid.size()<<std::endl;
1975 while (must_reflow.size() > 0)
1976 m_transforming_liquid.push_back(must_reflow.pop_front());
1977 while (must_reflow_second.size() > 0)
1978 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1979 updateLighting(lighting_modified_blocks, modified_blocks);
1982 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1985 if (g_settings->getBool("liquid_finite"))
1986 return Map::transformLiquidsFinite(modified_blocks);
1988 INodeDefManager *nodemgr = m_gamedef->ndef();
1990 DSTACK(__FUNCTION_NAME);
1991 //TimeTaker timer("transformLiquids()");
1994 u32 initial_size = m_transforming_liquid.size();
1996 /*if(initial_size != 0)
1997 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1999 // list of nodes that due to viscosity have not reached their max level height
2000 UniqueQueue<v3s16> must_reflow;
2002 // List of MapBlocks that will require a lighting update (due to lava)
2003 std::map<v3s16, MapBlock*> lighting_modified_blocks;
2005 u16 loop_max = g_settings->getU16("liquid_loop_max");
2007 while(m_transforming_liquid.size() != 0)
2009 // This should be done here so that it is done when continue is used
2010 if(loopcount >= initial_size || loopcount >= loop_max)
2015 Get a queued transforming liquid node
2017 v3s16 p0 = m_transforming_liquid.pop_front();
2019 MapNode n0 = getNodeNoEx(p0);
2022 Collect information about current node
2024 s8 liquid_level = -1;
2025 content_t liquid_kind = CONTENT_IGNORE;
2026 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2027 switch (liquid_type) {
2029 liquid_level = LIQUID_LEVEL_SOURCE;
2030 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2032 case LIQUID_FLOWING:
2033 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
2034 liquid_kind = n0.getContent();
2037 // if this is an air node, it *could* be transformed into a liquid. otherwise,
2038 // continue with the next node.
2039 if (n0.getContent() != CONTENT_AIR)
2041 liquid_kind = CONTENT_AIR;
2046 Collect information about the environment
2048 const v3s16 *dirs = g_6dirs;
2049 NodeNeighbor sources[6]; // surrounding sources
2050 int num_sources = 0;
2051 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2053 NodeNeighbor airs[6]; // surrounding air
2055 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2056 int num_neutrals = 0;
2057 bool flowing_down = false;
2058 for (u16 i = 0; i < 6; i++) {
2059 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2062 nt = NEIGHBOR_UPPER;
2065 nt = NEIGHBOR_LOWER;
2068 v3s16 npos = p0 + dirs[i];
2069 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2070 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2072 if (nb.n.getContent() == CONTENT_AIR) {
2073 airs[num_airs++] = nb;
2074 // if the current node is a water source the neighbor
2075 // should be enqueded for transformation regardless of whether the
2076 // current node changes or not.
2077 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2078 m_transforming_liquid.push_back(npos);
2079 // if the current node happens to be a flowing node, it will start to flow down here.
2080 if (nb.t == NEIGHBOR_LOWER) {
2081 flowing_down = true;
2084 neutrals[num_neutrals++] = nb;
2088 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2089 if (liquid_kind == CONTENT_AIR)
2090 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2091 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2092 neutrals[num_neutrals++] = nb;
2094 // Do not count bottom source, it will screw things up
2096 sources[num_sources++] = nb;
2099 case LIQUID_FLOWING:
2100 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2101 if (liquid_kind == CONTENT_AIR)
2102 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2103 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2104 neutrals[num_neutrals++] = nb;
2106 flows[num_flows++] = nb;
2107 if (nb.t == NEIGHBOR_LOWER)
2108 flowing_down = true;
2115 decide on the type (and possibly level) of the current node
2117 content_t new_node_content;
2118 s8 new_node_level = -1;
2119 s8 max_node_level = -1;
2120 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2121 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2122 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2123 // it's perfectly safe to use liquid_kind here to determine the new node content.
2124 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2125 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2126 // liquid_kind is set properly, see above
2127 new_node_content = liquid_kind;
2128 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2130 // no surrounding sources, so get the maximum level that can flow into this node
2131 for (u16 i = 0; i < num_flows; i++) {
2132 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2133 switch (flows[i].t) {
2134 case NEIGHBOR_UPPER:
2135 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2136 max_node_level = LIQUID_LEVEL_MAX;
2137 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2138 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2139 } else if (nb_liquid_level > max_node_level)
2140 max_node_level = nb_liquid_level;
2142 case NEIGHBOR_LOWER:
2144 case NEIGHBOR_SAME_LEVEL:
2145 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2146 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2147 max_node_level = nb_liquid_level - 1;
2153 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2154 if (viscosity > 1 && max_node_level != liquid_level) {
2155 // amount to gain, limited by viscosity
2156 // must be at least 1 in absolute value
2157 s8 level_inc = max_node_level - liquid_level;
2158 if (level_inc < -viscosity || level_inc > viscosity)
2159 new_node_level = liquid_level + level_inc/viscosity;
2160 else if (level_inc < 0)
2161 new_node_level = liquid_level - 1;
2162 else if (level_inc > 0)
2163 new_node_level = liquid_level + 1;
2164 if (new_node_level != max_node_level)
2165 must_reflow.push_back(p0);
2167 new_node_level = max_node_level;
2169 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
2170 if (new_node_level >= (LIQUID_LEVEL_MAX+1-range))
2171 new_node_content = liquid_kind;
2173 new_node_content = CONTENT_AIR;
2178 check if anything has changed. if not, just continue with the next node.
2180 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2181 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2182 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2188 update the current node
2191 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2192 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2193 // set level to last 3 bits, flowing down bit to 4th bit
2194 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2196 // set the liquid level and flow bit to 0
2197 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2199 n0.setContent(new_node_content);
2201 // Find out whether there is a suspect for this action
2202 std::string suspect;
2203 if(m_gamedef->rollback()){
2204 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2207 if(!suspect.empty()){
2209 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2210 // Get old node for rollback
2211 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2215 RollbackNode rollback_newnode(this, p0, m_gamedef);
2216 RollbackAction action;
2217 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2218 m_gamedef->rollback()->reportAction(action);
2224 v3s16 blockpos = getNodeBlockPos(p0);
2225 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2227 modified_blocks[blockpos] = block;
2228 // If new or old node emits light, MapBlock requires lighting update
2229 if(nodemgr->get(n0).light_source != 0 ||
2230 nodemgr->get(n00).light_source != 0)
2231 lighting_modified_blocks[block->getPos()] = block;
2235 enqueue neighbors for update if neccessary
2237 switch (nodemgr->get(n0.getContent()).liquid_type) {
2239 case LIQUID_FLOWING:
2240 // make sure source flows into all neighboring nodes
2241 for (u16 i = 0; i < num_flows; i++)
2242 if (flows[i].t != NEIGHBOR_UPPER)
2243 m_transforming_liquid.push_back(flows[i].p);
2244 for (u16 i = 0; i < num_airs; i++)
2245 if (airs[i].t != NEIGHBOR_UPPER)
2246 m_transforming_liquid.push_back(airs[i].p);
2249 // this flow has turned to air; neighboring flows might need to do the same
2250 for (u16 i = 0; i < num_flows; i++)
2251 m_transforming_liquid.push_back(flows[i].p);
2255 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2256 while (must_reflow.size() > 0)
2257 m_transforming_liquid.push_back(must_reflow.pop_front());
2258 updateLighting(lighting_modified_blocks, modified_blocks);
2261 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2263 v3s16 blockpos = getNodeBlockPos(p);
2264 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2265 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2267 infostream<<"Map::getNodeMetadata(): Need to emerge "
2268 <<PP(blockpos)<<std::endl;
2269 block = emergeBlock(blockpos, false);
2273 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2277 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2281 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2283 v3s16 blockpos = getNodeBlockPos(p);
2284 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2285 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2287 infostream<<"Map::setNodeMetadata(): Need to emerge "
2288 <<PP(blockpos)<<std::endl;
2289 block = emergeBlock(blockpos, false);
2293 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2297 block->m_node_metadata.set(p_rel, meta);
2300 void Map::removeNodeMetadata(v3s16 p)
2302 v3s16 blockpos = getNodeBlockPos(p);
2303 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2304 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2307 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2311 block->m_node_metadata.remove(p_rel);
2314 NodeTimer Map::getNodeTimer(v3s16 p)
2316 v3s16 blockpos = getNodeBlockPos(p);
2317 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2318 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2320 infostream<<"Map::getNodeTimer(): Need to emerge "
2321 <<PP(blockpos)<<std::endl;
2322 block = emergeBlock(blockpos, false);
2326 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2330 NodeTimer t = block->m_node_timers.get(p_rel);
2334 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2336 v3s16 blockpos = getNodeBlockPos(p);
2337 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2338 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2340 infostream<<"Map::setNodeTimer(): Need to emerge "
2341 <<PP(blockpos)<<std::endl;
2342 block = emergeBlock(blockpos, false);
2346 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2350 block->m_node_timers.set(p_rel, t);
2353 void Map::removeNodeTimer(v3s16 p)
2355 v3s16 blockpos = getNodeBlockPos(p);
2356 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2357 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2360 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2364 block->m_node_timers.remove(p_rel);
2370 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2371 Map(dout_server, gamedef),
2373 m_map_metadata_changed(true),
2375 m_database_read(NULL),
2376 m_database_write(NULL)
2378 verbosestream<<__FUNCTION_NAME<<std::endl;
2381 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2383 m_mgparams = new MapgenV6Params();
2385 m_seed = m_mgparams->seed;
2387 if (g_settings->get("fixed_map_seed").empty())
2389 m_seed = (((u64)(myrand() & 0xffff) << 0)
2390 | ((u64)(myrand() & 0xffff) << 16)
2391 | ((u64)(myrand() & 0xffff) << 32)
2392 | ((u64)(myrand() & 0xffff) << 48));
2393 m_mgparams->seed = m_seed;
2397 Experimental and debug stuff
2404 Try to load map; if not found, create a new one.
2407 m_savedir = savedir;
2408 m_map_saving_enabled = false;
2412 // If directory exists, check contents and load if possible
2413 if(fs::PathExists(m_savedir))
2415 // If directory is empty, it is safe to save into it.
2416 if(fs::GetDirListing(m_savedir).size() == 0)
2418 infostream<<"ServerMap: Empty save directory is valid."
2420 m_map_saving_enabled = true;
2425 // Load map metadata (seed, chunksize)
2428 catch(SettingNotFoundException &e){
2429 infostream<<"ServerMap: Some metadata not found."
2430 <<" Using default settings."<<std::endl;
2432 catch(FileNotGoodException &e){
2433 infostream<<"WARNING: Could not load map metadata"
2434 //<<" Disabling chunk-based generator."
2439 infostream<<"ServerMap: Successfully loaded map "
2440 <<"metadata from "<<savedir
2441 <<", assuming valid save directory."
2442 <<" seed="<<m_seed<<"."
2445 m_map_saving_enabled = true;
2446 // Map loaded, not creating new one
2450 // If directory doesn't exist, it is safe to save to it
2452 m_map_saving_enabled = true;
2455 catch(std::exception &e)
2457 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2458 <<", exception: "<<e.what()<<std::endl;
2459 infostream<<"Please remove the map or fix it."<<std::endl;
2460 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2463 infostream<<"Initializing new map."<<std::endl;
2465 // Create zero sector
2466 emergeSector(v2s16(0,0));
2468 // Initially write whole map
2469 save(MOD_STATE_CLEAN);
2472 ServerMap::~ServerMap()
2474 verbosestream<<__FUNCTION_NAME<<std::endl;
2478 if(m_map_saving_enabled)
2480 // Save only changed parts
2481 save(MOD_STATE_WRITE_AT_UNLOAD);
2482 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2486 infostream<<"ServerMap: Map not saved"<<std::endl;
2489 catch(std::exception &e)
2491 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2492 <<", exception: "<<e.what()<<std::endl;
2496 Close database if it was opened
2499 sqlite3_finalize(m_database_read);
2500 if(m_database_write)
2501 sqlite3_finalize(m_database_write);
2503 sqlite3_close(m_database);
2509 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2510 for(; i.atEnd() == false; i++)
2512 MapChunk *chunk = i.getNode()->getValue();
2520 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2522 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2523 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2525 s16 chunksize = m_mgparams->chunksize;
2526 s16 coffset = -chunksize / 2;
2527 v3s16 chunk_offset(coffset, coffset, coffset);
2528 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2529 v3s16 blockpos_min = blockpos_div * chunksize;
2530 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2531 blockpos_min += chunk_offset;
2532 blockpos_max += chunk_offset;
2534 v3s16 extra_borders(1,1,1);
2536 // Do nothing if not inside limits (+-1 because of neighbors)
2537 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2538 blockpos_over_limit(blockpos_max + extra_borders))
2541 data->seed = m_seed;
2542 data->blockpos_min = blockpos_min;
2543 data->blockpos_max = blockpos_max;
2544 data->blockpos_requested = blockpos;
2545 data->nodedef = m_gamedef->ndef();
2548 Create the whole area of this and the neighboring blocks
2551 //TimeTaker timer("initBlockMake() create area");
2553 for(s16 x=blockpos_min.X-extra_borders.X;
2554 x<=blockpos_max.X+extra_borders.X; x++)
2555 for(s16 z=blockpos_min.Z-extra_borders.Z;
2556 z<=blockpos_max.Z+extra_borders.Z; z++)
2558 v2s16 sectorpos(x, z);
2559 // Sector metadata is loaded from disk if not already loaded.
2560 ServerMapSector *sector = createSector(sectorpos);
2563 for(s16 y=blockpos_min.Y-extra_borders.Y;
2564 y<=blockpos_max.Y+extra_borders.Y; y++)
2567 //MapBlock *block = createBlock(p);
2568 // 1) get from memory, 2) load from disk
2569 MapBlock *block = emergeBlock(p, false);
2570 // 3) create a blank one
2573 block = createBlock(p);
2576 Block gets sunlight if this is true.
2578 Refer to the map generator heuristics.
2580 bool ug = m_emerge->isBlockUnderground(p);
2581 block->setIsUnderground(ug);
2584 // Lighting will not be valid after make_chunk is called
2585 block->setLightingExpired(true);
2586 // Lighting will be calculated
2587 //block->setLightingExpired(false);
2593 Now we have a big empty area.
2595 Make a ManualMapVoxelManipulator that contains this and the
2599 // The area that contains this block and it's neighbors
2600 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2601 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2603 data->vmanip = new ManualMapVoxelManipulator(this);
2604 //data->vmanip->setMap(this);
2608 //TimeTaker timer("initBlockMake() initialEmerge");
2609 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2612 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2613 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2614 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2615 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2616 core::map<v3s16, u8>::Node *n;
2617 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2620 u8 flags = n->getValue();
2621 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2627 // Data is ready now.
2631 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2632 std::map<v3s16, MapBlock*> &changed_blocks)
2634 v3s16 blockpos_min = data->blockpos_min;
2635 v3s16 blockpos_max = data->blockpos_max;
2636 v3s16 blockpos_requested = data->blockpos_requested;
2637 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2638 <<blockpos_requested.Y<<","
2639 <<blockpos_requested.Z<<")"<<std::endl;*/
2641 v3s16 extra_borders(1,1,1);
2643 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2645 /*infostream<<"Resulting vmanip:"<<std::endl;
2646 data->vmanip.print(infostream);*/
2648 // Make sure affected blocks are loaded
2649 for(s16 x=blockpos_min.X-extra_borders.X;
2650 x<=blockpos_max.X+extra_borders.X; x++)
2651 for(s16 z=blockpos_min.Z-extra_borders.Z;
2652 z<=blockpos_max.Z+extra_borders.Z; z++)
2653 for(s16 y=blockpos_min.Y-extra_borders.Y;
2654 y<=blockpos_max.Y+extra_borders.Y; y++)
2657 // Load from disk if not already in memory
2658 emergeBlock(p, false);
2662 Blit generated stuff to map
2663 NOTE: blitBackAll adds nearly everything to changed_blocks
2667 //TimeTaker timer("finishBlockMake() blitBackAll");
2668 data->vmanip->blitBackAll(&changed_blocks);
2671 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2674 Copy transforming liquid information
2676 while(data->transforming_liquid.size() > 0)
2678 v3s16 p = data->transforming_liquid.pop_front();
2679 m_transforming_liquid.push_back(p);
2683 Do stuff in central blocks
2691 TimeTaker t("finishBlockMake lighting update");
2693 core::map<v3s16, MapBlock*> lighting_update_blocks;
2696 for(s16 x=blockpos_min.X-extra_borders.X;
2697 x<=blockpos_max.X+extra_borders.X; x++)
2698 for(s16 z=blockpos_min.Z-extra_borders.Z;
2699 z<=blockpos_max.Z+extra_borders.Z; z++)
2700 for(s16 y=blockpos_min.Y-extra_borders.Y;
2701 y<=blockpos_max.Y+extra_borders.Y; y++)
2704 MapBlock *block = getBlockNoCreateNoEx(p);
2706 lighting_update_blocks.insert(block->getPos(), block);
2709 updateLighting(lighting_update_blocks, changed_blocks);
2713 Set lighting to non-expired state in all of them.
2714 This is cheating, but it is not fast enough if all of them
2715 would actually be updated.
2717 for(s16 x=blockpos_min.X-extra_borders.X;
2718 x<=blockpos_max.X+extra_borders.X; x++)
2719 for(s16 z=blockpos_min.Z-extra_borders.Z;
2720 z<=blockpos_max.Z+extra_borders.Z; z++)
2721 for(s16 y=blockpos_min.Y-extra_borders.Y;
2722 y<=blockpos_max.Y+extra_borders.Y; y++)
2725 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2729 if(enable_mapgen_debug_info == false)
2730 t.stop(true); // Hide output
2735 Go through changed blocks
2737 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2738 i != changed_blocks.end(); ++i)
2740 MapBlock *block = i->second;
2743 Update day/night difference cache of the MapBlocks
2745 block->expireDayNightDiff();
2747 Set block as modified
2749 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2750 "finishBlockMake expireDayNightDiff");
2754 Set central blocks as generated
2756 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2757 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2758 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2761 MapBlock *block = getBlockNoCreateNoEx(p);
2763 block->setGenerated(true);
2767 Save changed parts of map
2768 NOTE: Will be saved later.
2770 //save(MOD_STATE_WRITE_AT_UNLOAD);
2772 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2773 <<","<<blockpos_requested.Y<<","
2774 <<blockpos_requested.Z<<")"<<std::endl;*/
2776 if(enable_mapgen_debug_info)
2779 Analyze resulting blocks
2781 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2782 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2783 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2784 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2785 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2786 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2788 v3s16 p = v3s16(x,y,z);
2789 MapBlock *block = getBlockNoCreateNoEx(p);
2791 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2792 infostream<<"Generated "<<spos<<": "
2793 <<analyze_block(block)<<std::endl;
2798 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2804 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2806 DSTACKF("%s: p2d=(%d,%d)",
2811 Check if it exists already in memory
2813 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2818 Try to load it from disk (with blocks)
2820 //if(loadSectorFull(p2d) == true)
2823 Try to load metadata from disk
2826 if(loadSectorMeta(p2d) == true)
2828 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2831 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2832 throw InvalidPositionException("");
2838 Do not create over-limit
2840 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2841 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2842 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2843 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2844 throw InvalidPositionException("createSector(): pos. over limit");
2847 Generate blank sector
2850 sector = new ServerMapSector(this, p2d, m_gamedef);
2852 // Sector position on map in nodes
2853 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2858 m_sectors[p2d] = sector;
2865 This is a quick-hand function for calling makeBlock().
2867 MapBlock * ServerMap::generateBlock(
2869 std::map<v3s16, MapBlock*> &modified_blocks
2872 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2874 /*infostream<<"generateBlock(): "
2875 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2878 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2880 TimeTaker timer("generateBlock");
2882 //MapBlock *block = original_dummy;
2884 v2s16 p2d(p.X, p.Z);
2885 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2888 Do not generate over-limit
2890 if(blockpos_over_limit(p))
2892 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2893 throw InvalidPositionException("generateBlock(): pos. over limit");
2897 Create block make data
2900 initBlockMake(&data, p);
2906 TimeTaker t("mapgen::make_block()");
2907 mapgen->makeChunk(&data);
2908 //mapgen::make_block(&data);
2910 if(enable_mapgen_debug_info == false)
2911 t.stop(true); // Hide output
2915 Blit data back on map, update lighting, add mobs and whatever this does
2917 finishBlockMake(&data, modified_blocks);
2922 MapBlock *block = getBlockNoCreateNoEx(p);
2930 bool erroneus_content = false;
2931 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2932 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2933 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2936 MapNode n = block->getNode(p);
2937 if(n.getContent() == CONTENT_IGNORE)
2939 infostream<<"CONTENT_IGNORE at "
2940 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2942 erroneus_content = true;
2946 if(erroneus_content)
2955 Generate a completely empty block
2959 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2960 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2962 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2965 n.setContent(CONTENT_AIR);
2966 block->setNode(v3s16(x0,y0,z0), n);
2972 if(enable_mapgen_debug_info == false)
2973 timer.stop(true); // Hide output
2979 MapBlock * ServerMap::createBlock(v3s16 p)
2981 DSTACKF("%s: p=(%d,%d,%d)",
2982 __FUNCTION_NAME, p.X, p.Y, p.Z);
2985 Do not create over-limit
2987 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2988 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2989 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2990 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2991 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2992 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2993 throw InvalidPositionException("createBlock(): pos. over limit");
2995 v2s16 p2d(p.X, p.Z);
2998 This will create or load a sector if not found in memory.
2999 If block exists on disk, it will be loaded.
3001 NOTE: On old save formats, this will be slow, as it generates
3002 lighting on blocks for them.
3004 ServerMapSector *sector;
3006 sector = (ServerMapSector*)createSector(p2d);
3007 assert(sector->getId() == MAPSECTOR_SERVER);
3009 catch(InvalidPositionException &e)
3011 infostream<<"createBlock: createSector() failed"<<std::endl;
3015 NOTE: This should not be done, or at least the exception
3016 should not be passed on as std::exception, because it
3017 won't be catched at all.
3019 /*catch(std::exception &e)
3021 infostream<<"createBlock: createSector() failed: "
3022 <<e.what()<<std::endl;
3027 Try to get a block from the sector
3030 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3033 if(block->isDummy())
3038 block = sector->createBlankBlock(block_y);
3043 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3045 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3047 p.X, p.Y, p.Z, create_blank);
3050 MapBlock *block = getBlockNoCreateNoEx(p);
3051 if(block && block->isDummy() == false)
3056 MapBlock *block = loadBlock(p);
3062 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3063 MapBlock *block = sector->createBlankBlock(p.Y);
3067 /*if(allow_generate)
3069 std::map<v3s16, MapBlock*> modified_blocks;
3070 MapBlock *block = generateBlock(p, modified_blocks);
3074 event.type = MEET_OTHER;
3077 // Copy modified_blocks to event
3078 for(std::map<v3s16, MapBlock*>::iterator
3079 i = modified_blocks.begin();
3080 i != modified_blocks.end(); ++i)
3082 event.modified_blocks.insert(i->first);
3086 dispatchEvent(&event);
3095 s16 ServerMap::findGroundLevel(v2s16 p2d)
3099 Uh, just do something random...
3101 // Find existing map from top to down
3104 v3s16 p(p2d.X, max, p2d.Y);
3105 for(; p.Y>min; p.Y--)
3107 MapNode n = getNodeNoEx(p);
3108 if(n.getContent() != CONTENT_IGNORE)
3113 // If this node is not air, go to plan b
3114 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3116 // Search existing walkable and return it
3117 for(; p.Y>min; p.Y--)
3119 MapNode n = getNodeNoEx(p);
3120 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3129 Determine from map generator noise functions
3132 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3135 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3136 //return (s16)level;
3139 void ServerMap::createDatabase() {
3142 e = sqlite3_exec(m_database,
3143 "CREATE TABLE IF NOT EXISTS `blocks` ("
3144 "`pos` INT NOT NULL PRIMARY KEY,"
3147 , NULL, NULL, NULL);
3148 if(e == SQLITE_ABORT)
3149 throw FileNotGoodException("Could not create database structure");
3151 infostream<<"ServerMap: Database structure was created";
3154 void ServerMap::verifyDatabase() {
3159 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3160 bool needs_create = false;
3164 Open the database connection
3167 createDirs(m_savedir);
3169 if(!fs::PathExists(dbp))
3170 needs_create = true;
3172 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3173 if(d != SQLITE_OK) {
3174 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3175 throw FileNotGoodException("Cannot open database file");
3181 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3182 if(d != SQLITE_OK) {
3183 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3184 throw FileNotGoodException("Cannot prepare read statement");
3187 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3188 if(d != SQLITE_OK) {
3189 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3190 throw FileNotGoodException("Cannot prepare write statement");
3193 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3194 if(d != SQLITE_OK) {
3195 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3196 throw FileNotGoodException("Cannot prepare read statement");
3199 infostream<<"ServerMap: Database opened"<<std::endl;
3203 bool ServerMap::loadFromFolders() {
3204 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3209 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3210 return (sqlite3_int64)pos.Z*16777216 +
3211 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3214 void ServerMap::createDirs(std::string path)
3216 if(fs::CreateAllDirs(path) == false)
3218 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3219 <<"\""<<path<<"\""<<std::endl;
3220 throw BaseException("ServerMap failed to create directory");
3224 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3230 snprintf(cc, 9, "%.4x%.4x",
3231 (unsigned int)pos.X&0xffff,
3232 (unsigned int)pos.Y&0xffff);
3234 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3236 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3237 (unsigned int)pos.X&0xfff,
3238 (unsigned int)pos.Y&0xfff);
3240 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3246 v2s16 ServerMap::getSectorPos(std::string dirname)
3250 std::string component;
3251 fs::RemoveLastPathComponent(dirname, &component, 1);
3252 if(component.size() == 8)
3255 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
3257 else if(component.size() == 3)
3260 fs::RemoveLastPathComponent(dirname, &component, 2);
3261 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3262 // Sign-extend the 12 bit values up to 16 bits...
3263 if(x&0x800) x|=0xF000;
3264 if(y&0x800) y|=0xF000;
3271 v2s16 pos((s16)x, (s16)y);
3275 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3277 v2s16 p2d = getSectorPos(sectordir);
3279 if(blockfile.size() != 4){
3280 throw InvalidFilenameException("Invalid block filename");
3283 int r = sscanf(blockfile.c_str(), "%4x", &y);
3285 throw InvalidFilenameException("Invalid block filename");
3286 return v3s16(p2d.X, y, p2d.Y);
3289 std::string ServerMap::getBlockFilename(v3s16 p)
3292 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3296 void ServerMap::save(ModifiedState save_level)
3298 DSTACK(__FUNCTION_NAME);
3299 if(m_map_saving_enabled == false)
3301 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3305 if(save_level == MOD_STATE_CLEAN)
3306 infostream<<"ServerMap: Saving whole map, this can take time."
3309 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3314 // Profile modified reasons
3315 Profiler modprofiler;
3317 u32 sector_meta_count = 0;
3318 u32 block_count = 0;
3319 u32 block_count_all = 0; // Number of blocks in memory
3321 // Don't do anything with sqlite unless something is really saved
3322 bool save_started = false;
3324 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3325 i != m_sectors.end(); ++i)
3327 ServerMapSector *sector = (ServerMapSector*)i->second;
3328 assert(sector->getId() == MAPSECTOR_SERVER);
3330 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3332 saveSectorMeta(sector);
3333 sector_meta_count++;
3335 std::list<MapBlock*> blocks;
3336 sector->getBlocks(blocks);
3338 for(std::list<MapBlock*>::iterator j = blocks.begin();
3339 j != blocks.end(); ++j)
3341 MapBlock *block = *j;
3345 if(block->getModified() >= (u32)save_level)
3350 save_started = true;
3353 modprofiler.add(block->getModifiedReason(), 1);
3358 /*infostream<<"ServerMap: Written block ("
3359 <<block->getPos().X<<","
3360 <<block->getPos().Y<<","
3361 <<block->getPos().Z<<")"
3370 Only print if something happened or saved whole map
3372 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3373 || block_count != 0)
3375 infostream<<"ServerMap: Written: "
3376 <<sector_meta_count<<" sector metadata files, "
3377 <<block_count<<" block files"
3378 <<", "<<block_count_all<<" blocks in memory."
3380 PrintInfo(infostream); // ServerMap/ClientMap:
3381 infostream<<"Blocks modified by: "<<std::endl;
3382 modprofiler.print(infostream);
3386 static s32 unsignedToSigned(s32 i, s32 max_positive)
3388 if(i < max_positive)
3391 return i - 2*max_positive;
3394 // modulo of a negative number does not work consistently in C
3395 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3399 return mod - ((-i) % mod);
3402 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3404 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3406 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3408 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3409 return v3s16(x,y,z);
3412 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3414 if(loadFromFolders()){
3415 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3416 <<"all blocks that are stored in flat files"<<std::endl;
3422 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3424 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3425 v3s16 p = getIntegerAsBlock(block_i);
3426 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3432 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3434 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3435 si != m_sectors.end(); ++si)
3437 MapSector *sector = si->second;
3439 std::list<MapBlock*> blocks;
3440 sector->getBlocks(blocks);
3442 for(std::list<MapBlock*>::iterator i = blocks.begin();
3443 i != blocks.end(); ++i)
3445 MapBlock *block = (*i);
3446 v3s16 p = block->getPos();
3452 void ServerMap::saveMapMeta()
3454 DSTACK(__FUNCTION_NAME);
3456 /*infostream<<"ServerMap::saveMapMeta(): "
3460 createDirs(m_savedir);
3462 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3463 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3464 if(os.good() == false)
3466 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3467 <<"could not open"<<fullpath<<std::endl;
3468 throw FileNotGoodException("Cannot open chunk metadata");
3473 m_emerge->setParamsToSettings(¶ms);
3474 params.writeLines(os);
3476 os<<"[end_of_params]\n";
3478 m_map_metadata_changed = false;
3481 void ServerMap::loadMapMeta()
3483 DSTACK(__FUNCTION_NAME);
3485 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3488 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3489 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3490 if(is.good() == false)
3492 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3493 <<"could not open"<<fullpath<<std::endl;
3494 throw FileNotGoodException("Cannot open map metadata");
3502 throw SerializationError
3503 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3505 std::getline(is, line);
3506 std::string trimmedline = trim(line);
3507 if(trimmedline == "[end_of_params]")
3509 params.parseConfigLine(line);
3512 MapgenParams *mgparams;
3514 mgparams = m_emerge->getParamsFromSettings(¶ms);
3515 } catch (SettingNotFoundException &e) {
3516 infostream << "Couldn't get a setting from map_meta.txt: "
3517 << e.what() << std::endl;
3524 m_mgparams = mgparams;
3525 m_seed = mgparams->seed;
3527 if (params.exists("seed")) {
3528 m_seed = params.getU64("seed");
3529 m_mgparams->seed = m_seed;
3533 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3536 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3538 DSTACK(__FUNCTION_NAME);
3539 // Format used for writing
3540 u8 version = SER_FMT_VER_HIGHEST;
3542 v2s16 pos = sector->getPos();
3543 std::string dir = getSectorDir(pos);
3546 std::string fullpath = dir + DIR_DELIM + "meta";
3547 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3548 if(o.good() == false)
3549 throw FileNotGoodException("Cannot open sector metafile");
3551 sector->serialize(o, version);
3553 sector->differs_from_disk = false;
3556 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3558 DSTACK(__FUNCTION_NAME);
3560 v2s16 p2d = getSectorPos(sectordir);
3562 ServerMapSector *sector = NULL;
3564 std::string fullpath = sectordir + DIR_DELIM + "meta";
3565 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3566 if(is.good() == false)
3568 // If the directory exists anyway, it probably is in some old
3569 // format. Just go ahead and create the sector.
3570 if(fs::PathExists(sectordir))
3572 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3573 <<fullpath<<" doesn't exist but directory does."
3574 <<" Continuing with a sector with no metadata."
3576 sector = new ServerMapSector(this, p2d, m_gamedef);
3577 m_sectors[p2d] = sector;
3581 throw FileNotGoodException("Cannot open sector metafile");
3586 sector = ServerMapSector::deSerialize
3587 (is, this, p2d, m_sectors, m_gamedef);
3589 saveSectorMeta(sector);
3592 sector->differs_from_disk = false;
3597 bool ServerMap::loadSectorMeta(v2s16 p2d)
3599 DSTACK(__FUNCTION_NAME);
3601 MapSector *sector = NULL;
3603 // The directory layout we're going to load from.
3604 // 1 - original sectors/xxxxzzzz/
3605 // 2 - new sectors2/xxx/zzz/
3606 // If we load from anything but the latest structure, we will
3607 // immediately save to the new one, and remove the old.
3609 std::string sectordir1 = getSectorDir(p2d, 1);
3610 std::string sectordir;
3611 if(fs::PathExists(sectordir1))
3613 sectordir = sectordir1;
3618 sectordir = getSectorDir(p2d, 2);
3622 sector = loadSectorMeta(sectordir, loadlayout != 2);
3624 catch(InvalidFilenameException &e)
3628 catch(FileNotGoodException &e)
3632 catch(std::exception &e)
3641 bool ServerMap::loadSectorFull(v2s16 p2d)
3643 DSTACK(__FUNCTION_NAME);
3645 MapSector *sector = NULL;
3647 // The directory layout we're going to load from.
3648 // 1 - original sectors/xxxxzzzz/
3649 // 2 - new sectors2/xxx/zzz/
3650 // If we load from anything but the latest structure, we will
3651 // immediately save to the new one, and remove the old.
3653 std::string sectordir1 = getSectorDir(p2d, 1);
3654 std::string sectordir;
3655 if(fs::PathExists(sectordir1))
3657 sectordir = sectordir1;
3662 sectordir = getSectorDir(p2d, 2);
3666 sector = loadSectorMeta(sectordir, loadlayout != 2);
3668 catch(InvalidFilenameException &e)
3672 catch(FileNotGoodException &e)
3676 catch(std::exception &e)
3684 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3686 std::vector<fs::DirListNode>::iterator i2;
3687 for(i2=list2.begin(); i2!=list2.end(); i2++)
3693 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3695 catch(InvalidFilenameException &e)
3697 // This catches unknown crap in directory
3703 infostream<<"Sector converted to new layout - deleting "<<
3704 sectordir1<<std::endl;
3705 fs::RecursiveDelete(sectordir1);
3712 void ServerMap::beginSave() {
3714 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3715 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3718 void ServerMap::endSave() {
3720 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3721 infostream<<"WARNING: endSave() failed, map might not have saved.";
3724 void ServerMap::saveBlock(MapBlock *block)
3726 DSTACK(__FUNCTION_NAME);
3728 Dummy blocks are not written
3730 if(block->isDummy())
3732 /*v3s16 p = block->getPos();
3733 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3734 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3738 // Format used for writing
3739 u8 version = SER_FMT_VER_HIGHEST;
3741 v3s16 p3d = block->getPos();
3745 v2s16 p2d(p3d.X, p3d.Z);
3746 std::string sectordir = getSectorDir(p2d);
3748 createDirs(sectordir);
3750 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3751 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3752 if(o.good() == false)
3753 throw FileNotGoodException("Cannot open block data");
3756 [0] u8 serialization version
3762 std::ostringstream o(std::ios_base::binary);
3764 o.write((char*)&version, 1);
3767 block->serialize(o, version, true);
3769 // Write block to database
3771 std::string tmp = o.str();
3772 const char *bytes = tmp.c_str();
3774 bool success = true;
3775 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3776 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3779 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3780 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3783 int written = sqlite3_step(m_database_write);
3784 if(written != SQLITE_DONE) {
3785 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3786 <<sqlite3_errmsg(m_database)<<std::endl;
3789 // Make ready for later reuse
3790 sqlite3_reset(m_database_write);
3792 // We just wrote it to the disk so clear modified flag
3794 block->resetModified();
3797 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3799 DSTACK(__FUNCTION_NAME);
3801 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3804 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3805 if(is.good() == false)
3806 throw FileNotGoodException("Cannot open block file");
3808 v3s16 p3d = getBlockPos(sectordir, blockfile);
3809 v2s16 p2d(p3d.X, p3d.Z);
3811 assert(sector->getPos() == p2d);
3813 u8 version = SER_FMT_VER_INVALID;
3814 is.read((char*)&version, 1);
3817 throw SerializationError("ServerMap::loadBlock(): Failed"
3818 " to read MapBlock version");
3820 /*u32 block_size = MapBlock::serializedLength(version);
3821 SharedBuffer<u8> data(block_size);
3822 is.read((char*)*data, block_size);*/
3824 // This will always return a sector because we're the server
3825 //MapSector *sector = emergeSector(p2d);
3827 MapBlock *block = NULL;
3828 bool created_new = false;
3829 block = sector->getBlockNoCreateNoEx(p3d.Y);
3832 block = sector->createBlankBlockNoInsert(p3d.Y);
3837 block->deSerialize(is, version, true);
3839 // If it's a new block, insert it to the map
3841 sector->insertBlock(block);
3844 Save blocks loaded in old format in new format
3847 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3851 // Should be in database now, so delete the old file
3852 fs::RecursiveDelete(fullpath);
3855 // We just loaded it from the disk, so it's up-to-date.
3856 block->resetModified();
3859 catch(SerializationError &e)
3861 infostream<<"WARNING: Invalid block data on disk "
3862 <<"fullpath="<<fullpath
3863 <<" (SerializationError). "
3864 <<"what()="<<e.what()
3866 //" Ignoring. A new one will be generated.
3869 // TODO: Backup file; name is in fullpath.
3873 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3875 DSTACK(__FUNCTION_NAME);
3878 std::istringstream is(*blob, std::ios_base::binary);
3880 u8 version = SER_FMT_VER_INVALID;
3881 is.read((char*)&version, 1);
3884 throw SerializationError("ServerMap::loadBlock(): Failed"
3885 " to read MapBlock version");
3887 /*u32 block_size = MapBlock::serializedLength(version);
3888 SharedBuffer<u8> data(block_size);
3889 is.read((char*)*data, block_size);*/
3891 // This will always return a sector because we're the server
3892 //MapSector *sector = emergeSector(p2d);
3894 MapBlock *block = NULL;
3895 bool created_new = false;
3896 block = sector->getBlockNoCreateNoEx(p3d.Y);
3899 block = sector->createBlankBlockNoInsert(p3d.Y);
3904 block->deSerialize(is, version, true);
3906 // If it's a new block, insert it to the map
3908 sector->insertBlock(block);
3911 Save blocks loaded in old format in new format
3914 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3915 // Only save if asked to; no need to update version
3919 // We just loaded it from, so it's up-to-date.
3920 block->resetModified();
3923 catch(SerializationError &e)
3925 errorstream<<"Invalid block data in database"
3926 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3927 <<" (SerializationError): "<<e.what()<<std::endl;
3929 // TODO: Block should be marked as invalid in memory so that it is
3930 // not touched but the game can run
3932 if(g_settings->getBool("ignore_world_load_errors")){
3933 errorstream<<"Ignoring block load error. Duck and cover! "
3934 <<"(ignore_world_load_errors)"<<std::endl;
3936 throw SerializationError("Invalid block data in database");
3942 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3944 DSTACK(__FUNCTION_NAME);
3946 v2s16 p2d(blockpos.X, blockpos.Z);
3948 if(!loadFromFolders()) {
3951 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3952 infostream<<"WARNING: Could not bind block position for load: "
3953 <<sqlite3_errmsg(m_database)<<std::endl;
3954 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3956 Make sure sector is loaded
3958 MapSector *sector = createSector(p2d);
3963 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3964 size_t len = sqlite3_column_bytes(m_database_read, 0);
3966 std::string datastr(data, len);
3968 loadBlock(&datastr, blockpos, sector, false);
3970 sqlite3_step(m_database_read);
3971 // We should never get more than 1 row, so ok to reset
3972 sqlite3_reset(m_database_read);
3974 return getBlockNoCreateNoEx(blockpos);
3976 sqlite3_reset(m_database_read);
3978 // Not found in database, try the files
3981 // The directory layout we're going to load from.
3982 // 1 - original sectors/xxxxzzzz/
3983 // 2 - new sectors2/xxx/zzz/
3984 // If we load from anything but the latest structure, we will
3985 // immediately save to the new one, and remove the old.
3987 std::string sectordir1 = getSectorDir(p2d, 1);
3988 std::string sectordir;
3989 if(fs::PathExists(sectordir1))
3991 sectordir = sectordir1;
3996 sectordir = getSectorDir(p2d, 2);
4000 Make sure sector is loaded
4002 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4006 sector = loadSectorMeta(sectordir, loadlayout != 2);
4008 catch(InvalidFilenameException &e)
4012 catch(FileNotGoodException &e)
4016 catch(std::exception &e)
4023 Make sure file exists
4026 std::string blockfilename = getBlockFilename(blockpos);
4027 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
4031 Load block and save it to the database
4033 loadBlock(sectordir, blockfilename, sector, true);
4034 return getBlockNoCreateNoEx(blockpos);
4037 void ServerMap::PrintInfo(std::ostream &out)
4046 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4051 MapVoxelManipulator::~MapVoxelManipulator()
4053 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4057 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4059 TimeTaker timer1("emerge", &emerge_time);
4061 // Units of these are MapBlocks
4062 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4063 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4065 VoxelArea block_area_nodes
4066 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4068 addArea(block_area_nodes);
4070 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4071 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4072 for(s32 x=p_min.X; x<=p_max.X; x++)
4077 std::map<v3s16, u8>::iterator n;
4078 n = m_loaded_blocks.find(p);
4079 if(n != m_loaded_blocks.end())
4082 bool block_data_inexistent = false;
4085 TimeTaker timer1("emerge load", &emerge_load_time);
4087 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4088 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4090 a.print(infostream);
4091 infostream<<std::endl;*/
4093 block = m_map->getBlockNoCreate(p);
4094 if(block->isDummy())
4095 block_data_inexistent = true;
4097 block->copyTo(*this);
4099 catch(InvalidPositionException &e)
4101 block_data_inexistent = true;
4104 if(block_data_inexistent)
4106 flags |= VMANIP_BLOCK_DATA_INEXIST;
4108 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4109 // Fill with VOXELFLAG_INEXISTENT
4110 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4111 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4113 s32 i = m_area.index(a.MinEdge.X,y,z);
4114 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4117 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4119 // Mark that block was loaded as blank
4120 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4123 m_loaded_blocks[p] = flags;
4126 //infostream<<"emerge done"<<std::endl;
4130 SUGG: Add an option to only update eg. water and air nodes.
4131 This will make it interfere less with important stuff if
4134 void MapVoxelManipulator::blitBack
4135 (std::map<v3s16, MapBlock*> & modified_blocks)
4137 if(m_area.getExtent() == v3s16(0,0,0))
4140 //TimeTaker timer1("blitBack");
4142 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4143 <<m_loaded_blocks.size()<<std::endl;*/
4146 Initialize block cache
4148 v3s16 blockpos_last;
4149 MapBlock *block = NULL;
4150 bool block_checked_in_modified = false;
4152 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4153 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4154 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4158 u8 f = m_flags[m_area.index(p)];
4159 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4162 MapNode &n = m_data[m_area.index(p)];
4164 v3s16 blockpos = getNodeBlockPos(p);
4169 if(block == NULL || blockpos != blockpos_last){
4170 block = m_map->getBlockNoCreate(blockpos);
4171 blockpos_last = blockpos;
4172 block_checked_in_modified = false;
4175 // Calculate relative position in block
4176 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4178 // Don't continue if nothing has changed here
4179 if(block->getNode(relpos) == n)
4182 //m_map->setNode(m_area.MinEdge + p, n);
4183 block->setNode(relpos, n);
4186 Make sure block is in modified_blocks
4188 if(block_checked_in_modified == false)
4190 modified_blocks[blockpos] = block;
4191 block_checked_in_modified = true;
4194 catch(InvalidPositionException &e)
4200 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4201 MapVoxelManipulator(map),
4202 m_create_area(false)
4206 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4210 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4212 // Just create the area so that it can be pointed to
4213 VoxelManipulator::emerge(a, caller_id);
4216 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
4217 v3s16 blockpos_max, bool load_if_inexistent)
4219 TimeTaker timer1("initialEmerge", &emerge_time);
4221 // Units of these are MapBlocks
4222 v3s16 p_min = blockpos_min;
4223 v3s16 p_max = blockpos_max;
4225 VoxelArea block_area_nodes
4226 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4228 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4231 infostream<<"initialEmerge: area: ";
4232 block_area_nodes.print(infostream);
4233 infostream<<" ("<<size_MB<<"MB)";
4234 infostream<<std::endl;
4237 addArea(block_area_nodes);
4239 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4240 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4241 for(s32 x=p_min.X; x<=p_max.X; x++)
4246 std::map<v3s16, u8>::iterator n;
4247 n = m_loaded_blocks.find(p);
4248 if(n != m_loaded_blocks.end())
4251 bool block_data_inexistent = false;
4254 TimeTaker timer1("emerge load", &emerge_load_time);
4256 block = m_map->getBlockNoCreate(p);
4257 if(block->isDummy())
4258 block_data_inexistent = true;
4260 block->copyTo(*this);
4262 catch(InvalidPositionException &e)
4264 block_data_inexistent = true;
4267 if(block_data_inexistent)
4270 if (load_if_inexistent) {
4271 ServerMap *svrmap = (ServerMap *)m_map;
4272 block = svrmap->emergeBlock(p, false);
4274 block = svrmap->createBlock(p);
4276 block->copyTo(*this);
4278 flags |= VMANIP_BLOCK_DATA_INEXIST;
4281 Mark area inexistent
4283 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4284 // Fill with VOXELFLAG_INEXISTENT
4285 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4286 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4288 s32 i = m_area.index(a.MinEdge.X,y,z);
4289 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4293 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4295 // Mark that block was loaded as blank
4296 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4299 m_loaded_blocks[p] = flags;
4303 void ManualMapVoxelManipulator::blitBackAll(
4304 std::map<v3s16, MapBlock*> * modified_blocks)
4306 if(m_area.getExtent() == v3s16(0,0,0))
4310 Copy data of all blocks
4312 for(std::map<v3s16, u8>::iterator
4313 i = m_loaded_blocks.begin();
4314 i != m_loaded_blocks.end(); ++i)
4317 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4318 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
4319 if(existed == false)
4324 block->copyFrom(*this);
4327 (*modified_blocks)[p] = block;