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"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/mathconstants.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
44 #include "database-dummy.h"
45 #include "database-sqlite3.h"
47 #include "database-leveldb.h"
50 #include "database-redis.h"
53 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
56 SQLite format specification:
57 - Initially only replaces sectors/ and sectors2/
59 If map.sqlite does not exist in the save dir
60 or the block was not found in the database
61 the map will try to load from sectors folder.
62 In either case, map.sqlite will be created
63 and all future saves will save there.
65 Structure of map.sqlite:
76 Map::Map(std::ostream &dout, IGameDef *gamedef):
88 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
89 i != m_sectors.end(); ++i)
95 void Map::addEventReceiver(MapEventReceiver *event_receiver)
97 m_event_receivers.insert(event_receiver);
100 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
102 m_event_receivers.erase(event_receiver);
105 void Map::dispatchEvent(MapEditEvent *event)
107 for(std::set<MapEventReceiver*>::iterator
108 i = m_event_receivers.begin();
109 i != m_event_receivers.end(); ++i)
111 (*i)->onMapEditEvent(event);
115 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
117 if(m_sector_cache != NULL && p == m_sector_cache_p){
118 MapSector * sector = m_sector_cache;
122 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
124 if(n == m_sectors.end())
127 MapSector *sector = n->second;
129 // Cache the last result
130 m_sector_cache_p = p;
131 m_sector_cache = sector;
136 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
138 return getSectorNoGenerateNoExNoLock(p);
141 MapSector * Map::getSectorNoGenerate(v2s16 p)
143 MapSector *sector = getSectorNoGenerateNoEx(p);
145 throw InvalidPositionException();
150 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
152 v2s16 p2d(p3d.X, p3d.Z);
153 MapSector * sector = getSectorNoGenerateNoEx(p2d);
156 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
160 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
162 MapBlock *block = getBlockNoCreateNoEx(p3d);
164 throw InvalidPositionException();
168 bool Map::isNodeUnderground(v3s16 p)
170 v3s16 blockpos = getNodeBlockPos(p);
172 MapBlock * block = getBlockNoCreate(blockpos);
173 return block->getIsUnderground();
175 catch(InvalidPositionException &e)
181 bool Map::isValidPosition(v3s16 p)
183 v3s16 blockpos = getNodeBlockPos(p);
184 MapBlock *block = getBlockNoCreate(blockpos);
185 return (block != NULL);
188 // Returns a CONTENT_IGNORE node if not found
189 MapNode Map::getNodeNoEx(v3s16 p)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 return MapNode(CONTENT_IGNORE);
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 return block->getNodeNoCheck(relpos);
199 // throws InvalidPositionException if not found
200 MapNode Map::getNode(v3s16 p)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreateNoEx(blockpos);
205 throw InvalidPositionException();
206 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
207 return block->getNodeNoCheck(relpos);
210 // throws InvalidPositionException if not found
211 void Map::setNode(v3s16 p, MapNode & n)
213 v3s16 blockpos = getNodeBlockPos(p);
214 MapBlock *block = getBlockNoCreate(blockpos);
215 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
216 // Never allow placing CONTENT_IGNORE, it fucks up stuff
217 if(n.getContent() == CONTENT_IGNORE){
218 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
219 <<" while trying to replace \""
220 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
221 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
222 debug_stacks_print_to(infostream);
225 block->setNodeNoCheck(relpos, n);
230 Goes recursively through the neighbours of the node.
232 Alters only transparent nodes.
234 If the lighting of the neighbour is lower than the lighting of
235 the node was (before changing it to 0 at the step before), the
236 lighting of the neighbour is set to 0 and then the same stuff
237 repeats for the neighbour.
239 The ending nodes of the routine are stored in light_sources.
240 This is useful when a light is removed. In such case, this
241 routine can be called for the light node and then again for
242 light_sources to re-light the area without the removed light.
244 values of from_nodes are lighting values.
246 void Map::unspreadLight(enum LightBank bank,
247 std::map<v3s16, u8> & from_nodes,
248 std::set<v3s16> & light_sources,
249 std::map<v3s16, MapBlock*> & modified_blocks)
251 INodeDefManager *nodemgr = m_gamedef->ndef();
254 v3s16(0,0,1), // back
256 v3s16(1,0,0), // right
257 v3s16(0,0,-1), // front
258 v3s16(0,-1,0), // bottom
259 v3s16(-1,0,0), // left
262 if(from_nodes.size() == 0)
265 u32 blockchangecount = 0;
267 std::map<v3s16, u8> unlighted_nodes;
270 Initialize block cache
273 MapBlock *block = NULL;
274 // Cache this a bit, too
275 bool block_checked_in_modified = false;
277 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
278 j != from_nodes.end(); ++j)
280 v3s16 pos = j->first;
281 v3s16 blockpos = getNodeBlockPos(pos);
283 // Only fetch a new block if the block position has changed
285 if(block == NULL || blockpos != blockpos_last){
286 block = getBlockNoCreate(blockpos);
287 blockpos_last = blockpos;
289 block_checked_in_modified = false;
293 catch(InvalidPositionException &e)
301 // Calculate relative position in block
302 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
304 // Get node straight from the block
305 //MapNode n = block->getNode(relpos);
307 u8 oldlight = j->second;
309 // Loop through 6 neighbors
310 for(u16 i=0; i<6; i++)
312 // Get the position of the neighbor node
313 v3s16 n2pos = pos + dirs[i];
315 // Get the block where the node is located
316 v3s16 blockpos = getNodeBlockPos(n2pos);
320 // Only fetch a new block if the block position has changed
322 if(block == NULL || blockpos != blockpos_last){
323 block = getBlockNoCreate(blockpos);
324 blockpos_last = blockpos;
326 block_checked_in_modified = false;
330 catch(InvalidPositionException &e)
335 // Calculate relative position in block
336 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
337 // Get node straight from the block
338 MapNode n2 = block->getNode(relpos);
340 bool changed = false;
342 //TODO: Optimize output by optimizing light_sources?
345 If the neighbor is dimmer than what was specified
346 as oldlight (the light of the previous node)
348 if(n2.getLight(bank, nodemgr) < oldlight)
351 And the neighbor is transparent and it has some light
353 if(nodemgr->get(n2).light_propagates
354 && n2.getLight(bank, nodemgr) != 0)
357 Set light to 0 and add to queue
360 u8 current_light = n2.getLight(bank, nodemgr);
361 n2.setLight(bank, 0, nodemgr);
362 block->setNode(relpos, n2);
364 unlighted_nodes[n2pos] = current_light;
368 Remove from light_sources if it is there
369 NOTE: This doesn't happen nearly at all
371 /*if(light_sources.find(n2pos))
373 infostream<<"Removed from light_sources"<<std::endl;
374 light_sources.remove(n2pos);
379 if(light_sources.find(n2pos) != NULL)
380 light_sources.remove(n2pos);*/
383 light_sources.insert(n2pos);
386 // Add to modified_blocks
387 if(changed == true && block_checked_in_modified == false)
389 // If the block is not found in modified_blocks, add.
390 if(modified_blocks.find(blockpos) == modified_blocks.end())
392 modified_blocks[blockpos] = block;
394 block_checked_in_modified = true;
397 catch(InvalidPositionException &e)
404 /*infostream<<"unspreadLight(): Changed block "
405 <<blockchangecount<<" times"
406 <<" for "<<from_nodes.size()<<" nodes"
409 if(unlighted_nodes.size() > 0)
410 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
414 A single-node wrapper of the above
416 void Map::unLightNeighbors(enum LightBank bank,
417 v3s16 pos, u8 lightwas,
418 std::set<v3s16> & light_sources,
419 std::map<v3s16, MapBlock*> & modified_blocks)
421 std::map<v3s16, u8> from_nodes;
422 from_nodes[pos] = lightwas;
424 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
428 Lights neighbors of from_nodes, collects all them and then
431 void Map::spreadLight(enum LightBank bank,
432 std::set<v3s16> & from_nodes,
433 std::map<v3s16, MapBlock*> & modified_blocks)
435 INodeDefManager *nodemgr = m_gamedef->ndef();
437 const v3s16 dirs[6] = {
438 v3s16(0,0,1), // back
440 v3s16(1,0,0), // right
441 v3s16(0,0,-1), // front
442 v3s16(0,-1,0), // bottom
443 v3s16(-1,0,0), // left
446 if(from_nodes.size() == 0)
449 u32 blockchangecount = 0;
451 std::set<v3s16> lighted_nodes;
454 Initialize block cache
457 MapBlock *block = NULL;
458 // Cache this a bit, too
459 bool block_checked_in_modified = false;
461 for(std::set<v3s16>::iterator j = from_nodes.begin();
462 j != from_nodes.end(); ++j)
465 v3s16 blockpos = getNodeBlockPos(pos);
467 // Only fetch a new block if the block position has changed
469 if(block == NULL || blockpos != blockpos_last){
470 block = getBlockNoCreate(blockpos);
471 blockpos_last = blockpos;
473 block_checked_in_modified = false;
477 catch(InvalidPositionException &e)
485 // Calculate relative position in block
486 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
488 // Get node straight from the block
489 MapNode n = block->getNode(relpos);
491 u8 oldlight = n.getLight(bank, nodemgr);
492 u8 newlight = diminish_light(oldlight);
494 // Loop through 6 neighbors
495 for(u16 i=0; i<6; i++){
496 // Get the position of the neighbor node
497 v3s16 n2pos = pos + dirs[i];
499 // Get the block where the node is located
500 v3s16 blockpos = getNodeBlockPos(n2pos);
504 // Only fetch a new block if the block position has changed
506 if(block == NULL || blockpos != blockpos_last){
507 block = getBlockNoCreate(blockpos);
508 blockpos_last = blockpos;
510 block_checked_in_modified = false;
514 catch(InvalidPositionException &e)
519 // Calculate relative position in block
520 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
521 // Get node straight from the block
522 MapNode n2 = block->getNode(relpos);
524 bool changed = false;
526 If the neighbor is brighter than the current node,
527 add to list (it will light up this node on its turn)
529 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
531 lighted_nodes.insert(n2pos);
535 If the neighbor is dimmer than how much light this node
536 would spread on it, add to list
538 if(n2.getLight(bank, nodemgr) < newlight)
540 if(nodemgr->get(n2).light_propagates)
542 n2.setLight(bank, newlight, nodemgr);
543 block->setNode(relpos, n2);
544 lighted_nodes.insert(n2pos);
549 // Add to modified_blocks
550 if(changed == true && block_checked_in_modified == false)
552 // If the block is not found in modified_blocks, add.
553 if(modified_blocks.find(blockpos) == modified_blocks.end())
555 modified_blocks[blockpos] = block;
557 block_checked_in_modified = true;
560 catch(InvalidPositionException &e)
567 /*infostream<<"spreadLight(): Changed block "
568 <<blockchangecount<<" times"
569 <<" for "<<from_nodes.size()<<" nodes"
572 if(lighted_nodes.size() > 0)
573 spreadLight(bank, lighted_nodes, modified_blocks);
577 A single-node source variation of the above.
579 void Map::lightNeighbors(enum LightBank bank,
581 std::map<v3s16, MapBlock*> & modified_blocks)
583 std::set<v3s16> from_nodes;
584 from_nodes.insert(pos);
585 spreadLight(bank, from_nodes, modified_blocks);
588 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
590 INodeDefManager *nodemgr = m_gamedef->ndef();
593 v3s16(0,0,1), // back
595 v3s16(1,0,0), // right
596 v3s16(0,0,-1), // front
597 v3s16(0,-1,0), // bottom
598 v3s16(-1,0,0), // left
601 u8 brightest_light = 0;
602 v3s16 brightest_pos(0,0,0);
603 bool found_something = false;
605 // Loop through 6 neighbors
606 for(u16 i=0; i<6; i++){
607 // Get the position of the neighbor node
608 v3s16 n2pos = p + dirs[i];
613 catch(InvalidPositionException &e)
617 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
618 brightest_light = n2.getLight(bank, nodemgr);
619 brightest_pos = n2pos;
620 found_something = true;
624 if(found_something == false)
625 throw InvalidPositionException();
627 return brightest_pos;
631 Propagates sunlight down from a node.
632 Starting point gets sunlight.
634 Returns the lowest y value of where the sunlight went.
636 Mud is turned into grass in where the sunlight stops.
638 s16 Map::propagateSunlight(v3s16 start,
639 std::map<v3s16, MapBlock*> & modified_blocks)
641 INodeDefManager *nodemgr = m_gamedef->ndef();
646 v3s16 pos(start.X, y, start.Z);
648 v3s16 blockpos = getNodeBlockPos(pos);
651 block = getBlockNoCreate(blockpos);
653 catch(InvalidPositionException &e)
658 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
659 MapNode n = block->getNode(relpos);
661 if(nodemgr->get(n).sunlight_propagates)
663 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
664 block->setNode(relpos, n);
666 modified_blocks[blockpos] = block;
670 // Sunlight goes no further
677 void Map::updateLighting(enum LightBank bank,
678 std::map<v3s16, MapBlock*> & a_blocks,
679 std::map<v3s16, MapBlock*> & modified_blocks)
681 INodeDefManager *nodemgr = m_gamedef->ndef();
683 /*m_dout<<DTIME<<"Map::updateLighting(): "
684 <<a_blocks.size()<<" blocks."<<std::endl;*/
686 //TimeTaker timer("updateLighting");
690 //u32 count_was = modified_blocks.size();
692 std::map<v3s16, MapBlock*> blocks_to_update;
694 std::set<v3s16> light_sources;
696 std::map<v3s16, u8> unlight_from;
698 int num_bottom_invalid = 0;
701 //TimeTaker t("first stuff");
703 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
704 i != a_blocks.end(); ++i)
706 MapBlock *block = i->second;
710 // Don't bother with dummy blocks.
714 v3s16 pos = block->getPos();
715 v3s16 posnodes = block->getPosRelative();
716 modified_blocks[pos] = block;
717 blocks_to_update[pos] = block;
720 Clear all light from block
722 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
723 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
724 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
729 MapNode n = block->getNode(p);
730 u8 oldlight = n.getLight(bank, nodemgr);
731 n.setLight(bank, 0, nodemgr);
732 block->setNode(p, n);
734 // If node sources light, add to list
735 u8 source = nodemgr->get(n).light_source;
737 light_sources.insert(p + posnodes);
739 // Collect borders for unlighting
740 if((x==0 || x == MAP_BLOCKSIZE-1
741 || y==0 || y == MAP_BLOCKSIZE-1
742 || z==0 || z == MAP_BLOCKSIZE-1)
745 v3s16 p_map = p + posnodes;
746 unlight_from[p_map] = oldlight;
749 catch(InvalidPositionException &e)
752 This would happen when dealing with a
756 infostream<<"updateLighting(): InvalidPositionException"
761 if(bank == LIGHTBANK_DAY)
763 bool bottom_valid = block->propagateSunlight(light_sources);
766 num_bottom_invalid++;
768 // If bottom is valid, we're done.
772 else if(bank == LIGHTBANK_NIGHT)
774 // For night lighting, sunlight is not propagated
779 // Invalid lighting bank
783 /*infostream<<"Bottom for sunlight-propagated block ("
784 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
787 // Bottom sunlight is not valid; get the block and loop to it
791 block = getBlockNoCreate(pos);
793 catch(InvalidPositionException &e)
804 Enable this to disable proper lighting for speeding up map
805 generation for testing or whatever
808 //if(g_settings->get(""))
810 core::map<v3s16, MapBlock*>::Iterator i;
811 i = blocks_to_update.getIterator();
812 for(; i.atEnd() == false; i++)
814 MapBlock *block = i.getNode()->getValue();
815 v3s16 p = block->getPos();
816 block->setLightingExpired(false);
824 //TimeTaker timer("unspreadLight");
825 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
830 u32 diff = modified_blocks.size() - count_was;
831 count_was = modified_blocks.size();
832 infostream<<"unspreadLight modified "<<diff<<std::endl;
836 //TimeTaker timer("spreadLight");
837 spreadLight(bank, light_sources, modified_blocks);
842 u32 diff = modified_blocks.size() - count_was;
843 count_was = modified_blocks.size();
844 infostream<<"spreadLight modified "<<diff<<std::endl;
850 //MapVoxelManipulator vmanip(this);
852 // Make a manual voxel manipulator and load all the blocks
853 // that touch the requested blocks
854 ManualMapVoxelManipulator vmanip(this);
857 //TimeTaker timer("initialEmerge");
859 core::map<v3s16, MapBlock*>::Iterator i;
860 i = blocks_to_update.getIterator();
861 for(; i.atEnd() == false; i++)
863 MapBlock *block = i.getNode()->getValue();
864 v3s16 p = block->getPos();
866 // Add all surrounding blocks
867 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
870 Add all surrounding blocks that have up-to-date lighting
871 NOTE: This doesn't quite do the job (not everything
872 appropriate is lighted)
874 /*for(s16 z=-1; z<=1; z++)
875 for(s16 y=-1; y<=1; y++)
876 for(s16 x=-1; x<=1; x++)
878 v3s16 p2 = p + v3s16(x,y,z);
879 MapBlock *block = getBlockNoCreateNoEx(p2);
884 if(block->getLightingExpired())
886 vmanip.initialEmerge(p2, p2);
889 // Lighting of block will be updated completely
890 block->setLightingExpired(false);
895 //TimeTaker timer("unSpreadLight");
896 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
899 //TimeTaker timer("spreadLight");
900 vmanip.spreadLight(bank, light_sources, nodemgr);
903 //TimeTaker timer("blitBack");
904 vmanip.blitBack(modified_blocks);
906 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
911 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
914 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
915 std::map<v3s16, MapBlock*> & modified_blocks)
917 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
918 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
921 Update information about whether day and night light differ
923 for(std::map<v3s16, MapBlock*>::iterator
924 i = modified_blocks.begin();
925 i != modified_blocks.end(); ++i)
927 MapBlock *block = i->second;
928 block->expireDayNightDiff();
934 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
935 std::map<v3s16, MapBlock*> &modified_blocks,
936 bool remove_metadata)
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 std::set<v3s16> 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[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
1023 if (remove_metadata) {
1024 removeNodeMetadata(p);
1028 Set the node on the map
1034 If node is under sunlight and doesn't let sunlight through,
1035 take all sunlighted nodes under it and clear light from them
1036 and from where the light has been spread.
1037 TODO: This could be optimized by mass-unlighting instead
1040 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1044 //m_dout<<DTIME<<"y="<<y<<std::endl;
1045 v3s16 n2pos(p.X, y, p.Z);
1049 n2 = getNode(n2pos);
1051 catch(InvalidPositionException &e)
1056 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1058 unLightNeighbors(LIGHTBANK_DAY,
1059 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1060 light_sources, modified_blocks);
1061 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1069 for(s32 i=0; i<2; i++)
1071 enum LightBank bank = banks[i];
1074 Spread light from all nodes that might be capable of doing so
1076 spreadLight(bank, light_sources, modified_blocks);
1080 Update information about whether day and night light differ
1082 for(std::map<v3s16, MapBlock*>::iterator
1083 i = modified_blocks.begin();
1084 i != modified_blocks.end(); ++i)
1086 i->second->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 std::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 std::set<v3s16> 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[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(std::map<v3s16, MapBlock*>::iterator
1274 i = modified_blocks.begin();
1275 i != modified_blocks.end(); ++i)
1277 i->second->expireDayNightDiff();
1283 if(m_gamedef->rollback())
1285 RollbackNode rollback_newnode(this, p, m_gamedef);
1286 RollbackAction action;
1287 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1288 m_gamedef->rollback()->reportAction(action);
1292 Add neighboring liquid nodes and this node to transform queue.
1293 (it's vital for the node itself to get updated last.)
1296 v3s16(0,0,1), // back
1297 v3s16(0,1,0), // top
1298 v3s16(1,0,0), // right
1299 v3s16(0,0,-1), // front
1300 v3s16(0,-1,0), // bottom
1301 v3s16(-1,0,0), // left
1302 v3s16(0,0,0), // self
1304 for(u16 i=0; i<7; i++)
1309 v3s16 p2 = p + dirs[i];
1311 MapNode n2 = getNode(p2);
1312 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1314 m_transforming_liquid.push_back(p2);
1317 }catch(InvalidPositionException &e)
1323 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1326 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1330 bool succeeded = true;
1332 std::map<v3s16, MapBlock*> modified_blocks;
1333 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1335 // Copy modified_blocks to event
1336 for(std::map<v3s16, MapBlock*>::iterator
1337 i = modified_blocks.begin();
1338 i != modified_blocks.end(); ++i)
1340 event.modified_blocks.insert(i->first);
1343 catch(InvalidPositionException &e){
1347 dispatchEvent(&event);
1352 bool Map::removeNodeWithEvent(v3s16 p)
1355 event.type = MEET_REMOVENODE;
1358 bool succeeded = true;
1360 std::map<v3s16, MapBlock*> modified_blocks;
1361 removeNodeAndUpdate(p, modified_blocks);
1363 // Copy modified_blocks to event
1364 for(std::map<v3s16, MapBlock*>::iterator
1365 i = modified_blocks.begin();
1366 i != modified_blocks.end(); ++i)
1368 event.modified_blocks.insert(i->first);
1371 catch(InvalidPositionException &e){
1375 dispatchEvent(&event);
1380 bool Map::getDayNightDiff(v3s16 blockpos)
1383 v3s16 p = blockpos + v3s16(0,0,0);
1384 MapBlock *b = getBlockNoCreate(p);
1385 if(b->getDayNightDiff())
1388 catch(InvalidPositionException &e){}
1391 v3s16 p = blockpos + v3s16(-1,0,0);
1392 MapBlock *b = getBlockNoCreate(p);
1393 if(b->getDayNightDiff())
1396 catch(InvalidPositionException &e){}
1398 v3s16 p = blockpos + v3s16(0,-1,0);
1399 MapBlock *b = getBlockNoCreate(p);
1400 if(b->getDayNightDiff())
1403 catch(InvalidPositionException &e){}
1405 v3s16 p = blockpos + v3s16(0,0,-1);
1406 MapBlock *b = getBlockNoCreate(p);
1407 if(b->getDayNightDiff())
1410 catch(InvalidPositionException &e){}
1413 v3s16 p = blockpos + v3s16(1,0,0);
1414 MapBlock *b = getBlockNoCreate(p);
1415 if(b->getDayNightDiff())
1418 catch(InvalidPositionException &e){}
1420 v3s16 p = blockpos + v3s16(0,1,0);
1421 MapBlock *b = getBlockNoCreate(p);
1422 if(b->getDayNightDiff())
1425 catch(InvalidPositionException &e){}
1427 v3s16 p = blockpos + v3s16(0,0,1);
1428 MapBlock *b = getBlockNoCreate(p);
1429 if(b->getDayNightDiff())
1432 catch(InvalidPositionException &e){}
1438 Updates usage timers
1440 void Map::timerUpdate(float dtime, float unload_timeout,
1441 std::list<v3s16> *unloaded_blocks)
1443 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1445 // Profile modified reasons
1446 Profiler modprofiler;
1448 std::list<v2s16> sector_deletion_queue;
1449 u32 deleted_blocks_count = 0;
1450 u32 saved_blocks_count = 0;
1451 u32 block_count_all = 0;
1454 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1455 si != m_sectors.end(); ++si)
1457 MapSector *sector = si->second;
1459 bool all_blocks_deleted = true;
1461 std::list<MapBlock*> blocks;
1462 sector->getBlocks(blocks);
1464 for(std::list<MapBlock*>::iterator i = blocks.begin();
1465 i != blocks.end(); ++i)
1467 MapBlock *block = (*i);
1469 block->incrementUsageTimer(dtime);
1471 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1473 v3s16 p = block->getPos();
1476 if(block->getModified() != MOD_STATE_CLEAN
1477 && save_before_unloading)
1479 modprofiler.add(block->getModifiedReason(), 1);
1481 saved_blocks_count++;
1484 // Delete from memory
1485 sector->deleteBlock(block);
1488 unloaded_blocks->push_back(p);
1490 deleted_blocks_count++;
1494 all_blocks_deleted = false;
1499 if(all_blocks_deleted)
1501 sector_deletion_queue.push_back(si->first);
1506 // Finally delete the empty sectors
1507 deleteSectors(sector_deletion_queue);
1509 if(deleted_blocks_count != 0)
1511 PrintInfo(infostream); // ServerMap/ClientMap:
1512 infostream<<"Unloaded "<<deleted_blocks_count
1513 <<" blocks from memory";
1514 if(save_before_unloading)
1515 infostream<<", of which "<<saved_blocks_count<<" were written";
1516 infostream<<", "<<block_count_all<<" blocks in memory";
1517 infostream<<"."<<std::endl;
1518 if(saved_blocks_count != 0){
1519 PrintInfo(infostream); // ServerMap/ClientMap:
1520 infostream<<"Blocks modified by: "<<std::endl;
1521 modprofiler.print(infostream);
1526 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1528 timerUpdate(0.0, -1.0, unloaded_blocks);
1531 void Map::deleteSectors(std::list<v2s16> &list)
1533 for(std::list<v2s16>::iterator j = list.begin();
1534 j != list.end(); ++j)
1536 MapSector *sector = m_sectors[*j];
1537 // If sector is in sector cache, remove it from there
1538 if(m_sector_cache == sector)
1539 m_sector_cache = NULL;
1540 // Remove from map and delete
1541 m_sectors.erase(*j);
1547 void Map::unloadUnusedData(float timeout,
1548 core::list<v3s16> *deleted_blocks)
1550 core::list<v2s16> sector_deletion_queue;
1551 u32 deleted_blocks_count = 0;
1552 u32 saved_blocks_count = 0;
1554 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1555 for(; si.atEnd() == false; si++)
1557 MapSector *sector = si.getNode()->getValue();
1559 bool all_blocks_deleted = true;
1561 core::list<MapBlock*> blocks;
1562 sector->getBlocks(blocks);
1563 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1564 i != blocks.end(); i++)
1566 MapBlock *block = (*i);
1568 if(block->getUsageTimer() > timeout)
1571 if(block->getModified() != MOD_STATE_CLEAN)
1574 saved_blocks_count++;
1576 // Delete from memory
1577 sector->deleteBlock(block);
1578 deleted_blocks_count++;
1582 all_blocks_deleted = false;
1586 if(all_blocks_deleted)
1588 sector_deletion_queue.push_back(si.getNode()->getKey());
1592 deleteSectors(sector_deletion_queue);
1594 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1595 <<", of which "<<saved_blocks_count<<" were wr."
1598 //return sector_deletion_queue.getSize();
1599 //return deleted_blocks_count;
1603 void Map::PrintInfo(std::ostream &out)
1608 #define WATER_DROP_BOOST 4
1612 NEIGHBOR_SAME_LEVEL,
1615 struct NodeNeighbor {
1619 bool l; //can liquid
1622 void Map::transforming_liquid_add(v3s16 p) {
1623 m_transforming_liquid.push_back(p);
1626 s32 Map::transforming_liquid_size() {
1627 return m_transforming_liquid.size();
1630 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1632 INodeDefManager *nodemgr = m_gamedef->ndef();
1634 DSTACK(__FUNCTION_NAME);
1635 //TimeTaker timer("transformLiquids()");
1638 u32 initial_size = m_transforming_liquid.size();
1640 /*if(initial_size != 0)
1641 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1643 // list of nodes that due to viscosity have not reached their max level height
1644 UniqueQueue<v3s16> must_reflow;
1646 // List of MapBlocks that will require a lighting update (due to lava)
1647 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1649 u16 loop_max = g_settings->getU16("liquid_loop_max");
1651 while(m_transforming_liquid.size() != 0)
1653 // This should be done here so that it is done when continue is used
1654 if(loopcount >= initial_size || loopcount >= loop_max)
1659 Get a queued transforming liquid node
1661 v3s16 p0 = m_transforming_liquid.pop_front();
1663 MapNode n0 = getNodeNoEx(p0);
1666 Collect information about current node
1668 s8 liquid_level = -1;
1669 content_t liquid_kind = CONTENT_IGNORE;
1670 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1671 switch (liquid_type) {
1673 liquid_level = LIQUID_LEVEL_SOURCE;
1674 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1676 case LIQUID_FLOWING:
1677 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1678 liquid_kind = n0.getContent();
1681 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1682 // continue with the next node.
1683 if (n0.getContent() != CONTENT_AIR)
1685 liquid_kind = CONTENT_AIR;
1690 Collect information about the environment
1692 const v3s16 *dirs = g_6dirs;
1693 NodeNeighbor sources[6]; // surrounding sources
1694 int num_sources = 0;
1695 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1697 NodeNeighbor airs[6]; // surrounding air
1699 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1700 int num_neutrals = 0;
1701 bool flowing_down = false;
1702 for (u16 i = 0; i < 6; i++) {
1703 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1706 nt = NEIGHBOR_UPPER;
1709 nt = NEIGHBOR_LOWER;
1712 v3s16 npos = p0 + dirs[i];
1713 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1714 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1716 if (nb.n.getContent() == CONTENT_AIR) {
1717 airs[num_airs++] = nb;
1718 // if the current node is a water source the neighbor
1719 // should be enqueded for transformation regardless of whether the
1720 // current node changes or not.
1721 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1722 m_transforming_liquid.push_back(npos);
1723 // if the current node happens to be a flowing node, it will start to flow down here.
1724 if (nb.t == NEIGHBOR_LOWER) {
1725 flowing_down = true;
1728 neutrals[num_neutrals++] = nb;
1732 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1733 if (liquid_kind == CONTENT_AIR)
1734 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1735 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1736 neutrals[num_neutrals++] = nb;
1738 // Do not count bottom source, it will screw things up
1740 sources[num_sources++] = nb;
1743 case LIQUID_FLOWING:
1744 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1745 if (liquid_kind == CONTENT_AIR)
1746 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1747 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1748 neutrals[num_neutrals++] = nb;
1750 flows[num_flows++] = nb;
1751 if (nb.t == NEIGHBOR_LOWER)
1752 flowing_down = true;
1759 decide on the type (and possibly level) of the current node
1761 content_t new_node_content;
1762 s8 new_node_level = -1;
1763 s8 max_node_level = -1;
1764 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
1765 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1766 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1767 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1768 // it's perfectly safe to use liquid_kind here to determine the new node content.
1769 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1770 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1771 // liquid_kind is set properly, see above
1772 new_node_content = liquid_kind;
1773 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1774 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1775 new_node_content = CONTENT_AIR;
1777 // no surrounding sources, so get the maximum level that can flow into this node
1778 for (u16 i = 0; i < num_flows; i++) {
1779 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1780 switch (flows[i].t) {
1781 case NEIGHBOR_UPPER:
1782 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1783 max_node_level = LIQUID_LEVEL_MAX;
1784 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1785 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1786 } else if (nb_liquid_level > max_node_level)
1787 max_node_level = nb_liquid_level;
1789 case NEIGHBOR_LOWER:
1791 case NEIGHBOR_SAME_LEVEL:
1792 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1793 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1794 max_node_level = nb_liquid_level - 1;
1800 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1801 if (viscosity > 1 && max_node_level != liquid_level) {
1802 // amount to gain, limited by viscosity
1803 // must be at least 1 in absolute value
1804 s8 level_inc = max_node_level - liquid_level;
1805 if (level_inc < -viscosity || level_inc > viscosity)
1806 new_node_level = liquid_level + level_inc/viscosity;
1807 else if (level_inc < 0)
1808 new_node_level = liquid_level - 1;
1809 else if (level_inc > 0)
1810 new_node_level = liquid_level + 1;
1811 if (new_node_level != max_node_level)
1812 must_reflow.push_back(p0);
1814 new_node_level = max_node_level;
1816 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1817 new_node_content = liquid_kind;
1819 new_node_content = CONTENT_AIR;
1824 check if anything has changed. if not, just continue with the next node.
1826 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1827 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1828 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1834 update the current node
1837 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1838 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1839 // set level to last 3 bits, flowing down bit to 4th bit
1840 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1842 // set the liquid level and flow bit to 0
1843 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1845 n0.setContent(new_node_content);
1847 // Find out whether there is a suspect for this action
1848 std::string suspect;
1849 if(m_gamedef->rollback()){
1850 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1853 if(!suspect.empty()){
1855 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1856 // Get old node for rollback
1857 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1861 RollbackNode rollback_newnode(this, p0, m_gamedef);
1862 RollbackAction action;
1863 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1864 m_gamedef->rollback()->reportAction(action);
1870 v3s16 blockpos = getNodeBlockPos(p0);
1871 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1873 modified_blocks[blockpos] = block;
1874 // If new or old node emits light, MapBlock requires lighting update
1875 if(nodemgr->get(n0).light_source != 0 ||
1876 nodemgr->get(n00).light_source != 0)
1877 lighting_modified_blocks[block->getPos()] = block;
1881 enqueue neighbors for update if neccessary
1883 switch (nodemgr->get(n0.getContent()).liquid_type) {
1885 case LIQUID_FLOWING:
1886 // make sure source flows into all neighboring nodes
1887 for (u16 i = 0; i < num_flows; i++)
1888 if (flows[i].t != NEIGHBOR_UPPER)
1889 m_transforming_liquid.push_back(flows[i].p);
1890 for (u16 i = 0; i < num_airs; i++)
1891 if (airs[i].t != NEIGHBOR_UPPER)
1892 m_transforming_liquid.push_back(airs[i].p);
1895 // this flow has turned to air; neighboring flows might need to do the same
1896 for (u16 i = 0; i < num_flows; i++)
1897 m_transforming_liquid.push_back(flows[i].p);
1901 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1902 while (must_reflow.size() > 0)
1903 m_transforming_liquid.push_back(must_reflow.pop_front());
1904 updateLighting(lighting_modified_blocks, modified_blocks);
1907 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1909 v3s16 blockpos = getNodeBlockPos(p);
1910 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1911 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1913 infostream<<"Map::getNodeMetadata(): Need to emerge "
1914 <<PP(blockpos)<<std::endl;
1915 block = emergeBlock(blockpos, false);
1918 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1922 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1926 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1928 v3s16 blockpos = getNodeBlockPos(p);
1929 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1930 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1932 infostream<<"Map::setNodeMetadata(): Need to emerge "
1933 <<PP(blockpos)<<std::endl;
1934 block = emergeBlock(blockpos, false);
1937 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1941 block->m_node_metadata.set(p_rel, meta);
1945 void Map::removeNodeMetadata(v3s16 p)
1947 v3s16 blockpos = getNodeBlockPos(p);
1948 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1949 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1952 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1956 block->m_node_metadata.remove(p_rel);
1959 NodeTimer Map::getNodeTimer(v3s16 p)
1961 v3s16 blockpos = getNodeBlockPos(p);
1962 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1963 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1965 infostream<<"Map::getNodeTimer(): Need to emerge "
1966 <<PP(blockpos)<<std::endl;
1967 block = emergeBlock(blockpos, false);
1970 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1974 NodeTimer t = block->m_node_timers.get(p_rel);
1978 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1980 v3s16 blockpos = getNodeBlockPos(p);
1981 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1982 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1984 infostream<<"Map::setNodeTimer(): Need to emerge "
1985 <<PP(blockpos)<<std::endl;
1986 block = emergeBlock(blockpos, false);
1989 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1993 block->m_node_timers.set(p_rel, t);
1996 void Map::removeNodeTimer(v3s16 p)
1998 v3s16 blockpos = getNodeBlockPos(p);
1999 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2000 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2003 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2007 block->m_node_timers.remove(p_rel);
2013 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2014 Map(dout_server, gamedef),
2016 m_map_metadata_changed(true)
2018 verbosestream<<__FUNCTION_NAME<<std::endl;
2021 Try to load map; if not found, create a new one.
2024 // Determine which database backend to use
2025 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2027 bool succeeded = conf.readConfigFile(conf_path.c_str());
2028 if (!succeeded || !conf.exists("backend")) {
2029 // fall back to sqlite3
2030 dbase = new Database_SQLite3(this, savedir);
2031 conf.set("backend", "sqlite3");
2033 std::string backend = conf.get("backend");
2034 if (backend == "dummy")
2035 dbase = new Database_Dummy(this);
2036 else if (backend == "sqlite3")
2037 dbase = new Database_SQLite3(this, savedir);
2039 else if (backend == "leveldb")
2040 dbase = new Database_LevelDB(this, savedir);
2043 else if (backend == "redis")
2044 dbase = new Database_Redis(this, savedir);
2047 throw BaseException("Unknown map backend");
2050 m_savedir = savedir;
2051 m_map_saving_enabled = false;
2055 // If directory exists, check contents and load if possible
2056 if(fs::PathExists(m_savedir))
2058 // If directory is empty, it is safe to save into it.
2059 if(fs::GetDirListing(m_savedir).size() == 0)
2061 infostream<<"ServerMap: Empty save directory is valid."
2063 m_map_saving_enabled = true;
2068 // Load map metadata (seed, chunksize)
2071 catch(SettingNotFoundException &e){
2072 infostream<<"ServerMap: Some metadata not found."
2073 <<" Using default settings."<<std::endl;
2075 catch(FileNotGoodException &e){
2076 infostream<<"WARNING: Could not load map metadata"
2077 //<<" Disabling chunk-based generator."
2082 infostream<<"ServerMap: Successfully loaded map "
2083 <<"metadata from "<<savedir
2084 <<", assuming valid save directory."
2085 <<" seed="<< m_emerge->params.seed <<"."
2088 m_map_saving_enabled = true;
2089 // Map loaded, not creating new one
2093 // If directory doesn't exist, it is safe to save to it
2095 m_map_saving_enabled = true;
2098 catch(std::exception &e)
2100 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2101 <<", exception: "<<e.what()<<std::endl;
2102 infostream<<"Please remove the map or fix it."<<std::endl;
2103 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2106 infostream<<"Initializing new map."<<std::endl;
2108 // Create zero sector
2109 emergeSector(v2s16(0,0));
2111 // Initially write whole map
2112 save(MOD_STATE_CLEAN);
2115 ServerMap::~ServerMap()
2117 verbosestream<<__FUNCTION_NAME<<std::endl;
2121 if(m_map_saving_enabled)
2123 // Save only changed parts
2124 save(MOD_STATE_WRITE_AT_UNLOAD);
2125 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2129 infostream<<"ServerMap: Map not saved"<<std::endl;
2132 catch(std::exception &e)
2134 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2135 <<", exception: "<<e.what()<<std::endl;
2139 Close database if it was opened
2147 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2148 for(; i.atEnd() == false; i++)
2150 MapChunk *chunk = i.getNode()->getValue();
2156 u64 ServerMap::getSeed()
2158 return m_emerge->params.seed;
2161 s16 ServerMap::getWaterLevel()
2163 return m_emerge->params.water_level;
2166 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2168 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2169 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2171 s16 chunksize = m_emerge->params.chunksize;
2172 s16 coffset = -chunksize / 2;
2173 v3s16 chunk_offset(coffset, coffset, coffset);
2174 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2175 v3s16 blockpos_min = blockpos_div * chunksize;
2176 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2177 blockpos_min += chunk_offset;
2178 blockpos_max += chunk_offset;
2180 v3s16 extra_borders(1,1,1);
2182 // Do nothing if not inside limits (+-1 because of neighbors)
2183 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2184 blockpos_over_limit(blockpos_max + extra_borders))
2187 data->seed = m_emerge->params.seed;
2188 data->blockpos_min = blockpos_min;
2189 data->blockpos_max = blockpos_max;
2190 data->blockpos_requested = blockpos;
2191 data->nodedef = m_gamedef->ndef();
2194 Create the whole area of this and the neighboring blocks
2197 //TimeTaker timer("initBlockMake() create area");
2199 for(s16 x=blockpos_min.X-extra_borders.X;
2200 x<=blockpos_max.X+extra_borders.X; x++)
2201 for(s16 z=blockpos_min.Z-extra_borders.Z;
2202 z<=blockpos_max.Z+extra_borders.Z; z++)
2204 v2s16 sectorpos(x, z);
2205 // Sector metadata is loaded from disk if not already loaded.
2206 ServerMapSector *sector = createSector(sectorpos);
2210 for(s16 y=blockpos_min.Y-extra_borders.Y;
2211 y<=blockpos_max.Y+extra_borders.Y; y++)
2214 //MapBlock *block = createBlock(p);
2215 // 1) get from memory, 2) load from disk
2216 MapBlock *block = emergeBlock(p, false);
2217 // 3) create a blank one
2220 block = createBlock(p);
2223 Block gets sunlight if this is true.
2225 Refer to the map generator heuristics.
2227 bool ug = m_emerge->isBlockUnderground(p);
2228 block->setIsUnderground(ug);
2231 // Lighting will not be valid after make_chunk is called
2232 block->setLightingExpired(true);
2233 // Lighting will be calculated
2234 //block->setLightingExpired(false);
2240 Now we have a big empty area.
2242 Make a ManualMapVoxelManipulator that contains this and the
2246 // The area that contains this block and it's neighbors
2247 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2248 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2250 data->vmanip = new ManualMapVoxelManipulator(this);
2251 //data->vmanip->setMap(this);
2255 //TimeTaker timer("initBlockMake() initialEmerge");
2256 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2259 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2260 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2261 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2262 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2263 core::map<v3s16, u8>::Node *n;
2264 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2267 u8 flags = n->getValue();
2268 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2274 // Data is ready now.
2278 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2279 std::map<v3s16, MapBlock*> &changed_blocks)
2281 v3s16 blockpos_min = data->blockpos_min;
2282 v3s16 blockpos_max = data->blockpos_max;
2283 v3s16 blockpos_requested = data->blockpos_requested;
2284 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2285 <<blockpos_requested.Y<<","
2286 <<blockpos_requested.Z<<")"<<std::endl;*/
2288 v3s16 extra_borders(1,1,1);
2290 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2292 /*infostream<<"Resulting vmanip:"<<std::endl;
2293 data->vmanip.print(infostream);*/
2295 // Make sure affected blocks are loaded
2296 for(s16 x=blockpos_min.X-extra_borders.X;
2297 x<=blockpos_max.X+extra_borders.X; x++)
2298 for(s16 z=blockpos_min.Z-extra_borders.Z;
2299 z<=blockpos_max.Z+extra_borders.Z; z++)
2300 for(s16 y=blockpos_min.Y-extra_borders.Y;
2301 y<=blockpos_max.Y+extra_borders.Y; y++)
2304 // Load from disk if not already in memory
2305 emergeBlock(p, false);
2309 Blit generated stuff to map
2310 NOTE: blitBackAll adds nearly everything to changed_blocks
2314 //TimeTaker timer("finishBlockMake() blitBackAll");
2315 data->vmanip->blitBackAll(&changed_blocks);
2318 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2321 Copy transforming liquid information
2323 while(data->transforming_liquid.size() > 0)
2325 v3s16 p = data->transforming_liquid.pop_front();
2326 m_transforming_liquid.push_back(p);
2330 Do stuff in central blocks
2338 TimeTaker t("finishBlockMake lighting update");
2340 core::map<v3s16, MapBlock*> lighting_update_blocks;
2343 for(s16 x=blockpos_min.X-extra_borders.X;
2344 x<=blockpos_max.X+extra_borders.X; x++)
2345 for(s16 z=blockpos_min.Z-extra_borders.Z;
2346 z<=blockpos_max.Z+extra_borders.Z; z++)
2347 for(s16 y=blockpos_min.Y-extra_borders.Y;
2348 y<=blockpos_max.Y+extra_borders.Y; y++)
2351 MapBlock *block = getBlockNoCreateNoEx(p);
2353 lighting_update_blocks.insert(block->getPos(), block);
2356 updateLighting(lighting_update_blocks, changed_blocks);
2360 Set lighting to non-expired state in all of them.
2361 This is cheating, but it is not fast enough if all of them
2362 would actually be updated.
2364 for(s16 x=blockpos_min.X-extra_borders.X;
2365 x<=blockpos_max.X+extra_borders.X; x++)
2366 for(s16 z=blockpos_min.Z-extra_borders.Z;
2367 z<=blockpos_max.Z+extra_borders.Z; z++)
2368 for(s16 y=blockpos_min.Y-extra_borders.Y;
2369 y<=blockpos_max.Y+extra_borders.Y; y++)
2372 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2376 if(enable_mapgen_debug_info == false)
2377 t.stop(true); // Hide output
2382 Go through changed blocks
2384 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2385 i != changed_blocks.end(); ++i)
2387 MapBlock *block = i->second;
2390 Update day/night difference cache of the MapBlocks
2392 block->expireDayNightDiff();
2394 Set block as modified
2396 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2397 "finishBlockMake expireDayNightDiff");
2401 Set central blocks as generated
2403 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2404 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2405 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2408 MapBlock *block = getBlockNoCreateNoEx(p);
2410 block->setGenerated(true);
2414 Save changed parts of map
2415 NOTE: Will be saved later.
2417 //save(MOD_STATE_WRITE_AT_UNLOAD);
2419 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2420 <<","<<blockpos_requested.Y<<","
2421 <<blockpos_requested.Z<<")"<<std::endl;*/
2425 if(enable_mapgen_debug_info)
2428 Analyze resulting blocks
2430 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2431 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2432 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2433 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2434 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2435 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2437 v3s16 p = v3s16(x,y,z);
2438 MapBlock *block = getBlockNoCreateNoEx(p);
2440 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2441 infostream<<"Generated "<<spos<<": "
2442 <<analyze_block(block)<<std::endl;
2447 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2453 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2455 DSTACKF("%s: p2d=(%d,%d)",
2460 Check if it exists already in memory
2462 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2467 Try to load it from disk (with blocks)
2469 //if(loadSectorFull(p2d) == true)
2472 Try to load metadata from disk
2475 if(loadSectorMeta(p2d) == true)
2477 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2480 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2481 throw InvalidPositionException("");
2487 Do not create over-limit
2489 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2490 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2491 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2492 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2493 throw InvalidPositionException("createSector(): pos. over limit");
2496 Generate blank sector
2499 sector = new ServerMapSector(this, p2d, m_gamedef);
2501 // Sector position on map in nodes
2502 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2507 m_sectors[p2d] = sector;
2514 This is a quick-hand function for calling makeBlock().
2516 MapBlock * ServerMap::generateBlock(
2518 std::map<v3s16, MapBlock*> &modified_blocks
2521 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2523 /*infostream<<"generateBlock(): "
2524 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2527 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2529 TimeTaker timer("generateBlock");
2531 //MapBlock *block = original_dummy;
2533 v2s16 p2d(p.X, p.Z);
2534 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2537 Do not generate over-limit
2539 if(blockpos_over_limit(p))
2541 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2542 throw InvalidPositionException("generateBlock(): pos. over limit");
2546 Create block make data
2549 initBlockMake(&data, p);
2555 TimeTaker t("mapgen::make_block()");
2556 mapgen->makeChunk(&data);
2557 //mapgen::make_block(&data);
2559 if(enable_mapgen_debug_info == false)
2560 t.stop(true); // Hide output
2564 Blit data back on map, update lighting, add mobs and whatever this does
2566 finishBlockMake(&data, modified_blocks);
2571 MapBlock *block = getBlockNoCreateNoEx(p);
2579 bool erroneus_content = false;
2580 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2581 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2582 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2585 MapNode n = block->getNode(p);
2586 if(n.getContent() == CONTENT_IGNORE)
2588 infostream<<"CONTENT_IGNORE at "
2589 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2591 erroneus_content = true;
2595 if(erroneus_content)
2604 Generate a completely empty block
2608 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2609 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2611 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2614 n.setContent(CONTENT_AIR);
2615 block->setNode(v3s16(x0,y0,z0), n);
2621 if(enable_mapgen_debug_info == false)
2622 timer.stop(true); // Hide output
2628 MapBlock * ServerMap::createBlock(v3s16 p)
2630 DSTACKF("%s: p=(%d,%d,%d)",
2631 __FUNCTION_NAME, p.X, p.Y, p.Z);
2634 Do not create over-limit
2636 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2637 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2638 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2639 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2640 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2641 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2642 throw InvalidPositionException("createBlock(): pos. over limit");
2644 v2s16 p2d(p.X, p.Z);
2647 This will create or load a sector if not found in memory.
2648 If block exists on disk, it will be loaded.
2650 NOTE: On old save formats, this will be slow, as it generates
2651 lighting on blocks for them.
2653 ServerMapSector *sector;
2655 sector = (ServerMapSector*)createSector(p2d);
2656 assert(sector->getId() == MAPSECTOR_SERVER);
2658 catch(InvalidPositionException &e)
2660 infostream<<"createBlock: createSector() failed"<<std::endl;
2664 NOTE: This should not be done, or at least the exception
2665 should not be passed on as std::exception, because it
2666 won't be catched at all.
2668 /*catch(std::exception &e)
2670 infostream<<"createBlock: createSector() failed: "
2671 <<e.what()<<std::endl;
2676 Try to get a block from the sector
2679 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2682 if(block->isDummy())
2687 block = sector->createBlankBlock(block_y);
2692 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2694 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2696 p.X, p.Y, p.Z, create_blank);
2699 MapBlock *block = getBlockNoCreateNoEx(p);
2700 if(block && block->isDummy() == false)
2705 MapBlock *block = loadBlock(p);
2711 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2712 MapBlock *block = sector->createBlankBlock(p.Y);
2720 std::map<v3s16, MapBlock*> modified_blocks;
2721 MapBlock *block = generateBlock(p, modified_blocks);
2725 event.type = MEET_OTHER;
2728 // Copy modified_blocks to event
2729 for(std::map<v3s16, MapBlock*>::iterator
2730 i = modified_blocks.begin();
2731 i != modified_blocks.end(); ++i)
2733 event.modified_blocks.insert(i->first);
2737 dispatchEvent(&event);
2747 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2749 MapBlock *block = getBlockNoCreateNoEx(p3d);
2751 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2756 void ServerMap::prepareBlock(MapBlock *block) {
2759 s16 ServerMap::findGroundLevel(v2s16 p2d)
2763 Uh, just do something random...
2765 // Find existing map from top to down
2768 v3s16 p(p2d.X, max, p2d.Y);
2769 for(; p.Y>min; p.Y--)
2771 MapNode n = getNodeNoEx(p);
2772 if(n.getContent() != CONTENT_IGNORE)
2777 // If this node is not air, go to plan b
2778 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2780 // Search existing walkable and return it
2781 for(; p.Y>min; p.Y--)
2783 MapNode n = getNodeNoEx(p);
2784 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2793 Determine from map generator noise functions
2796 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2799 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2800 //return (s16)level;
2803 bool ServerMap::loadFromFolders() {
2804 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2809 void ServerMap::createDirs(std::string path)
2811 if(fs::CreateAllDirs(path) == false)
2813 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2814 <<"\""<<path<<"\""<<std::endl;
2815 throw BaseException("ServerMap failed to create directory");
2819 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2825 snprintf(cc, 9, "%.4x%.4x",
2826 (unsigned int)pos.X&0xffff,
2827 (unsigned int)pos.Y&0xffff);
2829 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2831 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2832 (unsigned int)pos.X&0xfff,
2833 (unsigned int)pos.Y&0xfff);
2835 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2842 v2s16 ServerMap::getSectorPos(std::string dirname)
2844 unsigned int x = 0, y = 0;
2846 std::string component;
2847 fs::RemoveLastPathComponent(dirname, &component, 1);
2848 if(component.size() == 8)
2851 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2853 else if(component.size() == 3)
2856 fs::RemoveLastPathComponent(dirname, &component, 2);
2857 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2858 // Sign-extend the 12 bit values up to 16 bits...
2859 if(x&0x800) x|=0xF000;
2860 if(y&0x800) y|=0xF000;
2867 v2s16 pos((s16)x, (s16)y);
2871 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2873 v2s16 p2d = getSectorPos(sectordir);
2875 if(blockfile.size() != 4){
2876 throw InvalidFilenameException("Invalid block filename");
2879 int r = sscanf(blockfile.c_str(), "%4x", &y);
2881 throw InvalidFilenameException("Invalid block filename");
2882 return v3s16(p2d.X, y, p2d.Y);
2885 std::string ServerMap::getBlockFilename(v3s16 p)
2888 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2892 void ServerMap::save(ModifiedState save_level)
2894 DSTACK(__FUNCTION_NAME);
2895 if(m_map_saving_enabled == false)
2897 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2901 if(save_level == MOD_STATE_CLEAN)
2902 infostream<<"ServerMap: Saving whole map, this can take time."
2905 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2910 // Profile modified reasons
2911 Profiler modprofiler;
2913 u32 sector_meta_count = 0;
2914 u32 block_count = 0;
2915 u32 block_count_all = 0; // Number of blocks in memory
2917 // Don't do anything with sqlite unless something is really saved
2918 bool save_started = false;
2920 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2921 i != m_sectors.end(); ++i)
2923 ServerMapSector *sector = (ServerMapSector*)i->second;
2924 assert(sector->getId() == MAPSECTOR_SERVER);
2926 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2928 saveSectorMeta(sector);
2929 sector_meta_count++;
2931 std::list<MapBlock*> blocks;
2932 sector->getBlocks(blocks);
2934 for(std::list<MapBlock*>::iterator j = blocks.begin();
2935 j != blocks.end(); ++j)
2937 MapBlock *block = *j;
2941 if(block->getModified() >= (u32)save_level)
2946 save_started = true;
2949 modprofiler.add(block->getModifiedReason(), 1);
2954 /*infostream<<"ServerMap: Written block ("
2955 <<block->getPos().X<<","
2956 <<block->getPos().Y<<","
2957 <<block->getPos().Z<<")"
2966 Only print if something happened or saved whole map
2968 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2969 || block_count != 0)
2971 infostream<<"ServerMap: Written: "
2972 <<sector_meta_count<<" sector metadata files, "
2973 <<block_count<<" block files"
2974 <<", "<<block_count_all<<" blocks in memory."
2976 PrintInfo(infostream); // ServerMap/ClientMap:
2977 infostream<<"Blocks modified by: "<<std::endl;
2978 modprofiler.print(infostream);
2982 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
2984 if(loadFromFolders()){
2985 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2986 <<"all blocks that are stored in flat files"<<std::endl;
2988 dbase->listAllLoadableBlocks(dst);
2991 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
2993 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2994 si != m_sectors.end(); ++si)
2996 MapSector *sector = si->second;
2998 std::list<MapBlock*> blocks;
2999 sector->getBlocks(blocks);
3001 for(std::list<MapBlock*>::iterator i = blocks.begin();
3002 i != blocks.end(); ++i)
3004 MapBlock *block = (*i);
3005 v3s16 p = block->getPos();
3011 void ServerMap::saveMapMeta()
3013 DSTACK(__FUNCTION_NAME);
3015 /*infostream<<"ServerMap::saveMapMeta(): "
3019 createDirs(m_savedir);
3021 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3022 std::ostringstream ss(std::ios_base::binary);
3026 m_emerge->saveParamsToSettings(¶ms);
3027 params.writeLines(ss);
3029 ss<<"[end_of_params]\n";
3031 if(!fs::safeWriteToFile(fullpath, ss.str()))
3033 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3034 <<"could not write "<<fullpath<<std::endl;
3035 throw FileNotGoodException("Cannot save chunk metadata");
3038 m_map_metadata_changed = false;
3041 void ServerMap::loadMapMeta()
3043 DSTACK(__FUNCTION_NAME);
3045 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3048 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3049 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3050 if(is.good() == false)
3052 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3053 <<"could not open"<<fullpath<<std::endl;
3054 throw FileNotGoodException("Cannot open map metadata");
3062 throw SerializationError
3063 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3065 std::getline(is, line);
3066 std::string trimmedline = trim(line);
3067 if(trimmedline == "[end_of_params]")
3069 params.parseConfigLine(line);
3072 m_emerge->loadParamsFromSettings(¶ms);
3074 verbosestream<<"ServerMap::loadMapMeta(): seed="
3075 << m_emerge->params.seed<<std::endl;
3078 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3080 DSTACK(__FUNCTION_NAME);
3081 // Format used for writing
3082 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3084 v2s16 pos = sector->getPos();
3085 std::string dir = getSectorDir(pos);
3088 std::string fullpath = dir + DIR_DELIM + "meta";
3089 std::ostringstream ss(std::ios_base::binary);
3091 sector->serialize(ss, version);
3093 if(!fs::safeWriteToFile(fullpath, ss.str()))
3094 throw FileNotGoodException("Cannot write sector metafile");
3096 sector->differs_from_disk = false;
3099 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3101 DSTACK(__FUNCTION_NAME);
3103 v2s16 p2d = getSectorPos(sectordir);
3105 ServerMapSector *sector = NULL;
3107 std::string fullpath = sectordir + DIR_DELIM + "meta";
3108 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3109 if(is.good() == false)
3111 // If the directory exists anyway, it probably is in some old
3112 // format. Just go ahead and create the sector.
3113 if(fs::PathExists(sectordir))
3115 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3116 <<fullpath<<" doesn't exist but directory does."
3117 <<" Continuing with a sector with no metadata."
3119 sector = new ServerMapSector(this, p2d, m_gamedef);
3120 m_sectors[p2d] = sector;
3124 throw FileNotGoodException("Cannot open sector metafile");
3129 sector = ServerMapSector::deSerialize
3130 (is, this, p2d, m_sectors, m_gamedef);
3132 saveSectorMeta(sector);
3135 sector->differs_from_disk = false;
3140 bool ServerMap::loadSectorMeta(v2s16 p2d)
3142 DSTACK(__FUNCTION_NAME);
3144 MapSector *sector = NULL;
3146 // The directory layout we're going to load from.
3147 // 1 - original sectors/xxxxzzzz/
3148 // 2 - new sectors2/xxx/zzz/
3149 // If we load from anything but the latest structure, we will
3150 // immediately save to the new one, and remove the old.
3152 std::string sectordir1 = getSectorDir(p2d, 1);
3153 std::string sectordir;
3154 if(fs::PathExists(sectordir1))
3156 sectordir = sectordir1;
3161 sectordir = getSectorDir(p2d, 2);
3165 sector = loadSectorMeta(sectordir, loadlayout != 2);
3167 catch(InvalidFilenameException &e)
3171 catch(FileNotGoodException &e)
3175 catch(std::exception &e)
3184 bool ServerMap::loadSectorFull(v2s16 p2d)
3186 DSTACK(__FUNCTION_NAME);
3188 MapSector *sector = NULL;
3190 // The directory layout we're going to load from.
3191 // 1 - original sectors/xxxxzzzz/
3192 // 2 - new sectors2/xxx/zzz/
3193 // If we load from anything but the latest structure, we will
3194 // immediately save to the new one, and remove the old.
3196 std::string sectordir1 = getSectorDir(p2d, 1);
3197 std::string sectordir;
3198 if(fs::PathExists(sectordir1))
3200 sectordir = sectordir1;
3205 sectordir = getSectorDir(p2d, 2);
3209 sector = loadSectorMeta(sectordir, loadlayout != 2);
3211 catch(InvalidFilenameException &e)
3215 catch(FileNotGoodException &e)
3219 catch(std::exception &e)
3227 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3229 std::vector<fs::DirListNode>::iterator i2;
3230 for(i2=list2.begin(); i2!=list2.end(); i2++)
3236 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3238 catch(InvalidFilenameException &e)
3240 // This catches unknown crap in directory
3246 infostream<<"Sector converted to new layout - deleting "<<
3247 sectordir1<<std::endl;
3248 fs::RecursiveDelete(sectordir1);
3255 void ServerMap::beginSave() {
3259 void ServerMap::endSave() {
3263 void ServerMap::saveBlock(MapBlock *block)
3265 dbase->saveBlock(block);
3268 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3270 DSTACK(__FUNCTION_NAME);
3272 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3275 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3276 if(is.good() == false)
3277 throw FileNotGoodException("Cannot open block file");
3279 v3s16 p3d = getBlockPos(sectordir, blockfile);
3280 v2s16 p2d(p3d.X, p3d.Z);
3282 assert(sector->getPos() == p2d);
3284 u8 version = SER_FMT_VER_INVALID;
3285 is.read((char*)&version, 1);
3288 throw SerializationError("ServerMap::loadBlock(): Failed"
3289 " to read MapBlock version");
3291 /*u32 block_size = MapBlock::serializedLength(version);
3292 SharedBuffer<u8> data(block_size);
3293 is.read((char*)*data, block_size);*/
3295 // This will always return a sector because we're the server
3296 //MapSector *sector = emergeSector(p2d);
3298 MapBlock *block = NULL;
3299 bool created_new = false;
3300 block = sector->getBlockNoCreateNoEx(p3d.Y);
3303 block = sector->createBlankBlockNoInsert(p3d.Y);
3308 block->deSerialize(is, version, true);
3310 // If it's a new block, insert it to the map
3312 sector->insertBlock(block);
3315 Save blocks loaded in old format in new format
3318 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3322 // Should be in database now, so delete the old file
3323 fs::RecursiveDelete(fullpath);
3326 // We just loaded it from the disk, so it's up-to-date.
3327 block->resetModified();
3330 catch(SerializationError &e)
3332 infostream<<"WARNING: Invalid block data on disk "
3333 <<"fullpath="<<fullpath
3334 <<" (SerializationError). "
3335 <<"what()="<<e.what()
3337 // Ignoring. A new one will be generated.
3340 // TODO: Backup file; name is in fullpath.
3344 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3346 DSTACK(__FUNCTION_NAME);
3349 std::istringstream is(*blob, std::ios_base::binary);
3351 u8 version = SER_FMT_VER_INVALID;
3352 is.read((char*)&version, 1);
3355 throw SerializationError("ServerMap::loadBlock(): Failed"
3356 " to read MapBlock version");
3358 /*u32 block_size = MapBlock::serializedLength(version);
3359 SharedBuffer<u8> data(block_size);
3360 is.read((char*)*data, block_size);*/
3362 // This will always return a sector because we're the server
3363 //MapSector *sector = emergeSector(p2d);
3365 MapBlock *block = NULL;
3366 bool created_new = false;
3367 block = sector->getBlockNoCreateNoEx(p3d.Y);
3370 block = sector->createBlankBlockNoInsert(p3d.Y);
3375 block->deSerialize(is, version, true);
3377 // If it's a new block, insert it to the map
3379 sector->insertBlock(block);
3382 Save blocks loaded in old format in new format
3385 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3386 // Only save if asked to; no need to update version
3390 // We just loaded it from, so it's up-to-date.
3391 block->resetModified();
3394 catch(SerializationError &e)
3396 errorstream<<"Invalid block data in database"
3397 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3398 <<" (SerializationError): "<<e.what()<<std::endl;
3400 // TODO: Block should be marked as invalid in memory so that it is
3401 // not touched but the game can run
3403 if(g_settings->getBool("ignore_world_load_errors")){
3404 errorstream<<"Ignoring block load error. Duck and cover! "
3405 <<"(ignore_world_load_errors)"<<std::endl;
3407 throw SerializationError("Invalid block data in database");
3413 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3415 DSTACK(__FUNCTION_NAME);
3417 v2s16 p2d(blockpos.X, blockpos.Z);
3421 ret = dbase->loadBlock(blockpos);
3422 if (ret) return (ret);
3423 // Not found in database, try the files
3425 // The directory layout we're going to load from.
3426 // 1 - original sectors/xxxxzzzz/
3427 // 2 - new sectors2/xxx/zzz/
3428 // If we load from anything but the latest structure, we will
3429 // immediately save to the new one, and remove the old.
3431 std::string sectordir1 = getSectorDir(p2d, 1);
3432 std::string sectordir;
3433 if(fs::PathExists(sectordir1))
3435 sectordir = sectordir1;
3440 sectordir = getSectorDir(p2d, 2);
3444 Make sure sector is loaded
3446 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3450 sector = loadSectorMeta(sectordir, loadlayout != 2);
3452 catch(InvalidFilenameException &e)
3456 catch(FileNotGoodException &e)
3460 catch(std::exception &e)
3467 Make sure file exists
3470 std::string blockfilename = getBlockFilename(blockpos);
3471 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3475 Load block and save it to the database
3477 loadBlock(sectordir, blockfilename, sector, true);
3478 return getBlockNoCreateNoEx(blockpos);
3481 void ServerMap::PrintInfo(std::ostream &out)
3490 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3495 MapVoxelManipulator::~MapVoxelManipulator()
3497 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3501 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3503 TimeTaker timer1("emerge", &emerge_time);
3505 // Units of these are MapBlocks
3506 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3507 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3509 VoxelArea block_area_nodes
3510 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3512 addArea(block_area_nodes);
3514 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3515 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3516 for(s32 x=p_min.X; x<=p_max.X; x++)
3521 std::map<v3s16, u8>::iterator n;
3522 n = m_loaded_blocks.find(p);
3523 if(n != m_loaded_blocks.end())
3526 bool block_data_inexistent = false;
3529 TimeTaker timer1("emerge load", &emerge_load_time);
3531 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3532 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3534 a.print(infostream);
3535 infostream<<std::endl;*/
3537 block = m_map->getBlockNoCreate(p);
3538 if(block->isDummy())
3539 block_data_inexistent = true;
3541 block->copyTo(*this);
3543 catch(InvalidPositionException &e)
3545 block_data_inexistent = true;
3548 if(block_data_inexistent)
3550 flags |= VMANIP_BLOCK_DATA_INEXIST;
3552 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3553 // Fill with VOXELFLAG_INEXISTENT
3554 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3555 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3557 s32 i = m_area.index(a.MinEdge.X,y,z);
3558 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3561 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3563 // Mark that block was loaded as blank
3564 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3567 m_loaded_blocks[p] = flags;
3570 //infostream<<"emerge done"<<std::endl;
3574 SUGG: Add an option to only update eg. water and air nodes.
3575 This will make it interfere less with important stuff if
3578 void MapVoxelManipulator::blitBack
3579 (std::map<v3s16, MapBlock*> & modified_blocks)
3581 if(m_area.getExtent() == v3s16(0,0,0))
3584 //TimeTaker timer1("blitBack");
3586 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3587 <<m_loaded_blocks.size()<<std::endl;*/
3590 Initialize block cache
3592 v3s16 blockpos_last;
3593 MapBlock *block = NULL;
3594 bool block_checked_in_modified = false;
3596 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3597 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3598 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3602 u8 f = m_flags[m_area.index(p)];
3603 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3606 MapNode &n = m_data[m_area.index(p)];
3608 v3s16 blockpos = getNodeBlockPos(p);
3613 if(block == NULL || blockpos != blockpos_last){
3614 block = m_map->getBlockNoCreate(blockpos);
3615 blockpos_last = blockpos;
3616 block_checked_in_modified = false;
3619 // Calculate relative position in block
3620 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3622 // Don't continue if nothing has changed here
3623 if(block->getNode(relpos) == n)
3626 //m_map->setNode(m_area.MinEdge + p, n);
3627 block->setNode(relpos, n);
3630 Make sure block is in modified_blocks
3632 if(block_checked_in_modified == false)
3634 modified_blocks[blockpos] = block;
3635 block_checked_in_modified = true;
3638 catch(InvalidPositionException &e)
3644 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3645 MapVoxelManipulator(map),
3646 m_create_area(false)
3650 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3654 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3656 // Just create the area so that it can be pointed to
3657 VoxelManipulator::emerge(a, caller_id);
3660 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
3661 v3s16 blockpos_max, bool load_if_inexistent)
3663 TimeTaker timer1("initialEmerge", &emerge_time);
3665 // Units of these are MapBlocks
3666 v3s16 p_min = blockpos_min;
3667 v3s16 p_max = blockpos_max;
3669 VoxelArea block_area_nodes
3670 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3672 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3675 infostream<<"initialEmerge: area: ";
3676 block_area_nodes.print(infostream);
3677 infostream<<" ("<<size_MB<<"MB)";
3678 infostream<<std::endl;
3681 addArea(block_area_nodes);
3683 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3684 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3685 for(s32 x=p_min.X; x<=p_max.X; x++)
3690 std::map<v3s16, u8>::iterator n;
3691 n = m_loaded_blocks.find(p);
3692 if(n != m_loaded_blocks.end())
3695 bool block_data_inexistent = false;
3698 TimeTaker timer1("emerge load", &emerge_load_time);
3700 block = m_map->getBlockNoCreate(p);
3701 if(block->isDummy())
3702 block_data_inexistent = true;
3704 block->copyTo(*this);
3706 catch(InvalidPositionException &e)
3708 block_data_inexistent = true;
3711 if(block_data_inexistent)
3714 if (load_if_inexistent) {
3715 ServerMap *svrmap = (ServerMap *)m_map;
3716 block = svrmap->emergeBlock(p, false);
3718 block = svrmap->createBlock(p);
3720 block->copyTo(*this);
3722 flags |= VMANIP_BLOCK_DATA_INEXIST;
3725 Mark area inexistent
3727 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3728 // Fill with VOXELFLAG_INEXISTENT
3729 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3730 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3732 s32 i = m_area.index(a.MinEdge.X,y,z);
3733 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3737 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3739 // Mark that block was loaded as blank
3740 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3743 m_loaded_blocks[p] = flags;
3747 void ManualMapVoxelManipulator::blitBackAll(
3748 std::map<v3s16, MapBlock*> * modified_blocks)
3750 if(m_area.getExtent() == v3s16(0,0,0))
3754 Copy data of all blocks
3756 for(std::map<v3s16, u8>::iterator
3757 i = m_loaded_blocks.begin();
3758 i != m_loaded_blocks.end(); ++i)
3761 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3762 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3763 if(existed == false)
3768 block->copyFrom(*this);
3771 (*modified_blocks)[p] = block;