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):
80 m_transforming_liquid_loop_count_multiplier(1.0f),
81 m_unprocessed_count(0),
82 m_inc_trending_up_start_time(0),
83 m_queue_size_timer_started(false)
92 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
93 i != m_sectors.end(); ++i)
99 void Map::addEventReceiver(MapEventReceiver *event_receiver)
101 m_event_receivers.insert(event_receiver);
104 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
106 m_event_receivers.erase(event_receiver);
109 void Map::dispatchEvent(MapEditEvent *event)
111 for(std::set<MapEventReceiver*>::iterator
112 i = m_event_receivers.begin();
113 i != m_event_receivers.end(); ++i)
115 (*i)->onMapEditEvent(event);
119 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
121 if(m_sector_cache != NULL && p == m_sector_cache_p){
122 MapSector * sector = m_sector_cache;
126 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
128 if(n == m_sectors.end())
131 MapSector *sector = n->second;
133 // Cache the last result
134 m_sector_cache_p = p;
135 m_sector_cache = sector;
140 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
142 return getSectorNoGenerateNoExNoLock(p);
145 MapSector * Map::getSectorNoGenerate(v2s16 p)
147 MapSector *sector = getSectorNoGenerateNoEx(p);
149 throw InvalidPositionException();
154 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
156 v2s16 p2d(p3d.X, p3d.Z);
157 MapSector * sector = getSectorNoGenerateNoEx(p2d);
160 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
164 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
166 MapBlock *block = getBlockNoCreateNoEx(p3d);
168 throw InvalidPositionException();
172 bool Map::isNodeUnderground(v3s16 p)
174 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock * block = getBlockNoCreate(blockpos);
177 return block->getIsUnderground();
179 catch(InvalidPositionException &e)
185 bool Map::isValidPosition(v3s16 p)
187 v3s16 blockpos = getNodeBlockPos(p);
188 MapBlock *block = getBlockNoCreate(blockpos);
189 return (block != NULL);
192 // Returns a CONTENT_IGNORE node if not found
193 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
195 v3s16 blockpos = getNodeBlockPos(p);
196 MapBlock *block = getBlockNoCreateNoEx(blockpos);
198 if (is_valid_position != NULL)
199 *is_valid_position = false;
200 return MapNode(CONTENT_IGNORE);
203 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
206 if (is_valid_position != NULL)
207 *is_valid_position = is_valid_p;
213 // throws InvalidPositionException if not found
214 // TODO: Now this is deprecated, getNodeNoEx should be renamed
215 MapNode Map::getNode(v3s16 p)
217 v3s16 blockpos = getNodeBlockPos(p);
218 MapBlock *block = getBlockNoCreateNoEx(blockpos);
220 throw InvalidPositionException();
221 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
222 bool is_valid_position;
223 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
224 if (!is_valid_position)
225 throw InvalidPositionException();
230 // throws InvalidPositionException if not found
231 void Map::setNode(v3s16 p, MapNode & n)
233 v3s16 blockpos = getNodeBlockPos(p);
234 MapBlock *block = getBlockNoCreate(blockpos);
235 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
236 // Never allow placing CONTENT_IGNORE, it fucks up stuff
237 if(n.getContent() == CONTENT_IGNORE){
239 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
240 <<" while trying to replace \""
241 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
242 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
243 debug_stacks_print_to(infostream);
246 block->setNodeNoCheck(relpos, n);
251 Goes recursively through the neighbours of the node.
253 Alters only transparent nodes.
255 If the lighting of the neighbour is lower than the lighting of
256 the node was (before changing it to 0 at the step before), the
257 lighting of the neighbour is set to 0 and then the same stuff
258 repeats for the neighbour.
260 The ending nodes of the routine are stored in light_sources.
261 This is useful when a light is removed. In such case, this
262 routine can be called for the light node and then again for
263 light_sources to re-light the area without the removed light.
265 values of from_nodes are lighting values.
267 void Map::unspreadLight(enum LightBank bank,
268 std::map<v3s16, u8> & from_nodes,
269 std::set<v3s16> & light_sources,
270 std::map<v3s16, MapBlock*> & modified_blocks)
272 INodeDefManager *nodemgr = m_gamedef->ndef();
275 v3s16(0,0,1), // back
277 v3s16(1,0,0), // right
278 v3s16(0,0,-1), // front
279 v3s16(0,-1,0), // bottom
280 v3s16(-1,0,0), // left
283 if(from_nodes.empty())
286 u32 blockchangecount = 0;
288 std::map<v3s16, u8> unlighted_nodes;
291 Initialize block cache
294 MapBlock *block = NULL;
295 // Cache this a bit, too
296 bool block_checked_in_modified = false;
298 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
299 j != from_nodes.end(); ++j)
301 v3s16 pos = j->first;
302 v3s16 blockpos = getNodeBlockPos(pos);
304 // Only fetch a new block if the block position has changed
306 if(block == NULL || blockpos != blockpos_last){
307 block = getBlockNoCreate(blockpos);
308 blockpos_last = blockpos;
310 block_checked_in_modified = false;
314 catch(InvalidPositionException &e)
322 // Calculate relative position in block
323 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
325 // Get node straight from the block
326 //MapNode n = block->getNode(relpos);
328 u8 oldlight = j->second;
330 // Loop through 6 neighbors
331 for(u16 i=0; i<6; i++)
333 // Get the position of the neighbor node
334 v3s16 n2pos = pos + dirs[i];
336 // Get the block where the node is located
337 v3s16 blockpos = getNodeBlockPos(n2pos);
339 // Only fetch a new block if the block position has changed
341 if(block == NULL || blockpos != blockpos_last){
342 block = getBlockNoCreate(blockpos);
343 blockpos_last = blockpos;
345 block_checked_in_modified = false;
349 catch(InvalidPositionException &e) {
353 // Calculate relative position in block
354 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
355 // Get node straight from the block
356 bool is_valid_position;
357 MapNode n2 = block->getNode(relpos, &is_valid_position);
358 if (!is_valid_position)
361 bool changed = false;
363 //TODO: Optimize output by optimizing light_sources?
366 If the neighbor is dimmer than what was specified
367 as oldlight (the light of the previous node)
369 if(n2.getLight(bank, nodemgr) < oldlight)
372 And the neighbor is transparent and it has some light
374 if(nodemgr->get(n2).light_propagates
375 && n2.getLight(bank, nodemgr) != 0)
378 Set light to 0 and add to queue
381 u8 current_light = n2.getLight(bank, nodemgr);
382 n2.setLight(bank, 0, nodemgr);
383 block->setNode(relpos, n2);
385 unlighted_nodes[n2pos] = current_light;
389 Remove from light_sources if it is there
390 NOTE: This doesn't happen nearly at all
392 /*if(light_sources.find(n2pos))
394 infostream<<"Removed from light_sources"<<std::endl;
395 light_sources.remove(n2pos);
400 if(light_sources.find(n2pos) != NULL)
401 light_sources.remove(n2pos);*/
404 light_sources.insert(n2pos);
407 // Add to modified_blocks
408 if(changed == true && block_checked_in_modified == false)
410 // If the block is not found in modified_blocks, add.
411 if(modified_blocks.find(blockpos) == modified_blocks.end())
413 modified_blocks[blockpos] = block;
415 block_checked_in_modified = true;
420 /*infostream<<"unspreadLight(): Changed block "
421 <<blockchangecount<<" times"
422 <<" for "<<from_nodes.size()<<" nodes"
425 if(!unlighted_nodes.empty())
426 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
430 A single-node wrapper of the above
432 void Map::unLightNeighbors(enum LightBank bank,
433 v3s16 pos, u8 lightwas,
434 std::set<v3s16> & light_sources,
435 std::map<v3s16, MapBlock*> & modified_blocks)
437 std::map<v3s16, u8> from_nodes;
438 from_nodes[pos] = lightwas;
440 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
444 Lights neighbors of from_nodes, collects all them and then
447 void Map::spreadLight(enum LightBank bank,
448 std::set<v3s16> & from_nodes,
449 std::map<v3s16, MapBlock*> & modified_blocks)
451 INodeDefManager *nodemgr = m_gamedef->ndef();
453 const v3s16 dirs[6] = {
454 v3s16(0,0,1), // back
456 v3s16(1,0,0), // right
457 v3s16(0,0,-1), // front
458 v3s16(0,-1,0), // bottom
459 v3s16(-1,0,0), // left
462 if(from_nodes.empty())
465 u32 blockchangecount = 0;
467 std::set<v3s16> lighted_nodes;
470 Initialize block cache
473 MapBlock *block = NULL;
474 // Cache this a bit, too
475 bool block_checked_in_modified = false;
477 for(std::set<v3s16>::iterator j = from_nodes.begin();
478 j != from_nodes.end(); ++j)
481 v3s16 blockpos = getNodeBlockPos(pos);
483 // Only fetch a new block if the block position has changed
485 if(block == NULL || blockpos != blockpos_last){
486 block = getBlockNoCreate(blockpos);
487 blockpos_last = blockpos;
489 block_checked_in_modified = false;
493 catch(InvalidPositionException &e) {
500 // Calculate relative position in block
501 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
503 // Get node straight from the block
504 bool is_valid_position;
505 MapNode n = block->getNode(relpos, &is_valid_position);
507 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
508 u8 newlight = diminish_light(oldlight);
510 // Loop through 6 neighbors
511 for(u16 i=0; i<6; i++){
512 // Get the position of the neighbor node
513 v3s16 n2pos = pos + dirs[i];
515 // Get the block where the node is located
516 v3s16 blockpos = getNodeBlockPos(n2pos);
518 // Only fetch a new block if the block position has changed
520 if(block == NULL || blockpos != blockpos_last){
521 block = getBlockNoCreate(blockpos);
522 blockpos_last = blockpos;
524 block_checked_in_modified = false;
528 catch(InvalidPositionException &e) {
532 // Calculate relative position in block
533 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
534 // Get node straight from the block
535 MapNode n2 = block->getNode(relpos, &is_valid_position);
536 if (!is_valid_position)
539 bool changed = false;
541 If the neighbor is brighter than the current node,
542 add to list (it will light up this node on its turn)
544 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
546 lighted_nodes.insert(n2pos);
550 If the neighbor is dimmer than how much light this node
551 would spread on it, add to list
553 if(n2.getLight(bank, nodemgr) < newlight)
555 if(nodemgr->get(n2).light_propagates)
557 n2.setLight(bank, newlight, nodemgr);
558 block->setNode(relpos, n2);
559 lighted_nodes.insert(n2pos);
564 // Add to modified_blocks
565 if(changed == true && block_checked_in_modified == false)
567 // If the block is not found in modified_blocks, add.
568 if(modified_blocks.find(blockpos) == modified_blocks.end())
570 modified_blocks[blockpos] = block;
572 block_checked_in_modified = true;
577 /*infostream<<"spreadLight(): Changed block "
578 <<blockchangecount<<" times"
579 <<" for "<<from_nodes.size()<<" nodes"
582 if(!lighted_nodes.empty())
583 spreadLight(bank, lighted_nodes, modified_blocks);
587 A single-node source variation of the above.
589 void Map::lightNeighbors(enum LightBank bank,
591 std::map<v3s16, MapBlock*> & modified_blocks)
593 std::set<v3s16> from_nodes;
594 from_nodes.insert(pos);
595 spreadLight(bank, from_nodes, modified_blocks);
598 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
600 INodeDefManager *nodemgr = m_gamedef->ndef();
603 v3s16(0,0,1), // back
605 v3s16(1,0,0), // right
606 v3s16(0,0,-1), // front
607 v3s16(0,-1,0), // bottom
608 v3s16(-1,0,0), // left
611 u8 brightest_light = 0;
612 v3s16 brightest_pos(0,0,0);
613 bool found_something = false;
615 // Loop through 6 neighbors
616 for(u16 i=0; i<6; i++){
617 // Get the position of the neighbor node
618 v3s16 n2pos = p + dirs[i];
620 bool is_valid_position;
621 n2 = getNodeNoEx(n2pos, &is_valid_position);
622 if (!is_valid_position)
625 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
626 brightest_light = n2.getLight(bank, nodemgr);
627 brightest_pos = n2pos;
628 found_something = true;
632 if(found_something == false)
633 throw InvalidPositionException();
635 return brightest_pos;
639 Propagates sunlight down from a node.
640 Starting point gets sunlight.
642 Returns the lowest y value of where the sunlight went.
644 Mud is turned into grass in where the sunlight stops.
646 s16 Map::propagateSunlight(v3s16 start,
647 std::map<v3s16, MapBlock*> & modified_blocks)
649 INodeDefManager *nodemgr = m_gamedef->ndef();
654 v3s16 pos(start.X, y, start.Z);
656 v3s16 blockpos = getNodeBlockPos(pos);
659 block = getBlockNoCreate(blockpos);
661 catch(InvalidPositionException &e)
666 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
667 bool is_valid_position;
668 MapNode n = block->getNode(relpos, &is_valid_position);
669 if (!is_valid_position)
672 if(nodemgr->get(n).sunlight_propagates)
674 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
675 block->setNode(relpos, n);
677 modified_blocks[blockpos] = block;
681 // Sunlight goes no further
688 void Map::updateLighting(enum LightBank bank,
689 std::map<v3s16, MapBlock*> & a_blocks,
690 std::map<v3s16, MapBlock*> & modified_blocks)
692 INodeDefManager *nodemgr = m_gamedef->ndef();
694 /*m_dout<<DTIME<<"Map::updateLighting(): "
695 <<a_blocks.size()<<" blocks."<<std::endl;*/
697 //TimeTaker timer("updateLighting");
701 //u32 count_was = modified_blocks.size();
703 std::map<v3s16, MapBlock*> blocks_to_update;
705 std::set<v3s16> light_sources;
707 std::map<v3s16, u8> unlight_from;
709 int num_bottom_invalid = 0;
712 //TimeTaker t("first stuff");
714 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
715 i != a_blocks.end(); ++i)
717 MapBlock *block = i->second;
721 // Don't bother with dummy blocks.
725 v3s16 pos = block->getPos();
726 v3s16 posnodes = block->getPosRelative();
727 modified_blocks[pos] = block;
728 blocks_to_update[pos] = block;
731 Clear all light from block
733 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
734 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
735 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
738 bool is_valid_position;
739 MapNode n = block->getNode(p, &is_valid_position);
740 if (!is_valid_position) {
741 /* This would happen when dealing with a
744 infostream<<"updateLighting(): InvalidPositionException"
748 u8 oldlight = n.getLight(bank, nodemgr);
749 n.setLight(bank, 0, nodemgr);
750 block->setNode(p, n);
752 // If node sources light, add to list
753 u8 source = nodemgr->get(n).light_source;
755 light_sources.insert(p + posnodes);
757 // Collect borders for unlighting
758 if((x==0 || x == MAP_BLOCKSIZE-1
759 || y==0 || y == MAP_BLOCKSIZE-1
760 || z==0 || z == MAP_BLOCKSIZE-1)
763 v3s16 p_map = p + posnodes;
764 unlight_from[p_map] = oldlight;
770 if(bank == LIGHTBANK_DAY)
772 bool bottom_valid = block->propagateSunlight(light_sources);
775 num_bottom_invalid++;
777 // If bottom is valid, we're done.
781 else if(bank == LIGHTBANK_NIGHT)
783 // For night lighting, sunlight is not propagated
788 // Invalid lighting bank
792 /*infostream<<"Bottom for sunlight-propagated block ("
793 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
796 // Bottom sunlight is not valid; get the block and loop to it
800 block = getBlockNoCreate(pos);
802 catch(InvalidPositionException &e)
813 Enable this to disable proper lighting for speeding up map
814 generation for testing or whatever
817 //if(g_settings->get(""))
819 core::map<v3s16, MapBlock*>::Iterator i;
820 i = blocks_to_update.getIterator();
821 for(; i.atEnd() == false; i++)
823 MapBlock *block = i.getNode()->getValue();
824 v3s16 p = block->getPos();
825 block->setLightingExpired(false);
833 //TimeTaker timer("unspreadLight");
834 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
839 u32 diff = modified_blocks.size() - count_was;
840 count_was = modified_blocks.size();
841 infostream<<"unspreadLight modified "<<diff<<std::endl;
845 //TimeTaker timer("spreadLight");
846 spreadLight(bank, light_sources, modified_blocks);
851 u32 diff = modified_blocks.size() - count_was;
852 count_was = modified_blocks.size();
853 infostream<<"spreadLight modified "<<diff<<std::endl;
859 //MapVoxelManipulator vmanip(this);
861 // Make a manual voxel manipulator and load all the blocks
862 // that touch the requested blocks
863 ManualMapVoxelManipulator vmanip(this);
866 //TimeTaker timer("initialEmerge");
868 core::map<v3s16, MapBlock*>::Iterator i;
869 i = blocks_to_update.getIterator();
870 for(; i.atEnd() == false; i++)
872 MapBlock *block = i.getNode()->getValue();
873 v3s16 p = block->getPos();
875 // Add all surrounding blocks
876 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
879 Add all surrounding blocks that have up-to-date lighting
880 NOTE: This doesn't quite do the job (not everything
881 appropriate is lighted)
883 /*for(s16 z=-1; z<=1; z++)
884 for(s16 y=-1; y<=1; y++)
885 for(s16 x=-1; x<=1; x++)
887 v3s16 p2 = p + v3s16(x,y,z);
888 MapBlock *block = getBlockNoCreateNoEx(p2);
893 if(block->getLightingExpired())
895 vmanip.initialEmerge(p2, p2);
898 // Lighting of block will be updated completely
899 block->setLightingExpired(false);
904 //TimeTaker timer("unSpreadLight");
905 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
908 //TimeTaker timer("spreadLight");
909 vmanip.spreadLight(bank, light_sources, nodemgr);
912 //TimeTaker timer("blitBack");
913 vmanip.blitBack(modified_blocks);
915 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
920 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
923 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
924 std::map<v3s16, MapBlock*> & modified_blocks)
926 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
927 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
930 Update information about whether day and night light differ
932 for(std::map<v3s16, MapBlock*>::iterator
933 i = modified_blocks.begin();
934 i != modified_blocks.end(); ++i)
936 MapBlock *block = i->second;
937 block->expireDayNightDiff();
943 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
944 std::map<v3s16, MapBlock*> &modified_blocks,
945 bool remove_metadata)
947 INodeDefManager *ndef = m_gamedef->ndef();
950 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
951 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
954 From this node to nodes underneath:
955 If lighting is sunlight (1.0), unlight neighbours and
960 v3s16 toppos = p + v3s16(0,1,0);
961 //v3s16 bottompos = p + v3s16(0,-1,0);
963 bool node_under_sunlight = true;
964 std::set<v3s16> light_sources;
967 Collect old node for rollback
969 RollbackNode rollback_oldnode(this, p, m_gamedef);
972 If there is a node at top and it doesn't have sunlight,
973 there has not been any sunlight going down.
975 Otherwise there probably is.
978 bool is_valid_position;
979 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
981 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
982 node_under_sunlight = false;
985 Remove all light that has come out of this node
988 enum LightBank banks[] =
993 for(s32 i=0; i<2; i++)
995 enum LightBank bank = banks[i];
997 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
999 // Add the block of the added node to modified_blocks
1000 v3s16 blockpos = getNodeBlockPos(p);
1001 MapBlock * block = getBlockNoCreate(blockpos);
1002 assert(block != NULL);
1003 modified_blocks[blockpos] = block;
1005 assert(isValidPosition(p));
1007 // Unlight neighbours of node.
1008 // This means setting light of all consequent dimmer nodes
1010 // This also collects the nodes at the border which will spread
1011 // light again into this.
1012 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1014 n.setLight(bank, 0, ndef);
1018 If node lets sunlight through and is under sunlight, it has
1021 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1023 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1027 Remove node metadata
1029 if (remove_metadata) {
1030 removeNodeMetadata(p);
1034 Set the node on the map
1040 If node is under sunlight and doesn't let sunlight through,
1041 take all sunlighted nodes under it and clear light from them
1042 and from where the light has been spread.
1043 TODO: This could be optimized by mass-unlighting instead
1046 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1050 //m_dout<<DTIME<<"y="<<y<<std::endl;
1051 v3s16 n2pos(p.X, y, p.Z);
1055 n2 = getNodeNoEx(n2pos, &is_valid_position);
1056 if (!is_valid_position)
1059 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1061 unLightNeighbors(LIGHTBANK_DAY,
1062 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1063 light_sources, modified_blocks);
1064 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1072 for(s32 i=0; i<2; i++)
1074 enum LightBank bank = banks[i];
1077 Spread light from all nodes that might be capable of doing so
1079 spreadLight(bank, light_sources, modified_blocks);
1083 Update information about whether day and night light differ
1085 for(std::map<v3s16, MapBlock*>::iterator
1086 i = modified_blocks.begin();
1087 i != modified_blocks.end(); ++i)
1089 i->second->expireDayNightDiff();
1095 if(m_gamedef->rollback())
1097 RollbackNode rollback_newnode(this, p, m_gamedef);
1098 RollbackAction action;
1099 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1100 m_gamedef->rollback()->reportAction(action);
1104 Add neighboring liquid nodes and the node itself if it is
1105 liquid (=water node was added) to transform queue.
1108 v3s16(0,0,0), // self
1109 v3s16(0,0,1), // back
1110 v3s16(0,1,0), // top
1111 v3s16(1,0,0), // right
1112 v3s16(0,0,-1), // front
1113 v3s16(0,-1,0), // bottom
1114 v3s16(-1,0,0), // left
1116 for(u16 i=0; i<7; i++)
1118 v3s16 p2 = p + dirs[i];
1120 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1121 if(is_valid_position
1122 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1124 m_transforming_liquid.push_back(p2);
1131 void Map::removeNodeAndUpdate(v3s16 p,
1132 std::map<v3s16, MapBlock*> &modified_blocks)
1134 INodeDefManager *ndef = m_gamedef->ndef();
1136 /*PrintInfo(m_dout);
1137 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1138 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1140 bool node_under_sunlight = true;
1142 v3s16 toppos = p + v3s16(0,1,0);
1144 // Node will be replaced with this
1145 content_t replace_material = CONTENT_AIR;
1148 Collect old node for rollback
1150 RollbackNode rollback_oldnode(this, p, m_gamedef);
1153 If there is a node at top and it doesn't have sunlight,
1154 there will be no sunlight going down.
1156 bool is_valid_position;
1157 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1159 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1160 node_under_sunlight = false;
1162 std::set<v3s16> light_sources;
1164 enum LightBank banks[] =
1169 for(s32 i=0; i<2; i++)
1171 enum LightBank bank = banks[i];
1174 Unlight neighbors (in case the node is a light source)
1176 unLightNeighbors(bank, p,
1177 getNodeNoEx(p).getLight(bank, ndef),
1178 light_sources, modified_blocks);
1182 Remove node metadata
1185 removeNodeMetadata(p);
1189 This also clears the lighting.
1192 MapNode n(replace_material);
1195 for(s32 i=0; i<2; i++)
1197 enum LightBank bank = banks[i];
1200 Recalculate lighting
1202 spreadLight(bank, light_sources, modified_blocks);
1205 // Add the block of the removed node to modified_blocks
1206 v3s16 blockpos = getNodeBlockPos(p);
1207 MapBlock * block = getBlockNoCreate(blockpos);
1208 assert(block != NULL);
1209 modified_blocks[blockpos] = block;
1212 If the removed node was under sunlight, propagate the
1213 sunlight down from it and then light all neighbors
1214 of the propagated blocks.
1216 if(node_under_sunlight)
1218 s16 ybottom = propagateSunlight(p, modified_blocks);
1219 /*m_dout<<DTIME<<"Node was under sunlight. "
1220 "Propagating sunlight";
1221 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1223 for(; y >= ybottom; y--)
1225 v3s16 p2(p.X, y, p.Z);
1226 /*m_dout<<DTIME<<"lighting neighbors of node ("
1227 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1229 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1234 // Set the lighting of this node to 0
1235 // TODO: Is this needed? Lighting is cleared up there already.
1236 MapNode n = getNodeNoEx(p, &is_valid_position);
1237 if (is_valid_position) {
1238 n.setLight(LIGHTBANK_DAY, 0, ndef);
1245 for(s32 i=0; i<2; i++)
1247 enum LightBank bank = banks[i];
1249 // Get the brightest neighbour node and propagate light from it
1250 v3s16 n2p = getBrightestNeighbour(bank, p);
1252 //MapNode n2 = getNode(n2p);
1253 lightNeighbors(bank, n2p, modified_blocks);
1255 catch(InvalidPositionException &e)
1261 Update information about whether day and night light differ
1263 for(std::map<v3s16, MapBlock*>::iterator
1264 i = modified_blocks.begin();
1265 i != modified_blocks.end(); ++i)
1267 i->second->expireDayNightDiff();
1273 if(m_gamedef->rollback())
1275 RollbackNode rollback_newnode(this, p, m_gamedef);
1276 RollbackAction action;
1277 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1278 m_gamedef->rollback()->reportAction(action);
1282 Add neighboring liquid nodes and this node to transform queue.
1283 (it's vital for the node itself to get updated last.)
1286 v3s16(0,0,1), // back
1287 v3s16(0,1,0), // top
1288 v3s16(1,0,0), // right
1289 v3s16(0,0,-1), // front
1290 v3s16(0,-1,0), // bottom
1291 v3s16(-1,0,0), // left
1292 v3s16(0,0,0), // self
1294 for(u16 i=0; i<7; i++)
1296 v3s16 p2 = p + dirs[i];
1298 bool is_position_valid;
1299 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1300 if (is_position_valid
1301 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1303 m_transforming_liquid.push_back(p2);
1308 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1311 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1315 bool succeeded = true;
1317 std::map<v3s16, MapBlock*> modified_blocks;
1318 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1320 // Copy modified_blocks to event
1321 for(std::map<v3s16, MapBlock*>::iterator
1322 i = modified_blocks.begin();
1323 i != modified_blocks.end(); ++i)
1325 event.modified_blocks.insert(i->first);
1328 catch(InvalidPositionException &e){
1332 dispatchEvent(&event);
1337 bool Map::removeNodeWithEvent(v3s16 p)
1340 event.type = MEET_REMOVENODE;
1343 bool succeeded = true;
1345 std::map<v3s16, MapBlock*> modified_blocks;
1346 removeNodeAndUpdate(p, modified_blocks);
1348 // Copy modified_blocks to event
1349 for(std::map<v3s16, MapBlock*>::iterator
1350 i = modified_blocks.begin();
1351 i != modified_blocks.end(); ++i)
1353 event.modified_blocks.insert(i->first);
1356 catch(InvalidPositionException &e){
1360 dispatchEvent(&event);
1365 bool Map::getDayNightDiff(v3s16 blockpos)
1368 v3s16 p = blockpos + v3s16(0,0,0);
1369 MapBlock *b = getBlockNoCreate(p);
1370 if(b->getDayNightDiff())
1373 catch(InvalidPositionException &e){}
1376 v3s16 p = blockpos + v3s16(-1,0,0);
1377 MapBlock *b = getBlockNoCreate(p);
1378 if(b->getDayNightDiff())
1381 catch(InvalidPositionException &e){}
1383 v3s16 p = blockpos + v3s16(0,-1,0);
1384 MapBlock *b = getBlockNoCreate(p);
1385 if(b->getDayNightDiff())
1388 catch(InvalidPositionException &e){}
1390 v3s16 p = blockpos + v3s16(0,0,-1);
1391 MapBlock *b = getBlockNoCreate(p);
1392 if(b->getDayNightDiff())
1395 catch(InvalidPositionException &e){}
1398 v3s16 p = blockpos + v3s16(1,0,0);
1399 MapBlock *b = getBlockNoCreate(p);
1400 if(b->getDayNightDiff())
1403 catch(InvalidPositionException &e){}
1405 v3s16 p = blockpos + v3s16(0,1,0);
1406 MapBlock *b = getBlockNoCreate(p);
1407 if(b->getDayNightDiff())
1410 catch(InvalidPositionException &e){}
1412 v3s16 p = blockpos + v3s16(0,0,1);
1413 MapBlock *b = getBlockNoCreate(p);
1414 if(b->getDayNightDiff())
1417 catch(InvalidPositionException &e){}
1423 Updates usage timers
1425 void Map::timerUpdate(float dtime, float unload_timeout,
1426 std::list<v3s16> *unloaded_blocks)
1428 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1430 // Profile modified reasons
1431 Profiler modprofiler;
1433 std::list<v2s16> sector_deletion_queue;
1434 u32 deleted_blocks_count = 0;
1435 u32 saved_blocks_count = 0;
1436 u32 block_count_all = 0;
1439 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1440 si != m_sectors.end(); ++si)
1442 MapSector *sector = si->second;
1444 bool all_blocks_deleted = true;
1446 std::list<MapBlock*> blocks;
1447 sector->getBlocks(blocks);
1449 for(std::list<MapBlock*>::iterator i = blocks.begin();
1450 i != blocks.end(); ++i)
1452 MapBlock *block = (*i);
1454 block->incrementUsageTimer(dtime);
1456 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1458 v3s16 p = block->getPos();
1461 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading)
1463 modprofiler.add(block->getModifiedReason(), 1);
1464 if (!saveBlock(block))
1466 saved_blocks_count++;
1469 // Delete from memory
1470 sector->deleteBlock(block);
1473 unloaded_blocks->push_back(p);
1475 deleted_blocks_count++;
1479 all_blocks_deleted = false;
1484 if(all_blocks_deleted)
1486 sector_deletion_queue.push_back(si->first);
1491 // Finally delete the empty sectors
1492 deleteSectors(sector_deletion_queue);
1494 if(deleted_blocks_count != 0)
1496 PrintInfo(infostream); // ServerMap/ClientMap:
1497 infostream<<"Unloaded "<<deleted_blocks_count
1498 <<" blocks from memory";
1499 if(save_before_unloading)
1500 infostream<<", of which "<<saved_blocks_count<<" were written";
1501 infostream<<", "<<block_count_all<<" blocks in memory";
1502 infostream<<"."<<std::endl;
1503 if(saved_blocks_count != 0){
1504 PrintInfo(infostream); // ServerMap/ClientMap:
1505 infostream<<"Blocks modified by: "<<std::endl;
1506 modprofiler.print(infostream);
1511 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1513 timerUpdate(0.0, -1.0, unloaded_blocks);
1516 void Map::deleteSectors(std::list<v2s16> &list)
1518 for(std::list<v2s16>::iterator j = list.begin();
1519 j != list.end(); ++j)
1521 MapSector *sector = m_sectors[*j];
1522 // If sector is in sector cache, remove it from there
1523 if(m_sector_cache == sector)
1524 m_sector_cache = NULL;
1525 // Remove from map and delete
1526 m_sectors.erase(*j);
1532 void Map::unloadUnusedData(float timeout,
1533 core::list<v3s16> *deleted_blocks)
1535 core::list<v2s16> sector_deletion_queue;
1536 u32 deleted_blocks_count = 0;
1537 u32 saved_blocks_count = 0;
1539 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1540 for(; si.atEnd() == false; si++)
1542 MapSector *sector = si.getNode()->getValue();
1544 bool all_blocks_deleted = true;
1546 core::list<MapBlock*> blocks;
1547 sector->getBlocks(blocks);
1548 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1549 i != blocks.end(); i++)
1551 MapBlock *block = (*i);
1553 if(block->getUsageTimer() > timeout)
1556 if(block->getModified() != MOD_STATE_CLEAN)
1559 saved_blocks_count++;
1561 // Delete from memory
1562 sector->deleteBlock(block);
1563 deleted_blocks_count++;
1567 all_blocks_deleted = false;
1571 if(all_blocks_deleted)
1573 sector_deletion_queue.push_back(si.getNode()->getKey());
1577 deleteSectors(sector_deletion_queue);
1579 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1580 <<", of which "<<saved_blocks_count<<" were wr."
1583 //return sector_deletion_queue.getSize();
1584 //return deleted_blocks_count;
1588 void Map::PrintInfo(std::ostream &out)
1593 #define WATER_DROP_BOOST 4
1597 NEIGHBOR_SAME_LEVEL,
1600 struct NodeNeighbor {
1604 bool l; //can liquid
1610 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1617 void Map::transforming_liquid_add(v3s16 p) {
1618 m_transforming_liquid.push_back(p);
1621 s32 Map::transforming_liquid_size() {
1622 return m_transforming_liquid.size();
1625 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1628 INodeDefManager *nodemgr = m_gamedef->ndef();
1630 DSTACK(__FUNCTION_NAME);
1631 //TimeTaker timer("transformLiquids()");
1634 u32 initial_size = m_transforming_liquid.size();
1636 /*if(initial_size != 0)
1637 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1639 // list of nodes that due to viscosity have not reached their max level height
1640 UniqueQueue<v3s16> must_reflow;
1642 // List of MapBlocks that will require a lighting update (due to lava)
1643 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1645 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1646 u32 loop_max = liquid_loop_max;
1650 /* If liquid_loop_max is not keeping up with the queue size increase
1651 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1653 if (m_transforming_liquid.size() > loop_max * 2) {
1655 float server_step = g_settings->getFloat("dedicated_server_step");
1656 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1657 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1659 m_transforming_liquid_loop_count_multiplier = 1.0;
1662 loop_max *= m_transforming_liquid_loop_count_multiplier;
1665 while(m_transforming_liquid.size() != 0)
1667 // This should be done here so that it is done when continue is used
1668 if(loopcount >= initial_size || loopcount >= loop_max)
1673 Get a queued transforming liquid node
1675 v3s16 p0 = m_transforming_liquid.front();
1676 m_transforming_liquid.pop_front();
1678 MapNode n0 = getNodeNoEx(p0);
1681 Collect information about current node
1683 s8 liquid_level = -1;
1684 content_t liquid_kind = CONTENT_IGNORE;
1685 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1686 switch (liquid_type) {
1688 liquid_level = LIQUID_LEVEL_SOURCE;
1689 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1691 case LIQUID_FLOWING:
1692 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1693 liquid_kind = n0.getContent();
1696 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1697 // continue with the next node.
1698 if (n0.getContent() != CONTENT_AIR)
1700 liquid_kind = CONTENT_AIR;
1705 Collect information about the environment
1707 const v3s16 *dirs = g_6dirs;
1708 NodeNeighbor sources[6]; // surrounding sources
1709 int num_sources = 0;
1710 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1712 NodeNeighbor airs[6]; // surrounding air
1714 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1715 int num_neutrals = 0;
1716 bool flowing_down = false;
1717 for (u16 i = 0; i < 6; i++) {
1718 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1721 nt = NEIGHBOR_UPPER;
1724 nt = NEIGHBOR_LOWER;
1727 v3s16 npos = p0 + dirs[i];
1728 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1729 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1731 if (nb.n.getContent() == CONTENT_AIR) {
1732 airs[num_airs++] = nb;
1733 // if the current node is a water source the neighbor
1734 // should be enqueded for transformation regardless of whether the
1735 // current node changes or not.
1736 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1737 m_transforming_liquid.push_back(npos);
1738 // if the current node happens to be a flowing node, it will start to flow down here.
1739 if (nb.t == NEIGHBOR_LOWER) {
1740 flowing_down = true;
1743 neutrals[num_neutrals++] = nb;
1747 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1748 if (liquid_kind == CONTENT_AIR)
1749 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1750 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1751 neutrals[num_neutrals++] = nb;
1753 // Do not count bottom source, it will screw things up
1755 sources[num_sources++] = nb;
1758 case LIQUID_FLOWING:
1759 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1760 if (liquid_kind == CONTENT_AIR)
1761 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1762 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1763 neutrals[num_neutrals++] = nb;
1765 flows[num_flows++] = nb;
1766 if (nb.t == NEIGHBOR_LOWER)
1767 flowing_down = true;
1774 decide on the type (and possibly level) of the current node
1776 content_t new_node_content;
1777 s8 new_node_level = -1;
1778 s8 max_node_level = -1;
1780 u8 range = nodemgr->get(liquid_kind).liquid_range;
1781 if (range > LIQUID_LEVEL_MAX+1)
1782 range = LIQUID_LEVEL_MAX+1;
1784 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1785 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1786 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1787 // it's perfectly safe to use liquid_kind here to determine the new node content.
1788 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1789 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1790 // liquid_kind is set properly, see above
1791 new_node_content = liquid_kind;
1792 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1793 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1794 new_node_content = CONTENT_AIR;
1796 // no surrounding sources, so get the maximum level that can flow into this node
1797 for (u16 i = 0; i < num_flows; i++) {
1798 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1799 switch (flows[i].t) {
1800 case NEIGHBOR_UPPER:
1801 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1802 max_node_level = LIQUID_LEVEL_MAX;
1803 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1804 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1805 } else if (nb_liquid_level > max_node_level)
1806 max_node_level = nb_liquid_level;
1808 case NEIGHBOR_LOWER:
1810 case NEIGHBOR_SAME_LEVEL:
1811 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1812 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1813 max_node_level = nb_liquid_level - 1;
1819 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1820 if (viscosity > 1 && max_node_level != liquid_level) {
1821 // amount to gain, limited by viscosity
1822 // must be at least 1 in absolute value
1823 s8 level_inc = max_node_level - liquid_level;
1824 if (level_inc < -viscosity || level_inc > viscosity)
1825 new_node_level = liquid_level + level_inc/viscosity;
1826 else if (level_inc < 0)
1827 new_node_level = liquid_level - 1;
1828 else if (level_inc > 0)
1829 new_node_level = liquid_level + 1;
1830 if (new_node_level != max_node_level)
1831 must_reflow.push_back(p0);
1833 new_node_level = max_node_level;
1835 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1836 new_node_content = liquid_kind;
1838 new_node_content = CONTENT_AIR;
1843 check if anything has changed. if not, just continue with the next node.
1845 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1846 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1847 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1853 update the current node
1856 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1857 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1858 // set level to last 3 bits, flowing down bit to 4th bit
1859 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1861 // set the liquid level and flow bit to 0
1862 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1864 n0.setContent(new_node_content);
1866 // Find out whether there is a suspect for this action
1867 std::string suspect;
1868 if(m_gamedef->rollback()){
1869 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1872 if(!suspect.empty()){
1874 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1875 // Get old node for rollback
1876 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1880 RollbackNode rollback_newnode(this, p0, m_gamedef);
1881 RollbackAction action;
1882 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1883 m_gamedef->rollback()->reportAction(action);
1889 v3s16 blockpos = getNodeBlockPos(p0);
1890 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1892 modified_blocks[blockpos] = block;
1893 // If new or old node emits light, MapBlock requires lighting update
1894 if(nodemgr->get(n0).light_source != 0 ||
1895 nodemgr->get(n00).light_source != 0)
1896 lighting_modified_blocks[block->getPos()] = block;
1900 enqueue neighbors for update if neccessary
1902 switch (nodemgr->get(n0.getContent()).liquid_type) {
1904 case LIQUID_FLOWING:
1905 // make sure source flows into all neighboring nodes
1906 for (u16 i = 0; i < num_flows; i++)
1907 if (flows[i].t != NEIGHBOR_UPPER)
1908 m_transforming_liquid.push_back(flows[i].p);
1909 for (u16 i = 0; i < num_airs; i++)
1910 if (airs[i].t != NEIGHBOR_UPPER)
1911 m_transforming_liquid.push_back(airs[i].p);
1914 // this flow has turned to air; neighboring flows might need to do the same
1915 for (u16 i = 0; i < num_flows; i++)
1916 m_transforming_liquid.push_back(flows[i].p);
1920 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1921 while (must_reflow.size() > 0)
1923 m_transforming_liquid.push_back(must_reflow.front());
1924 must_reflow.pop_front();
1926 updateLighting(lighting_modified_blocks, modified_blocks);
1929 /* ----------------------------------------------------------------------
1930 * Manage the queue so that it does not grow indefinately
1932 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1934 if (time_until_purge == 0)
1935 return; // Feature disabled
1937 time_until_purge *= 1000; // seconds -> milliseconds
1939 u32 curr_time = getTime(PRECISION_MILLI);
1940 u32 prev_unprocessed = m_unprocessed_count;
1941 m_unprocessed_count = m_transforming_liquid.size();
1943 // if unprocessed block count is decreasing or stable
1944 if (m_unprocessed_count <= prev_unprocessed) {
1945 m_queue_size_timer_started = false;
1947 if (!m_queue_size_timer_started)
1948 m_inc_trending_up_start_time = curr_time;
1949 m_queue_size_timer_started = true;
1952 // Account for curr_time overflowing
1953 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1954 m_queue_size_timer_started = false;
1956 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1957 * and the number of unprocessed blocks is still > liquid_loop_max then we
1958 * cannot keep up; dump the oldest blocks from the queue so that the queue
1959 * has liquid_loop_max items in it
1961 if (m_queue_size_timer_started
1962 && curr_time - m_inc_trending_up_start_time > time_until_purge
1963 && m_unprocessed_count > liquid_loop_max) {
1965 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1967 infostream << "transformLiquids(): DUMPING " << dump_qty
1968 << " blocks from the queue" << std::endl;
1971 m_transforming_liquid.pop_front();
1973 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1974 m_unprocessed_count = m_transforming_liquid.size();
1978 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1980 v3s16 blockpos = getNodeBlockPos(p);
1981 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1982 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1984 infostream<<"Map::getNodeMetadata(): Need to emerge "
1985 <<PP(blockpos)<<std::endl;
1986 block = emergeBlock(blockpos, false);
1989 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1993 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1997 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1999 v3s16 blockpos = getNodeBlockPos(p);
2000 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2001 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2003 infostream<<"Map::setNodeMetadata(): Need to emerge "
2004 <<PP(blockpos)<<std::endl;
2005 block = emergeBlock(blockpos, false);
2008 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2012 block->m_node_metadata.set(p_rel, meta);
2016 void Map::removeNodeMetadata(v3s16 p)
2018 v3s16 blockpos = getNodeBlockPos(p);
2019 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2020 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2023 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2027 block->m_node_metadata.remove(p_rel);
2030 NodeTimer Map::getNodeTimer(v3s16 p)
2032 v3s16 blockpos = getNodeBlockPos(p);
2033 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2034 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2036 infostream<<"Map::getNodeTimer(): Need to emerge "
2037 <<PP(blockpos)<<std::endl;
2038 block = emergeBlock(blockpos, false);
2041 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2045 NodeTimer t = block->m_node_timers.get(p_rel);
2049 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2051 v3s16 blockpos = getNodeBlockPos(p);
2052 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2053 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2055 infostream<<"Map::setNodeTimer(): Need to emerge "
2056 <<PP(blockpos)<<std::endl;
2057 block = emergeBlock(blockpos, false);
2060 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2064 block->m_node_timers.set(p_rel, t);
2067 void Map::removeNodeTimer(v3s16 p)
2069 v3s16 blockpos = getNodeBlockPos(p);
2070 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2071 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2074 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2078 block->m_node_timers.remove(p_rel);
2084 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2085 Map(dout_server, gamedef),
2087 m_map_metadata_changed(true)
2089 verbosestream<<__FUNCTION_NAME<<std::endl;
2092 Try to load map; if not found, create a new one.
2095 // Determine which database backend to use
2096 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2098 bool succeeded = conf.readConfigFile(conf_path.c_str());
2099 if (!succeeded || !conf.exists("backend")) {
2100 // fall back to sqlite3
2101 dbase = new Database_SQLite3(this, savedir);
2102 conf.set("backend", "sqlite3");
2104 std::string backend = conf.get("backend");
2105 if (backend == "dummy")
2106 dbase = new Database_Dummy(this);
2107 else if (backend == "sqlite3")
2108 dbase = new Database_SQLite3(this, savedir);
2110 else if (backend == "leveldb")
2111 dbase = new Database_LevelDB(this, savedir);
2114 else if (backend == "redis")
2115 dbase = new Database_Redis(this, savedir);
2118 throw BaseException("Unknown map backend");
2121 m_savedir = savedir;
2122 m_map_saving_enabled = false;
2126 // If directory exists, check contents and load if possible
2127 if(fs::PathExists(m_savedir))
2129 // If directory is empty, it is safe to save into it.
2130 if(fs::GetDirListing(m_savedir).size() == 0)
2132 infostream<<"ServerMap: Empty save directory is valid."
2134 m_map_saving_enabled = true;
2139 // Load map metadata (seed, chunksize)
2142 catch(SettingNotFoundException &e){
2143 infostream<<"ServerMap: Some metadata not found."
2144 <<" Using default settings."<<std::endl;
2146 catch(FileNotGoodException &e){
2147 infostream<<"WARNING: Could not load map metadata"
2148 //<<" Disabling chunk-based generator."
2153 infostream<<"ServerMap: Successfully loaded map "
2154 <<"metadata from "<<savedir
2155 <<", assuming valid save directory."
2156 <<" seed="<< m_emerge->params.seed <<"."
2159 m_map_saving_enabled = true;
2160 // Map loaded, not creating new one
2164 // If directory doesn't exist, it is safe to save to it
2166 m_map_saving_enabled = true;
2169 catch(std::exception &e)
2171 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2172 <<", exception: "<<e.what()<<std::endl;
2173 infostream<<"Please remove the map or fix it."<<std::endl;
2174 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2177 infostream<<"Initializing new map."<<std::endl;
2179 // Create zero sector
2180 emergeSector(v2s16(0,0));
2182 // Initially write whole map
2183 save(MOD_STATE_CLEAN);
2186 ServerMap::~ServerMap()
2188 verbosestream<<__FUNCTION_NAME<<std::endl;
2192 if(m_map_saving_enabled)
2194 // Save only changed parts
2195 save(MOD_STATE_WRITE_AT_UNLOAD);
2196 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2200 infostream<<"ServerMap: Map not saved"<<std::endl;
2203 catch(std::exception &e)
2205 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2206 <<", exception: "<<e.what()<<std::endl;
2210 Close database if it was opened
2218 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2219 for(; i.atEnd() == false; i++)
2221 MapChunk *chunk = i.getNode()->getValue();
2227 u64 ServerMap::getSeed()
2229 return m_emerge->params.seed;
2232 s16 ServerMap::getWaterLevel()
2234 return m_emerge->params.water_level;
2237 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2239 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2240 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2242 s16 chunksize = m_emerge->params.chunksize;
2243 s16 coffset = -chunksize / 2;
2244 v3s16 chunk_offset(coffset, coffset, coffset);
2245 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2246 v3s16 blockpos_min = blockpos_div * chunksize;
2247 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2248 blockpos_min += chunk_offset;
2249 blockpos_max += chunk_offset;
2251 v3s16 extra_borders(1,1,1);
2253 // Do nothing if not inside limits (+-1 because of neighbors)
2254 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2255 blockpos_over_limit(blockpos_max + extra_borders))
2258 data->seed = m_emerge->params.seed;
2259 data->blockpos_min = blockpos_min;
2260 data->blockpos_max = blockpos_max;
2261 data->blockpos_requested = blockpos;
2262 data->nodedef = m_gamedef->ndef();
2265 Create the whole area of this and the neighboring blocks
2268 //TimeTaker timer("initBlockMake() create area");
2270 for(s16 x=blockpos_min.X-extra_borders.X;
2271 x<=blockpos_max.X+extra_borders.X; x++)
2272 for(s16 z=blockpos_min.Z-extra_borders.Z;
2273 z<=blockpos_max.Z+extra_borders.Z; z++)
2275 v2s16 sectorpos(x, z);
2276 // Sector metadata is loaded from disk if not already loaded.
2277 ServerMapSector *sector = createSector(sectorpos);
2281 for(s16 y=blockpos_min.Y-extra_borders.Y;
2282 y<=blockpos_max.Y+extra_borders.Y; y++)
2285 //MapBlock *block = createBlock(p);
2286 // 1) get from memory, 2) load from disk
2287 MapBlock *block = emergeBlock(p, false);
2288 // 3) create a blank one
2291 block = createBlock(p);
2294 Block gets sunlight if this is true.
2296 Refer to the map generator heuristics.
2298 bool ug = m_emerge->isBlockUnderground(p);
2299 block->setIsUnderground(ug);
2302 // Lighting will not be valid after make_chunk is called
2303 block->setLightingExpired(true);
2304 // Lighting will be calculated
2305 //block->setLightingExpired(false);
2311 Now we have a big empty area.
2313 Make a ManualMapVoxelManipulator that contains this and the
2317 // The area that contains this block and it's neighbors
2318 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2319 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2321 data->vmanip = new MMVManip(this);
2322 //data->vmanip->setMap(this);
2326 //TimeTaker timer("initBlockMake() initialEmerge");
2327 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2330 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2331 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2332 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2333 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2334 core::map<v3s16, u8>::Node *n;
2335 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2338 u8 flags = n->getValue();
2339 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2345 // Data is ready now.
2349 void ServerMap::finishBlockMake(BlockMakeData *data,
2350 std::map<v3s16, MapBlock*> &changed_blocks)
2352 v3s16 blockpos_min = data->blockpos_min;
2353 v3s16 blockpos_max = data->blockpos_max;
2354 v3s16 blockpos_requested = data->blockpos_requested;
2355 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2356 <<blockpos_requested.Y<<","
2357 <<blockpos_requested.Z<<")"<<std::endl;*/
2359 v3s16 extra_borders(1,1,1);
2361 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2363 /*infostream<<"Resulting vmanip:"<<std::endl;
2364 data->vmanip.print(infostream);*/
2366 // Make sure affected blocks are loaded
2367 for(s16 x=blockpos_min.X-extra_borders.X;
2368 x<=blockpos_max.X+extra_borders.X; x++)
2369 for(s16 z=blockpos_min.Z-extra_borders.Z;
2370 z<=blockpos_max.Z+extra_borders.Z; z++)
2371 for(s16 y=blockpos_min.Y-extra_borders.Y;
2372 y<=blockpos_max.Y+extra_borders.Y; y++)
2375 // Load from disk if not already in memory
2376 emergeBlock(p, false);
2380 Blit generated stuff to map
2381 NOTE: blitBackAll adds nearly everything to changed_blocks
2385 //TimeTaker timer("finishBlockMake() blitBackAll");
2386 data->vmanip->blitBackAll(&changed_blocks);
2389 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2392 Copy transforming liquid information
2394 while(data->transforming_liquid.size() > 0)
2396 m_transforming_liquid.push_back(data->transforming_liquid.front());
2397 data->transforming_liquid.pop_front();
2401 Do stuff in central blocks
2409 TimeTaker t("finishBlockMake lighting update");
2411 core::map<v3s16, MapBlock*> lighting_update_blocks;
2414 for(s16 x=blockpos_min.X-extra_borders.X;
2415 x<=blockpos_max.X+extra_borders.X; x++)
2416 for(s16 z=blockpos_min.Z-extra_borders.Z;
2417 z<=blockpos_max.Z+extra_borders.Z; z++)
2418 for(s16 y=blockpos_min.Y-extra_borders.Y;
2419 y<=blockpos_max.Y+extra_borders.Y; y++)
2422 MapBlock *block = getBlockNoCreateNoEx(p);
2424 lighting_update_blocks.insert(block->getPos(), block);
2427 updateLighting(lighting_update_blocks, changed_blocks);
2431 Set lighting to non-expired state in all of them.
2432 This is cheating, but it is not fast enough if all of them
2433 would actually be updated.
2435 for(s16 x=blockpos_min.X-extra_borders.X;
2436 x<=blockpos_max.X+extra_borders.X; x++)
2437 for(s16 z=blockpos_min.Z-extra_borders.Z;
2438 z<=blockpos_max.Z+extra_borders.Z; z++)
2439 for(s16 y=blockpos_min.Y-extra_borders.Y;
2440 y<=blockpos_max.Y+extra_borders.Y; y++)
2443 MapBlock * block = getBlockNoCreateNoEx(p);
2445 block->setLightingExpired(false);
2449 if(enable_mapgen_debug_info == false)
2450 t.stop(true); // Hide output
2455 Go through changed blocks
2457 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2458 i != changed_blocks.end(); ++i)
2460 MapBlock *block = i->second;
2464 Update day/night difference cache of the MapBlocks
2466 block->expireDayNightDiff();
2468 Set block as modified
2470 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2471 "finishBlockMake expireDayNightDiff");
2475 Set central blocks as generated
2477 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2478 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2479 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2482 MapBlock *block = getBlockNoCreateNoEx(p);
2485 block->setGenerated(true);
2489 Save changed parts of map
2490 NOTE: Will be saved later.
2492 //save(MOD_STATE_WRITE_AT_UNLOAD);
2494 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2495 <<","<<blockpos_requested.Y<<","
2496 <<blockpos_requested.Z<<")"<<std::endl;*/
2500 if(enable_mapgen_debug_info)
2503 Analyze resulting blocks
2505 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2506 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2507 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2508 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2509 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2510 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2512 v3s16 p = v3s16(x,y,z);
2513 MapBlock *block = getBlockNoCreateNoEx(p);
2515 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2516 infostream<<"Generated "<<spos<<": "
2517 <<analyze_block(block)<<std::endl;
2522 getBlockNoCreateNoEx(blockpos_requested);
2525 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2527 DSTACKF("%s: p2d=(%d,%d)",
2532 Check if it exists already in memory
2534 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2539 Try to load it from disk (with blocks)
2541 //if(loadSectorFull(p2d) == true)
2544 Try to load metadata from disk
2547 if(loadSectorMeta(p2d) == true)
2549 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2552 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2553 throw InvalidPositionException("");
2559 Do not create over-limit
2561 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2562 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2563 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2564 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2565 throw InvalidPositionException("createSector(): pos. over limit");
2568 Generate blank sector
2571 sector = new ServerMapSector(this, p2d, m_gamedef);
2573 // Sector position on map in nodes
2574 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2579 m_sectors[p2d] = sector;
2586 This is a quick-hand function for calling makeBlock().
2588 MapBlock * ServerMap::generateBlock(
2590 std::map<v3s16, MapBlock*> &modified_blocks
2593 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2595 /*infostream<<"generateBlock(): "
2596 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2599 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2601 TimeTaker timer("generateBlock");
2603 //MapBlock *block = original_dummy;
2605 v2s16 p2d(p.X, p.Z);
2606 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2609 Do not generate over-limit
2611 if(blockpos_over_limit(p))
2613 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2614 throw InvalidPositionException("generateBlock(): pos. over limit");
2618 Create block make data
2621 initBlockMake(&data, p);
2627 TimeTaker t("mapgen::make_block()");
2628 mapgen->makeChunk(&data);
2629 //mapgen::make_block(&data);
2631 if(enable_mapgen_debug_info == false)
2632 t.stop(true); // Hide output
2636 Blit data back on map, update lighting, add mobs and whatever this does
2638 finishBlockMake(&data, modified_blocks);
2643 MapBlock *block = getBlockNoCreateNoEx(p);
2651 bool erroneus_content = false;
2652 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2653 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2654 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2657 MapNode n = block->getNode(p);
2658 if(n.getContent() == CONTENT_IGNORE)
2660 infostream<<"CONTENT_IGNORE at "
2661 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2663 erroneus_content = true;
2667 if(erroneus_content)
2676 Generate a completely empty block
2680 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2681 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2683 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2686 n.setContent(CONTENT_AIR);
2687 block->setNode(v3s16(x0,y0,z0), n);
2693 if(enable_mapgen_debug_info == false)
2694 timer.stop(true); // Hide output
2700 MapBlock * ServerMap::createBlock(v3s16 p)
2702 DSTACKF("%s: p=(%d,%d,%d)",
2703 __FUNCTION_NAME, p.X, p.Y, p.Z);
2706 Do not create over-limit
2708 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2709 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2710 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2711 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2712 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2713 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2714 throw InvalidPositionException("createBlock(): pos. over limit");
2716 v2s16 p2d(p.X, p.Z);
2719 This will create or load a sector if not found in memory.
2720 If block exists on disk, it will be loaded.
2722 NOTE: On old save formats, this will be slow, as it generates
2723 lighting on blocks for them.
2725 ServerMapSector *sector;
2727 sector = (ServerMapSector*)createSector(p2d);
2728 assert(sector->getId() == MAPSECTOR_SERVER);
2730 catch(InvalidPositionException &e)
2732 infostream<<"createBlock: createSector() failed"<<std::endl;
2736 NOTE: This should not be done, or at least the exception
2737 should not be passed on as std::exception, because it
2738 won't be catched at all.
2740 /*catch(std::exception &e)
2742 infostream<<"createBlock: createSector() failed: "
2743 <<e.what()<<std::endl;
2748 Try to get a block from the sector
2751 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2754 if(block->isDummy())
2759 block = sector->createBlankBlock(block_y);
2764 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2766 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2768 p.X, p.Y, p.Z, create_blank);
2771 MapBlock *block = getBlockNoCreateNoEx(p);
2772 if(block && block->isDummy() == false)
2777 MapBlock *block = loadBlock(p);
2783 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2784 MapBlock *block = sector->createBlankBlock(p.Y);
2792 std::map<v3s16, MapBlock*> modified_blocks;
2793 MapBlock *block = generateBlock(p, modified_blocks);
2797 event.type = MEET_OTHER;
2800 // Copy modified_blocks to event
2801 for(std::map<v3s16, MapBlock*>::iterator
2802 i = modified_blocks.begin();
2803 i != modified_blocks.end(); ++i)
2805 event.modified_blocks.insert(i->first);
2809 dispatchEvent(&event);
2819 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2821 MapBlock *block = getBlockNoCreateNoEx(p3d);
2823 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2828 void ServerMap::prepareBlock(MapBlock *block) {
2831 // N.B. This requires no synchronization, since data will not be modified unless
2832 // the VoxelManipulator being updated belongs to the same thread.
2833 void ServerMap::updateVManip(v3s16 pos)
2835 Mapgen *mg = m_emerge->getCurrentMapgen();
2839 MMVManip *vm = mg->vm;
2843 if (!vm->m_area.contains(pos))
2846 s32 idx = vm->m_area.index(pos);
2847 vm->m_data[idx] = getNodeNoEx(pos);
2848 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2850 vm->m_is_dirty = true;
2853 s16 ServerMap::findGroundLevel(v2s16 p2d)
2857 Uh, just do something random...
2859 // Find existing map from top to down
2862 v3s16 p(p2d.X, max, p2d.Y);
2863 for(; p.Y>min; p.Y--)
2865 MapNode n = getNodeNoEx(p);
2866 if(n.getContent() != CONTENT_IGNORE)
2871 // If this node is not air, go to plan b
2872 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2874 // Search existing walkable and return it
2875 for(; p.Y>min; p.Y--)
2877 MapNode n = getNodeNoEx(p);
2878 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2887 Determine from map generator noise functions
2890 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2893 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2894 //return (s16)level;
2897 bool ServerMap::loadFromFolders() {
2898 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2903 void ServerMap::createDirs(std::string path)
2905 if(fs::CreateAllDirs(path) == false)
2907 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2908 <<"\""<<path<<"\""<<std::endl;
2909 throw BaseException("ServerMap failed to create directory");
2913 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2919 snprintf(cc, 9, "%.4x%.4x",
2920 (unsigned int)pos.X&0xffff,
2921 (unsigned int)pos.Y&0xffff);
2923 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2925 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2926 (unsigned int)pos.X&0xfff,
2927 (unsigned int)pos.Y&0xfff);
2929 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2936 v2s16 ServerMap::getSectorPos(std::string dirname)
2938 unsigned int x = 0, y = 0;
2940 std::string component;
2941 fs::RemoveLastPathComponent(dirname, &component, 1);
2942 if(component.size() == 8)
2945 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2947 else if(component.size() == 3)
2950 fs::RemoveLastPathComponent(dirname, &component, 2);
2951 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2952 // Sign-extend the 12 bit values up to 16 bits...
2953 if(x&0x800) x|=0xF000;
2954 if(y&0x800) y|=0xF000;
2961 v2s16 pos((s16)x, (s16)y);
2965 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2967 v2s16 p2d = getSectorPos(sectordir);
2969 if(blockfile.size() != 4){
2970 throw InvalidFilenameException("Invalid block filename");
2973 int r = sscanf(blockfile.c_str(), "%4x", &y);
2975 throw InvalidFilenameException("Invalid block filename");
2976 return v3s16(p2d.X, y, p2d.Y);
2979 std::string ServerMap::getBlockFilename(v3s16 p)
2982 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2986 void ServerMap::save(ModifiedState save_level)
2988 DSTACK(__FUNCTION_NAME);
2989 if(m_map_saving_enabled == false)
2991 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2995 if(save_level == MOD_STATE_CLEAN)
2996 infostream<<"ServerMap: Saving whole map, this can take time."
2999 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3004 // Profile modified reasons
3005 Profiler modprofiler;
3007 u32 sector_meta_count = 0;
3008 u32 block_count = 0;
3009 u32 block_count_all = 0; // Number of blocks in memory
3011 // Don't do anything with sqlite unless something is really saved
3012 bool save_started = false;
3014 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3015 i != m_sectors.end(); ++i)
3017 ServerMapSector *sector = (ServerMapSector*)i->second;
3018 assert(sector->getId() == MAPSECTOR_SERVER);
3020 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3022 saveSectorMeta(sector);
3023 sector_meta_count++;
3025 std::list<MapBlock*> blocks;
3026 sector->getBlocks(blocks);
3028 for(std::list<MapBlock*>::iterator j = blocks.begin();
3029 j != blocks.end(); ++j)
3031 MapBlock *block = *j;
3035 if(block->getModified() >= (u32)save_level)
3040 save_started = true;
3043 modprofiler.add(block->getModifiedReason(), 1);
3048 /*infostream<<"ServerMap: Written block ("
3049 <<block->getPos().X<<","
3050 <<block->getPos().Y<<","
3051 <<block->getPos().Z<<")"
3060 Only print if something happened or saved whole map
3062 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3063 || block_count != 0)
3065 infostream<<"ServerMap: Written: "
3066 <<sector_meta_count<<" sector metadata files, "
3067 <<block_count<<" block files"
3068 <<", "<<block_count_all<<" blocks in memory."
3070 PrintInfo(infostream); // ServerMap/ClientMap:
3071 infostream<<"Blocks modified by: "<<std::endl;
3072 modprofiler.print(infostream);
3076 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3078 if(loadFromFolders()){
3079 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3080 <<"all blocks that are stored in flat files"<<std::endl;
3082 dbase->listAllLoadableBlocks(dst);
3085 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3087 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3088 si != m_sectors.end(); ++si)
3090 MapSector *sector = si->second;
3092 std::list<MapBlock*> blocks;
3093 sector->getBlocks(blocks);
3095 for(std::list<MapBlock*>::iterator i = blocks.begin();
3096 i != blocks.end(); ++i)
3098 MapBlock *block = (*i);
3099 v3s16 p = block->getPos();
3105 void ServerMap::saveMapMeta()
3107 DSTACK(__FUNCTION_NAME);
3109 /*infostream<<"ServerMap::saveMapMeta(): "
3113 createDirs(m_savedir);
3115 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3116 std::ostringstream ss(std::ios_base::binary);
3120 m_emerge->saveParamsToSettings(¶ms);
3121 params.writeLines(ss);
3123 ss<<"[end_of_params]\n";
3125 if(!fs::safeWriteToFile(fullpath, ss.str()))
3127 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3128 <<"could not write "<<fullpath<<std::endl;
3129 throw FileNotGoodException("Cannot save chunk metadata");
3132 m_map_metadata_changed = false;
3135 void ServerMap::loadMapMeta()
3137 DSTACK(__FUNCTION_NAME);
3139 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3140 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3142 errorstream << "ServerMap::loadMapMeta(): "
3143 << "could not open" << fullpath << std::endl;
3144 throw FileNotGoodException("Cannot open map metadata");
3149 if (!params.parseConfigLines(is, "[end_of_params]")) {
3150 throw SerializationError("ServerMap::loadMapMeta(): "
3151 "[end_of_params] not found!");
3154 m_emerge->loadParamsFromSettings(¶ms);
3156 verbosestream << "ServerMap::loadMapMeta(): seed="
3157 << m_emerge->params.seed << std::endl;
3160 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3162 DSTACK(__FUNCTION_NAME);
3163 // Format used for writing
3164 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3166 v2s16 pos = sector->getPos();
3167 std::string dir = getSectorDir(pos);
3170 std::string fullpath = dir + DIR_DELIM + "meta";
3171 std::ostringstream ss(std::ios_base::binary);
3173 sector->serialize(ss, version);
3175 if(!fs::safeWriteToFile(fullpath, ss.str()))
3176 throw FileNotGoodException("Cannot write sector metafile");
3178 sector->differs_from_disk = false;
3181 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3183 DSTACK(__FUNCTION_NAME);
3185 v2s16 p2d = getSectorPos(sectordir);
3187 ServerMapSector *sector = NULL;
3189 std::string fullpath = sectordir + DIR_DELIM + "meta";
3190 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3191 if(is.good() == false)
3193 // If the directory exists anyway, it probably is in some old
3194 // format. Just go ahead and create the sector.
3195 if(fs::PathExists(sectordir))
3197 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3198 <<fullpath<<" doesn't exist but directory does."
3199 <<" Continuing with a sector with no metadata."
3201 sector = new ServerMapSector(this, p2d, m_gamedef);
3202 m_sectors[p2d] = sector;
3206 throw FileNotGoodException("Cannot open sector metafile");
3211 sector = ServerMapSector::deSerialize
3212 (is, this, p2d, m_sectors, m_gamedef);
3214 saveSectorMeta(sector);
3217 sector->differs_from_disk = false;
3222 bool ServerMap::loadSectorMeta(v2s16 p2d)
3224 DSTACK(__FUNCTION_NAME);
3226 MapSector *sector = NULL;
3228 // The directory layout we're going to load from.
3229 // 1 - original sectors/xxxxzzzz/
3230 // 2 - new sectors2/xxx/zzz/
3231 // If we load from anything but the latest structure, we will
3232 // immediately save to the new one, and remove the old.
3234 std::string sectordir1 = getSectorDir(p2d, 1);
3235 std::string sectordir;
3236 if(fs::PathExists(sectordir1))
3238 sectordir = sectordir1;
3243 sectordir = getSectorDir(p2d, 2);
3247 sector = loadSectorMeta(sectordir, loadlayout != 2);
3249 catch(InvalidFilenameException &e)
3253 catch(FileNotGoodException &e)
3257 catch(std::exception &e)
3266 bool ServerMap::loadSectorFull(v2s16 p2d)
3268 DSTACK(__FUNCTION_NAME);
3270 MapSector *sector = NULL;
3272 // The directory layout we're going to load from.
3273 // 1 - original sectors/xxxxzzzz/
3274 // 2 - new sectors2/xxx/zzz/
3275 // If we load from anything but the latest structure, we will
3276 // immediately save to the new one, and remove the old.
3278 std::string sectordir1 = getSectorDir(p2d, 1);
3279 std::string sectordir;
3280 if(fs::PathExists(sectordir1))
3282 sectordir = sectordir1;
3287 sectordir = getSectorDir(p2d, 2);
3291 sector = loadSectorMeta(sectordir, loadlayout != 2);
3293 catch(InvalidFilenameException &e)
3297 catch(FileNotGoodException &e)
3301 catch(std::exception &e)
3309 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3311 std::vector<fs::DirListNode>::iterator i2;
3312 for(i2=list2.begin(); i2!=list2.end(); i2++)
3318 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3320 catch(InvalidFilenameException &e)
3322 // This catches unknown crap in directory
3328 infostream<<"Sector converted to new layout - deleting "<<
3329 sectordir1<<std::endl;
3330 fs::RecursiveDelete(sectordir1);
3337 void ServerMap::beginSave()
3342 void ServerMap::endSave()
3347 bool ServerMap::saveBlock(MapBlock *block)
3349 return saveBlock(block, dbase);
3352 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3354 v3s16 p3d = block->getPos();
3356 // Dummy blocks are not written
3357 if (block->isDummy()) {
3358 errorstream << "WARNING: saveBlock: Not writing dummy block "
3359 << PP(p3d) << std::endl;
3363 // Format used for writing
3364 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3367 [0] u8 serialization version
3370 std::ostringstream o(std::ios_base::binary);
3371 o.write((char*) &version, 1);
3372 block->serialize(o, version, true);
3374 std::string data = o.str();
3375 bool ret = db->saveBlock(p3d, data);
3377 // We just wrote it to the disk so clear modified flag
3378 block->resetModified();
3383 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3384 MapSector *sector, bool save_after_load)
3386 DSTACK(__FUNCTION_NAME);
3388 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3391 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3392 if(is.good() == false)
3393 throw FileNotGoodException("Cannot open block file");
3395 v3s16 p3d = getBlockPos(sectordir, blockfile);
3396 v2s16 p2d(p3d.X, p3d.Z);
3398 assert(sector->getPos() == p2d);
3400 u8 version = SER_FMT_VER_INVALID;
3401 is.read((char*)&version, 1);
3404 throw SerializationError("ServerMap::loadBlock(): Failed"
3405 " to read MapBlock version");
3407 /*u32 block_size = MapBlock::serializedLength(version);
3408 SharedBuffer<u8> data(block_size);
3409 is.read((char*)*data, block_size);*/
3411 // This will always return a sector because we're the server
3412 //MapSector *sector = emergeSector(p2d);
3414 MapBlock *block = NULL;
3415 bool created_new = false;
3416 block = sector->getBlockNoCreateNoEx(p3d.Y);
3419 block = sector->createBlankBlockNoInsert(p3d.Y);
3424 block->deSerialize(is, version, true);
3426 // If it's a new block, insert it to the map
3428 sector->insertBlock(block);
3431 Save blocks loaded in old format in new format
3434 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3438 // Should be in database now, so delete the old file
3439 fs::RecursiveDelete(fullpath);
3442 // We just loaded it from the disk, so it's up-to-date.
3443 block->resetModified();
3446 catch(SerializationError &e)
3448 infostream<<"WARNING: Invalid block data on disk "
3449 <<"fullpath="<<fullpath
3450 <<" (SerializationError). "
3451 <<"what()="<<e.what()
3453 // Ignoring. A new one will be generated.
3456 // TODO: Backup file; name is in fullpath.
3460 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3462 DSTACK(__FUNCTION_NAME);
3465 std::istringstream is(*blob, std::ios_base::binary);
3467 u8 version = SER_FMT_VER_INVALID;
3468 is.read((char*)&version, 1);
3471 throw SerializationError("ServerMap::loadBlock(): Failed"
3472 " to read MapBlock version");
3474 /*u32 block_size = MapBlock::serializedLength(version);
3475 SharedBuffer<u8> data(block_size);
3476 is.read((char*)*data, block_size);*/
3478 // This will always return a sector because we're the server
3479 //MapSector *sector = emergeSector(p2d);
3481 MapBlock *block = NULL;
3482 bool created_new = false;
3483 block = sector->getBlockNoCreateNoEx(p3d.Y);
3486 block = sector->createBlankBlockNoInsert(p3d.Y);
3491 block->deSerialize(is, version, true);
3493 // If it's a new block, insert it to the map
3495 sector->insertBlock(block);
3498 Save blocks loaded in old format in new format
3501 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3502 // Only save if asked to; no need to update version
3506 // We just loaded it from, so it's up-to-date.
3507 block->resetModified();
3510 catch(SerializationError &e)
3512 errorstream<<"Invalid block data in database"
3513 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3514 <<" (SerializationError): "<<e.what()<<std::endl;
3516 // TODO: Block should be marked as invalid in memory so that it is
3517 // not touched but the game can run
3519 if(g_settings->getBool("ignore_world_load_errors")){
3520 errorstream<<"Ignoring block load error. Duck and cover! "
3521 <<"(ignore_world_load_errors)"<<std::endl;
3523 throw SerializationError("Invalid block data in database");
3529 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3531 DSTACK(__FUNCTION_NAME);
3533 v2s16 p2d(blockpos.X, blockpos.Z);
3537 ret = dbase->loadBlock(blockpos);
3539 loadBlock(&ret, blockpos, createSector(p2d), false);
3540 return getBlockNoCreateNoEx(blockpos);
3542 // Not found in database, try the files
3544 // The directory layout we're going to load from.
3545 // 1 - original sectors/xxxxzzzz/
3546 // 2 - new sectors2/xxx/zzz/
3547 // If we load from anything but the latest structure, we will
3548 // immediately save to the new one, and remove the old.
3550 std::string sectordir1 = getSectorDir(p2d, 1);
3551 std::string sectordir;
3552 if(fs::PathExists(sectordir1))
3554 sectordir = sectordir1;
3559 sectordir = getSectorDir(p2d, 2);
3563 Make sure sector is loaded
3565 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3569 sector = loadSectorMeta(sectordir, loadlayout != 2);
3571 catch(InvalidFilenameException &e)
3575 catch(FileNotGoodException &e)
3579 catch(std::exception &e)
3586 Make sure file exists
3589 std::string blockfilename = getBlockFilename(blockpos);
3590 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3594 Load block and save it to the database
3596 loadBlock(sectordir, blockfilename, sector, true);
3597 return getBlockNoCreateNoEx(blockpos);
3600 bool ServerMap::deleteBlock(v3s16 blockpos)
3602 if (!dbase->deleteBlock(blockpos))
3605 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3607 v2s16 p2d(blockpos.X, blockpos.Z);
3608 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3611 sector->deleteBlock(block);
3617 void ServerMap::PrintInfo(std::ostream &out)
3622 MMVManip::MMVManip(Map *map):
3625 m_create_area(false),
3630 MMVManip::~MMVManip()
3634 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3635 bool load_if_inexistent)
3637 TimeTaker timer1("initialEmerge", &emerge_time);
3639 // Units of these are MapBlocks
3640 v3s16 p_min = blockpos_min;
3641 v3s16 p_max = blockpos_max;
3643 VoxelArea block_area_nodes
3644 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3646 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3649 infostream<<"initialEmerge: area: ";
3650 block_area_nodes.print(infostream);
3651 infostream<<" ("<<size_MB<<"MB)";
3652 infostream<<std::endl;
3655 addArea(block_area_nodes);
3657 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3658 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3659 for(s32 x=p_min.X; x<=p_max.X; x++)
3664 std::map<v3s16, u8>::iterator n;
3665 n = m_loaded_blocks.find(p);
3666 if(n != m_loaded_blocks.end())
3669 bool block_data_inexistent = false;
3672 TimeTaker timer1("emerge load", &emerge_load_time);
3674 block = m_map->getBlockNoCreate(p);
3675 if(block->isDummy())
3676 block_data_inexistent = true;
3678 block->copyTo(*this);
3680 catch(InvalidPositionException &e)
3682 block_data_inexistent = true;
3685 if(block_data_inexistent)
3688 if (load_if_inexistent) {
3689 ServerMap *svrmap = (ServerMap *)m_map;
3690 block = svrmap->emergeBlock(p, false);
3692 block = svrmap->createBlock(p);
3693 block->copyTo(*this);
3695 flags |= VMANIP_BLOCK_DATA_INEXIST;
3698 Mark area inexistent
3700 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3701 // Fill with VOXELFLAG_NO_DATA
3702 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3703 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3705 s32 i = m_area.index(a.MinEdge.X,y,z);
3706 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3710 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3712 // Mark that block was loaded as blank
3713 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3716 m_loaded_blocks[p] = flags;
3722 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3723 bool overwrite_generated)
3725 if(m_area.getExtent() == v3s16(0,0,0))
3729 Copy data of all blocks
3731 for(std::map<v3s16, u8>::iterator
3732 i = m_loaded_blocks.begin();
3733 i != m_loaded_blocks.end(); ++i)
3736 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3737 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3738 if ((existed == false) || (block == NULL) ||
3739 (overwrite_generated == false && block->isGenerated() == true))
3742 block->copyFrom(*this);
3745 (*modified_blocks)[p] = block;