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 {
1619 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1621 INodeDefManager *nodemgr = m_gamedef->ndef();
1623 DSTACK(__FUNCTION_NAME);
1624 //TimeTaker timer("transformLiquids()");
1627 u32 initial_size = m_transforming_liquid.size();
1629 /*if(initial_size != 0)
1630 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1632 // list of nodes that due to viscosity have not reached their max level height
1633 UniqueQueue<v3s16> must_reflow;
1635 // List of MapBlocks that will require a lighting update (due to lava)
1636 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1638 while(m_transforming_liquid.size() != 0)
1640 // This should be done here so that it is done when continue is used
1641 if(loopcount >= initial_size || loopcount >= 10000)
1646 Get a queued transforming liquid node
1648 v3s16 p0 = m_transforming_liquid.pop_front();
1650 MapNode n0 = getNodeNoEx(p0);
1653 Collect information about current node
1655 s8 liquid_level = -1;
1656 content_t liquid_kind = CONTENT_IGNORE;
1657 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1658 switch (liquid_type) {
1660 liquid_level = LIQUID_LEVEL_SOURCE;
1661 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1663 case LIQUID_FLOWING:
1664 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1665 liquid_kind = n0.getContent();
1668 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1669 // continue with the next node.
1670 if (n0.getContent() != CONTENT_AIR)
1672 liquid_kind = CONTENT_AIR;
1677 Collect information about the environment
1679 const v3s16 *dirs = g_6dirs;
1680 NodeNeighbor sources[6]; // surrounding sources
1681 int num_sources = 0;
1682 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1684 NodeNeighbor airs[6]; // surrounding air
1686 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1687 int num_neutrals = 0;
1688 bool flowing_down = false;
1689 for (u16 i = 0; i < 6; i++) {
1690 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1693 nt = NEIGHBOR_UPPER;
1696 nt = NEIGHBOR_LOWER;
1699 v3s16 npos = p0 + dirs[i];
1700 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1701 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1703 if (nb.n.getContent() == CONTENT_AIR) {
1704 airs[num_airs++] = nb;
1705 // if the current node is a water source the neighbor
1706 // should be enqueded for transformation regardless of whether the
1707 // current node changes or not.
1708 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1709 m_transforming_liquid.push_back(npos);
1710 // if the current node happens to be a flowing node, it will start to flow down here.
1711 if (nb.t == NEIGHBOR_LOWER) {
1712 flowing_down = true;
1715 neutrals[num_neutrals++] = nb;
1719 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1720 if (liquid_kind == CONTENT_AIR)
1721 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1722 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1723 neutrals[num_neutrals++] = nb;
1725 // Do not count bottom source, it will screw things up
1727 sources[num_sources++] = nb;
1730 case LIQUID_FLOWING:
1731 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1732 if (liquid_kind == CONTENT_AIR)
1733 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1734 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1735 neutrals[num_neutrals++] = nb;
1737 flows[num_flows++] = nb;
1738 if (nb.t == NEIGHBOR_LOWER)
1739 flowing_down = true;
1746 decide on the type (and possibly level) of the current node
1748 content_t new_node_content;
1749 s8 new_node_level = -1;
1750 s8 max_node_level = -1;
1751 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1752 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1753 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1754 // it's perfectly safe to use liquid_kind here to determine the new node content.
1755 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1756 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1757 // liquid_kind is set properly, see above
1758 new_node_content = liquid_kind;
1759 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1761 // no surrounding sources, so get the maximum level that can flow into this node
1762 for (u16 i = 0; i < num_flows; i++) {
1763 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1764 switch (flows[i].t) {
1765 case NEIGHBOR_UPPER:
1766 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1767 max_node_level = LIQUID_LEVEL_MAX;
1768 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1769 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1770 } else if (nb_liquid_level > max_node_level)
1771 max_node_level = nb_liquid_level;
1773 case NEIGHBOR_LOWER:
1775 case NEIGHBOR_SAME_LEVEL:
1776 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1777 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1778 max_node_level = nb_liquid_level - 1;
1784 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1785 if (viscosity > 1 && max_node_level != liquid_level) {
1786 // amount to gain, limited by viscosity
1787 // must be at least 1 in absolute value
1788 s8 level_inc = max_node_level - liquid_level;
1789 if (level_inc < -viscosity || level_inc > viscosity)
1790 new_node_level = liquid_level + level_inc/viscosity;
1791 else if (level_inc < 0)
1792 new_node_level = liquid_level - 1;
1793 else if (level_inc > 0)
1794 new_node_level = liquid_level + 1;
1795 if (new_node_level != max_node_level)
1796 must_reflow.push_back(p0);
1798 new_node_level = max_node_level;
1800 if (new_node_level >= 0)
1801 new_node_content = liquid_kind;
1803 new_node_content = CONTENT_AIR;
1808 check if anything has changed. if not, just continue with the next node.
1810 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1811 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1812 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1818 update the current node
1820 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1821 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1822 // set level to last 3 bits, flowing down bit to 4th bit
1823 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1825 // set the liquid level and flow bit to 0
1826 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1828 n0.setContent(new_node_content);
1830 // Find out whether there is a suspect for this action
1831 std::string suspect;
1832 if(m_gamedef->rollback()){
1833 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1836 if(!suspect.empty()){
1838 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1839 // Get old node for rollback
1840 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1844 RollbackNode rollback_newnode(this, p0, m_gamedef);
1845 RollbackAction action;
1846 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1847 m_gamedef->rollback()->reportAction(action);
1853 v3s16 blockpos = getNodeBlockPos(p0);
1854 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1856 modified_blocks.insert(blockpos, block);
1857 // If node emits light, MapBlock requires lighting update
1858 if(nodemgr->get(n0).light_source != 0)
1859 lighting_modified_blocks[block->getPos()] = block;
1863 enqueue neighbors for update if neccessary
1865 switch (nodemgr->get(n0.getContent()).liquid_type) {
1867 case LIQUID_FLOWING:
1868 // make sure source flows into all neighboring nodes
1869 for (u16 i = 0; i < num_flows; i++)
1870 if (flows[i].t != NEIGHBOR_UPPER)
1871 m_transforming_liquid.push_back(flows[i].p);
1872 for (u16 i = 0; i < num_airs; i++)
1873 if (airs[i].t != NEIGHBOR_UPPER)
1874 m_transforming_liquid.push_back(airs[i].p);
1877 // this flow has turned to air; neighboring flows might need to do the same
1878 for (u16 i = 0; i < num_flows; i++)
1879 m_transforming_liquid.push_back(flows[i].p);
1883 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1884 while (must_reflow.size() > 0)
1885 m_transforming_liquid.push_back(must_reflow.pop_front());
1886 updateLighting(lighting_modified_blocks, modified_blocks);
1889 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1891 v3s16 blockpos = getNodeBlockPos(p);
1892 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1893 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1895 infostream<<"Map::getNodeMetadata(): Need to emerge "
1896 <<PP(blockpos)<<std::endl;
1897 block = emergeBlock(blockpos, false);
1901 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1905 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1909 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1911 v3s16 blockpos = getNodeBlockPos(p);
1912 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1913 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1915 infostream<<"Map::setNodeMetadata(): Need to emerge "
1916 <<PP(blockpos)<<std::endl;
1917 block = emergeBlock(blockpos, false);
1921 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1925 block->m_node_metadata.set(p_rel, meta);
1928 void Map::removeNodeMetadata(v3s16 p)
1930 v3s16 blockpos = getNodeBlockPos(p);
1931 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1932 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1935 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1939 block->m_node_metadata.remove(p_rel);
1942 NodeTimer Map::getNodeTimer(v3s16 p)
1944 v3s16 blockpos = getNodeBlockPos(p);
1945 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1946 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1948 infostream<<"Map::getNodeTimer(): Need to emerge "
1949 <<PP(blockpos)<<std::endl;
1950 block = emergeBlock(blockpos, false);
1954 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1958 NodeTimer t = block->m_node_timers.get(p_rel);
1962 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1964 v3s16 blockpos = getNodeBlockPos(p);
1965 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1966 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1968 infostream<<"Map::setNodeTimer(): Need to emerge "
1969 <<PP(blockpos)<<std::endl;
1970 block = emergeBlock(blockpos, false);
1974 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1978 block->m_node_timers.set(p_rel, t);
1981 void Map::removeNodeTimer(v3s16 p)
1983 v3s16 blockpos = getNodeBlockPos(p);
1984 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1985 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1988 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
1992 block->m_node_timers.remove(p_rel);
1998 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1999 Map(dout_server, gamedef),
2001 m_map_metadata_changed(true),
2003 m_database_read(NULL),
2004 m_database_write(NULL)
2006 verbosestream<<__FUNCTION_NAME<<std::endl;
2009 m_mgparams = m_emerge->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 m_emerge->setParamsToSettings(¶ms);
3083 params.writeLines(os);
3085 os<<"[end_of_params]\n";
3087 m_map_metadata_changed = false;
3090 void ServerMap::loadMapMeta()
3092 DSTACK(__FUNCTION_NAME);
3094 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3097 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3098 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3099 if(is.good() == false)
3101 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3102 <<"could not open"<<fullpath<<std::endl;
3103 throw FileNotGoodException("Cannot open map metadata");
3111 throw SerializationError
3112 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3114 std::getline(is, line);
3115 std::string trimmedline = trim(line);
3116 if(trimmedline == "[end_of_params]")
3118 params.parseConfigLine(line);
3121 MapgenParams *mgparams = m_emerge->getParamsFromSettings(¶ms);
3125 m_mgparams = mgparams;
3126 m_seed = mgparams->seed;
3128 if (params.exists("seed")) {
3129 m_seed = params.getU64("seed");
3130 m_mgparams->seed = m_seed;
3134 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3137 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3139 DSTACK(__FUNCTION_NAME);
3140 // Format used for writing
3141 u8 version = SER_FMT_VER_HIGHEST;
3143 v2s16 pos = sector->getPos();
3144 std::string dir = getSectorDir(pos);
3147 std::string fullpath = dir + DIR_DELIM + "meta";
3148 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3149 if(o.good() == false)
3150 throw FileNotGoodException("Cannot open sector metafile");
3152 sector->serialize(o, version);
3154 sector->differs_from_disk = false;
3157 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3159 DSTACK(__FUNCTION_NAME);
3161 v2s16 p2d = getSectorPos(sectordir);
3163 ServerMapSector *sector = NULL;
3165 std::string fullpath = sectordir + DIR_DELIM + "meta";
3166 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3167 if(is.good() == false)
3169 // If the directory exists anyway, it probably is in some old
3170 // format. Just go ahead and create the sector.
3171 if(fs::PathExists(sectordir))
3173 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3174 <<fullpath<<" doesn't exist but directory does."
3175 <<" Continuing with a sector with no metadata."
3177 sector = new ServerMapSector(this, p2d, m_gamedef);
3178 m_sectors.insert(p2d, sector);
3182 throw FileNotGoodException("Cannot open sector metafile");
3187 sector = ServerMapSector::deSerialize
3188 (is, this, p2d, m_sectors, m_gamedef);
3190 saveSectorMeta(sector);
3193 sector->differs_from_disk = false;
3198 bool ServerMap::loadSectorMeta(v2s16 p2d)
3200 DSTACK(__FUNCTION_NAME);
3202 MapSector *sector = NULL;
3204 // The directory layout we're going to load from.
3205 // 1 - original sectors/xxxxzzzz/
3206 // 2 - new sectors2/xxx/zzz/
3207 // If we load from anything but the latest structure, we will
3208 // immediately save to the new one, and remove the old.
3210 std::string sectordir1 = getSectorDir(p2d, 1);
3211 std::string sectordir;
3212 if(fs::PathExists(sectordir1))
3214 sectordir = sectordir1;
3219 sectordir = getSectorDir(p2d, 2);
3223 sector = loadSectorMeta(sectordir, loadlayout != 2);
3225 catch(InvalidFilenameException &e)
3229 catch(FileNotGoodException &e)
3233 catch(std::exception &e)
3242 bool ServerMap::loadSectorFull(v2s16 p2d)
3244 DSTACK(__FUNCTION_NAME);
3246 MapSector *sector = NULL;
3248 // The directory layout we're going to load from.
3249 // 1 - original sectors/xxxxzzzz/
3250 // 2 - new sectors2/xxx/zzz/
3251 // If we load from anything but the latest structure, we will
3252 // immediately save to the new one, and remove the old.
3254 std::string sectordir1 = getSectorDir(p2d, 1);
3255 std::string sectordir;
3256 if(fs::PathExists(sectordir1))
3258 sectordir = sectordir1;
3263 sectordir = getSectorDir(p2d, 2);
3267 sector = loadSectorMeta(sectordir, loadlayout != 2);
3269 catch(InvalidFilenameException &e)
3273 catch(FileNotGoodException &e)
3277 catch(std::exception &e)
3285 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3287 std::vector<fs::DirListNode>::iterator i2;
3288 for(i2=list2.begin(); i2!=list2.end(); i2++)
3294 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3296 catch(InvalidFilenameException &e)
3298 // This catches unknown crap in directory
3304 infostream<<"Sector converted to new layout - deleting "<<
3305 sectordir1<<std::endl;
3306 fs::RecursiveDelete(sectordir1);
3313 void ServerMap::beginSave() {
3315 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3316 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3319 void ServerMap::endSave() {
3321 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3322 infostream<<"WARNING: endSave() failed, map might not have saved.";
3325 void ServerMap::saveBlock(MapBlock *block)
3327 DSTACK(__FUNCTION_NAME);
3329 Dummy blocks are not written
3331 if(block->isDummy())
3333 /*v3s16 p = block->getPos();
3334 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3335 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3339 // Format used for writing
3340 u8 version = SER_FMT_VER_HIGHEST;
3342 v3s16 p3d = block->getPos();
3346 v2s16 p2d(p3d.X, p3d.Z);
3347 std::string sectordir = getSectorDir(p2d);
3349 createDirs(sectordir);
3351 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3352 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3353 if(o.good() == false)
3354 throw FileNotGoodException("Cannot open block data");
3357 [0] u8 serialization version
3363 std::ostringstream o(std::ios_base::binary);
3365 o.write((char*)&version, 1);
3368 block->serialize(o, version, true);
3370 // Write block to database
3372 std::string tmp = o.str();
3373 const char *bytes = tmp.c_str();
3375 bool success = true;
3376 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3377 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3380 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3381 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3384 int written = sqlite3_step(m_database_write);
3385 if(written != SQLITE_DONE) {
3386 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3387 <<sqlite3_errmsg(m_database)<<std::endl;
3390 // Make ready for later reuse
3391 sqlite3_reset(m_database_write);
3393 // We just wrote it to the disk so clear modified flag
3395 block->resetModified();
3398 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3400 DSTACK(__FUNCTION_NAME);
3402 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3405 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3406 if(is.good() == false)
3407 throw FileNotGoodException("Cannot open block file");
3409 v3s16 p3d = getBlockPos(sectordir, blockfile);
3410 v2s16 p2d(p3d.X, p3d.Z);
3412 assert(sector->getPos() == p2d);
3414 u8 version = SER_FMT_VER_INVALID;
3415 is.read((char*)&version, 1);
3418 throw SerializationError("ServerMap::loadBlock(): Failed"
3419 " to read MapBlock version");
3421 /*u32 block_size = MapBlock::serializedLength(version);
3422 SharedBuffer<u8> data(block_size);
3423 is.read((char*)*data, block_size);*/
3425 // This will always return a sector because we're the server
3426 //MapSector *sector = emergeSector(p2d);
3428 MapBlock *block = NULL;
3429 bool created_new = false;
3430 block = sector->getBlockNoCreateNoEx(p3d.Y);
3433 block = sector->createBlankBlockNoInsert(p3d.Y);
3438 block->deSerialize(is, version, true);
3440 // If it's a new block, insert it to the map
3442 sector->insertBlock(block);
3445 Save blocks loaded in old format in new format
3448 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3452 // Should be in database now, so delete the old file
3453 fs::RecursiveDelete(fullpath);
3456 // We just loaded it from the disk, so it's up-to-date.
3457 block->resetModified();
3460 catch(SerializationError &e)
3462 infostream<<"WARNING: Invalid block data on disk "
3463 <<"fullpath="<<fullpath
3464 <<" (SerializationError). "
3465 <<"what()="<<e.what()
3467 //" Ignoring. A new one will be generated.
3470 // TODO: Backup file; name is in fullpath.
3474 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3476 DSTACK(__FUNCTION_NAME);
3479 std::istringstream is(*blob, std::ios_base::binary);
3481 u8 version = SER_FMT_VER_INVALID;
3482 is.read((char*)&version, 1);
3485 throw SerializationError("ServerMap::loadBlock(): Failed"
3486 " to read MapBlock version");
3488 /*u32 block_size = MapBlock::serializedLength(version);
3489 SharedBuffer<u8> data(block_size);
3490 is.read((char*)*data, block_size);*/
3492 // This will always return a sector because we're the server
3493 //MapSector *sector = emergeSector(p2d);
3495 MapBlock *block = NULL;
3496 bool created_new = false;
3497 block = sector->getBlockNoCreateNoEx(p3d.Y);
3500 block = sector->createBlankBlockNoInsert(p3d.Y);
3505 block->deSerialize(is, version, true);
3507 // If it's a new block, insert it to the map
3509 sector->insertBlock(block);
3512 Save blocks loaded in old format in new format
3515 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3516 // Only save if asked to; no need to update version
3520 // We just loaded it from, so it's up-to-date.
3521 block->resetModified();
3524 catch(SerializationError &e)
3526 errorstream<<"Invalid block data in database"
3527 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3528 <<" (SerializationError): "<<e.what()<<std::endl;
3530 // TODO: Block should be marked as invalid in memory so that it is
3531 // not touched but the game can run
3533 if(g_settings->getBool("ignore_world_load_errors")){
3534 errorstream<<"Ignoring block load error. Duck and cover! "
3535 <<"(ignore_world_load_errors)"<<std::endl;
3537 throw SerializationError("Invalid block data in database");
3543 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3545 DSTACK(__FUNCTION_NAME);
3547 v2s16 p2d(blockpos.X, blockpos.Z);
3549 if(!loadFromFolders()) {
3552 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3553 infostream<<"WARNING: Could not bind block position for load: "
3554 <<sqlite3_errmsg(m_database)<<std::endl;
3555 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3557 Make sure sector is loaded
3559 MapSector *sector = createSector(p2d);
3564 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3565 size_t len = sqlite3_column_bytes(m_database_read, 0);
3567 std::string datastr(data, len);
3569 loadBlock(&datastr, blockpos, sector, false);
3571 sqlite3_step(m_database_read);
3572 // We should never get more than 1 row, so ok to reset
3573 sqlite3_reset(m_database_read);
3575 return getBlockNoCreateNoEx(blockpos);
3577 sqlite3_reset(m_database_read);
3579 // Not found in database, try the files
3582 // The directory layout we're going to load from.
3583 // 1 - original sectors/xxxxzzzz/
3584 // 2 - new sectors2/xxx/zzz/
3585 // If we load from anything but the latest structure, we will
3586 // immediately save to the new one, and remove the old.
3588 std::string sectordir1 = getSectorDir(p2d, 1);
3589 std::string sectordir;
3590 if(fs::PathExists(sectordir1))
3592 sectordir = sectordir1;
3597 sectordir = getSectorDir(p2d, 2);
3601 Make sure sector is loaded
3603 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3607 sector = loadSectorMeta(sectordir, loadlayout != 2);
3609 catch(InvalidFilenameException &e)
3613 catch(FileNotGoodException &e)
3617 catch(std::exception &e)
3624 Make sure file exists
3627 std::string blockfilename = getBlockFilename(blockpos);
3628 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3632 Load block and save it to the database
3634 loadBlock(sectordir, blockfilename, sector, true);
3635 return getBlockNoCreateNoEx(blockpos);
3638 void ServerMap::PrintInfo(std::ostream &out)
3647 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3652 MapVoxelManipulator::~MapVoxelManipulator()
3654 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3658 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3660 TimeTaker timer1("emerge", &emerge_time);
3662 // Units of these are MapBlocks
3663 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3664 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3666 VoxelArea block_area_nodes
3667 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3669 addArea(block_area_nodes);
3671 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3672 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3673 for(s32 x=p_min.X; x<=p_max.X; x++)
3676 core::map<v3s16, bool>::Node *n;
3677 n = m_loaded_blocks.find(p);
3681 bool block_data_inexistent = false;
3684 TimeTaker timer1("emerge load", &emerge_load_time);
3686 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3687 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3689 a.print(infostream);
3690 infostream<<std::endl;*/
3692 MapBlock *block = m_map->getBlockNoCreate(p);
3693 if(block->isDummy())
3694 block_data_inexistent = true;
3696 block->copyTo(*this);
3698 catch(InvalidPositionException &e)
3700 block_data_inexistent = true;
3703 if(block_data_inexistent)
3705 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3706 // Fill with VOXELFLAG_INEXISTENT
3707 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3708 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3710 s32 i = m_area.index(a.MinEdge.X,y,z);
3711 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3715 m_loaded_blocks.insert(p, !block_data_inexistent);
3718 //infostream<<"emerge done"<<std::endl;
3722 SUGG: Add an option to only update eg. water and air nodes.
3723 This will make it interfere less with important stuff if
3726 void MapVoxelManipulator::blitBack
3727 (core::map<v3s16, MapBlock*> & modified_blocks)
3729 if(m_area.getExtent() == v3s16(0,0,0))
3732 //TimeTaker timer1("blitBack");
3734 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3735 <<m_loaded_blocks.size()<<std::endl;*/
3738 Initialize block cache
3740 v3s16 blockpos_last;
3741 MapBlock *block = NULL;
3742 bool block_checked_in_modified = false;
3744 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3745 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3746 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3750 u8 f = m_flags[m_area.index(p)];
3751 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3754 MapNode &n = m_data[m_area.index(p)];
3756 v3s16 blockpos = getNodeBlockPos(p);
3761 if(block == NULL || blockpos != blockpos_last){
3762 block = m_map->getBlockNoCreate(blockpos);
3763 blockpos_last = blockpos;
3764 block_checked_in_modified = false;
3767 // Calculate relative position in block
3768 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3770 // Don't continue if nothing has changed here
3771 if(block->getNode(relpos) == n)
3774 //m_map->setNode(m_area.MinEdge + p, n);
3775 block->setNode(relpos, n);
3778 Make sure block is in modified_blocks
3780 if(block_checked_in_modified == false)
3782 modified_blocks[blockpos] = block;
3783 block_checked_in_modified = true;
3786 catch(InvalidPositionException &e)
3792 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3793 MapVoxelManipulator(map),
3794 m_create_area(false)
3798 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3802 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3804 // Just create the area so that it can be pointed to
3805 VoxelManipulator::emerge(a, caller_id);
3808 void ManualMapVoxelManipulator::initialEmerge(
3809 v3s16 blockpos_min, v3s16 blockpos_max)
3811 TimeTaker timer1("initialEmerge", &emerge_time);
3813 // Units of these are MapBlocks
3814 v3s16 p_min = blockpos_min;
3815 v3s16 p_max = blockpos_max;
3817 VoxelArea block_area_nodes
3818 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3820 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3823 infostream<<"initialEmerge: area: ";
3824 block_area_nodes.print(infostream);
3825 infostream<<" ("<<size_MB<<"MB)";
3826 infostream<<std::endl;
3829 addArea(block_area_nodes);
3831 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3832 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3833 for(s32 x=p_min.X; x<=p_max.X; x++)
3836 core::map<v3s16, bool>::Node *n;
3837 n = m_loaded_blocks.find(p);
3841 bool block_data_inexistent = false;
3844 TimeTaker timer1("emerge load", &emerge_load_time);
3846 MapBlock *block = m_map->getBlockNoCreate(p);
3847 if(block->isDummy())
3848 block_data_inexistent = true;
3850 block->copyTo(*this);
3852 catch(InvalidPositionException &e)
3854 block_data_inexistent = true;
3857 if(block_data_inexistent)
3860 Mark area inexistent
3862 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3863 // Fill with VOXELFLAG_INEXISTENT
3864 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3865 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3867 s32 i = m_area.index(a.MinEdge.X,y,z);
3868 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3872 m_loaded_blocks.insert(p, !block_data_inexistent);
3876 void ManualMapVoxelManipulator::blitBackAll(
3877 core::map<v3s16, MapBlock*> * modified_blocks)
3879 if(m_area.getExtent() == v3s16(0,0,0))
3883 Copy data of all blocks
3885 for(core::map<v3s16, bool>::Iterator
3886 i = m_loaded_blocks.getIterator();
3887 i.atEnd() == false; i++)
3889 v3s16 p = i.getNode()->getKey();
3890 bool existed = i.getNode()->getValue();
3891 if(existed == false)
3893 // The Great Bug was found using this
3894 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3895 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3899 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3902 infostream<<"WARNING: "<<__FUNCTION_NAME
3903 <<": got NULL block "
3904 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3909 block->copyFrom(*this);
3912 modified_blocks->insert(p, block);