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"
39 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
42 SQLite format specification:
43 - Initially only replaces sectors/ and sectors2/
45 If map.sqlite does not exist in the save dir
46 or the block was not found in the database
47 the map will try to load from sectors folder.
48 In either case, map.sqlite will be created
49 and all future saves will save there.
51 Structure of map.sqlite:
62 Map::Map(std::ostream &dout, IGameDef *gamedef):
67 /*m_sector_mutex.Init();
68 assert(m_sector_mutex.IsInitialized());*/
76 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
77 for(; i.atEnd() == false; i++)
79 MapSector *sector = i.getNode()->getValue();
84 void Map::addEventReceiver(MapEventReceiver *event_receiver)
86 m_event_receivers.insert(event_receiver, false);
89 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
91 if(m_event_receivers.find(event_receiver) == NULL)
93 m_event_receivers.remove(event_receiver);
96 void Map::dispatchEvent(MapEditEvent *event)
98 for(core::map<MapEventReceiver*, bool>::Iterator
99 i = m_event_receivers.getIterator();
100 i.atEnd()==false; i++)
102 MapEventReceiver* event_receiver = i.getNode()->getKey();
103 event_receiver->onMapEditEvent(event);
107 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
109 if(m_sector_cache != NULL && p == m_sector_cache_p){
110 MapSector * sector = m_sector_cache;
114 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
119 MapSector *sector = n->getValue();
121 // Cache the last result
122 m_sector_cache_p = p;
123 m_sector_cache = sector;
128 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
130 return getSectorNoGenerateNoExNoLock(p);
133 MapSector * Map::getSectorNoGenerate(v2s16 p)
135 MapSector *sector = getSectorNoGenerateNoEx(p);
137 throw InvalidPositionException();
142 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
144 v2s16 p2d(p3d.X, p3d.Z);
145 MapSector * sector = getSectorNoGenerateNoEx(p2d);
148 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
152 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
154 MapBlock *block = getBlockNoCreateNoEx(p3d);
156 throw InvalidPositionException();
160 bool Map::isNodeUnderground(v3s16 p)
162 v3s16 blockpos = getNodeBlockPos(p);
164 MapBlock * block = getBlockNoCreate(blockpos);
165 return block->getIsUnderground();
167 catch(InvalidPositionException &e)
173 bool Map::isValidPosition(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock *block = getBlockNoCreate(blockpos);
177 return (block != NULL);
180 // Returns a CONTENT_IGNORE node if not found
181 MapNode Map::getNodeNoEx(v3s16 p)
183 v3s16 blockpos = getNodeBlockPos(p);
184 MapBlock *block = getBlockNoCreateNoEx(blockpos);
186 return MapNode(CONTENT_IGNORE);
187 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
188 return block->getNodeNoCheck(relpos);
191 // throws InvalidPositionException if not found
192 MapNode Map::getNode(v3s16 p)
194 v3s16 blockpos = getNodeBlockPos(p);
195 MapBlock *block = getBlockNoCreateNoEx(blockpos);
197 throw InvalidPositionException();
198 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
199 return block->getNodeNoCheck(relpos);
202 // throws InvalidPositionException if not found
203 void Map::setNode(v3s16 p, MapNode & n)
205 v3s16 blockpos = getNodeBlockPos(p);
206 MapBlock *block = getBlockNoCreate(blockpos);
207 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
208 // Never allow placing CONTENT_IGNORE, it fucks up stuff
209 if(n.getContent() == CONTENT_IGNORE){
210 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
211 <<" while trying to replace \""
212 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
213 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
214 debug_stacks_print_to(infostream);
217 block->setNodeNoCheck(relpos, n);
222 Goes recursively through the neighbours of the node.
224 Alters only transparent nodes.
226 If the lighting of the neighbour is lower than the lighting of
227 the node was (before changing it to 0 at the step before), the
228 lighting of the neighbour is set to 0 and then the same stuff
229 repeats for the neighbour.
231 The ending nodes of the routine are stored in light_sources.
232 This is useful when a light is removed. In such case, this
233 routine can be called for the light node and then again for
234 light_sources to re-light the area without the removed light.
236 values of from_nodes are lighting values.
238 void Map::unspreadLight(enum LightBank bank,
239 core::map<v3s16, u8> & from_nodes,
240 core::map<v3s16, bool> & light_sources,
241 core::map<v3s16, MapBlock*> & modified_blocks)
243 INodeDefManager *nodemgr = m_gamedef->ndef();
246 v3s16(0,0,1), // back
248 v3s16(1,0,0), // right
249 v3s16(0,0,-1), // front
250 v3s16(0,-1,0), // bottom
251 v3s16(-1,0,0), // left
254 if(from_nodes.size() == 0)
257 u32 blockchangecount = 0;
259 core::map<v3s16, u8> unlighted_nodes;
260 core::map<v3s16, u8>::Iterator j;
261 j = from_nodes.getIterator();
264 Initialize block cache
267 MapBlock *block = NULL;
268 // Cache this a bit, too
269 bool block_checked_in_modified = false;
271 for(; j.atEnd() == false; j++)
273 v3s16 pos = j.getNode()->getKey();
274 v3s16 blockpos = getNodeBlockPos(pos);
276 // Only fetch a new block if the block position has changed
278 if(block == NULL || blockpos != blockpos_last){
279 block = getBlockNoCreate(blockpos);
280 blockpos_last = blockpos;
282 block_checked_in_modified = false;
286 catch(InvalidPositionException &e)
294 // Calculate relative position in block
295 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
297 // Get node straight from the block
298 MapNode n = block->getNode(relpos);
300 u8 oldlight = j.getNode()->getValue();
302 // Loop through 6 neighbors
303 for(u16 i=0; i<6; i++)
305 // Get the position of the neighbor node
306 v3s16 n2pos = pos + dirs[i];
308 // Get the block where the node is located
309 v3s16 blockpos = getNodeBlockPos(n2pos);
313 // Only fetch a new block if the block position has changed
315 if(block == NULL || blockpos != blockpos_last){
316 block = getBlockNoCreate(blockpos);
317 blockpos_last = blockpos;
319 block_checked_in_modified = false;
323 catch(InvalidPositionException &e)
328 // Calculate relative position in block
329 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
330 // Get node straight from the block
331 MapNode n2 = block->getNode(relpos);
333 bool changed = false;
335 //TODO: Optimize output by optimizing light_sources?
338 If the neighbor is dimmer than what was specified
339 as oldlight (the light of the previous node)
341 if(n2.getLight(bank, nodemgr) < oldlight)
344 And the neighbor is transparent and it has some light
346 if(nodemgr->get(n2).light_propagates
347 && n2.getLight(bank, nodemgr) != 0)
350 Set light to 0 and add to queue
353 u8 current_light = n2.getLight(bank, nodemgr);
354 n2.setLight(bank, 0, nodemgr);
355 block->setNode(relpos, n2);
357 unlighted_nodes.insert(n2pos, current_light);
361 Remove from light_sources if it is there
362 NOTE: This doesn't happen nearly at all
364 /*if(light_sources.find(n2pos))
366 infostream<<"Removed from light_sources"<<std::endl;
367 light_sources.remove(n2pos);
372 if(light_sources.find(n2pos) != NULL)
373 light_sources.remove(n2pos);*/
376 light_sources.insert(n2pos, true);
379 // Add to modified_blocks
380 if(changed == true && block_checked_in_modified == false)
382 // If the block is not found in modified_blocks, add.
383 if(modified_blocks.find(blockpos) == NULL)
385 modified_blocks.insert(blockpos, block);
387 block_checked_in_modified = true;
390 catch(InvalidPositionException &e)
397 /*infostream<<"unspreadLight(): Changed block "
398 <<blockchangecount<<" times"
399 <<" for "<<from_nodes.size()<<" nodes"
402 if(unlighted_nodes.size() > 0)
403 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
407 A single-node wrapper of the above
409 void Map::unLightNeighbors(enum LightBank bank,
410 v3s16 pos, u8 lightwas,
411 core::map<v3s16, bool> & light_sources,
412 core::map<v3s16, MapBlock*> & modified_blocks)
414 core::map<v3s16, u8> from_nodes;
415 from_nodes.insert(pos, lightwas);
417 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
421 Lights neighbors of from_nodes, collects all them and then
424 void Map::spreadLight(enum LightBank bank,
425 core::map<v3s16, bool> & from_nodes,
426 core::map<v3s16, MapBlock*> & modified_blocks)
428 INodeDefManager *nodemgr = m_gamedef->ndef();
430 const v3s16 dirs[6] = {
431 v3s16(0,0,1), // back
433 v3s16(1,0,0), // right
434 v3s16(0,0,-1), // front
435 v3s16(0,-1,0), // bottom
436 v3s16(-1,0,0), // left
439 if(from_nodes.size() == 0)
442 u32 blockchangecount = 0;
444 core::map<v3s16, bool> lighted_nodes;
445 core::map<v3s16, bool>::Iterator j;
446 j = from_nodes.getIterator();
449 Initialize block cache
452 MapBlock *block = NULL;
453 // Cache this a bit, too
454 bool block_checked_in_modified = false;
456 for(; j.atEnd() == false; j++)
457 //for(; j != from_nodes.end(); j++)
459 v3s16 pos = j.getNode()->getKey();
461 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
462 v3s16 blockpos = getNodeBlockPos(pos);
464 // Only fetch a new block if the block position has changed
466 if(block == NULL || blockpos != blockpos_last){
467 block = getBlockNoCreate(blockpos);
468 blockpos_last = blockpos;
470 block_checked_in_modified = false;
474 catch(InvalidPositionException &e)
482 // Calculate relative position in block
483 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
485 // Get node straight from the block
486 MapNode n = block->getNode(relpos);
488 u8 oldlight = n.getLight(bank, nodemgr);
489 u8 newlight = diminish_light(oldlight);
491 // Loop through 6 neighbors
492 for(u16 i=0; i<6; i++){
493 // Get the position of the neighbor node
494 v3s16 n2pos = pos + dirs[i];
496 // Get the block where the node is located
497 v3s16 blockpos = getNodeBlockPos(n2pos);
501 // Only fetch a new block if the block position has changed
503 if(block == NULL || blockpos != blockpos_last){
504 block = getBlockNoCreate(blockpos);
505 blockpos_last = blockpos;
507 block_checked_in_modified = false;
511 catch(InvalidPositionException &e)
516 // Calculate relative position in block
517 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
518 // Get node straight from the block
519 MapNode n2 = block->getNode(relpos);
521 bool changed = false;
523 If the neighbor is brighter than the current node,
524 add to list (it will light up this node on its turn)
526 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
528 lighted_nodes.insert(n2pos, true);
529 //lighted_nodes.push_back(n2pos);
533 If the neighbor is dimmer than how much light this node
534 would spread on it, add to list
536 if(n2.getLight(bank, nodemgr) < newlight)
538 if(nodemgr->get(n2).light_propagates)
540 n2.setLight(bank, newlight, nodemgr);
541 block->setNode(relpos, n2);
542 lighted_nodes.insert(n2pos, true);
543 //lighted_nodes.push_back(n2pos);
548 // Add to modified_blocks
549 if(changed == true && block_checked_in_modified == false)
551 // If the block is not found in modified_blocks, add.
552 if(modified_blocks.find(blockpos) == NULL)
554 modified_blocks.insert(blockpos, block);
556 block_checked_in_modified = true;
559 catch(InvalidPositionException &e)
566 /*infostream<<"spreadLight(): Changed block "
567 <<blockchangecount<<" times"
568 <<" for "<<from_nodes.size()<<" nodes"
571 if(lighted_nodes.size() > 0)
572 spreadLight(bank, lighted_nodes, modified_blocks);
576 A single-node source variation of the above.
578 void Map::lightNeighbors(enum LightBank bank,
580 core::map<v3s16, MapBlock*> & modified_blocks)
582 core::map<v3s16, bool> from_nodes;
583 from_nodes.insert(pos, true);
584 spreadLight(bank, from_nodes, modified_blocks);
587 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
589 INodeDefManager *nodemgr = m_gamedef->ndef();
592 v3s16(0,0,1), // back
594 v3s16(1,0,0), // right
595 v3s16(0,0,-1), // front
596 v3s16(0,-1,0), // bottom
597 v3s16(-1,0,0), // left
600 u8 brightest_light = 0;
601 v3s16 brightest_pos(0,0,0);
602 bool found_something = false;
604 // Loop through 6 neighbors
605 for(u16 i=0; i<6; i++){
606 // Get the position of the neighbor node
607 v3s16 n2pos = p + dirs[i];
612 catch(InvalidPositionException &e)
616 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
617 brightest_light = n2.getLight(bank, nodemgr);
618 brightest_pos = n2pos;
619 found_something = true;
623 if(found_something == false)
624 throw InvalidPositionException();
626 return brightest_pos;
630 Propagates sunlight down from a node.
631 Starting point gets sunlight.
633 Returns the lowest y value of where the sunlight went.
635 Mud is turned into grass in where the sunlight stops.
637 s16 Map::propagateSunlight(v3s16 start,
638 core::map<v3s16, MapBlock*> & modified_blocks)
640 INodeDefManager *nodemgr = m_gamedef->ndef();
645 v3s16 pos(start.X, y, start.Z);
647 v3s16 blockpos = getNodeBlockPos(pos);
650 block = getBlockNoCreate(blockpos);
652 catch(InvalidPositionException &e)
657 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
658 MapNode n = block->getNode(relpos);
660 if(nodemgr->get(n).sunlight_propagates)
662 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
663 block->setNode(relpos, n);
665 modified_blocks.insert(blockpos, block);
669 // Sunlight goes no further
676 void Map::updateLighting(enum LightBank bank,
677 core::map<v3s16, MapBlock*> & a_blocks,
678 core::map<v3s16, MapBlock*> & modified_blocks)
680 INodeDefManager *nodemgr = m_gamedef->ndef();
682 /*m_dout<<DTIME<<"Map::updateLighting(): "
683 <<a_blocks.size()<<" blocks."<<std::endl;*/
685 //TimeTaker timer("updateLighting");
689 //u32 count_was = modified_blocks.size();
691 core::map<v3s16, MapBlock*> blocks_to_update;
693 core::map<v3s16, bool> light_sources;
695 core::map<v3s16, u8> unlight_from;
697 int num_bottom_invalid = 0;
700 //TimeTaker t("first stuff");
702 core::map<v3s16, MapBlock*>::Iterator i;
703 i = a_blocks.getIterator();
704 for(; i.atEnd() == false; i++)
706 MapBlock *block = i.getNode()->getValue();
710 // Don't bother with dummy blocks.
714 v3s16 pos = block->getPos();
715 v3s16 posnodes = block->getPosRelative();
716 modified_blocks.insert(pos, block);
718 blocks_to_update.insert(pos, block);
721 Clear all light from block
723 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
724 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
725 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
730 MapNode n = block->getNode(p);
731 u8 oldlight = n.getLight(bank, nodemgr);
732 n.setLight(bank, 0, nodemgr);
733 block->setNode(p, n);
735 // If node sources light, add to list
736 u8 source = nodemgr->get(n).light_source;
738 light_sources[p + posnodes] = true;
740 // Collect borders for unlighting
741 if((x==0 || x == MAP_BLOCKSIZE-1
742 || y==0 || y == MAP_BLOCKSIZE-1
743 || z==0 || z == MAP_BLOCKSIZE-1)
746 v3s16 p_map = p + posnodes;
747 unlight_from.insert(p_map, oldlight);
750 catch(InvalidPositionException &e)
753 This would happen when dealing with a
757 infostream<<"updateLighting(): InvalidPositionException"
762 if(bank == LIGHTBANK_DAY)
764 bool bottom_valid = block->propagateSunlight(light_sources);
767 num_bottom_invalid++;
769 // If bottom is valid, we're done.
773 else if(bank == LIGHTBANK_NIGHT)
775 // For night lighting, sunlight is not propagated
780 // Invalid lighting bank
784 /*infostream<<"Bottom for sunlight-propagated block ("
785 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
788 // Bottom sunlight is not valid; get the block and loop to it
792 block = getBlockNoCreate(pos);
794 catch(InvalidPositionException &e)
805 Enable this to disable proper lighting for speeding up map
806 generation for testing or whatever
809 //if(g_settings->get(""))
811 core::map<v3s16, MapBlock*>::Iterator i;
812 i = blocks_to_update.getIterator();
813 for(; i.atEnd() == false; i++)
815 MapBlock *block = i.getNode()->getValue();
816 v3s16 p = block->getPos();
817 block->setLightingExpired(false);
825 //TimeTaker timer("unspreadLight");
826 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
831 u32 diff = modified_blocks.size() - count_was;
832 count_was = modified_blocks.size();
833 infostream<<"unspreadLight modified "<<diff<<std::endl;
837 //TimeTaker timer("spreadLight");
838 spreadLight(bank, light_sources, modified_blocks);
843 u32 diff = modified_blocks.size() - count_was;
844 count_was = modified_blocks.size();
845 infostream<<"spreadLight modified "<<diff<<std::endl;
851 //MapVoxelManipulator vmanip(this);
853 // Make a manual voxel manipulator and load all the blocks
854 // that touch the requested blocks
855 ManualMapVoxelManipulator vmanip(this);
858 //TimeTaker timer("initialEmerge");
860 core::map<v3s16, MapBlock*>::Iterator i;
861 i = blocks_to_update.getIterator();
862 for(; i.atEnd() == false; i++)
864 MapBlock *block = i.getNode()->getValue();
865 v3s16 p = block->getPos();
867 // Add all surrounding blocks
868 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
871 Add all surrounding blocks that have up-to-date lighting
872 NOTE: This doesn't quite do the job (not everything
873 appropriate is lighted)
875 /*for(s16 z=-1; z<=1; z++)
876 for(s16 y=-1; y<=1; y++)
877 for(s16 x=-1; x<=1; x++)
879 v3s16 p2 = p + v3s16(x,y,z);
880 MapBlock *block = getBlockNoCreateNoEx(p2);
885 if(block->getLightingExpired())
887 vmanip.initialEmerge(p2, p2);
890 // Lighting of block will be updated completely
891 block->setLightingExpired(false);
896 //TimeTaker timer("unSpreadLight");
897 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
900 //TimeTaker timer("spreadLight");
901 vmanip.spreadLight(bank, light_sources, nodemgr);
904 //TimeTaker timer("blitBack");
905 vmanip.blitBack(modified_blocks);
907 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
912 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
915 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
916 core::map<v3s16, MapBlock*> & modified_blocks)
918 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
919 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
922 Update information about whether day and night light differ
924 for(core::map<v3s16, MapBlock*>::Iterator
925 i = modified_blocks.getIterator();
926 i.atEnd() == false; i++)
928 MapBlock *block = i.getNode()->getValue();
929 block->expireDayNightDiff();
935 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
936 core::map<v3s16, MapBlock*> &modified_blocks)
938 INodeDefManager *ndef = m_gamedef->ndef();
941 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
942 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
945 From this node to nodes underneath:
946 If lighting is sunlight (1.0), unlight neighbours and
951 v3s16 toppos = p + v3s16(0,1,0);
952 v3s16 bottompos = p + v3s16(0,-1,0);
954 bool node_under_sunlight = true;
955 core::map<v3s16, bool> light_sources;
958 Collect old node for rollback
960 RollbackNode rollback_oldnode(this, p, m_gamedef);
963 If there is a node at top and it doesn't have sunlight,
964 there has not been any sunlight going down.
966 Otherwise there probably is.
969 MapNode topnode = getNode(toppos);
971 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
972 node_under_sunlight = false;
974 catch(InvalidPositionException &e)
979 Remove all light that has come out of this node
982 enum LightBank banks[] =
987 for(s32 i=0; i<2; i++)
989 enum LightBank bank = banks[i];
991 u8 lightwas = getNode(p).getLight(bank, ndef);
993 // Add the block of the added node to modified_blocks
994 v3s16 blockpos = getNodeBlockPos(p);
995 MapBlock * block = getBlockNoCreate(blockpos);
996 assert(block != NULL);
997 modified_blocks.insert(blockpos, block);
999 assert(isValidPosition(p));
1001 // Unlight neighbours of node.
1002 // This means setting light of all consequent dimmer nodes
1004 // This also collects the nodes at the border which will spread
1005 // light again into this.
1006 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1008 n.setLight(bank, 0, ndef);
1012 If node lets sunlight through and is under sunlight, it has
1015 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1017 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1021 Remove node metadata
1024 removeNodeMetadata(p);
1027 Set the node on the map
1033 If node is under sunlight and doesn't let sunlight through,
1034 take all sunlighted nodes under it and clear light from them
1035 and from where the light has been spread.
1036 TODO: This could be optimized by mass-unlighting instead
1039 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1043 //m_dout<<DTIME<<"y="<<y<<std::endl;
1044 v3s16 n2pos(p.X, y, p.Z);
1048 n2 = getNode(n2pos);
1050 catch(InvalidPositionException &e)
1055 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1057 unLightNeighbors(LIGHTBANK_DAY,
1058 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1059 light_sources, modified_blocks);
1060 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1068 for(s32 i=0; i<2; i++)
1070 enum LightBank bank = banks[i];
1073 Spread light from all nodes that might be capable of doing so
1075 spreadLight(bank, light_sources, modified_blocks);
1079 Update information about whether day and night light differ
1081 for(core::map<v3s16, MapBlock*>::Iterator
1082 i = modified_blocks.getIterator();
1083 i.atEnd() == false; i++)
1085 MapBlock *block = i.getNode()->getValue();
1086 block->expireDayNightDiff();
1092 if(m_gamedef->rollback())
1094 RollbackNode rollback_newnode(this, p, m_gamedef);
1095 RollbackAction action;
1096 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1097 m_gamedef->rollback()->reportAction(action);
1101 Add neighboring liquid nodes and the node itself if it is
1102 liquid (=water node was added) to transform queue.
1105 v3s16(0,0,0), // self
1106 v3s16(0,0,1), // back
1107 v3s16(0,1,0), // top
1108 v3s16(1,0,0), // right
1109 v3s16(0,0,-1), // front
1110 v3s16(0,-1,0), // bottom
1111 v3s16(-1,0,0), // left
1113 for(u16 i=0; i<7; i++)
1118 v3s16 p2 = p + dirs[i];
1120 MapNode n2 = getNode(p2);
1121 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1123 m_transforming_liquid.push_back(p2);
1126 }catch(InvalidPositionException &e)
1134 void Map::removeNodeAndUpdate(v3s16 p,
1135 core::map<v3s16, MapBlock*> &modified_blocks)
1137 INodeDefManager *ndef = m_gamedef->ndef();
1139 /*PrintInfo(m_dout);
1140 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1141 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1143 bool node_under_sunlight = true;
1145 v3s16 toppos = p + v3s16(0,1,0);
1147 // Node will be replaced with this
1148 content_t replace_material = CONTENT_AIR;
1151 Collect old node for rollback
1153 RollbackNode rollback_oldnode(this, p, m_gamedef);
1156 If there is a node at top and it doesn't have sunlight,
1157 there will be no sunlight going down.
1160 MapNode topnode = getNode(toppos);
1162 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1163 node_under_sunlight = false;
1165 catch(InvalidPositionException &e)
1169 core::map<v3s16, bool> light_sources;
1171 enum LightBank banks[] =
1176 for(s32 i=0; i<2; i++)
1178 enum LightBank bank = banks[i];
1181 Unlight neighbors (in case the node is a light source)
1183 unLightNeighbors(bank, p,
1184 getNode(p).getLight(bank, ndef),
1185 light_sources, modified_blocks);
1189 Remove node metadata
1192 removeNodeMetadata(p);
1196 This also clears the lighting.
1200 n.setContent(replace_material);
1203 for(s32 i=0; i<2; i++)
1205 enum LightBank bank = banks[i];
1208 Recalculate lighting
1210 spreadLight(bank, light_sources, modified_blocks);
1213 // Add the block of the removed node to modified_blocks
1214 v3s16 blockpos = getNodeBlockPos(p);
1215 MapBlock * block = getBlockNoCreate(blockpos);
1216 assert(block != NULL);
1217 modified_blocks.insert(blockpos, block);
1220 If the removed node was under sunlight, propagate the
1221 sunlight down from it and then light all neighbors
1222 of the propagated blocks.
1224 if(node_under_sunlight)
1226 s16 ybottom = propagateSunlight(p, modified_blocks);
1227 /*m_dout<<DTIME<<"Node was under sunlight. "
1228 "Propagating sunlight";
1229 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1231 for(; y >= ybottom; y--)
1233 v3s16 p2(p.X, y, p.Z);
1234 /*m_dout<<DTIME<<"lighting neighbors of node ("
1235 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1237 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1242 // Set the lighting of this node to 0
1243 // TODO: Is this needed? Lighting is cleared up there already.
1245 MapNode n = getNode(p);
1246 n.setLight(LIGHTBANK_DAY, 0, ndef);
1249 catch(InvalidPositionException &e)
1255 for(s32 i=0; i<2; i++)
1257 enum LightBank bank = banks[i];
1259 // Get the brightest neighbour node and propagate light from it
1260 v3s16 n2p = getBrightestNeighbour(bank, p);
1262 MapNode n2 = getNode(n2p);
1263 lightNeighbors(bank, n2p, modified_blocks);
1265 catch(InvalidPositionException &e)
1271 Update information about whether day and night light differ
1273 for(core::map<v3s16, MapBlock*>::Iterator
1274 i = modified_blocks.getIterator();
1275 i.atEnd() == false; i++)
1277 MapBlock *block = i.getNode()->getValue();
1278 block->expireDayNightDiff();
1284 if(m_gamedef->rollback())
1286 RollbackNode rollback_newnode(this, p, m_gamedef);
1287 RollbackAction action;
1288 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1289 m_gamedef->rollback()->reportAction(action);
1293 Add neighboring liquid nodes and this node to transform queue.
1294 (it's vital for the node itself to get updated last.)
1297 v3s16(0,0,1), // back
1298 v3s16(0,1,0), // top
1299 v3s16(1,0,0), // right
1300 v3s16(0,0,-1), // front
1301 v3s16(0,-1,0), // bottom
1302 v3s16(-1,0,0), // left
1303 v3s16(0,0,0), // self
1305 for(u16 i=0; i<7; i++)
1310 v3s16 p2 = p + dirs[i];
1312 MapNode n2 = getNode(p2);
1313 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1315 m_transforming_liquid.push_back(p2);
1318 }catch(InvalidPositionException &e)
1324 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1327 event.type = MEET_ADDNODE;
1331 bool succeeded = true;
1333 core::map<v3s16, MapBlock*> modified_blocks;
1334 addNodeAndUpdate(p, n, modified_blocks);
1336 // Copy modified_blocks to event
1337 for(core::map<v3s16, MapBlock*>::Iterator
1338 i = modified_blocks.getIterator();
1339 i.atEnd()==false; i++)
1341 event.modified_blocks.insert(i.getNode()->getKey(), false);
1344 catch(InvalidPositionException &e){
1348 dispatchEvent(&event);
1353 bool Map::removeNodeWithEvent(v3s16 p)
1356 event.type = MEET_REMOVENODE;
1359 bool succeeded = true;
1361 core::map<v3s16, MapBlock*> modified_blocks;
1362 removeNodeAndUpdate(p, modified_blocks);
1364 // Copy modified_blocks to event
1365 for(core::map<v3s16, MapBlock*>::Iterator
1366 i = modified_blocks.getIterator();
1367 i.atEnd()==false; i++)
1369 event.modified_blocks.insert(i.getNode()->getKey(), false);
1372 catch(InvalidPositionException &e){
1376 dispatchEvent(&event);
1381 bool Map::getDayNightDiff(v3s16 blockpos)
1384 v3s16 p = blockpos + v3s16(0,0,0);
1385 MapBlock *b = getBlockNoCreate(p);
1386 if(b->getDayNightDiff())
1389 catch(InvalidPositionException &e){}
1392 v3s16 p = blockpos + v3s16(-1,0,0);
1393 MapBlock *b = getBlockNoCreate(p);
1394 if(b->getDayNightDiff())
1397 catch(InvalidPositionException &e){}
1399 v3s16 p = blockpos + v3s16(0,-1,0);
1400 MapBlock *b = getBlockNoCreate(p);
1401 if(b->getDayNightDiff())
1404 catch(InvalidPositionException &e){}
1406 v3s16 p = blockpos + v3s16(0,0,-1);
1407 MapBlock *b = getBlockNoCreate(p);
1408 if(b->getDayNightDiff())
1411 catch(InvalidPositionException &e){}
1414 v3s16 p = blockpos + v3s16(1,0,0);
1415 MapBlock *b = getBlockNoCreate(p);
1416 if(b->getDayNightDiff())
1419 catch(InvalidPositionException &e){}
1421 v3s16 p = blockpos + v3s16(0,1,0);
1422 MapBlock *b = getBlockNoCreate(p);
1423 if(b->getDayNightDiff())
1426 catch(InvalidPositionException &e){}
1428 v3s16 p = blockpos + v3s16(0,0,1);
1429 MapBlock *b = getBlockNoCreate(p);
1430 if(b->getDayNightDiff())
1433 catch(InvalidPositionException &e){}
1439 Updates usage timers
1441 void Map::timerUpdate(float dtime, float unload_timeout,
1442 core::list<v3s16> *unloaded_blocks)
1444 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1446 // Profile modified reasons
1447 Profiler modprofiler;
1449 core::list<v2s16> sector_deletion_queue;
1450 u32 deleted_blocks_count = 0;
1451 u32 saved_blocks_count = 0;
1452 u32 block_count_all = 0;
1454 core::map<v2s16, MapSector*>::Iterator si;
1457 si = m_sectors.getIterator();
1458 for(; si.atEnd() == false; si++)
1460 MapSector *sector = si.getNode()->getValue();
1462 bool all_blocks_deleted = true;
1464 core::list<MapBlock*> blocks;
1465 sector->getBlocks(blocks);
1467 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1468 i != blocks.end(); i++)
1470 MapBlock *block = (*i);
1472 block->incrementUsageTimer(dtime);
1474 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1476 v3s16 p = block->getPos();
1479 if(block->getModified() != MOD_STATE_CLEAN
1480 && save_before_unloading)
1482 modprofiler.add(block->getModifiedReason(), 1);
1484 saved_blocks_count++;
1487 // Delete from memory
1488 sector->deleteBlock(block);
1491 unloaded_blocks->push_back(p);
1493 deleted_blocks_count++;
1497 all_blocks_deleted = false;
1502 if(all_blocks_deleted)
1504 sector_deletion_queue.push_back(si.getNode()->getKey());
1509 // Finally delete the empty sectors
1510 deleteSectors(sector_deletion_queue);
1512 if(deleted_blocks_count != 0)
1514 PrintInfo(infostream); // ServerMap/ClientMap:
1515 infostream<<"Unloaded "<<deleted_blocks_count
1516 <<" blocks from memory";
1517 if(save_before_unloading)
1518 infostream<<", of which "<<saved_blocks_count<<" were written";
1519 infostream<<", "<<block_count_all<<" blocks in memory";
1520 infostream<<"."<<std::endl;
1521 if(saved_blocks_count != 0){
1522 PrintInfo(infostream); // ServerMap/ClientMap:
1523 infostream<<"Blocks modified by: "<<std::endl;
1524 modprofiler.print(infostream);
1529 void Map::deleteSectors(core::list<v2s16> &list)
1531 core::list<v2s16>::Iterator j;
1532 for(j=list.begin(); j!=list.end(); j++)
1534 MapSector *sector = m_sectors[*j];
1535 // If sector is in sector cache, remove it from there
1536 if(m_sector_cache == sector)
1537 m_sector_cache = NULL;
1538 // Remove from map and delete
1539 m_sectors.remove(*j);
1545 void Map::unloadUnusedData(float timeout,
1546 core::list<v3s16> *deleted_blocks)
1548 core::list<v2s16> sector_deletion_queue;
1549 u32 deleted_blocks_count = 0;
1550 u32 saved_blocks_count = 0;
1552 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1553 for(; si.atEnd() == false; si++)
1555 MapSector *sector = si.getNode()->getValue();
1557 bool all_blocks_deleted = true;
1559 core::list<MapBlock*> blocks;
1560 sector->getBlocks(blocks);
1561 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1562 i != blocks.end(); i++)
1564 MapBlock *block = (*i);
1566 if(block->getUsageTimer() > timeout)
1569 if(block->getModified() != MOD_STATE_CLEAN)
1572 saved_blocks_count++;
1574 // Delete from memory
1575 sector->deleteBlock(block);
1576 deleted_blocks_count++;
1580 all_blocks_deleted = false;
1584 if(all_blocks_deleted)
1586 sector_deletion_queue.push_back(si.getNode()->getKey());
1590 deleteSectors(sector_deletion_queue);
1592 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1593 <<", of which "<<saved_blocks_count<<" were wr."
1596 //return sector_deletion_queue.getSize();
1597 //return deleted_blocks_count;
1601 void Map::PrintInfo(std::ostream &out)
1606 #define WATER_DROP_BOOST 4
1610 NEIGHBOR_SAME_LEVEL,
1613 struct NodeNeighbor {
1617 bool l; //can liquid
1621 void Map::transforming_liquid_add(v3s16 p) {
1622 m_transforming_liquid.push_back(p);
1625 s32 Map::transforming_liquid_size() {
1626 return m_transforming_liquid.size();
1629 const v3s16 g_7dirs[7] =
1631 // +right, +top, +back
1632 v3s16( 0,-1, 0), // bottom
1633 v3s16( 0, 0, 0), // self
1634 v3s16( 0, 0, 1), // back
1635 v3s16( 0, 0,-1), // front
1636 v3s16( 1, 0, 0), // right
1637 v3s16(-1, 0, 0), // left
1638 v3s16( 0, 1, 0) // top
1645 void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
1647 INodeDefManager *nodemgr = m_gamedef->ndef();
1649 DSTACK(__FUNCTION_NAME);
1650 //TimeTaker timer("transformLiquids()");
1653 u32 initial_size = m_transforming_liquid.size();
1655 u8 relax = g_settings->getS16("liquid_relax");
1656 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1657 int water_level = g_settings->getS16("water_level");
1659 /*if(initial_size != 0)
1660 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1662 // list of nodes that due to viscosity have not reached their max level height
1663 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1665 // List of MapBlocks that will require a lighting update (due to lava)
1666 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1668 while(m_transforming_liquid.size() > 0)
1670 // This should be done here so that it is done when continue is used
1671 if(loopcount >= initial_size || loopcount >= 1000)
1675 Get a queued transforming liquid node
1677 v3s16 p0 = m_transforming_liquid.pop_front();
1678 u16 total_level = 0;
1679 NodeNeighbor neighbors[7]; // surrounding flowing liquid nodes
1680 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1}; // current level of every block
1681 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1}; // target levels
1682 s8 can_liquid_same_level = 0;
1683 content_t liquid_kind = CONTENT_IGNORE;
1684 content_t liquid_kind_flowing = CONTENT_IGNORE;
1686 Collect information about the environment
1688 const v3s16 *dirs = g_7dirs;
1689 for (u16 i = 0; i < 7; i++) {
1690 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1693 nt = NEIGHBOR_UPPER;
1696 nt = NEIGHBOR_LOWER;
1699 v3s16 npos = p0 + dirs[i];
1701 neighbors[i].n = getNodeNoEx(npos);
1702 neighbors[i].t = nt;
1703 neighbors[i].p = npos;
1706 NodeNeighbor & nb = neighbors[i];
1708 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1710 if (nb.n.getContent() == CONTENT_AIR) {
1711 liquid_levels[i] = 0;
1716 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1717 if (liquid_kind_flowing == CONTENT_IGNORE)
1718 liquid_kind_flowing = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1719 if (liquid_kind == CONTENT_IGNORE)
1720 liquid_kind = nb.n.getContent();
1721 if (nb.n.getContent() == liquid_kind) {
1722 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1724 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1727 case LIQUID_FLOWING:
1728 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1729 if (liquid_kind_flowing == CONTENT_IGNORE)
1730 liquid_kind_flowing = nb.n.getContent();
1731 if (liquid_kind == CONTENT_IGNORE)
1732 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_source);
1733 if (nb.n.getContent() == liquid_kind_flowing) {
1734 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1739 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL) ++can_liquid_same_level;
1740 if (liquid_levels[i] > 0) total_level += liquid_levels[i];
1743 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="<<nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="<< (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="<<nodemgr->get(nb.n.getContent()).liquid_type
1744 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1745 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i] << " tlevel=" << (int)total_level << " cansame="<<(int)can_liquid_same_level<<std::endl;
1749 if (liquid_kind == CONTENT_IGNORE || !neighbors[D_SELF].l || total_level <= 0)
1752 // fill bottom block
1753 if (neighbors[D_BOTTOM].l) {
1754 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level;
1755 total_level -= liquid_levels_want[D_BOTTOM];
1758 if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 && total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level - can_liquid_same_level + 2 && can_liquid_same_level >= relax + 1) { //relax up
1759 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1762 // calculate self level 5 blocks
1764 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1765 ? LIQUID_LEVEL_SOURCE
1766 : total_level / can_liquid_same_level;
1767 total_level -= want_level * can_liquid_same_level;
1769 if (relax && p0.Y > water_level && liquid_levels[D_TOP] == 0 && liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 && total_level <= can_liquid_same_level - 2 && can_liquid_same_level >= relax + 1) { //relax down
1773 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1774 if (!neighbors[ii].l)
1776 liquid_levels_want[ii] = want_level;
1777 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0
1778 && liquid_levels[ii] > liquid_levels_want[ii]
1780 ++liquid_levels_want[ii];
1785 for (u16 ii = 0; ii < 7; ++ii) {
1786 if (total_level < 1) break;
1787 if (liquid_levels_want[ii] >= 0 && liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1788 ++liquid_levels_want[ii];
1793 // fill top block if can
1794 if (neighbors[D_TOP].l) {
1795 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level ;
1796 total_level -= liquid_levels_want[D_TOP];
1799 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1800 if (liquid_levels_want[ii] >= 0 &&
1802 (fast_flood && p0.Y < water_level &&
1803 (initial_size >= 1000
1805 && want_level >= LIQUID_LEVEL_SOURCE/4
1806 && can_liquid_same_level >= 5
1807 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1808 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1810 //if (total_level > 0 /*|| flowed != volume*/) infostream <<" AFTER level=" << (int)total_level /*<< " flowed="<<flowed<< " volume=" <<volume*/<< " wantsame="<<(int)want_level<< " top="<< (int)liquid_levels_want[D_TOP]<< " topwas="<< (int)liquid_levels[D_TOP]<< " bot="<< (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1813 for (u16 i = 0; i < 7; i++) {
1814 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1816 MapNode & n0 = neighbors[i].n;
1817 p0 = neighbors[i].p;
1819 decide on the type (and possibly level) of the current node
1821 content_t new_node_content;
1822 s8 new_node_level = -1;
1823 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1824 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1825 // amount to gain, limited by viscosity
1826 // must be at least 1 in absolute value
1827 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1828 if (level_inc < -viscosity || level_inc > viscosity)
1829 new_node_level = liquid_levels[i] + level_inc/viscosity;
1830 else if (level_inc < 0)
1831 new_node_level = liquid_levels[i] - 1;
1832 else if (level_inc > 0)
1833 new_node_level = liquid_levels[i] + 1;
1835 new_node_level = liquid_levels_want[i];
1836 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1837 new_node_content = liquid_kind;
1838 else if (new_node_level > 0)
1839 new_node_content = liquid_kind_flowing;
1841 new_node_content = CONTENT_AIR;
1843 // last level must flow down on stairs
1844 if (liquid_levels_want[i] != liquid_levels[i] && liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l && new_node_level >= 1 && new_node_level <= 2) //maybe == 1 //
1845 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1846 if (!neighbors[ii].l)
1848 must_reflow_second.push_back(p0 + dirs[ii]);
1852 check if anything has changed. if not, just continue with the next node.
1855 new_node_content == n0.getContent()
1856 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1857 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1858 // &&((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)== flowing_down
1861 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1862 (((n0.param2 & LIQUID_INFINITY_MASK) == LIQUID_INFINITY_MASK) == neighbors[i].i
1870 update the current node
1872 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1873 // set level to last 3 bits, flowing down bit to 4th bit
1874 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1875 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1876 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1877 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1879 //infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="<<new_node_content<< " p2="<<(int)n0.param2<< " nl="<<(int)new_node_level<<std::endl;
1880 n0.setContent(new_node_content);
1881 // Find out whether there is a suspect for this action
1882 std::string suspect;
1883 if(m_gamedef->rollback()){
1884 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1887 if(!suspect.empty()){
1889 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1890 // Get old node for rollback
1891 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1895 RollbackNode rollback_newnode(this, p0, m_gamedef);
1896 RollbackAction action;
1897 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1898 m_gamedef->rollback()->reportAction(action);
1904 v3s16 blockpos = getNodeBlockPos(p0);
1905 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1907 modified_blocks.insert(blockpos, block);
1908 // If node emits light, MapBlock requires lighting update
1909 if(nodemgr->get(n0).light_source != 0)
1910 lighting_modified_blocks[block->getPos()] = block;
1912 must_reflow.push_back(neighbors[i].p);
1914 /* //for better relax
1915 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1916 if (!neighbors[ii].l) continue;
1917 must_reflow.push_back(p0 + dirs[ii]);
1920 //if (loopcount) infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<" reflow="<<must_reflow.size()<<" queue="<< m_transforming_liquid.size()<<std::endl;
1921 while (must_reflow.size() > 0)
1922 m_transforming_liquid.push_back(must_reflow.pop_front());
1923 while (must_reflow_second.size() > 0)
1924 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1925 updateLighting(lighting_modified_blocks, modified_blocks);
1928 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1931 if (g_settings->getBool("liquid_finite")) return Map::transformLiquidsFinite(modified_blocks);
1933 INodeDefManager *nodemgr = m_gamedef->ndef();
1935 DSTACK(__FUNCTION_NAME);
1936 //TimeTaker timer("transformLiquids()");
1939 u32 initial_size = m_transforming_liquid.size();
1941 /*if(initial_size != 0)
1942 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1944 // list of nodes that due to viscosity have not reached their max level height
1945 UniqueQueue<v3s16> must_reflow;
1947 // List of MapBlocks that will require a lighting update (due to lava)
1948 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1950 while(m_transforming_liquid.size() != 0)
1952 // This should be done here so that it is done when continue is used
1953 if(loopcount >= initial_size || loopcount >= 10000)
1958 Get a queued transforming liquid node
1960 v3s16 p0 = m_transforming_liquid.pop_front();
1962 MapNode n0 = getNodeNoEx(p0);
1965 Collect information about current node
1967 s8 liquid_level = -1;
1968 content_t liquid_kind = CONTENT_IGNORE;
1969 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1970 switch (liquid_type) {
1972 liquid_level = LIQUID_LEVEL_SOURCE;
1973 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1975 case LIQUID_FLOWING:
1976 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1977 liquid_kind = n0.getContent();
1980 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1981 // continue with the next node.
1982 if (n0.getContent() != CONTENT_AIR)
1984 liquid_kind = CONTENT_AIR;
1989 Collect information about the environment
1991 const v3s16 *dirs = g_6dirs;
1992 NodeNeighbor sources[6]; // surrounding sources
1993 int num_sources = 0;
1994 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1996 NodeNeighbor airs[6]; // surrounding air
1998 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1999 int num_neutrals = 0;
2000 bool flowing_down = false;
2001 for (u16 i = 0; i < 6; i++) {
2002 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2005 nt = NEIGHBOR_UPPER;
2008 nt = NEIGHBOR_LOWER;
2011 v3s16 npos = p0 + dirs[i];
2012 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2013 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2015 if (nb.n.getContent() == CONTENT_AIR) {
2016 airs[num_airs++] = nb;
2017 // if the current node is a water source the neighbor
2018 // should be enqueded for transformation regardless of whether the
2019 // current node changes or not.
2020 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2021 m_transforming_liquid.push_back(npos);
2022 // if the current node happens to be a flowing node, it will start to flow down here.
2023 if (nb.t == NEIGHBOR_LOWER) {
2024 flowing_down = true;
2027 neutrals[num_neutrals++] = nb;
2031 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2032 if (liquid_kind == CONTENT_AIR)
2033 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2034 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2035 neutrals[num_neutrals++] = nb;
2037 // Do not count bottom source, it will screw things up
2039 sources[num_sources++] = nb;
2042 case LIQUID_FLOWING:
2043 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2044 if (liquid_kind == CONTENT_AIR)
2045 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2046 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2047 neutrals[num_neutrals++] = nb;
2049 flows[num_flows++] = nb;
2050 if (nb.t == NEIGHBOR_LOWER)
2051 flowing_down = true;
2058 decide on the type (and possibly level) of the current node
2060 content_t new_node_content;
2061 s8 new_node_level = -1;
2062 s8 max_node_level = -1;
2063 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2064 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2065 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2066 // it's perfectly safe to use liquid_kind here to determine the new node content.
2067 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2068 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2069 // liquid_kind is set properly, see above
2070 new_node_content = liquid_kind;
2071 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2073 // no surrounding sources, so get the maximum level that can flow into this node
2074 for (u16 i = 0; i < num_flows; i++) {
2075 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2076 switch (flows[i].t) {
2077 case NEIGHBOR_UPPER:
2078 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2079 max_node_level = LIQUID_LEVEL_MAX;
2080 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2081 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2082 } else if (nb_liquid_level > max_node_level)
2083 max_node_level = nb_liquid_level;
2085 case NEIGHBOR_LOWER:
2087 case NEIGHBOR_SAME_LEVEL:
2088 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2089 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2090 max_node_level = nb_liquid_level - 1;
2096 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2097 if (viscosity > 1 && max_node_level != liquid_level) {
2098 // amount to gain, limited by viscosity
2099 // must be at least 1 in absolute value
2100 s8 level_inc = max_node_level - liquid_level;
2101 if (level_inc < -viscosity || level_inc > viscosity)
2102 new_node_level = liquid_level + level_inc/viscosity;
2103 else if (level_inc < 0)
2104 new_node_level = liquid_level - 1;
2105 else if (level_inc > 0)
2106 new_node_level = liquid_level + 1;
2107 if (new_node_level != max_node_level)
2108 must_reflow.push_back(p0);
2110 new_node_level = max_node_level;
2112 if (new_node_level >= 0)
2113 new_node_content = liquid_kind;
2115 new_node_content = CONTENT_AIR;
2120 check if anything has changed. if not, just continue with the next node.
2122 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2123 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2124 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2130 update the current node
2132 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2133 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2134 // set level to last 3 bits, flowing down bit to 4th bit
2135 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2137 // set the liquid level and flow bit to 0
2138 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2140 n0.setContent(new_node_content);
2142 // Find out whether there is a suspect for this action
2143 std::string suspect;
2144 if(m_gamedef->rollback()){
2145 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2148 if(!suspect.empty()){
2150 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2151 // Get old node for rollback
2152 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2156 RollbackNode rollback_newnode(this, p0, m_gamedef);
2157 RollbackAction action;
2158 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2159 m_gamedef->rollback()->reportAction(action);
2165 v3s16 blockpos = getNodeBlockPos(p0);
2166 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2168 modified_blocks.insert(blockpos, block);
2169 // If node emits light, MapBlock requires lighting update
2170 if(nodemgr->get(n0).light_source != 0)
2171 lighting_modified_blocks[block->getPos()] = block;
2175 enqueue neighbors for update if neccessary
2177 switch (nodemgr->get(n0.getContent()).liquid_type) {
2179 case LIQUID_FLOWING:
2180 // make sure source flows into all neighboring nodes
2181 for (u16 i = 0; i < num_flows; i++)
2182 if (flows[i].t != NEIGHBOR_UPPER)
2183 m_transforming_liquid.push_back(flows[i].p);
2184 for (u16 i = 0; i < num_airs; i++)
2185 if (airs[i].t != NEIGHBOR_UPPER)
2186 m_transforming_liquid.push_back(airs[i].p);
2189 // this flow has turned to air; neighboring flows might need to do the same
2190 for (u16 i = 0; i < num_flows; i++)
2191 m_transforming_liquid.push_back(flows[i].p);
2195 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2196 while (must_reflow.size() > 0)
2197 m_transforming_liquid.push_back(must_reflow.pop_front());
2198 updateLighting(lighting_modified_blocks, modified_blocks);
2201 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2203 v3s16 blockpos = getNodeBlockPos(p);
2204 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2205 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2207 infostream<<"Map::getNodeMetadata(): Need to emerge "
2208 <<PP(blockpos)<<std::endl;
2209 block = emergeBlock(blockpos, false);
2213 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2217 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2221 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2223 v3s16 blockpos = getNodeBlockPos(p);
2224 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2225 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2227 infostream<<"Map::setNodeMetadata(): Need to emerge "
2228 <<PP(blockpos)<<std::endl;
2229 block = emergeBlock(blockpos, false);
2233 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2237 block->m_node_metadata.set(p_rel, meta);
2240 void Map::removeNodeMetadata(v3s16 p)
2242 v3s16 blockpos = getNodeBlockPos(p);
2243 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2244 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2247 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2251 block->m_node_metadata.remove(p_rel);
2254 NodeTimer Map::getNodeTimer(v3s16 p)
2256 v3s16 blockpos = getNodeBlockPos(p);
2257 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2258 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2260 infostream<<"Map::getNodeTimer(): Need to emerge "
2261 <<PP(blockpos)<<std::endl;
2262 block = emergeBlock(blockpos, false);
2266 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2270 NodeTimer t = block->m_node_timers.get(p_rel);
2274 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2276 v3s16 blockpos = getNodeBlockPos(p);
2277 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2278 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2280 infostream<<"Map::setNodeTimer(): Need to emerge "
2281 <<PP(blockpos)<<std::endl;
2282 block = emergeBlock(blockpos, false);
2286 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2290 block->m_node_timers.set(p_rel, t);
2293 void Map::removeNodeTimer(v3s16 p)
2295 v3s16 blockpos = getNodeBlockPos(p);
2296 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2297 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2300 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2304 block->m_node_timers.remove(p_rel);
2310 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2311 Map(dout_server, gamedef),
2313 m_map_metadata_changed(true),
2315 m_database_read(NULL),
2316 m_database_write(NULL)
2318 verbosestream<<__FUNCTION_NAME<<std::endl;
2321 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2323 m_mgparams = new MapgenV6Params();
2325 m_seed = m_mgparams->seed;
2327 if (g_settings->get("fixed_map_seed").empty())
2329 m_seed = (((u64)(myrand() & 0xffff) << 0)
2330 | ((u64)(myrand() & 0xffff) << 16)
2331 | ((u64)(myrand() & 0xffff) << 32)
2332 | ((u64)(myrand() & 0xffff) << 48));
2333 m_mgparams->seed = m_seed;
2337 Experimental and debug stuff
2344 Try to load map; if not found, create a new one.
2347 m_savedir = savedir;
2348 m_map_saving_enabled = false;
2352 // If directory exists, check contents and load if possible
2353 if(fs::PathExists(m_savedir))
2355 // If directory is empty, it is safe to save into it.
2356 if(fs::GetDirListing(m_savedir).size() == 0)
2358 infostream<<"ServerMap: Empty save directory is valid."
2360 m_map_saving_enabled = true;
2365 // Load map metadata (seed, chunksize)
2368 catch(SettingNotFoundException &e){
2369 infostream<<"ServerMap: Some metadata not found."
2370 <<" Using default settings."<<std::endl;
2372 catch(FileNotGoodException &e){
2373 infostream<<"WARNING: Could not load map metadata"
2374 //<<" Disabling chunk-based generator."
2379 infostream<<"ServerMap: Successfully loaded map "
2380 <<"metadata from "<<savedir
2381 <<", assuming valid save directory."
2382 <<" seed="<<m_seed<<"."
2385 m_map_saving_enabled = true;
2386 // Map loaded, not creating new one
2390 // If directory doesn't exist, it is safe to save to it
2392 m_map_saving_enabled = true;
2395 catch(std::exception &e)
2397 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2398 <<", exception: "<<e.what()<<std::endl;
2399 infostream<<"Please remove the map or fix it."<<std::endl;
2400 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2403 infostream<<"Initializing new map."<<std::endl;
2405 // Create zero sector
2406 emergeSector(v2s16(0,0));
2408 // Initially write whole map
2409 save(MOD_STATE_CLEAN);
2412 ServerMap::~ServerMap()
2414 verbosestream<<__FUNCTION_NAME<<std::endl;
2418 if(m_map_saving_enabled)
2420 // Save only changed parts
2421 save(MOD_STATE_WRITE_AT_UNLOAD);
2422 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2426 infostream<<"ServerMap: Map not saved"<<std::endl;
2429 catch(std::exception &e)
2431 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2432 <<", exception: "<<e.what()<<std::endl;
2436 Close database if it was opened
2439 sqlite3_finalize(m_database_read);
2440 if(m_database_write)
2441 sqlite3_finalize(m_database_write);
2443 sqlite3_close(m_database);
2449 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2450 for(; i.atEnd() == false; i++)
2452 MapChunk *chunk = i.getNode()->getValue();
2458 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2460 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2461 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2463 //s16 chunksize = 3;
2464 //v3s16 chunk_offset(-1,-1,-1);
2465 //s16 chunksize = 4;
2466 //v3s16 chunk_offset(-1,-1,-1);
2468 v3s16 chunk_offset(-2,-2,-2);
2469 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2470 v3s16 blockpos_min = blockpos_div * chunksize;
2471 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2472 blockpos_min += chunk_offset;
2473 blockpos_max += chunk_offset;
2475 //v3s16 extra_borders(1,1,1);
2476 v3s16 extra_borders(1,1,1);
2478 // Do nothing if not inside limits (+-1 because of neighbors)
2479 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2480 blockpos_over_limit(blockpos_max + extra_borders))
2483 data->seed = m_seed;
2484 data->blockpos_min = blockpos_min;
2485 data->blockpos_max = blockpos_max;
2486 data->blockpos_requested = blockpos;
2487 data->nodedef = m_gamedef->ndef();
2490 Create the whole area of this and the neighboring blocks
2493 //TimeTaker timer("initBlockMake() create area");
2495 for(s16 x=blockpos_min.X-extra_borders.X;
2496 x<=blockpos_max.X+extra_borders.X; x++)
2497 for(s16 z=blockpos_min.Z-extra_borders.Z;
2498 z<=blockpos_max.Z+extra_borders.Z; z++)
2500 v2s16 sectorpos(x, z);
2501 // Sector metadata is loaded from disk if not already loaded.
2502 ServerMapSector *sector = createSector(sectorpos);
2505 for(s16 y=blockpos_min.Y-extra_borders.Y;
2506 y<=blockpos_max.Y+extra_borders.Y; y++)
2509 //MapBlock *block = createBlock(p);
2510 // 1) get from memory, 2) load from disk
2511 MapBlock *block = emergeBlock(p, false);
2512 // 3) create a blank one
2515 block = createBlock(p);
2518 Block gets sunlight if this is true.
2520 Refer to the map generator heuristics.
2522 bool ug = m_emerge->isBlockUnderground(p);
2523 block->setIsUnderground(ug);
2526 // Lighting will not be valid after make_chunk is called
2527 block->setLightingExpired(true);
2528 // Lighting will be calculated
2529 //block->setLightingExpired(false);
2535 Now we have a big empty area.
2537 Make a ManualMapVoxelManipulator that contains this and the
2541 // The area that contains this block and it's neighbors
2542 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2543 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2545 data->vmanip = new ManualMapVoxelManipulator(this);
2546 //data->vmanip->setMap(this);
2550 //TimeTaker timer("initBlockMake() initialEmerge");
2551 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2554 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2555 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2556 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2557 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2558 core::map<v3s16, u8>::Node *n;
2559 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2562 u8 flags = n->getValue();
2563 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2569 // Data is ready now.
2573 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2574 core::map<v3s16, MapBlock*> &changed_blocks)
2576 v3s16 blockpos_min = data->blockpos_min;
2577 v3s16 blockpos_max = data->blockpos_max;
2578 v3s16 blockpos_requested = data->blockpos_requested;
2579 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2580 <<blockpos_requested.Y<<","
2581 <<blockpos_requested.Z<<")"<<std::endl;*/
2583 v3s16 extra_borders(1,1,1);
2585 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2587 /*infostream<<"Resulting vmanip:"<<std::endl;
2588 data->vmanip.print(infostream);*/
2590 // Make sure affected blocks are loaded
2591 for(s16 x=blockpos_min.X-extra_borders.X;
2592 x<=blockpos_max.X+extra_borders.X; x++)
2593 for(s16 z=blockpos_min.Z-extra_borders.Z;
2594 z<=blockpos_max.Z+extra_borders.Z; z++)
2595 for(s16 y=blockpos_min.Y-extra_borders.Y;
2596 y<=blockpos_max.Y+extra_borders.Y; y++)
2599 // Load from disk if not already in memory
2600 emergeBlock(p, false);
2604 Blit generated stuff to map
2605 NOTE: blitBackAll adds nearly everything to changed_blocks
2609 //TimeTaker timer("finishBlockMake() blitBackAll");
2610 data->vmanip->blitBackAll(&changed_blocks);
2613 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2616 Copy transforming liquid information
2618 while(data->transforming_liquid.size() > 0)
2620 v3s16 p = data->transforming_liquid.pop_front();
2621 m_transforming_liquid.push_back(p);
2625 Do stuff in central blocks
2633 TimeTaker t("finishBlockMake lighting update");
2635 core::map<v3s16, MapBlock*> lighting_update_blocks;
2638 for(s16 x=blockpos_min.X-extra_borders.X;
2639 x<=blockpos_max.X+extra_borders.X; x++)
2640 for(s16 z=blockpos_min.Z-extra_borders.Z;
2641 z<=blockpos_max.Z+extra_borders.Z; z++)
2642 for(s16 y=blockpos_min.Y-extra_borders.Y;
2643 y<=blockpos_max.Y+extra_borders.Y; y++)
2646 MapBlock *block = getBlockNoCreateNoEx(p);
2648 lighting_update_blocks.insert(block->getPos(), block);
2651 updateLighting(lighting_update_blocks, changed_blocks);
2655 Set lighting to non-expired state in all of them.
2656 This is cheating, but it is not fast enough if all of them
2657 would actually be updated.
2659 for(s16 x=blockpos_min.X-extra_borders.X;
2660 x<=blockpos_max.X+extra_borders.X; x++)
2661 for(s16 z=blockpos_min.Z-extra_borders.Z;
2662 z<=blockpos_max.Z+extra_borders.Z; z++)
2663 for(s16 y=blockpos_min.Y-extra_borders.Y;
2664 y<=blockpos_max.Y+extra_borders.Y; y++)
2667 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2671 if(enable_mapgen_debug_info == false)
2672 t.stop(true); // Hide output
2677 Go through changed blocks
2679 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2680 i.atEnd() == false; i++)
2682 MapBlock *block = i.getNode()->getValue();
2685 Update day/night difference cache of the MapBlocks
2687 block->expireDayNightDiff();
2689 Set block as modified
2691 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2692 "finishBlockMake expireDayNightDiff");
2696 Set central blocks as generated
2698 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2699 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2700 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2703 MapBlock *block = getBlockNoCreateNoEx(p);
2705 block->setGenerated(true);
2709 Save changed parts of map
2710 NOTE: Will be saved later.
2712 //save(MOD_STATE_WRITE_AT_UNLOAD);
2714 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2715 <<","<<blockpos_requested.Y<<","
2716 <<blockpos_requested.Z<<")"<<std::endl;*/
2718 if(enable_mapgen_debug_info)
2721 Analyze resulting blocks
2723 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2724 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2725 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2726 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2727 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2728 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2730 v3s16 p = v3s16(x,y,z);
2731 MapBlock *block = getBlockNoCreateNoEx(p);
2733 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2734 infostream<<"Generated "<<spos<<": "
2735 <<analyze_block(block)<<std::endl;
2740 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2746 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2748 DSTACKF("%s: p2d=(%d,%d)",
2753 Check if it exists already in memory
2755 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2760 Try to load it from disk (with blocks)
2762 //if(loadSectorFull(p2d) == true)
2765 Try to load metadata from disk
2768 if(loadSectorMeta(p2d) == true)
2770 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2773 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2774 throw InvalidPositionException("");
2780 Do not create over-limit
2782 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2783 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2784 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2785 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2786 throw InvalidPositionException("createSector(): pos. over limit");
2789 Generate blank sector
2792 sector = new ServerMapSector(this, p2d, m_gamedef);
2794 // Sector position on map in nodes
2795 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2800 m_sectors.insert(p2d, sector);
2807 This is a quick-hand function for calling makeBlock().
2809 MapBlock * ServerMap::generateBlock(
2811 core::map<v3s16, MapBlock*> &modified_blocks
2814 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2816 /*infostream<<"generateBlock(): "
2817 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2820 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2822 TimeTaker timer("generateBlock");
2824 //MapBlock *block = original_dummy;
2826 v2s16 p2d(p.X, p.Z);
2827 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2830 Do not generate over-limit
2832 if(blockpos_over_limit(p))
2834 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2835 throw InvalidPositionException("generateBlock(): pos. over limit");
2839 Create block make data
2842 initBlockMake(&data, p);
2848 TimeTaker t("mapgen::make_block()");
2849 mapgen->makeChunk(&data);
2850 //mapgen::make_block(&data);
2852 if(enable_mapgen_debug_info == false)
2853 t.stop(true); // Hide output
2857 Blit data back on map, update lighting, add mobs and whatever this does
2859 finishBlockMake(&data, modified_blocks);
2864 MapBlock *block = getBlockNoCreateNoEx(p);
2872 bool erroneus_content = false;
2873 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2874 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2875 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2878 MapNode n = block->getNode(p);
2879 if(n.getContent() == CONTENT_IGNORE)
2881 infostream<<"CONTENT_IGNORE at "
2882 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2884 erroneus_content = true;
2888 if(erroneus_content)
2897 Generate a completely empty block
2901 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2902 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2904 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2907 n.setContent(CONTENT_AIR);
2908 block->setNode(v3s16(x0,y0,z0), n);
2914 if(enable_mapgen_debug_info == false)
2915 timer.stop(true); // Hide output
2921 MapBlock * ServerMap::createBlock(v3s16 p)
2923 DSTACKF("%s: p=(%d,%d,%d)",
2924 __FUNCTION_NAME, p.X, p.Y, p.Z);
2927 Do not create over-limit
2929 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2930 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2931 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2932 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2933 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2934 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2935 throw InvalidPositionException("createBlock(): pos. over limit");
2937 v2s16 p2d(p.X, p.Z);
2940 This will create or load a sector if not found in memory.
2941 If block exists on disk, it will be loaded.
2943 NOTE: On old save formats, this will be slow, as it generates
2944 lighting on blocks for them.
2946 ServerMapSector *sector;
2948 sector = (ServerMapSector*)createSector(p2d);
2949 assert(sector->getId() == MAPSECTOR_SERVER);
2951 catch(InvalidPositionException &e)
2953 infostream<<"createBlock: createSector() failed"<<std::endl;
2957 NOTE: This should not be done, or at least the exception
2958 should not be passed on as std::exception, because it
2959 won't be catched at all.
2961 /*catch(std::exception &e)
2963 infostream<<"createBlock: createSector() failed: "
2964 <<e.what()<<std::endl;
2969 Try to get a block from the sector
2972 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2975 if(block->isDummy())
2980 block = sector->createBlankBlock(block_y);
2985 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2987 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2989 p.X, p.Y, p.Z, create_blank);
2992 MapBlock *block = getBlockNoCreateNoEx(p);
2993 if(block && block->isDummy() == false)
2998 MapBlock *block = loadBlock(p);
3004 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3005 MapBlock *block = sector->createBlankBlock(p.Y);
3009 /*if(allow_generate)
3011 core::map<v3s16, MapBlock*> modified_blocks;
3012 MapBlock *block = generateBlock(p, modified_blocks);
3016 event.type = MEET_OTHER;
3019 // Copy modified_blocks to event
3020 for(core::map<v3s16, MapBlock*>::Iterator
3021 i = modified_blocks.getIterator();
3022 i.atEnd()==false; i++)
3024 event.modified_blocks.insert(i.getNode()->getKey(), false);
3028 dispatchEvent(&event);
3037 s16 ServerMap::findGroundLevel(v2s16 p2d)
3041 Uh, just do something random...
3043 // Find existing map from top to down
3046 v3s16 p(p2d.X, max, p2d.Y);
3047 for(; p.Y>min; p.Y--)
3049 MapNode n = getNodeNoEx(p);
3050 if(n.getContent() != CONTENT_IGNORE)
3055 // If this node is not air, go to plan b
3056 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3058 // Search existing walkable and return it
3059 for(; p.Y>min; p.Y--)
3061 MapNode n = getNodeNoEx(p);
3062 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3071 Determine from map generator noise functions
3074 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3077 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3078 //return (s16)level;
3081 void ServerMap::createDatabase() {
3084 e = sqlite3_exec(m_database,
3085 "CREATE TABLE IF NOT EXISTS `blocks` ("
3086 "`pos` INT NOT NULL PRIMARY KEY,"
3089 , NULL, NULL, NULL);
3090 if(e == SQLITE_ABORT)
3091 throw FileNotGoodException("Could not create database structure");
3093 infostream<<"ServerMap: Database structure was created";
3096 void ServerMap::verifyDatabase() {
3101 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3102 bool needs_create = false;
3106 Open the database connection
3109 createDirs(m_savedir);
3111 if(!fs::PathExists(dbp))
3112 needs_create = true;
3114 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3115 if(d != SQLITE_OK) {
3116 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3117 throw FileNotGoodException("Cannot open database file");
3123 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3124 if(d != SQLITE_OK) {
3125 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3126 throw FileNotGoodException("Cannot prepare read statement");
3129 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3130 if(d != SQLITE_OK) {
3131 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3132 throw FileNotGoodException("Cannot prepare write statement");
3135 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3136 if(d != SQLITE_OK) {
3137 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3138 throw FileNotGoodException("Cannot prepare read statement");
3141 infostream<<"ServerMap: Database opened"<<std::endl;
3145 bool ServerMap::loadFromFolders() {
3146 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3151 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3152 return (sqlite3_int64)pos.Z*16777216 +
3153 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3156 void ServerMap::createDirs(std::string path)
3158 if(fs::CreateAllDirs(path) == false)
3160 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3161 <<"\""<<path<<"\""<<std::endl;
3162 throw BaseException("ServerMap failed to create directory");
3166 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3172 snprintf(cc, 9, "%.4x%.4x",
3173 (unsigned int)pos.X&0xffff,
3174 (unsigned int)pos.Y&0xffff);
3176 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3178 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3179 (unsigned int)pos.X&0xfff,
3180 (unsigned int)pos.Y&0xfff);
3182 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3188 v2s16 ServerMap::getSectorPos(std::string dirname)
3192 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
3193 assert(spos != std::string::npos);
3194 if(dirname.size() - spos == 8)
3197 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
3199 else if(dirname.size() - spos == 3)
3202 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3203 // Sign-extend the 12 bit values up to 16 bits...
3204 if(x&0x800) x|=0xF000;
3205 if(y&0x800) y|=0xF000;
3212 v2s16 pos((s16)x, (s16)y);
3216 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3218 v2s16 p2d = getSectorPos(sectordir);
3220 if(blockfile.size() != 4){
3221 throw InvalidFilenameException("Invalid block filename");
3224 int r = sscanf(blockfile.c_str(), "%4x", &y);
3226 throw InvalidFilenameException("Invalid block filename");
3227 return v3s16(p2d.X, y, p2d.Y);
3230 std::string ServerMap::getBlockFilename(v3s16 p)
3233 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3237 void ServerMap::save(ModifiedState save_level)
3239 DSTACK(__FUNCTION_NAME);
3240 if(m_map_saving_enabled == false)
3242 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3246 if(save_level == MOD_STATE_CLEAN)
3247 infostream<<"ServerMap: Saving whole map, this can take time."
3250 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3255 // Profile modified reasons
3256 Profiler modprofiler;
3258 u32 sector_meta_count = 0;
3259 u32 block_count = 0;
3260 u32 block_count_all = 0; // Number of blocks in memory
3262 // Don't do anything with sqlite unless something is really saved
3263 bool save_started = false;
3265 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3266 for(; i.atEnd() == false; i++)
3268 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3269 assert(sector->getId() == MAPSECTOR_SERVER);
3271 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3273 saveSectorMeta(sector);
3274 sector_meta_count++;
3276 core::list<MapBlock*> blocks;
3277 sector->getBlocks(blocks);
3278 core::list<MapBlock*>::Iterator j;
3280 for(j=blocks.begin(); j!=blocks.end(); j++)
3282 MapBlock *block = *j;
3286 if(block->getModified() >= (u32)save_level)
3291 save_started = true;
3294 modprofiler.add(block->getModifiedReason(), 1);
3299 /*infostream<<"ServerMap: Written block ("
3300 <<block->getPos().X<<","
3301 <<block->getPos().Y<<","
3302 <<block->getPos().Z<<")"
3311 Only print if something happened or saved whole map
3313 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3314 || block_count != 0)
3316 infostream<<"ServerMap: Written: "
3317 <<sector_meta_count<<" sector metadata files, "
3318 <<block_count<<" block files"
3319 <<", "<<block_count_all<<" blocks in memory."
3321 PrintInfo(infostream); // ServerMap/ClientMap:
3322 infostream<<"Blocks modified by: "<<std::endl;
3323 modprofiler.print(infostream);
3327 static s32 unsignedToSigned(s32 i, s32 max_positive)
3329 if(i < max_positive)
3332 return i - 2*max_positive;
3335 // modulo of a negative number does not work consistently in C
3336 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3340 return mod - ((-i) % mod);
3343 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3345 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3347 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3349 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3350 return v3s16(x,y,z);
3353 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
3355 if(loadFromFolders()){
3356 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3357 <<"all blocks that are stored in flat files"<<std::endl;
3363 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3365 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3366 v3s16 p = getIntegerAsBlock(block_i);
3367 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3373 void ServerMap::saveMapMeta()
3375 DSTACK(__FUNCTION_NAME);
3377 /*infostream<<"ServerMap::saveMapMeta(): "
3381 createDirs(m_savedir);
3383 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3384 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3385 if(os.good() == false)
3387 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3388 <<"could not open"<<fullpath<<std::endl;
3389 throw FileNotGoodException("Cannot open chunk metadata");
3394 m_emerge->setParamsToSettings(¶ms);
3395 params.writeLines(os);
3397 os<<"[end_of_params]\n";
3399 m_map_metadata_changed = false;
3402 void ServerMap::loadMapMeta()
3404 DSTACK(__FUNCTION_NAME);
3406 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3409 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3410 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3411 if(is.good() == false)
3413 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3414 <<"could not open"<<fullpath<<std::endl;
3415 throw FileNotGoodException("Cannot open map metadata");
3423 throw SerializationError
3424 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3426 std::getline(is, line);
3427 std::string trimmedline = trim(line);
3428 if(trimmedline == "[end_of_params]")
3430 params.parseConfigLine(line);
3433 MapgenParams *mgparams = m_emerge->getParamsFromSettings(¶ms);
3437 m_mgparams = mgparams;
3438 m_seed = mgparams->seed;
3440 if (params.exists("seed")) {
3441 m_seed = params.getU64("seed");
3442 m_mgparams->seed = m_seed;
3446 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3449 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3451 DSTACK(__FUNCTION_NAME);
3452 // Format used for writing
3453 u8 version = SER_FMT_VER_HIGHEST;
3455 v2s16 pos = sector->getPos();
3456 std::string dir = getSectorDir(pos);
3459 std::string fullpath = dir + DIR_DELIM + "meta";
3460 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3461 if(o.good() == false)
3462 throw FileNotGoodException("Cannot open sector metafile");
3464 sector->serialize(o, version);
3466 sector->differs_from_disk = false;
3469 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3471 DSTACK(__FUNCTION_NAME);
3473 v2s16 p2d = getSectorPos(sectordir);
3475 ServerMapSector *sector = NULL;
3477 std::string fullpath = sectordir + DIR_DELIM + "meta";
3478 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3479 if(is.good() == false)
3481 // If the directory exists anyway, it probably is in some old
3482 // format. Just go ahead and create the sector.
3483 if(fs::PathExists(sectordir))
3485 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3486 <<fullpath<<" doesn't exist but directory does."
3487 <<" Continuing with a sector with no metadata."
3489 sector = new ServerMapSector(this, p2d, m_gamedef);
3490 m_sectors.insert(p2d, sector);
3494 throw FileNotGoodException("Cannot open sector metafile");
3499 sector = ServerMapSector::deSerialize
3500 (is, this, p2d, m_sectors, m_gamedef);
3502 saveSectorMeta(sector);
3505 sector->differs_from_disk = false;
3510 bool ServerMap::loadSectorMeta(v2s16 p2d)
3512 DSTACK(__FUNCTION_NAME);
3514 MapSector *sector = NULL;
3516 // The directory layout we're going to load from.
3517 // 1 - original sectors/xxxxzzzz/
3518 // 2 - new sectors2/xxx/zzz/
3519 // If we load from anything but the latest structure, we will
3520 // immediately save to the new one, and remove the old.
3522 std::string sectordir1 = getSectorDir(p2d, 1);
3523 std::string sectordir;
3524 if(fs::PathExists(sectordir1))
3526 sectordir = sectordir1;
3531 sectordir = getSectorDir(p2d, 2);
3535 sector = loadSectorMeta(sectordir, loadlayout != 2);
3537 catch(InvalidFilenameException &e)
3541 catch(FileNotGoodException &e)
3545 catch(std::exception &e)
3554 bool ServerMap::loadSectorFull(v2s16 p2d)
3556 DSTACK(__FUNCTION_NAME);
3558 MapSector *sector = NULL;
3560 // The directory layout we're going to load from.
3561 // 1 - original sectors/xxxxzzzz/
3562 // 2 - new sectors2/xxx/zzz/
3563 // If we load from anything but the latest structure, we will
3564 // immediately save to the new one, and remove the old.
3566 std::string sectordir1 = getSectorDir(p2d, 1);
3567 std::string sectordir;
3568 if(fs::PathExists(sectordir1))
3570 sectordir = sectordir1;
3575 sectordir = getSectorDir(p2d, 2);
3579 sector = loadSectorMeta(sectordir, loadlayout != 2);
3581 catch(InvalidFilenameException &e)
3585 catch(FileNotGoodException &e)
3589 catch(std::exception &e)
3597 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3599 std::vector<fs::DirListNode>::iterator i2;
3600 for(i2=list2.begin(); i2!=list2.end(); i2++)
3606 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3608 catch(InvalidFilenameException &e)
3610 // This catches unknown crap in directory
3616 infostream<<"Sector converted to new layout - deleting "<<
3617 sectordir1<<std::endl;
3618 fs::RecursiveDelete(sectordir1);
3625 void ServerMap::beginSave() {
3627 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3628 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3631 void ServerMap::endSave() {
3633 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3634 infostream<<"WARNING: endSave() failed, map might not have saved.";
3637 void ServerMap::saveBlock(MapBlock *block)
3639 DSTACK(__FUNCTION_NAME);
3641 Dummy blocks are not written
3643 if(block->isDummy())
3645 /*v3s16 p = block->getPos();
3646 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3647 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3651 // Format used for writing
3652 u8 version = SER_FMT_VER_HIGHEST;
3654 v3s16 p3d = block->getPos();
3658 v2s16 p2d(p3d.X, p3d.Z);
3659 std::string sectordir = getSectorDir(p2d);
3661 createDirs(sectordir);
3663 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3664 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3665 if(o.good() == false)
3666 throw FileNotGoodException("Cannot open block data");
3669 [0] u8 serialization version
3675 std::ostringstream o(std::ios_base::binary);
3677 o.write((char*)&version, 1);
3680 block->serialize(o, version, true);
3682 // Write block to database
3684 std::string tmp = o.str();
3685 const char *bytes = tmp.c_str();
3687 bool success = true;
3688 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3689 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3692 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3693 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3696 int written = sqlite3_step(m_database_write);
3697 if(written != SQLITE_DONE) {
3698 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3699 <<sqlite3_errmsg(m_database)<<std::endl;
3702 // Make ready for later reuse
3703 sqlite3_reset(m_database_write);
3705 // We just wrote it to the disk so clear modified flag
3707 block->resetModified();
3710 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3712 DSTACK(__FUNCTION_NAME);
3714 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3717 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3718 if(is.good() == false)
3719 throw FileNotGoodException("Cannot open block file");
3721 v3s16 p3d = getBlockPos(sectordir, blockfile);
3722 v2s16 p2d(p3d.X, p3d.Z);
3724 assert(sector->getPos() == p2d);
3726 u8 version = SER_FMT_VER_INVALID;
3727 is.read((char*)&version, 1);
3730 throw SerializationError("ServerMap::loadBlock(): Failed"
3731 " to read MapBlock version");
3733 /*u32 block_size = MapBlock::serializedLength(version);
3734 SharedBuffer<u8> data(block_size);
3735 is.read((char*)*data, block_size);*/
3737 // This will always return a sector because we're the server
3738 //MapSector *sector = emergeSector(p2d);
3740 MapBlock *block = NULL;
3741 bool created_new = false;
3742 block = sector->getBlockNoCreateNoEx(p3d.Y);
3745 block = sector->createBlankBlockNoInsert(p3d.Y);
3750 block->deSerialize(is, version, true);
3752 // If it's a new block, insert it to the map
3754 sector->insertBlock(block);
3757 Save blocks loaded in old format in new format
3760 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3764 // Should be in database now, so delete the old file
3765 fs::RecursiveDelete(fullpath);
3768 // We just loaded it from the disk, so it's up-to-date.
3769 block->resetModified();
3772 catch(SerializationError &e)
3774 infostream<<"WARNING: Invalid block data on disk "
3775 <<"fullpath="<<fullpath
3776 <<" (SerializationError). "
3777 <<"what()="<<e.what()
3779 //" Ignoring. A new one will be generated.
3782 // TODO: Backup file; name is in fullpath.
3786 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3788 DSTACK(__FUNCTION_NAME);
3791 std::istringstream is(*blob, std::ios_base::binary);
3793 u8 version = SER_FMT_VER_INVALID;
3794 is.read((char*)&version, 1);
3797 throw SerializationError("ServerMap::loadBlock(): Failed"
3798 " to read MapBlock version");
3800 /*u32 block_size = MapBlock::serializedLength(version);
3801 SharedBuffer<u8> data(block_size);
3802 is.read((char*)*data, block_size);*/
3804 // This will always return a sector because we're the server
3805 //MapSector *sector = emergeSector(p2d);
3807 MapBlock *block = NULL;
3808 bool created_new = false;
3809 block = sector->getBlockNoCreateNoEx(p3d.Y);
3812 block = sector->createBlankBlockNoInsert(p3d.Y);
3817 block->deSerialize(is, version, true);
3819 // If it's a new block, insert it to the map
3821 sector->insertBlock(block);
3824 Save blocks loaded in old format in new format
3827 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3828 // Only save if asked to; no need to update version
3832 // We just loaded it from, so it's up-to-date.
3833 block->resetModified();
3836 catch(SerializationError &e)
3838 errorstream<<"Invalid block data in database"
3839 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3840 <<" (SerializationError): "<<e.what()<<std::endl;
3842 // TODO: Block should be marked as invalid in memory so that it is
3843 // not touched but the game can run
3845 if(g_settings->getBool("ignore_world_load_errors")){
3846 errorstream<<"Ignoring block load error. Duck and cover! "
3847 <<"(ignore_world_load_errors)"<<std::endl;
3849 throw SerializationError("Invalid block data in database");
3855 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3857 DSTACK(__FUNCTION_NAME);
3859 v2s16 p2d(blockpos.X, blockpos.Z);
3861 if(!loadFromFolders()) {
3864 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3865 infostream<<"WARNING: Could not bind block position for load: "
3866 <<sqlite3_errmsg(m_database)<<std::endl;
3867 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3869 Make sure sector is loaded
3871 MapSector *sector = createSector(p2d);
3876 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3877 size_t len = sqlite3_column_bytes(m_database_read, 0);
3879 std::string datastr(data, len);
3881 loadBlock(&datastr, blockpos, sector, false);
3883 sqlite3_step(m_database_read);
3884 // We should never get more than 1 row, so ok to reset
3885 sqlite3_reset(m_database_read);
3887 return getBlockNoCreateNoEx(blockpos);
3889 sqlite3_reset(m_database_read);
3891 // Not found in database, try the files
3894 // The directory layout we're going to load from.
3895 // 1 - original sectors/xxxxzzzz/
3896 // 2 - new sectors2/xxx/zzz/
3897 // If we load from anything but the latest structure, we will
3898 // immediately save to the new one, and remove the old.
3900 std::string sectordir1 = getSectorDir(p2d, 1);
3901 std::string sectordir;
3902 if(fs::PathExists(sectordir1))
3904 sectordir = sectordir1;
3909 sectordir = getSectorDir(p2d, 2);
3913 Make sure sector is loaded
3915 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3919 sector = loadSectorMeta(sectordir, loadlayout != 2);
3921 catch(InvalidFilenameException &e)
3925 catch(FileNotGoodException &e)
3929 catch(std::exception &e)
3936 Make sure file exists
3939 std::string blockfilename = getBlockFilename(blockpos);
3940 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3944 Load block and save it to the database
3946 loadBlock(sectordir, blockfilename, sector, true);
3947 return getBlockNoCreateNoEx(blockpos);
3950 void ServerMap::PrintInfo(std::ostream &out)
3959 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3964 MapVoxelManipulator::~MapVoxelManipulator()
3966 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3970 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3972 TimeTaker timer1("emerge", &emerge_time);
3974 // Units of these are MapBlocks
3975 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3976 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3978 VoxelArea block_area_nodes
3979 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3981 addArea(block_area_nodes);
3983 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3984 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3985 for(s32 x=p_min.X; x<=p_max.X; x++)
3990 core::map<v3s16, u8>::Node *n;
3991 n = m_loaded_blocks.find(p);
3995 bool block_data_inexistent = false;
3998 TimeTaker timer1("emerge load", &emerge_load_time);
4000 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4001 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4003 a.print(infostream);
4004 infostream<<std::endl;*/
4006 block = m_map->getBlockNoCreate(p);
4007 if(block->isDummy())
4008 block_data_inexistent = true;
4010 block->copyTo(*this);
4012 catch(InvalidPositionException &e)
4014 block_data_inexistent = true;
4017 if(block_data_inexistent)
4019 flags |= VMANIP_BLOCK_DATA_INEXIST;
4021 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4022 // Fill with VOXELFLAG_INEXISTENT
4023 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4024 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4026 s32 i = m_area.index(a.MinEdge.X,y,z);
4027 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4030 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4032 // Mark that block was loaded as blank
4033 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4036 m_loaded_blocks.insert(p, flags);
4039 //infostream<<"emerge done"<<std::endl;
4043 SUGG: Add an option to only update eg. water and air nodes.
4044 This will make it interfere less with important stuff if
4047 void MapVoxelManipulator::blitBack
4048 (core::map<v3s16, MapBlock*> & modified_blocks)
4050 if(m_area.getExtent() == v3s16(0,0,0))
4053 //TimeTaker timer1("blitBack");
4055 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4056 <<m_loaded_blocks.size()<<std::endl;*/
4059 Initialize block cache
4061 v3s16 blockpos_last;
4062 MapBlock *block = NULL;
4063 bool block_checked_in_modified = false;
4065 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4066 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4067 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4071 u8 f = m_flags[m_area.index(p)];
4072 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4075 MapNode &n = m_data[m_area.index(p)];
4077 v3s16 blockpos = getNodeBlockPos(p);
4082 if(block == NULL || blockpos != blockpos_last){
4083 block = m_map->getBlockNoCreate(blockpos);
4084 blockpos_last = blockpos;
4085 block_checked_in_modified = false;
4088 // Calculate relative position in block
4089 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4091 // Don't continue if nothing has changed here
4092 if(block->getNode(relpos) == n)
4095 //m_map->setNode(m_area.MinEdge + p, n);
4096 block->setNode(relpos, n);
4099 Make sure block is in modified_blocks
4101 if(block_checked_in_modified == false)
4103 modified_blocks[blockpos] = block;
4104 block_checked_in_modified = true;
4107 catch(InvalidPositionException &e)
4113 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4114 MapVoxelManipulator(map),
4115 m_create_area(false)
4119 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4123 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4125 // Just create the area so that it can be pointed to
4126 VoxelManipulator::emerge(a, caller_id);
4129 void ManualMapVoxelManipulator::initialEmerge(
4130 v3s16 blockpos_min, v3s16 blockpos_max)
4132 TimeTaker timer1("initialEmerge", &emerge_time);
4134 // Units of these are MapBlocks
4135 v3s16 p_min = blockpos_min;
4136 v3s16 p_max = blockpos_max;
4138 VoxelArea block_area_nodes
4139 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4141 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4144 infostream<<"initialEmerge: area: ";
4145 block_area_nodes.print(infostream);
4146 infostream<<" ("<<size_MB<<"MB)";
4147 infostream<<std::endl;
4150 addArea(block_area_nodes);
4152 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4153 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4154 for(s32 x=p_min.X; x<=p_max.X; x++)
4159 core::map<v3s16, u8>::Node *n;
4160 n = m_loaded_blocks.find(p);
4164 bool block_data_inexistent = false;
4167 TimeTaker timer1("emerge load", &emerge_load_time);
4169 block = m_map->getBlockNoCreate(p);
4170 if(block->isDummy())
4171 block_data_inexistent = true;
4173 block->copyTo(*this);
4175 catch(InvalidPositionException &e)
4177 block_data_inexistent = true;
4180 if(block_data_inexistent)
4182 flags |= VMANIP_BLOCK_DATA_INEXIST;
4185 Mark area inexistent
4187 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4188 // Fill with VOXELFLAG_INEXISTENT
4189 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4190 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4192 s32 i = m_area.index(a.MinEdge.X,y,z);
4193 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4196 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
4198 // Mark that block was loaded as blank
4199 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
4202 m_loaded_blocks.insert(p, flags);
4206 void ManualMapVoxelManipulator::blitBackAll(
4207 core::map<v3s16, MapBlock*> * modified_blocks)
4209 if(m_area.getExtent() == v3s16(0,0,0))
4213 Copy data of all blocks
4215 for(core::map<v3s16, u8>::Iterator
4216 i = m_loaded_blocks.getIterator();
4217 i.atEnd() == false; i++)
4219 v3s16 p = i.getNode()->getKey();
4220 u8 flags = i.getNode()->getValue();
4222 bool existed = !(flags & VMANIP_BLOCK_DATA_INEXIST);
4223 if(existed == false)
4225 // The Great Bug was found using this
4226 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4227 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4232 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4235 infostream<<"WARNING: "<<__FUNCTION_NAME
4236 <<": got NULL block "
4237 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4242 block->copyFrom(*this);
4245 modified_blocks->insert(p, block);