3 Copyright (C) 2010-2011 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 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
40 SQLite format specification:
41 - Initially only replaces sectors/ and sectors2/
43 If map.sqlite does not exist in the save dir
44 or the block was not found in the database
45 the map will try to load from sectors folder.
46 In either case, map.sqlite will be created
47 and all future saves will save there.
49 Structure of map.sqlite:
60 Map::Map(std::ostream &dout, IGameDef *gamedef):
65 /*m_sector_mutex.Init();
66 assert(m_sector_mutex.IsInitialized());*/
74 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
75 for(; i.atEnd() == false; i++)
77 MapSector *sector = i.getNode()->getValue();
82 void Map::addEventReceiver(MapEventReceiver *event_receiver)
84 m_event_receivers.insert(event_receiver, false);
87 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
89 if(m_event_receivers.find(event_receiver) == NULL)
91 m_event_receivers.remove(event_receiver);
94 void Map::dispatchEvent(MapEditEvent *event)
96 for(core::map<MapEventReceiver*, bool>::Iterator
97 i = m_event_receivers.getIterator();
98 i.atEnd()==false; i++)
100 MapEventReceiver* event_receiver = i.getNode()->getKey();
101 event_receiver->onMapEditEvent(event);
105 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
107 if(m_sector_cache != NULL && p == m_sector_cache_p){
108 MapSector * sector = m_sector_cache;
112 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
117 MapSector *sector = n->getValue();
119 // Cache the last result
120 m_sector_cache_p = p;
121 m_sector_cache = sector;
126 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
128 return getSectorNoGenerateNoExNoLock(p);
131 MapSector * Map::getSectorNoGenerate(v2s16 p)
133 MapSector *sector = getSectorNoGenerateNoEx(p);
135 throw InvalidPositionException();
140 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
142 v2s16 p2d(p3d.X, p3d.Z);
143 MapSector * sector = getSectorNoGenerateNoEx(p2d);
146 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
150 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
152 MapBlock *block = getBlockNoCreateNoEx(p3d);
154 throw InvalidPositionException();
158 bool Map::isNodeUnderground(v3s16 p)
160 v3s16 blockpos = getNodeBlockPos(p);
162 MapBlock * block = getBlockNoCreate(blockpos);
163 return block->getIsUnderground();
165 catch(InvalidPositionException &e)
171 bool Map::isValidPosition(v3s16 p)
173 v3s16 blockpos = getNodeBlockPos(p);
174 MapBlock *block = getBlockNoCreate(blockpos);
175 return (block != NULL);
178 // Returns a CONTENT_IGNORE node if not found
179 MapNode Map::getNodeNoEx(v3s16 p)
181 v3s16 blockpos = getNodeBlockPos(p);
182 MapBlock *block = getBlockNoCreateNoEx(blockpos);
184 return MapNode(CONTENT_IGNORE);
185 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
186 return block->getNodeNoCheck(relpos);
189 // throws InvalidPositionException if not found
190 MapNode Map::getNode(v3s16 p)
192 v3s16 blockpos = getNodeBlockPos(p);
193 MapBlock *block = getBlockNoCreateNoEx(blockpos);
195 throw InvalidPositionException();
196 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
197 return block->getNodeNoCheck(relpos);
200 // throws InvalidPositionException if not found
201 void Map::setNode(v3s16 p, MapNode & n)
203 v3s16 blockpos = getNodeBlockPos(p);
204 MapBlock *block = getBlockNoCreate(blockpos);
205 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 // Never allow placing CONTENT_IGNORE, it fucks up stuff
207 if(n.getContent() == CONTENT_IGNORE){
208 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
209 <<" while trying to replace \""
210 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
211 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
212 debug_stacks_print_to(infostream);
215 block->setNodeNoCheck(relpos, n);
220 Goes recursively through the neighbours of the node.
222 Alters only transparent nodes.
224 If the lighting of the neighbour is lower than the lighting of
225 the node was (before changing it to 0 at the step before), the
226 lighting of the neighbour is set to 0 and then the same stuff
227 repeats for the neighbour.
229 The ending nodes of the routine are stored in light_sources.
230 This is useful when a light is removed. In such case, this
231 routine can be called for the light node and then again for
232 light_sources to re-light the area without the removed light.
234 values of from_nodes are lighting values.
236 void Map::unspreadLight(enum LightBank bank,
237 core::map<v3s16, u8> & from_nodes,
238 core::map<v3s16, bool> & light_sources,
239 core::map<v3s16, MapBlock*> & modified_blocks)
241 INodeDefManager *nodemgr = m_gamedef->ndef();
244 v3s16(0,0,1), // back
246 v3s16(1,0,0), // right
247 v3s16(0,0,-1), // front
248 v3s16(0,-1,0), // bottom
249 v3s16(-1,0,0), // left
252 if(from_nodes.size() == 0)
255 u32 blockchangecount = 0;
257 core::map<v3s16, u8> unlighted_nodes;
258 core::map<v3s16, u8>::Iterator j;
259 j = from_nodes.getIterator();
262 Initialize block cache
265 MapBlock *block = NULL;
266 // Cache this a bit, too
267 bool block_checked_in_modified = false;
269 for(; j.atEnd() == false; j++)
271 v3s16 pos = j.getNode()->getKey();
272 v3s16 blockpos = getNodeBlockPos(pos);
274 // Only fetch a new block if the block position has changed
276 if(block == NULL || blockpos != blockpos_last){
277 block = getBlockNoCreate(blockpos);
278 blockpos_last = blockpos;
280 block_checked_in_modified = false;
284 catch(InvalidPositionException &e)
292 // Calculate relative position in block
293 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
295 // Get node straight from the block
296 MapNode n = block->getNode(relpos);
298 u8 oldlight = j.getNode()->getValue();
300 // Loop through 6 neighbors
301 for(u16 i=0; i<6; i++)
303 // Get the position of the neighbor node
304 v3s16 n2pos = pos + dirs[i];
306 // Get the block where the node is located
307 v3s16 blockpos = getNodeBlockPos(n2pos);
311 // Only fetch a new block if the block position has changed
313 if(block == NULL || blockpos != blockpos_last){
314 block = getBlockNoCreate(blockpos);
315 blockpos_last = blockpos;
317 block_checked_in_modified = false;
321 catch(InvalidPositionException &e)
326 // Calculate relative position in block
327 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
328 // Get node straight from the block
329 MapNode n2 = block->getNode(relpos);
331 bool changed = false;
333 //TODO: Optimize output by optimizing light_sources?
336 If the neighbor is dimmer than what was specified
337 as oldlight (the light of the previous node)
339 if(n2.getLight(bank, nodemgr) < oldlight)
342 And the neighbor is transparent and it has some light
344 if(nodemgr->get(n2).light_propagates
345 && n2.getLight(bank, nodemgr) != 0)
348 Set light to 0 and add to queue
351 u8 current_light = n2.getLight(bank, nodemgr);
352 n2.setLight(bank, 0, nodemgr);
353 block->setNode(relpos, n2);
355 unlighted_nodes.insert(n2pos, current_light);
359 Remove from light_sources if it is there
360 NOTE: This doesn't happen nearly at all
362 /*if(light_sources.find(n2pos))
364 infostream<<"Removed from light_sources"<<std::endl;
365 light_sources.remove(n2pos);
370 if(light_sources.find(n2pos) != NULL)
371 light_sources.remove(n2pos);*/
374 light_sources.insert(n2pos, true);
377 // Add to modified_blocks
378 if(changed == true && block_checked_in_modified == false)
380 // If the block is not found in modified_blocks, add.
381 if(modified_blocks.find(blockpos) == NULL)
383 modified_blocks.insert(blockpos, block);
385 block_checked_in_modified = true;
388 catch(InvalidPositionException &e)
395 /*infostream<<"unspreadLight(): Changed block "
396 <<blockchangecount<<" times"
397 <<" for "<<from_nodes.size()<<" nodes"
400 if(unlighted_nodes.size() > 0)
401 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
405 A single-node wrapper of the above
407 void Map::unLightNeighbors(enum LightBank bank,
408 v3s16 pos, u8 lightwas,
409 core::map<v3s16, bool> & light_sources,
410 core::map<v3s16, MapBlock*> & modified_blocks)
412 core::map<v3s16, u8> from_nodes;
413 from_nodes.insert(pos, lightwas);
415 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
419 Lights neighbors of from_nodes, collects all them and then
422 void Map::spreadLight(enum LightBank bank,
423 core::map<v3s16, bool> & from_nodes,
424 core::map<v3s16, MapBlock*> & modified_blocks)
426 INodeDefManager *nodemgr = m_gamedef->ndef();
428 const v3s16 dirs[6] = {
429 v3s16(0,0,1), // back
431 v3s16(1,0,0), // right
432 v3s16(0,0,-1), // front
433 v3s16(0,-1,0), // bottom
434 v3s16(-1,0,0), // left
437 if(from_nodes.size() == 0)
440 u32 blockchangecount = 0;
442 core::map<v3s16, bool> lighted_nodes;
443 core::map<v3s16, bool>::Iterator j;
444 j = from_nodes.getIterator();
447 Initialize block cache
450 MapBlock *block = NULL;
451 // Cache this a bit, too
452 bool block_checked_in_modified = false;
454 for(; j.atEnd() == false; j++)
455 //for(; j != from_nodes.end(); j++)
457 v3s16 pos = j.getNode()->getKey();
459 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
460 v3s16 blockpos = getNodeBlockPos(pos);
462 // Only fetch a new block if the block position has changed
464 if(block == NULL || blockpos != blockpos_last){
465 block = getBlockNoCreate(blockpos);
466 blockpos_last = blockpos;
468 block_checked_in_modified = false;
472 catch(InvalidPositionException &e)
480 // Calculate relative position in block
481 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
483 // Get node straight from the block
484 MapNode n = block->getNode(relpos);
486 u8 oldlight = n.getLight(bank, nodemgr);
487 u8 newlight = diminish_light(oldlight);
489 // Loop through 6 neighbors
490 for(u16 i=0; i<6; i++){
491 // Get the position of the neighbor node
492 v3s16 n2pos = pos + dirs[i];
494 // Get the block where the node is located
495 v3s16 blockpos = getNodeBlockPos(n2pos);
499 // Only fetch a new block if the block position has changed
501 if(block == NULL || blockpos != blockpos_last){
502 block = getBlockNoCreate(blockpos);
503 blockpos_last = blockpos;
505 block_checked_in_modified = false;
509 catch(InvalidPositionException &e)
514 // Calculate relative position in block
515 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
516 // Get node straight from the block
517 MapNode n2 = block->getNode(relpos);
519 bool changed = false;
521 If the neighbor is brighter than the current node,
522 add to list (it will light up this node on its turn)
524 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
526 lighted_nodes.insert(n2pos, true);
527 //lighted_nodes.push_back(n2pos);
531 If the neighbor is dimmer than how much light this node
532 would spread on it, add to list
534 if(n2.getLight(bank, nodemgr) < newlight)
536 if(nodemgr->get(n2).light_propagates)
538 n2.setLight(bank, newlight, nodemgr);
539 block->setNode(relpos, n2);
540 lighted_nodes.insert(n2pos, true);
541 //lighted_nodes.push_back(n2pos);
546 // Add to modified_blocks
547 if(changed == true && block_checked_in_modified == false)
549 // If the block is not found in modified_blocks, add.
550 if(modified_blocks.find(blockpos) == NULL)
552 modified_blocks.insert(blockpos, block);
554 block_checked_in_modified = true;
557 catch(InvalidPositionException &e)
564 /*infostream<<"spreadLight(): Changed block "
565 <<blockchangecount<<" times"
566 <<" for "<<from_nodes.size()<<" nodes"
569 if(lighted_nodes.size() > 0)
570 spreadLight(bank, lighted_nodes, modified_blocks);
574 A single-node source variation of the above.
576 void Map::lightNeighbors(enum LightBank bank,
578 core::map<v3s16, MapBlock*> & modified_blocks)
580 core::map<v3s16, bool> from_nodes;
581 from_nodes.insert(pos, true);
582 spreadLight(bank, from_nodes, modified_blocks);
585 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
587 INodeDefManager *nodemgr = m_gamedef->ndef();
590 v3s16(0,0,1), // back
592 v3s16(1,0,0), // right
593 v3s16(0,0,-1), // front
594 v3s16(0,-1,0), // bottom
595 v3s16(-1,0,0), // left
598 u8 brightest_light = 0;
599 v3s16 brightest_pos(0,0,0);
600 bool found_something = false;
602 // Loop through 6 neighbors
603 for(u16 i=0; i<6; i++){
604 // Get the position of the neighbor node
605 v3s16 n2pos = p + dirs[i];
610 catch(InvalidPositionException &e)
614 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
615 brightest_light = n2.getLight(bank, nodemgr);
616 brightest_pos = n2pos;
617 found_something = true;
621 if(found_something == false)
622 throw InvalidPositionException();
624 return brightest_pos;
628 Propagates sunlight down from a node.
629 Starting point gets sunlight.
631 Returns the lowest y value of where the sunlight went.
633 Mud is turned into grass in where the sunlight stops.
635 s16 Map::propagateSunlight(v3s16 start,
636 core::map<v3s16, MapBlock*> & modified_blocks)
638 INodeDefManager *nodemgr = m_gamedef->ndef();
643 v3s16 pos(start.X, y, start.Z);
645 v3s16 blockpos = getNodeBlockPos(pos);
648 block = getBlockNoCreate(blockpos);
650 catch(InvalidPositionException &e)
655 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
656 MapNode n = block->getNode(relpos);
658 if(nodemgr->get(n).sunlight_propagates)
660 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
661 block->setNode(relpos, n);
663 modified_blocks.insert(blockpos, block);
667 // Sunlight goes no further
674 void Map::updateLighting(enum LightBank bank,
675 core::map<v3s16, MapBlock*> & a_blocks,
676 core::map<v3s16, MapBlock*> & modified_blocks)
678 INodeDefManager *nodemgr = m_gamedef->ndef();
680 /*m_dout<<DTIME<<"Map::updateLighting(): "
681 <<a_blocks.size()<<" blocks."<<std::endl;*/
683 //TimeTaker timer("updateLighting");
687 //u32 count_was = modified_blocks.size();
689 core::map<v3s16, MapBlock*> blocks_to_update;
691 core::map<v3s16, bool> light_sources;
693 core::map<v3s16, u8> unlight_from;
695 int num_bottom_invalid = 0;
698 //TimeTaker t("first stuff");
700 core::map<v3s16, MapBlock*>::Iterator i;
701 i = a_blocks.getIterator();
702 for(; i.atEnd() == false; i++)
704 MapBlock *block = i.getNode()->getValue();
708 // Don't bother with dummy blocks.
712 v3s16 pos = block->getPos();
713 v3s16 posnodes = block->getPosRelative();
714 modified_blocks.insert(pos, block);
716 blocks_to_update.insert(pos, block);
719 Clear all light from block
721 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
722 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
723 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
728 MapNode n = block->getNode(p);
729 u8 oldlight = n.getLight(bank, nodemgr);
730 n.setLight(bank, 0, nodemgr);
731 block->setNode(p, n);
733 // If node sources light, add to list
734 u8 source = nodemgr->get(n).light_source;
736 light_sources[p + posnodes] = true;
738 // Collect borders for unlighting
739 if((x==0 || x == MAP_BLOCKSIZE-1
740 || y==0 || y == MAP_BLOCKSIZE-1
741 || z==0 || z == MAP_BLOCKSIZE-1)
744 v3s16 p_map = p + posnodes;
745 unlight_from.insert(p_map, oldlight);
748 catch(InvalidPositionException &e)
751 This would happen when dealing with a
755 infostream<<"updateLighting(): InvalidPositionException"
760 if(bank == LIGHTBANK_DAY)
762 bool bottom_valid = block->propagateSunlight(light_sources);
765 num_bottom_invalid++;
767 // If bottom is valid, we're done.
771 else if(bank == LIGHTBANK_NIGHT)
773 // For night lighting, sunlight is not propagated
778 // Invalid lighting bank
782 /*infostream<<"Bottom for sunlight-propagated block ("
783 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
786 // Bottom sunlight is not valid; get the block and loop to it
790 block = getBlockNoCreate(pos);
792 catch(InvalidPositionException &e)
803 Enable this to disable proper lighting for speeding up map
804 generation for testing or whatever
807 //if(g_settings->get(""))
809 core::map<v3s16, MapBlock*>::Iterator i;
810 i = blocks_to_update.getIterator();
811 for(; i.atEnd() == false; i++)
813 MapBlock *block = i.getNode()->getValue();
814 v3s16 p = block->getPos();
815 block->setLightingExpired(false);
823 //TimeTaker timer("unspreadLight");
824 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
829 u32 diff = modified_blocks.size() - count_was;
830 count_was = modified_blocks.size();
831 infostream<<"unspreadLight modified "<<diff<<std::endl;
835 //TimeTaker timer("spreadLight");
836 spreadLight(bank, light_sources, modified_blocks);
841 u32 diff = modified_blocks.size() - count_was;
842 count_was = modified_blocks.size();
843 infostream<<"spreadLight modified "<<diff<<std::endl;
849 //MapVoxelManipulator vmanip(this);
851 // Make a manual voxel manipulator and load all the blocks
852 // that touch the requested blocks
853 ManualMapVoxelManipulator vmanip(this);
856 //TimeTaker timer("initialEmerge");
858 core::map<v3s16, MapBlock*>::Iterator i;
859 i = blocks_to_update.getIterator();
860 for(; i.atEnd() == false; i++)
862 MapBlock *block = i.getNode()->getValue();
863 v3s16 p = block->getPos();
865 // Add all surrounding blocks
866 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
869 Add all surrounding blocks that have up-to-date lighting
870 NOTE: This doesn't quite do the job (not everything
871 appropriate is lighted)
873 /*for(s16 z=-1; z<=1; z++)
874 for(s16 y=-1; y<=1; y++)
875 for(s16 x=-1; x<=1; x++)
877 v3s16 p2 = p + v3s16(x,y,z);
878 MapBlock *block = getBlockNoCreateNoEx(p2);
883 if(block->getLightingExpired())
885 vmanip.initialEmerge(p2, p2);
888 // Lighting of block will be updated completely
889 block->setLightingExpired(false);
894 //TimeTaker timer("unSpreadLight");
895 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
898 //TimeTaker timer("spreadLight");
899 vmanip.spreadLight(bank, light_sources, nodemgr);
902 //TimeTaker timer("blitBack");
903 vmanip.blitBack(modified_blocks);
905 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
910 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
913 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
914 core::map<v3s16, MapBlock*> & modified_blocks)
916 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
917 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
920 Update information about whether day and night light differ
922 for(core::map<v3s16, MapBlock*>::Iterator
923 i = modified_blocks.getIterator();
924 i.atEnd() == false; i++)
926 MapBlock *block = i.getNode()->getValue();
927 block->expireDayNightDiff();
933 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
934 core::map<v3s16, MapBlock*> &modified_blocks)
936 INodeDefManager *ndef = m_gamedef->ndef();
939 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
940 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
943 From this node to nodes underneath:
944 If lighting is sunlight (1.0), unlight neighbours and
949 v3s16 toppos = p + v3s16(0,1,0);
950 v3s16 bottompos = p + v3s16(0,-1,0);
952 bool node_under_sunlight = true;
953 core::map<v3s16, bool> light_sources;
956 Collect old node for rollback
958 RollbackNode rollback_oldnode(this, p, m_gamedef);
961 If there is a node at top and it doesn't have sunlight,
962 there has not been any sunlight going down.
964 Otherwise there probably is.
967 MapNode topnode = getNode(toppos);
969 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
970 node_under_sunlight = false;
972 catch(InvalidPositionException &e)
977 Remove all light that has come out of this node
980 enum LightBank banks[] =
985 for(s32 i=0; i<2; i++)
987 enum LightBank bank = banks[i];
989 u8 lightwas = getNode(p).getLight(bank, ndef);
991 // Add the block of the added node to modified_blocks
992 v3s16 blockpos = getNodeBlockPos(p);
993 MapBlock * block = getBlockNoCreate(blockpos);
994 assert(block != NULL);
995 modified_blocks.insert(blockpos, block);
997 assert(isValidPosition(p));
999 // Unlight neighbours of node.
1000 // This means setting light of all consequent dimmer nodes
1002 // This also collects the nodes at the border which will spread
1003 // light again into this.
1004 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1006 n.setLight(bank, 0, ndef);
1010 If node lets sunlight through and is under sunlight, it has
1013 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1015 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1019 Remove node metadata
1022 removeNodeMetadata(p);
1025 Set the node on the map
1031 If node is under sunlight and doesn't let sunlight through,
1032 take all sunlighted nodes under it and clear light from them
1033 and from where the light has been spread.
1034 TODO: This could be optimized by mass-unlighting instead
1037 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1041 //m_dout<<DTIME<<"y="<<y<<std::endl;
1042 v3s16 n2pos(p.X, y, p.Z);
1046 n2 = getNode(n2pos);
1048 catch(InvalidPositionException &e)
1053 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1055 unLightNeighbors(LIGHTBANK_DAY,
1056 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1057 light_sources, modified_blocks);
1058 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1066 for(s32 i=0; i<2; i++)
1068 enum LightBank bank = banks[i];
1071 Spread light from all nodes that might be capable of doing so
1073 spreadLight(bank, light_sources, modified_blocks);
1077 Update information about whether day and night light differ
1079 for(core::map<v3s16, MapBlock*>::Iterator
1080 i = modified_blocks.getIterator();
1081 i.atEnd() == false; i++)
1083 MapBlock *block = i.getNode()->getValue();
1084 block->expireDayNightDiff();
1090 if(m_gamedef->rollback())
1092 RollbackNode rollback_newnode(this, p, m_gamedef);
1093 RollbackAction action;
1094 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1095 m_gamedef->rollback()->reportAction(action);
1099 Add neighboring liquid nodes and the node itself if it is
1100 liquid (=water node was added) to transform queue.
1103 v3s16(0,0,0), // self
1104 v3s16(0,0,1), // back
1105 v3s16(0,1,0), // top
1106 v3s16(1,0,0), // right
1107 v3s16(0,0,-1), // front
1108 v3s16(0,-1,0), // bottom
1109 v3s16(-1,0,0), // left
1111 for(u16 i=0; i<7; i++)
1116 v3s16 p2 = p + dirs[i];
1118 MapNode n2 = getNode(p2);
1119 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1121 m_transforming_liquid.push_back(p2);
1124 }catch(InvalidPositionException &e)
1132 void Map::removeNodeAndUpdate(v3s16 p,
1133 core::map<v3s16, MapBlock*> &modified_blocks)
1135 INodeDefManager *ndef = m_gamedef->ndef();
1137 /*PrintInfo(m_dout);
1138 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1139 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1141 bool node_under_sunlight = true;
1143 v3s16 toppos = p + v3s16(0,1,0);
1145 // Node will be replaced with this
1146 content_t replace_material = CONTENT_AIR;
1149 Collect old node for rollback
1151 RollbackNode rollback_oldnode(this, p, m_gamedef);
1154 If there is a node at top and it doesn't have sunlight,
1155 there will be no sunlight going down.
1158 MapNode topnode = getNode(toppos);
1160 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1161 node_under_sunlight = false;
1163 catch(InvalidPositionException &e)
1167 core::map<v3s16, bool> light_sources;
1169 enum LightBank banks[] =
1174 for(s32 i=0; i<2; i++)
1176 enum LightBank bank = banks[i];
1179 Unlight neighbors (in case the node is a light source)
1181 unLightNeighbors(bank, p,
1182 getNode(p).getLight(bank, ndef),
1183 light_sources, modified_blocks);
1187 Remove node metadata
1190 removeNodeMetadata(p);
1194 This also clears the lighting.
1198 n.setContent(replace_material);
1201 for(s32 i=0; i<2; i++)
1203 enum LightBank bank = banks[i];
1206 Recalculate lighting
1208 spreadLight(bank, light_sources, modified_blocks);
1211 // Add the block of the removed node to modified_blocks
1212 v3s16 blockpos = getNodeBlockPos(p);
1213 MapBlock * block = getBlockNoCreate(blockpos);
1214 assert(block != NULL);
1215 modified_blocks.insert(blockpos, block);
1218 If the removed node was under sunlight, propagate the
1219 sunlight down from it and then light all neighbors
1220 of the propagated blocks.
1222 if(node_under_sunlight)
1224 s16 ybottom = propagateSunlight(p, modified_blocks);
1225 /*m_dout<<DTIME<<"Node was under sunlight. "
1226 "Propagating sunlight";
1227 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1229 for(; y >= ybottom; y--)
1231 v3s16 p2(p.X, y, p.Z);
1232 /*m_dout<<DTIME<<"lighting neighbors of node ("
1233 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1235 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1240 // Set the lighting of this node to 0
1241 // TODO: Is this needed? Lighting is cleared up there already.
1243 MapNode n = getNode(p);
1244 n.setLight(LIGHTBANK_DAY, 0, ndef);
1247 catch(InvalidPositionException &e)
1253 for(s32 i=0; i<2; i++)
1255 enum LightBank bank = banks[i];
1257 // Get the brightest neighbour node and propagate light from it
1258 v3s16 n2p = getBrightestNeighbour(bank, p);
1260 MapNode n2 = getNode(n2p);
1261 lightNeighbors(bank, n2p, modified_blocks);
1263 catch(InvalidPositionException &e)
1269 Update information about whether day and night light differ
1271 for(core::map<v3s16, MapBlock*>::Iterator
1272 i = modified_blocks.getIterator();
1273 i.atEnd() == false; i++)
1275 MapBlock *block = i.getNode()->getValue();
1276 block->expireDayNightDiff();
1282 if(m_gamedef->rollback())
1284 RollbackNode rollback_newnode(this, p, m_gamedef);
1285 RollbackAction action;
1286 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1287 m_gamedef->rollback()->reportAction(action);
1291 Add neighboring liquid nodes and this node to transform queue.
1292 (it's vital for the node itself to get updated last.)
1295 v3s16(0,0,1), // back
1296 v3s16(0,1,0), // top
1297 v3s16(1,0,0), // right
1298 v3s16(0,0,-1), // front
1299 v3s16(0,-1,0), // bottom
1300 v3s16(-1,0,0), // left
1301 v3s16(0,0,0), // self
1303 for(u16 i=0; i<7; i++)
1308 v3s16 p2 = p + dirs[i];
1310 MapNode n2 = getNode(p2);
1311 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1313 m_transforming_liquid.push_back(p2);
1316 }catch(InvalidPositionException &e)
1322 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1325 event.type = MEET_ADDNODE;
1329 bool succeeded = true;
1331 core::map<v3s16, MapBlock*> modified_blocks;
1332 addNodeAndUpdate(p, n, modified_blocks);
1334 // Copy modified_blocks to event
1335 for(core::map<v3s16, MapBlock*>::Iterator
1336 i = modified_blocks.getIterator();
1337 i.atEnd()==false; i++)
1339 event.modified_blocks.insert(i.getNode()->getKey(), false);
1342 catch(InvalidPositionException &e){
1346 dispatchEvent(&event);
1351 bool Map::removeNodeWithEvent(v3s16 p)
1354 event.type = MEET_REMOVENODE;
1357 bool succeeded = true;
1359 core::map<v3s16, MapBlock*> modified_blocks;
1360 removeNodeAndUpdate(p, modified_blocks);
1362 // Copy modified_blocks to event
1363 for(core::map<v3s16, MapBlock*>::Iterator
1364 i = modified_blocks.getIterator();
1365 i.atEnd()==false; i++)
1367 event.modified_blocks.insert(i.getNode()->getKey(), false);
1370 catch(InvalidPositionException &e){
1374 dispatchEvent(&event);
1379 bool Map::getDayNightDiff(v3s16 blockpos)
1382 v3s16 p = blockpos + v3s16(0,0,0);
1383 MapBlock *b = getBlockNoCreate(p);
1384 if(b->getDayNightDiff())
1387 catch(InvalidPositionException &e){}
1390 v3s16 p = blockpos + v3s16(-1,0,0);
1391 MapBlock *b = getBlockNoCreate(p);
1392 if(b->getDayNightDiff())
1395 catch(InvalidPositionException &e){}
1397 v3s16 p = blockpos + v3s16(0,-1,0);
1398 MapBlock *b = getBlockNoCreate(p);
1399 if(b->getDayNightDiff())
1402 catch(InvalidPositionException &e){}
1404 v3s16 p = blockpos + v3s16(0,0,-1);
1405 MapBlock *b = getBlockNoCreate(p);
1406 if(b->getDayNightDiff())
1409 catch(InvalidPositionException &e){}
1412 v3s16 p = blockpos + v3s16(1,0,0);
1413 MapBlock *b = getBlockNoCreate(p);
1414 if(b->getDayNightDiff())
1417 catch(InvalidPositionException &e){}
1419 v3s16 p = blockpos + v3s16(0,1,0);
1420 MapBlock *b = getBlockNoCreate(p);
1421 if(b->getDayNightDiff())
1424 catch(InvalidPositionException &e){}
1426 v3s16 p = blockpos + v3s16(0,0,1);
1427 MapBlock *b = getBlockNoCreate(p);
1428 if(b->getDayNightDiff())
1431 catch(InvalidPositionException &e){}
1437 Updates usage timers
1439 void Map::timerUpdate(float dtime, float unload_timeout,
1440 core::list<v3s16> *unloaded_blocks)
1442 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1444 // Profile modified reasons
1445 Profiler modprofiler;
1447 core::list<v2s16> sector_deletion_queue;
1448 u32 deleted_blocks_count = 0;
1449 u32 saved_blocks_count = 0;
1450 u32 block_count_all = 0;
1452 core::map<v2s16, MapSector*>::Iterator si;
1455 si = m_sectors.getIterator();
1456 for(; si.atEnd() == false; si++)
1458 MapSector *sector = si.getNode()->getValue();
1460 bool all_blocks_deleted = true;
1462 core::list<MapBlock*> blocks;
1463 sector->getBlocks(blocks);
1465 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1466 i != blocks.end(); i++)
1468 MapBlock *block = (*i);
1470 block->incrementUsageTimer(dtime);
1472 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1474 v3s16 p = block->getPos();
1477 if(block->getModified() != MOD_STATE_CLEAN
1478 && save_before_unloading)
1480 modprofiler.add(block->getModifiedReason(), 1);
1482 saved_blocks_count++;
1485 // Delete from memory
1486 sector->deleteBlock(block);
1489 unloaded_blocks->push_back(p);
1491 deleted_blocks_count++;
1495 all_blocks_deleted = false;
1500 if(all_blocks_deleted)
1502 sector_deletion_queue.push_back(si.getNode()->getKey());
1507 // Finally delete the empty sectors
1508 deleteSectors(sector_deletion_queue);
1510 if(deleted_blocks_count != 0)
1512 PrintInfo(infostream); // ServerMap/ClientMap:
1513 infostream<<"Unloaded "<<deleted_blocks_count
1514 <<" blocks from memory";
1515 if(save_before_unloading)
1516 infostream<<", of which "<<saved_blocks_count<<" were written";
1517 infostream<<", "<<block_count_all<<" blocks in memory";
1518 infostream<<"."<<std::endl;
1519 if(saved_blocks_count != 0){
1520 PrintInfo(infostream); // ServerMap/ClientMap:
1521 infostream<<"Blocks modified by: "<<std::endl;
1522 modprofiler.print(infostream);
1527 void Map::deleteSectors(core::list<v2s16> &list)
1529 core::list<v2s16>::Iterator j;
1530 for(j=list.begin(); j!=list.end(); j++)
1532 MapSector *sector = m_sectors[*j];
1533 // If sector is in sector cache, remove it from there
1534 if(m_sector_cache == sector)
1535 m_sector_cache = NULL;
1536 // Remove from map and delete
1537 m_sectors.remove(*j);
1543 void Map::unloadUnusedData(float timeout,
1544 core::list<v3s16> *deleted_blocks)
1546 core::list<v2s16> sector_deletion_queue;
1547 u32 deleted_blocks_count = 0;
1548 u32 saved_blocks_count = 0;
1550 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1551 for(; si.atEnd() == false; si++)
1553 MapSector *sector = si.getNode()->getValue();
1555 bool all_blocks_deleted = true;
1557 core::list<MapBlock*> blocks;
1558 sector->getBlocks(blocks);
1559 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1560 i != blocks.end(); i++)
1562 MapBlock *block = (*i);
1564 if(block->getUsageTimer() > timeout)
1567 if(block->getModified() != MOD_STATE_CLEAN)
1570 saved_blocks_count++;
1572 // Delete from memory
1573 sector->deleteBlock(block);
1574 deleted_blocks_count++;
1578 all_blocks_deleted = false;
1582 if(all_blocks_deleted)
1584 sector_deletion_queue.push_back(si.getNode()->getKey());
1588 deleteSectors(sector_deletion_queue);
1590 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1591 <<", of which "<<saved_blocks_count<<" were wr."
1594 //return sector_deletion_queue.getSize();
1595 //return deleted_blocks_count;
1599 void Map::PrintInfo(std::ostream &out)
1604 #define WATER_DROP_BOOST 4
1608 NEIGHBOR_SAME_LEVEL,
1611 struct NodeNeighbor {
1617 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1619 INodeDefManager *nodemgr = m_gamedef->ndef();
1621 DSTACK(__FUNCTION_NAME);
1622 //TimeTaker timer("transformLiquids()");
1625 u32 initial_size = m_transforming_liquid.size();
1627 /*if(initial_size != 0)
1628 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1630 // list of nodes that due to viscosity have not reached their max level height
1631 UniqueQueue<v3s16> must_reflow;
1633 // List of MapBlocks that will require a lighting update (due to lava)
1634 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1636 while(m_transforming_liquid.size() != 0)
1638 // This should be done here so that it is done when continue is used
1639 if(loopcount >= initial_size || loopcount >= 10000)
1644 Get a queued transforming liquid node
1646 v3s16 p0 = m_transforming_liquid.pop_front();
1648 MapNode n0 = getNodeNoEx(p0);
1651 Collect information about current node
1653 s8 liquid_level = -1;
1654 content_t liquid_kind = CONTENT_IGNORE;
1655 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1656 switch (liquid_type) {
1658 liquid_level = LIQUID_LEVEL_SOURCE;
1659 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1661 case LIQUID_FLOWING:
1662 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1663 liquid_kind = n0.getContent();
1666 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1667 // continue with the next node.
1668 if (n0.getContent() != CONTENT_AIR)
1670 liquid_kind = CONTENT_AIR;
1675 Collect information about the environment
1677 const v3s16 *dirs = g_6dirs;
1678 NodeNeighbor sources[6]; // surrounding sources
1679 int num_sources = 0;
1680 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1682 NodeNeighbor airs[6]; // surrounding air
1684 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1685 int num_neutrals = 0;
1686 bool flowing_down = false;
1687 for (u16 i = 0; i < 6; i++) {
1688 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1691 nt = NEIGHBOR_UPPER;
1694 nt = NEIGHBOR_LOWER;
1697 v3s16 npos = p0 + dirs[i];
1698 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1699 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1701 if (nb.n.getContent() == CONTENT_AIR) {
1702 airs[num_airs++] = nb;
1703 // if the current node is a water source the neighbor
1704 // should be enqueded for transformation regardless of whether the
1705 // current node changes or not.
1706 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1707 m_transforming_liquid.push_back(npos);
1708 // if the current node happens to be a flowing node, it will start to flow down here.
1709 if (nb.t == NEIGHBOR_LOWER) {
1710 flowing_down = true;
1713 neutrals[num_neutrals++] = nb;
1717 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1718 if (liquid_kind == CONTENT_AIR)
1719 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1720 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1721 neutrals[num_neutrals++] = nb;
1723 // Do not count bottom source, it will screw things up
1725 sources[num_sources++] = nb;
1728 case LIQUID_FLOWING:
1729 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1730 if (liquid_kind == CONTENT_AIR)
1731 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1732 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1733 neutrals[num_neutrals++] = nb;
1735 flows[num_flows++] = nb;
1736 if (nb.t == NEIGHBOR_LOWER)
1737 flowing_down = true;
1744 decide on the type (and possibly level) of the current node
1746 content_t new_node_content;
1747 s8 new_node_level = -1;
1748 s8 max_node_level = -1;
1749 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1750 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1751 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1752 // it's perfectly safe to use liquid_kind here to determine the new node content.
1753 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1754 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1755 // liquid_kind is set properly, see above
1756 new_node_content = liquid_kind;
1757 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1759 // no surrounding sources, so get the maximum level that can flow into this node
1760 for (u16 i = 0; i < num_flows; i++) {
1761 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1762 switch (flows[i].t) {
1763 case NEIGHBOR_UPPER:
1764 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1765 max_node_level = LIQUID_LEVEL_MAX;
1766 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1767 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1768 } else if (nb_liquid_level > max_node_level)
1769 max_node_level = nb_liquid_level;
1771 case NEIGHBOR_LOWER:
1773 case NEIGHBOR_SAME_LEVEL:
1774 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1775 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1776 max_node_level = nb_liquid_level - 1;
1782 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1783 if (viscosity > 1 && max_node_level != liquid_level) {
1784 // amount to gain, limited by viscosity
1785 // must be at least 1 in absolute value
1786 s8 level_inc = max_node_level - liquid_level;
1787 if (level_inc < -viscosity || level_inc > viscosity)
1788 new_node_level = liquid_level + level_inc/viscosity;
1789 else if (level_inc < 0)
1790 new_node_level = liquid_level - 1;
1791 else if (level_inc > 0)
1792 new_node_level = liquid_level + 1;
1793 if (new_node_level != max_node_level)
1794 must_reflow.push_back(p0);
1796 new_node_level = max_node_level;
1798 if (new_node_level >= 0)
1799 new_node_content = liquid_kind;
1801 new_node_content = CONTENT_AIR;
1806 check if anything has changed. if not, just continue with the next node.
1808 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1809 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1810 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1816 update the current node
1818 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1819 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1820 // set level to last 3 bits, flowing down bit to 4th bit
1821 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1823 // set the liquid level and flow bit to 0
1824 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1826 n0.setContent(new_node_content);
1828 // Find out whether there is a suspect for this action
1829 std::string suspect;
1830 if(m_gamedef->rollback()){
1831 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1834 if(!suspect.empty()){
1836 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1837 // Get old node for rollback
1838 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1842 RollbackNode rollback_newnode(this, p0, m_gamedef);
1843 RollbackAction action;
1844 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1845 m_gamedef->rollback()->reportAction(action);
1851 v3s16 blockpos = getNodeBlockPos(p0);
1852 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1854 modified_blocks.insert(blockpos, block);
1855 // If node emits light, MapBlock requires lighting update
1856 if(nodemgr->get(n0).light_source != 0)
1857 lighting_modified_blocks[block->getPos()] = block;
1861 enqueue neighbors for update if neccessary
1863 switch (nodemgr->get(n0.getContent()).liquid_type) {
1865 case LIQUID_FLOWING:
1866 // make sure source flows into all neighboring nodes
1867 for (u16 i = 0; i < num_flows; i++)
1868 if (flows[i].t != NEIGHBOR_UPPER)
1869 m_transforming_liquid.push_back(flows[i].p);
1870 for (u16 i = 0; i < num_airs; i++)
1871 if (airs[i].t != NEIGHBOR_UPPER)
1872 m_transforming_liquid.push_back(airs[i].p);
1875 // this flow has turned to air; neighboring flows might need to do the same
1876 for (u16 i = 0; i < num_flows; i++)
1877 m_transforming_liquid.push_back(flows[i].p);
1881 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1882 while (must_reflow.size() > 0)
1883 m_transforming_liquid.push_back(must_reflow.pop_front());
1884 updateLighting(lighting_modified_blocks, modified_blocks);
1887 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1889 v3s16 blockpos = getNodeBlockPos(p);
1890 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1891 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1893 infostream<<"Map::getNodeMetadata(): Need to emerge "
1894 <<PP(blockpos)<<std::endl;
1895 block = emergeBlock(blockpos, false);
1899 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1903 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1907 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1909 v3s16 blockpos = getNodeBlockPos(p);
1910 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1911 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1913 infostream<<"Map::setNodeMetadata(): Need to emerge "
1914 <<PP(blockpos)<<std::endl;
1915 block = emergeBlock(blockpos, false);
1919 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1923 block->m_node_metadata.set(p_rel, meta);
1926 void Map::removeNodeMetadata(v3s16 p)
1928 v3s16 blockpos = getNodeBlockPos(p);
1929 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1930 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1933 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1937 block->m_node_metadata.remove(p_rel);
1940 NodeTimer Map::getNodeTimer(v3s16 p)
1942 v3s16 blockpos = getNodeBlockPos(p);
1943 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1944 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1946 infostream<<"Map::getNodeTimer(): Need to emerge "
1947 <<PP(blockpos)<<std::endl;
1948 block = emergeBlock(blockpos, false);
1952 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1956 NodeTimer t = block->m_node_timers.get(p_rel);
1960 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1962 v3s16 blockpos = getNodeBlockPos(p);
1963 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1964 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1966 infostream<<"Map::setNodeTimer(): Need to emerge "
1967 <<PP(blockpos)<<std::endl;
1968 block = emergeBlock(blockpos, false);
1972 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1976 block->m_node_timers.set(p_rel, t);
1979 void Map::removeNodeTimer(v3s16 p)
1981 v3s16 blockpos = getNodeBlockPos(p);
1982 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1983 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1986 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
1990 block->m_node_timers.remove(p_rel);
1996 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1997 Map(dout_server, gamedef),
2000 m_map_metadata_changed(true),
2002 m_database_read(NULL),
2003 m_database_write(NULL)
2005 verbosestream<<__FUNCTION_NAME<<std::endl;
2007 //m_chunksize = 8; // Takes a few seconds
2009 m_mgparams = MapgenParams::getParamsFromSettings(g_settings);
2011 m_mgparams = new MapgenV6Params();
2013 m_seed = m_mgparams->seed;
2015 if (g_settings->get("fixed_map_seed").empty())
2017 m_seed = (((u64)(myrand()%0xffff)<<0)
2018 + ((u64)(myrand()%0xffff)<<16)
2019 + ((u64)(myrand()%0xffff)<<32)
2020 + ((u64)(myrand()&0xffff)<<48));
2021 m_mgparams->seed = m_seed;
2025 Experimental and debug stuff
2032 Try to load map; if not found, create a new one.
2035 m_savedir = savedir;
2036 m_map_saving_enabled = false;
2040 // If directory exists, check contents and load if possible
2041 if(fs::PathExists(m_savedir))
2043 // If directory is empty, it is safe to save into it.
2044 if(fs::GetDirListing(m_savedir).size() == 0)
2046 infostream<<"ServerMap: Empty save directory is valid."
2048 m_map_saving_enabled = true;
2053 // Load map metadata (seed, chunksize)
2056 catch(SettingNotFoundException &e){
2057 infostream<<"ServerMap: Some metadata not found."
2058 <<" Using default settings."<<std::endl;
2060 catch(FileNotGoodException &e){
2061 infostream<<"WARNING: Could not load map metadata"
2062 //<<" Disabling chunk-based generator."
2067 infostream<<"ServerMap: Successfully loaded map "
2068 <<"metadata from "<<savedir
2069 <<", assuming valid save directory."
2070 <<" seed="<<m_seed<<"."
2073 m_map_saving_enabled = true;
2074 // Map loaded, not creating new one
2078 // If directory doesn't exist, it is safe to save to it
2080 m_map_saving_enabled = true;
2083 catch(std::exception &e)
2085 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2086 <<", exception: "<<e.what()<<std::endl;
2087 infostream<<"Please remove the map or fix it."<<std::endl;
2088 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2091 infostream<<"Initializing new map."<<std::endl;
2093 // Create zero sector
2094 emergeSector(v2s16(0,0));
2096 // Initially write whole map
2097 save(MOD_STATE_CLEAN);
2100 ServerMap::~ServerMap()
2102 verbosestream<<__FUNCTION_NAME<<std::endl;
2106 if(m_map_saving_enabled)
2108 // Save only changed parts
2109 save(MOD_STATE_WRITE_AT_UNLOAD);
2110 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2114 infostream<<"ServerMap: Map not saved"<<std::endl;
2117 catch(std::exception &e)
2119 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2120 <<", exception: "<<e.what()<<std::endl;
2124 Close database if it was opened
2127 sqlite3_finalize(m_database_read);
2128 if(m_database_write)
2129 sqlite3_finalize(m_database_write);
2131 sqlite3_close(m_database);
2137 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2138 for(; i.atEnd() == false; i++)
2140 MapChunk *chunk = i.getNode()->getValue();
2146 void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2148 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2149 if(enable_mapgen_debug_info)
2150 infostream<<"initBlockMake(): "
2151 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2152 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2155 //s16 chunksize = 3;
2156 //v3s16 chunk_offset(-1,-1,-1);
2157 //s16 chunksize = 4;
2158 //v3s16 chunk_offset(-1,-1,-1);
2160 v3s16 chunk_offset(-2,-2,-2);
2161 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2162 v3s16 blockpos_min = blockpos_div * chunksize;
2163 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2164 blockpos_min += chunk_offset;
2165 blockpos_max += chunk_offset;
2167 //v3s16 extra_borders(1,1,1);
2168 v3s16 extra_borders(1,1,1);
2170 // Do nothing if not inside limits (+-1 because of neighbors)
2171 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2172 blockpos_over_limit(blockpos_max + extra_borders))
2178 data->no_op = false;
2179 data->seed = m_seed;
2180 data->blockpos_min = blockpos_min;
2181 data->blockpos_max = blockpos_max;
2182 data->blockpos_requested = blockpos;
2183 data->nodedef = m_gamedef->ndef();
2186 Create the whole area of this and the neighboring blocks
2189 //TimeTaker timer("initBlockMake() create area");
2191 for(s16 x=blockpos_min.X-extra_borders.X;
2192 x<=blockpos_max.X+extra_borders.X; x++)
2193 for(s16 z=blockpos_min.Z-extra_borders.Z;
2194 z<=blockpos_max.Z+extra_borders.Z; z++)
2196 v2s16 sectorpos(x, z);
2197 // Sector metadata is loaded from disk if not already loaded.
2198 ServerMapSector *sector = createSector(sectorpos);
2201 for(s16 y=blockpos_min.Y-extra_borders.Y;
2202 y<=blockpos_max.Y+extra_borders.Y; y++)
2205 //MapBlock *block = createBlock(p);
2206 // 1) get from memory, 2) load from disk
2207 MapBlock *block = emergeBlock(p, false);
2208 // 3) create a blank one
2211 block = createBlock(p);
2214 Block gets sunlight if this is true.
2216 Refer to the map generator heuristics.
2218 bool ug = m_emerge->isBlockUnderground(p);
2219 block->setIsUnderground(ug);
2222 // Lighting will not be valid after make_chunk is called
2223 block->setLightingExpired(true);
2224 // Lighting will be calculated
2225 //block->setLightingExpired(false);
2231 Now we have a big empty area.
2233 Make a ManualMapVoxelManipulator that contains this and the
2237 // The area that contains this block and it's neighbors
2238 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2239 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2241 data->vmanip = new ManualMapVoxelManipulator(this);
2242 //data->vmanip->setMap(this);
2246 //TimeTaker timer("initBlockMake() initialEmerge");
2247 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2250 // Data is ready now.
2253 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2254 core::map<v3s16, MapBlock*> &changed_blocks)
2256 v3s16 blockpos_min = data->blockpos_min;
2257 v3s16 blockpos_max = data->blockpos_max;
2258 v3s16 blockpos_requested = data->blockpos_requested;
2259 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2260 <<blockpos_requested.Y<<","
2261 <<blockpos_requested.Z<<")"<<std::endl;*/
2263 v3s16 extra_borders(1,1,1);
2267 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2271 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2273 /*infostream<<"Resulting vmanip:"<<std::endl;
2274 data->vmanip.print(infostream);*/
2276 // Make sure affected blocks are loaded
2277 for(s16 x=blockpos_min.X-extra_borders.X;
2278 x<=blockpos_max.X+extra_borders.X; x++)
2279 for(s16 z=blockpos_min.Z-extra_borders.Z;
2280 z<=blockpos_max.Z+extra_borders.Z; z++)
2281 for(s16 y=blockpos_min.Y-extra_borders.Y;
2282 y<=blockpos_max.Y+extra_borders.Y; y++)
2285 // Load from disk if not already in memory
2286 emergeBlock(p, false);
2290 Blit generated stuff to map
2291 NOTE: blitBackAll adds nearly everything to changed_blocks
2295 //TimeTaker timer("finishBlockMake() blitBackAll");
2296 data->vmanip->blitBackAll(&changed_blocks);
2299 if(enable_mapgen_debug_info)
2300 infostream<<"finishBlockMake: changed_blocks.size()="
2301 <<changed_blocks.size()<<std::endl;
2304 Copy transforming liquid information
2306 while(data->transforming_liquid.size() > 0)
2308 v3s16 p = data->transforming_liquid.pop_front();
2309 m_transforming_liquid.push_back(p);
2313 Do stuff in central blocks
2321 TimeTaker t("finishBlockMake lighting update");
2323 core::map<v3s16, MapBlock*> lighting_update_blocks;
2326 for(s16 x=blockpos_min.X-extra_borders.X;
2327 x<=blockpos_max.X+extra_borders.X; x++)
2328 for(s16 z=blockpos_min.Z-extra_borders.Z;
2329 z<=blockpos_max.Z+extra_borders.Z; z++)
2330 for(s16 y=blockpos_min.Y-extra_borders.Y;
2331 y<=blockpos_max.Y+extra_borders.Y; y++)
2334 MapBlock *block = getBlockNoCreateNoEx(p);
2336 lighting_update_blocks.insert(block->getPos(), block);
2339 updateLighting(lighting_update_blocks, changed_blocks);
2343 Set lighting to non-expired state in all of them.
2344 This is cheating, but it is not fast enough if all of them
2345 would actually be updated.
2347 for(s16 x=blockpos_min.X-extra_borders.X;
2348 x<=blockpos_max.X+extra_borders.X; x++)
2349 for(s16 z=blockpos_min.Z-extra_borders.Z;
2350 z<=blockpos_max.Z+extra_borders.Z; z++)
2351 for(s16 y=blockpos_min.Y-extra_borders.Y;
2352 y<=blockpos_max.Y+extra_borders.Y; y++)
2355 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2359 if(enable_mapgen_debug_info == false)
2360 t.stop(true); // Hide output
2365 Go through changed blocks
2367 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2368 i.atEnd() == false; i++)
2370 MapBlock *block = i.getNode()->getValue();
2373 Update day/night difference cache of the MapBlocks
2375 block->expireDayNightDiff();
2377 Set block as modified
2379 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2380 "finishBlockMake expireDayNightDiff");
2384 Set central blocks as generated
2386 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2387 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2388 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2391 MapBlock *block = getBlockNoCreateNoEx(p);
2393 block->setGenerated(true);
2397 Save changed parts of map
2398 NOTE: Will be saved later.
2400 //save(MOD_STATE_WRITE_AT_UNLOAD);
2402 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2403 <<","<<blockpos_requested.Y<<","
2404 <<blockpos_requested.Z<<")"<<std::endl;*/
2406 if(enable_mapgen_debug_info)
2409 Analyze resulting blocks
2411 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2412 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2413 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2414 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2415 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2416 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2418 v3s16 p = v3s16(x,y,z);
2419 MapBlock *block = getBlockNoCreateNoEx(p);
2421 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2422 infostream<<"Generated "<<spos<<": "
2423 <<analyze_block(block)<<std::endl;
2428 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2434 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2436 DSTACKF("%s: p2d=(%d,%d)",
2441 Check if it exists already in memory
2443 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2448 Try to load it from disk (with blocks)
2450 //if(loadSectorFull(p2d) == true)
2453 Try to load metadata from disk
2456 if(loadSectorMeta(p2d) == true)
2458 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2461 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2462 throw InvalidPositionException("");
2468 Do not create over-limit
2470 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2471 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2472 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2473 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2474 throw InvalidPositionException("createSector(): pos. over limit");
2477 Generate blank sector
2480 sector = new ServerMapSector(this, p2d, m_gamedef);
2482 // Sector position on map in nodes
2483 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2488 m_sectors.insert(p2d, sector);
2495 This is a quick-hand function for calling makeBlock().
2497 MapBlock * ServerMap::generateBlock(
2499 core::map<v3s16, MapBlock*> &modified_blocks
2502 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2504 /*infostream<<"generateBlock(): "
2505 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2508 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2510 TimeTaker timer("generateBlock");
2512 //MapBlock *block = original_dummy;
2514 v2s16 p2d(p.X, p.Z);
2515 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2518 Do not generate over-limit
2520 if(blockpos_over_limit(p))
2522 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2523 throw InvalidPositionException("generateBlock(): pos. over limit");
2527 Create block make data
2530 initBlockMake(&data, p);
2536 TimeTaker t("mapgen::make_block()");
2537 mapgen->makeChunk(&data);
2538 //mapgen::make_block(&data);
2540 if(enable_mapgen_debug_info == false)
2541 t.stop(true); // Hide output
2545 Blit data back on map, update lighting, add mobs and whatever this does
2547 finishBlockMake(&data, modified_blocks);
2552 MapBlock *block = getBlockNoCreateNoEx(p);
2560 bool erroneus_content = false;
2561 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2562 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2563 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2566 MapNode n = block->getNode(p);
2567 if(n.getContent() == CONTENT_IGNORE)
2569 infostream<<"CONTENT_IGNORE at "
2570 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2572 erroneus_content = true;
2576 if(erroneus_content)
2585 Generate a completely empty block
2589 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2590 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2592 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2595 n.setContent(CONTENT_AIR);
2596 block->setNode(v3s16(x0,y0,z0), n);
2602 if(enable_mapgen_debug_info == false)
2603 timer.stop(true); // Hide output
2609 MapBlock * ServerMap::createBlock(v3s16 p)
2611 DSTACKF("%s: p=(%d,%d,%d)",
2612 __FUNCTION_NAME, p.X, p.Y, p.Z);
2615 Do not create over-limit
2617 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2618 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2619 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2620 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2621 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2622 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2623 throw InvalidPositionException("createBlock(): pos. over limit");
2625 v2s16 p2d(p.X, p.Z);
2628 This will create or load a sector if not found in memory.
2629 If block exists on disk, it will be loaded.
2631 NOTE: On old save formats, this will be slow, as it generates
2632 lighting on blocks for them.
2634 ServerMapSector *sector;
2636 sector = (ServerMapSector*)createSector(p2d);
2637 assert(sector->getId() == MAPSECTOR_SERVER);
2639 catch(InvalidPositionException &e)
2641 infostream<<"createBlock: createSector() failed"<<std::endl;
2645 NOTE: This should not be done, or at least the exception
2646 should not be passed on as std::exception, because it
2647 won't be catched at all.
2649 /*catch(std::exception &e)
2651 infostream<<"createBlock: createSector() failed: "
2652 <<e.what()<<std::endl;
2657 Try to get a block from the sector
2660 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2663 if(block->isDummy())
2668 block = sector->createBlankBlock(block_y);
2673 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2675 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2677 p.X, p.Y, p.Z, create_blank);
2680 MapBlock *block = getBlockNoCreateNoEx(p);
2681 if(block && block->isDummy() == false)
2686 MapBlock *block = loadBlock(p);
2692 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2693 MapBlock *block = sector->createBlankBlock(p.Y);
2697 /*if(allow_generate)
2699 core::map<v3s16, MapBlock*> modified_blocks;
2700 MapBlock *block = generateBlock(p, modified_blocks);
2704 event.type = MEET_OTHER;
2707 // Copy modified_blocks to event
2708 for(core::map<v3s16, MapBlock*>::Iterator
2709 i = modified_blocks.getIterator();
2710 i.atEnd()==false; i++)
2712 event.modified_blocks.insert(i.getNode()->getKey(), false);
2716 dispatchEvent(&event);
2725 s16 ServerMap::findGroundLevel(v2s16 p2d)
2729 Uh, just do something random...
2731 // Find existing map from top to down
2734 v3s16 p(p2d.X, max, p2d.Y);
2735 for(; p.Y>min; p.Y--)
2737 MapNode n = getNodeNoEx(p);
2738 if(n.getContent() != CONTENT_IGNORE)
2743 // If this node is not air, go to plan b
2744 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2746 // Search existing walkable and return it
2747 for(; p.Y>min; p.Y--)
2749 MapNode n = getNodeNoEx(p);
2750 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2759 Determine from map generator noise functions
2762 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2765 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2766 //return (s16)level;
2769 void ServerMap::createDatabase() {
2772 e = sqlite3_exec(m_database,
2773 "CREATE TABLE IF NOT EXISTS `blocks` ("
2774 "`pos` INT NOT NULL PRIMARY KEY,"
2777 , NULL, NULL, NULL);
2778 if(e == SQLITE_ABORT)
2779 throw FileNotGoodException("Could not create database structure");
2781 infostream<<"ServerMap: Database structure was created";
2784 void ServerMap::verifyDatabase() {
2789 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2790 bool needs_create = false;
2794 Open the database connection
2797 createDirs(m_savedir);
2799 if(!fs::PathExists(dbp))
2800 needs_create = true;
2802 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2803 if(d != SQLITE_OK) {
2804 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2805 throw FileNotGoodException("Cannot open database file");
2811 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2812 if(d != SQLITE_OK) {
2813 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2814 throw FileNotGoodException("Cannot prepare read statement");
2817 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2818 if(d != SQLITE_OK) {
2819 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2820 throw FileNotGoodException("Cannot prepare write statement");
2823 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2824 if(d != SQLITE_OK) {
2825 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2826 throw FileNotGoodException("Cannot prepare read statement");
2829 infostream<<"ServerMap: Database opened"<<std::endl;
2833 bool ServerMap::loadFromFolders() {
2834 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2839 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2840 return (sqlite3_int64)pos.Z*16777216 +
2841 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2844 void ServerMap::createDirs(std::string path)
2846 if(fs::CreateAllDirs(path) == false)
2848 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2849 <<"\""<<path<<"\""<<std::endl;
2850 throw BaseException("ServerMap failed to create directory");
2854 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2860 snprintf(cc, 9, "%.4x%.4x",
2861 (unsigned int)pos.X&0xffff,
2862 (unsigned int)pos.Y&0xffff);
2864 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2866 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2867 (unsigned int)pos.X&0xfff,
2868 (unsigned int)pos.Y&0xfff);
2870 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2876 v2s16 ServerMap::getSectorPos(std::string dirname)
2880 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2881 assert(spos != std::string::npos);
2882 if(dirname.size() - spos == 8)
2885 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2887 else if(dirname.size() - spos == 3)
2890 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2891 // Sign-extend the 12 bit values up to 16 bits...
2892 if(x&0x800) x|=0xF000;
2893 if(y&0x800) y|=0xF000;
2900 v2s16 pos((s16)x, (s16)y);
2904 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2906 v2s16 p2d = getSectorPos(sectordir);
2908 if(blockfile.size() != 4){
2909 throw InvalidFilenameException("Invalid block filename");
2912 int r = sscanf(blockfile.c_str(), "%4x", &y);
2914 throw InvalidFilenameException("Invalid block filename");
2915 return v3s16(p2d.X, y, p2d.Y);
2918 std::string ServerMap::getBlockFilename(v3s16 p)
2921 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2925 void ServerMap::save(ModifiedState save_level)
2927 DSTACK(__FUNCTION_NAME);
2928 if(m_map_saving_enabled == false)
2930 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2934 if(save_level == MOD_STATE_CLEAN)
2935 infostream<<"ServerMap: Saving whole map, this can take time."
2938 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2943 // Profile modified reasons
2944 Profiler modprofiler;
2946 u32 sector_meta_count = 0;
2947 u32 block_count = 0;
2948 u32 block_count_all = 0; // Number of blocks in memory
2950 // Don't do anything with sqlite unless something is really saved
2951 bool save_started = false;
2953 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2954 for(; i.atEnd() == false; i++)
2956 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2957 assert(sector->getId() == MAPSECTOR_SERVER);
2959 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2961 saveSectorMeta(sector);
2962 sector_meta_count++;
2964 core::list<MapBlock*> blocks;
2965 sector->getBlocks(blocks);
2966 core::list<MapBlock*>::Iterator j;
2968 for(j=blocks.begin(); j!=blocks.end(); j++)
2970 MapBlock *block = *j;
2974 if(block->getModified() >= save_level)
2979 save_started = true;
2982 modprofiler.add(block->getModifiedReason(), 1);
2987 /*infostream<<"ServerMap: Written block ("
2988 <<block->getPos().X<<","
2989 <<block->getPos().Y<<","
2990 <<block->getPos().Z<<")"
2999 Only print if something happened or saved whole map
3001 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3002 || block_count != 0)
3004 infostream<<"ServerMap: Written: "
3005 <<sector_meta_count<<" sector metadata files, "
3006 <<block_count<<" block files"
3007 <<", "<<block_count_all<<" blocks in memory."
3009 PrintInfo(infostream); // ServerMap/ClientMap:
3010 infostream<<"Blocks modified by: "<<std::endl;
3011 modprofiler.print(infostream);
3015 static s32 unsignedToSigned(s32 i, s32 max_positive)
3017 if(i < max_positive)
3020 return i - 2*max_positive;
3023 // modulo of a negative number does not work consistently in C
3024 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3028 return mod - ((-i) % mod);
3031 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3033 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3035 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3037 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3038 return v3s16(x,y,z);
3041 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
3043 if(loadFromFolders()){
3044 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3045 <<"all blocks that are stored in flat files"<<std::endl;
3051 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3053 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3054 v3s16 p = getIntegerAsBlock(block_i);
3055 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3061 void ServerMap::saveMapMeta()
3063 DSTACK(__FUNCTION_NAME);
3065 /*infostream<<"ServerMap::saveMapMeta(): "
3069 createDirs(m_savedir);
3071 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3072 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3073 if(os.good() == false)
3075 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3076 <<"could not open"<<fullpath<<std::endl;
3077 throw FileNotGoodException("Cannot open chunk metadata");
3082 params.setS16("mg_version", m_emerge->params->mg_version);
3084 params.setU64("seed", m_emerge->params->seed);
3085 params.setS16("water_level", m_emerge->params->water_level);
3086 params.setS16("chunksize", m_emerge->params->chunksize);
3087 params.setS32("mg_flags", m_emerge->params->flags);
3088 switch (m_emerge->params->mg_version) {
3091 MapgenV6Params *v6params = (MapgenV6Params *)m_emerge->params;
3093 params.setFloat("mgv6_freq_desert", v6params->freq_desert);
3094 params.setFloat("mgv6_freq_beach", v6params->freq_beach);
3095 params.setNoiseParams("mgv6_np_terrain_base", v6params->np_terrain_base);
3096 params.setNoiseParams("mgv6_np_terrain_higher", v6params->np_terrain_higher);
3097 params.setNoiseParams("mgv6_np_steepness", v6params->np_steepness);
3098 params.setNoiseParams("mgv6_np_height_select", v6params->np_height_select);
3099 params.setNoiseParams("mgv6_np_trees", v6params->np_trees);
3100 params.setNoiseParams("mgv6_np_mud", v6params->np_mud);
3101 params.setNoiseParams("mgv6_np_beach", v6params->np_beach);
3102 params.setNoiseParams("mgv6_np_biome", v6params->np_biome);
3103 params.setNoiseParams("mgv6_np_cave", v6params->np_cave);
3110 params.writeLines(os);
3112 os<<"[end_of_params]\n";
3114 m_map_metadata_changed = false;
3117 void ServerMap::loadMapMeta()
3119 DSTACK(__FUNCTION_NAME);
3121 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3124 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3125 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3126 if(is.good() == false)
3128 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3129 <<"could not open"<<fullpath<<std::endl;
3130 throw FileNotGoodException("Cannot open map metadata");
3138 throw SerializationError
3139 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3141 std::getline(is, line);
3142 std::string trimmedline = trim(line);
3143 if(trimmedline == "[end_of_params]")
3145 params.parseConfigLine(line);
3148 MapgenParams *mgparams = MapgenParams::getParamsFromSettings(¶ms);
3152 m_mgparams = mgparams;
3153 m_seed = mgparams->seed;
3155 if (params.exists("seed")) {
3156 m_seed = params.getU64("seed");
3157 m_mgparams->seed = m_seed;
3161 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3164 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3166 DSTACK(__FUNCTION_NAME);
3167 // Format used for writing
3168 u8 version = SER_FMT_VER_HIGHEST;
3170 v2s16 pos = sector->getPos();
3171 std::string dir = getSectorDir(pos);
3174 std::string fullpath = dir + DIR_DELIM + "meta";
3175 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3176 if(o.good() == false)
3177 throw FileNotGoodException("Cannot open sector metafile");
3179 sector->serialize(o, version);
3181 sector->differs_from_disk = false;
3184 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3186 DSTACK(__FUNCTION_NAME);
3188 v2s16 p2d = getSectorPos(sectordir);
3190 ServerMapSector *sector = NULL;
3192 std::string fullpath = sectordir + DIR_DELIM + "meta";
3193 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3194 if(is.good() == false)
3196 // If the directory exists anyway, it probably is in some old
3197 // format. Just go ahead and create the sector.
3198 if(fs::PathExists(sectordir))
3200 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3201 <<fullpath<<" doesn't exist but directory does."
3202 <<" Continuing with a sector with no metadata."
3204 sector = new ServerMapSector(this, p2d, m_gamedef);
3205 m_sectors.insert(p2d, sector);
3209 throw FileNotGoodException("Cannot open sector metafile");
3214 sector = ServerMapSector::deSerialize
3215 (is, this, p2d, m_sectors, m_gamedef);
3217 saveSectorMeta(sector);
3220 sector->differs_from_disk = false;
3225 bool ServerMap::loadSectorMeta(v2s16 p2d)
3227 DSTACK(__FUNCTION_NAME);
3229 MapSector *sector = NULL;
3231 // The directory layout we're going to load from.
3232 // 1 - original sectors/xxxxzzzz/
3233 // 2 - new sectors2/xxx/zzz/
3234 // If we load from anything but the latest structure, we will
3235 // immediately save to the new one, and remove the old.
3237 std::string sectordir1 = getSectorDir(p2d, 1);
3238 std::string sectordir;
3239 if(fs::PathExists(sectordir1))
3241 sectordir = sectordir1;
3246 sectordir = getSectorDir(p2d, 2);
3250 sector = loadSectorMeta(sectordir, loadlayout != 2);
3252 catch(InvalidFilenameException &e)
3256 catch(FileNotGoodException &e)
3260 catch(std::exception &e)
3269 bool ServerMap::loadSectorFull(v2s16 p2d)
3271 DSTACK(__FUNCTION_NAME);
3273 MapSector *sector = NULL;
3275 // The directory layout we're going to load from.
3276 // 1 - original sectors/xxxxzzzz/
3277 // 2 - new sectors2/xxx/zzz/
3278 // If we load from anything but the latest structure, we will
3279 // immediately save to the new one, and remove the old.
3281 std::string sectordir1 = getSectorDir(p2d, 1);
3282 std::string sectordir;
3283 if(fs::PathExists(sectordir1))
3285 sectordir = sectordir1;
3290 sectordir = getSectorDir(p2d, 2);
3294 sector = loadSectorMeta(sectordir, loadlayout != 2);
3296 catch(InvalidFilenameException &e)
3300 catch(FileNotGoodException &e)
3304 catch(std::exception &e)
3312 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3314 std::vector<fs::DirListNode>::iterator i2;
3315 for(i2=list2.begin(); i2!=list2.end(); i2++)
3321 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3323 catch(InvalidFilenameException &e)
3325 // This catches unknown crap in directory
3331 infostream<<"Sector converted to new layout - deleting "<<
3332 sectordir1<<std::endl;
3333 fs::RecursiveDelete(sectordir1);
3340 void ServerMap::beginSave() {
3342 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3343 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3346 void ServerMap::endSave() {
3348 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3349 infostream<<"WARNING: endSave() failed, map might not have saved.";
3352 void ServerMap::saveBlock(MapBlock *block)
3354 DSTACK(__FUNCTION_NAME);
3356 Dummy blocks are not written
3358 if(block->isDummy())
3360 /*v3s16 p = block->getPos();
3361 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3362 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3366 // Format used for writing
3367 u8 version = SER_FMT_VER_HIGHEST;
3369 v3s16 p3d = block->getPos();
3373 v2s16 p2d(p3d.X, p3d.Z);
3374 std::string sectordir = getSectorDir(p2d);
3376 createDirs(sectordir);
3378 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3379 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3380 if(o.good() == false)
3381 throw FileNotGoodException("Cannot open block data");
3384 [0] u8 serialization version
3390 std::ostringstream o(std::ios_base::binary);
3392 o.write((char*)&version, 1);
3395 block->serialize(o, version, true);
3397 // Write block to database
3399 std::string tmp = o.str();
3400 const char *bytes = tmp.c_str();
3402 bool success = true;
3403 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3404 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3407 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3408 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3411 int written = sqlite3_step(m_database_write);
3412 if(written != SQLITE_DONE) {
3413 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3414 <<sqlite3_errmsg(m_database)<<std::endl;
3417 // Make ready for later reuse
3418 sqlite3_reset(m_database_write);
3420 // We just wrote it to the disk so clear modified flag
3422 block->resetModified();
3425 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3427 DSTACK(__FUNCTION_NAME);
3429 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3432 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3433 if(is.good() == false)
3434 throw FileNotGoodException("Cannot open block file");
3436 v3s16 p3d = getBlockPos(sectordir, blockfile);
3437 v2s16 p2d(p3d.X, p3d.Z);
3439 assert(sector->getPos() == p2d);
3441 u8 version = SER_FMT_VER_INVALID;
3442 is.read((char*)&version, 1);
3445 throw SerializationError("ServerMap::loadBlock(): Failed"
3446 " to read MapBlock version");
3448 /*u32 block_size = MapBlock::serializedLength(version);
3449 SharedBuffer<u8> data(block_size);
3450 is.read((char*)*data, block_size);*/
3452 // This will always return a sector because we're the server
3453 //MapSector *sector = emergeSector(p2d);
3455 MapBlock *block = NULL;
3456 bool created_new = false;
3457 block = sector->getBlockNoCreateNoEx(p3d.Y);
3460 block = sector->createBlankBlockNoInsert(p3d.Y);
3465 block->deSerialize(is, version, true);
3467 // If it's a new block, insert it to the map
3469 sector->insertBlock(block);
3472 Save blocks loaded in old format in new format
3475 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3479 // Should be in database now, so delete the old file
3480 fs::RecursiveDelete(fullpath);
3483 // We just loaded it from the disk, so it's up-to-date.
3484 block->resetModified();
3487 catch(SerializationError &e)
3489 infostream<<"WARNING: Invalid block data on disk "
3490 <<"fullpath="<<fullpath
3491 <<" (SerializationError). "
3492 <<"what()="<<e.what()
3494 //" Ignoring. A new one will be generated.
3497 // TODO: Backup file; name is in fullpath.
3501 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3503 DSTACK(__FUNCTION_NAME);
3506 std::istringstream is(*blob, std::ios_base::binary);
3508 u8 version = SER_FMT_VER_INVALID;
3509 is.read((char*)&version, 1);
3512 throw SerializationError("ServerMap::loadBlock(): Failed"
3513 " to read MapBlock version");
3515 /*u32 block_size = MapBlock::serializedLength(version);
3516 SharedBuffer<u8> data(block_size);
3517 is.read((char*)*data, block_size);*/
3519 // This will always return a sector because we're the server
3520 //MapSector *sector = emergeSector(p2d);
3522 MapBlock *block = NULL;
3523 bool created_new = false;
3524 block = sector->getBlockNoCreateNoEx(p3d.Y);
3527 block = sector->createBlankBlockNoInsert(p3d.Y);
3532 block->deSerialize(is, version, true);
3534 // If it's a new block, insert it to the map
3536 sector->insertBlock(block);
3539 Save blocks loaded in old format in new format
3542 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3543 // Only save if asked to; no need to update version
3547 // We just loaded it from, so it's up-to-date.
3548 block->resetModified();
3551 catch(SerializationError &e)
3553 errorstream<<"Invalid block data in database"
3554 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3555 <<" (SerializationError): "<<e.what()<<std::endl;
3557 // TODO: Block should be marked as invalid in memory so that it is
3558 // not touched but the game can run
3560 if(g_settings->getBool("ignore_world_load_errors")){
3561 errorstream<<"Ignoring block load error. Duck and cover! "
3562 <<"(ignore_world_load_errors)"<<std::endl;
3564 throw SerializationError("Invalid block data in database");
3570 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3572 DSTACK(__FUNCTION_NAME);
3574 v2s16 p2d(blockpos.X, blockpos.Z);
3576 if(!loadFromFolders()) {
3579 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3580 infostream<<"WARNING: Could not bind block position for load: "
3581 <<sqlite3_errmsg(m_database)<<std::endl;
3582 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3584 Make sure sector is loaded
3586 MapSector *sector = createSector(p2d);
3591 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3592 size_t len = sqlite3_column_bytes(m_database_read, 0);
3594 std::string datastr(data, len);
3596 loadBlock(&datastr, blockpos, sector, false);
3598 sqlite3_step(m_database_read);
3599 // We should never get more than 1 row, so ok to reset
3600 sqlite3_reset(m_database_read);
3602 return getBlockNoCreateNoEx(blockpos);
3604 sqlite3_reset(m_database_read);
3606 // Not found in database, try the files
3609 // The directory layout we're going to load from.
3610 // 1 - original sectors/xxxxzzzz/
3611 // 2 - new sectors2/xxx/zzz/
3612 // If we load from anything but the latest structure, we will
3613 // immediately save to the new one, and remove the old.
3615 std::string sectordir1 = getSectorDir(p2d, 1);
3616 std::string sectordir;
3617 if(fs::PathExists(sectordir1))
3619 sectordir = sectordir1;
3624 sectordir = getSectorDir(p2d, 2);
3628 Make sure sector is loaded
3630 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3634 sector = loadSectorMeta(sectordir, loadlayout != 2);
3636 catch(InvalidFilenameException &e)
3640 catch(FileNotGoodException &e)
3644 catch(std::exception &e)
3651 Make sure file exists
3654 std::string blockfilename = getBlockFilename(blockpos);
3655 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3659 Load block and save it to the database
3661 loadBlock(sectordir, blockfilename, sector, true);
3662 return getBlockNoCreateNoEx(blockpos);
3665 void ServerMap::PrintInfo(std::ostream &out)
3674 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3679 MapVoxelManipulator::~MapVoxelManipulator()
3681 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3685 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3687 TimeTaker timer1("emerge", &emerge_time);
3689 // Units of these are MapBlocks
3690 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3691 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3693 VoxelArea block_area_nodes
3694 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3696 addArea(block_area_nodes);
3698 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3699 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3700 for(s32 x=p_min.X; x<=p_max.X; x++)
3703 core::map<v3s16, bool>::Node *n;
3704 n = m_loaded_blocks.find(p);
3708 bool block_data_inexistent = false;
3711 TimeTaker timer1("emerge load", &emerge_load_time);
3713 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3714 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3716 a.print(infostream);
3717 infostream<<std::endl;*/
3719 MapBlock *block = m_map->getBlockNoCreate(p);
3720 if(block->isDummy())
3721 block_data_inexistent = true;
3723 block->copyTo(*this);
3725 catch(InvalidPositionException &e)
3727 block_data_inexistent = true;
3730 if(block_data_inexistent)
3732 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3733 // Fill with VOXELFLAG_INEXISTENT
3734 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3735 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3737 s32 i = m_area.index(a.MinEdge.X,y,z);
3738 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3742 m_loaded_blocks.insert(p, !block_data_inexistent);
3745 //infostream<<"emerge done"<<std::endl;
3749 SUGG: Add an option to only update eg. water and air nodes.
3750 This will make it interfere less with important stuff if
3753 void MapVoxelManipulator::blitBack
3754 (core::map<v3s16, MapBlock*> & modified_blocks)
3756 if(m_area.getExtent() == v3s16(0,0,0))
3759 //TimeTaker timer1("blitBack");
3761 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3762 <<m_loaded_blocks.size()<<std::endl;*/
3765 Initialize block cache
3767 v3s16 blockpos_last;
3768 MapBlock *block = NULL;
3769 bool block_checked_in_modified = false;
3771 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3772 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3773 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3777 u8 f = m_flags[m_area.index(p)];
3778 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3781 MapNode &n = m_data[m_area.index(p)];
3783 v3s16 blockpos = getNodeBlockPos(p);
3788 if(block == NULL || blockpos != blockpos_last){
3789 block = m_map->getBlockNoCreate(blockpos);
3790 blockpos_last = blockpos;
3791 block_checked_in_modified = false;
3794 // Calculate relative position in block
3795 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3797 // Don't continue if nothing has changed here
3798 if(block->getNode(relpos) == n)
3801 //m_map->setNode(m_area.MinEdge + p, n);
3802 block->setNode(relpos, n);
3805 Make sure block is in modified_blocks
3807 if(block_checked_in_modified == false)
3809 modified_blocks[blockpos] = block;
3810 block_checked_in_modified = true;
3813 catch(InvalidPositionException &e)
3819 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3820 MapVoxelManipulator(map),
3821 m_create_area(false)
3825 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3829 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3831 // Just create the area so that it can be pointed to
3832 VoxelManipulator::emerge(a, caller_id);
3835 void ManualMapVoxelManipulator::initialEmerge(
3836 v3s16 blockpos_min, v3s16 blockpos_max)
3838 TimeTaker timer1("initialEmerge", &emerge_time);
3840 // Units of these are MapBlocks
3841 v3s16 p_min = blockpos_min;
3842 v3s16 p_max = blockpos_max;
3844 VoxelArea block_area_nodes
3845 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3847 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3850 infostream<<"initialEmerge: area: ";
3851 block_area_nodes.print(infostream);
3852 infostream<<" ("<<size_MB<<"MB)";
3853 infostream<<std::endl;
3856 addArea(block_area_nodes);
3858 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3859 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3860 for(s32 x=p_min.X; x<=p_max.X; x++)
3863 core::map<v3s16, bool>::Node *n;
3864 n = m_loaded_blocks.find(p);
3868 bool block_data_inexistent = false;
3871 TimeTaker timer1("emerge load", &emerge_load_time);
3873 MapBlock *block = m_map->getBlockNoCreate(p);
3874 if(block->isDummy())
3875 block_data_inexistent = true;
3877 block->copyTo(*this);
3879 catch(InvalidPositionException &e)
3881 block_data_inexistent = true;
3884 if(block_data_inexistent)
3887 Mark area inexistent
3889 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3890 // Fill with VOXELFLAG_INEXISTENT
3891 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3892 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3894 s32 i = m_area.index(a.MinEdge.X,y,z);
3895 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3899 m_loaded_blocks.insert(p, !block_data_inexistent);
3903 void ManualMapVoxelManipulator::blitBackAll(
3904 core::map<v3s16, MapBlock*> * modified_blocks)
3906 if(m_area.getExtent() == v3s16(0,0,0))
3910 Copy data of all blocks
3912 for(core::map<v3s16, bool>::Iterator
3913 i = m_loaded_blocks.getIterator();
3914 i.atEnd() == false; i++)
3916 v3s16 p = i.getNode()->getKey();
3917 bool existed = i.getNode()->getValue();
3918 if(existed == false)
3920 // The Great Bug was found using this
3921 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3922 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3926 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3929 infostream<<"WARNING: "<<__FUNCTION_NAME
3930 <<": got NULL block "
3931 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3936 block->copyFrom(*this);
3939 modified_blocks->insert(p, block);