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"
26 #include "serialization.h"
27 #include "nodemetadata.h"
33 #include "util/directiontables.h"
34 #include "util/mathconstants.h"
35 #include "rollback_interface.h"
36 #include "environment.h"
38 #include "mapgen_v6.h"
43 #include "database-dummy.h"
44 #include "database-sqlite3.h"
47 #include "database-leveldb.h"
50 #include "database-redis.h"
53 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
60 Map::Map(std::ostream &dout, IGameDef *gamedef):
64 m_transforming_liquid_loop_count_multiplier(1.0f),
65 m_unprocessed_count(0),
66 m_inc_trending_up_start_time(0),
67 m_queue_size_timer_started(false)
76 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
77 i != m_sectors.end(); ++i)
83 void Map::addEventReceiver(MapEventReceiver *event_receiver)
85 m_event_receivers.insert(event_receiver);
88 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
90 m_event_receivers.erase(event_receiver);
93 void Map::dispatchEvent(MapEditEvent *event)
95 for(std::set<MapEventReceiver*>::iterator
96 i = m_event_receivers.begin();
97 i != m_event_receivers.end(); ++i)
99 (*i)->onMapEditEvent(event);
103 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
105 if(m_sector_cache != NULL && p == m_sector_cache_p){
106 MapSector * sector = m_sector_cache;
110 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
112 if(n == m_sectors.end())
115 MapSector *sector = n->second;
117 // Cache the last result
118 m_sector_cache_p = p;
119 m_sector_cache = sector;
124 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
126 return getSectorNoGenerateNoExNoLock(p);
129 MapSector * Map::getSectorNoGenerate(v2s16 p)
131 MapSector *sector = getSectorNoGenerateNoEx(p);
133 throw InvalidPositionException();
138 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
140 v2s16 p2d(p3d.X, p3d.Z);
141 MapSector * sector = getSectorNoGenerateNoEx(p2d);
144 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
148 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
150 MapBlock *block = getBlockNoCreateNoEx(p3d);
152 throw InvalidPositionException();
156 bool Map::isNodeUnderground(v3s16 p)
158 v3s16 blockpos = getNodeBlockPos(p);
160 MapBlock * block = getBlockNoCreate(blockpos);
161 return block->getIsUnderground();
163 catch(InvalidPositionException &e)
169 bool Map::isValidPosition(v3s16 p)
171 v3s16 blockpos = getNodeBlockPos(p);
172 MapBlock *block = getBlockNoCreate(blockpos);
173 return (block != NULL);
176 // Returns a CONTENT_IGNORE node if not found
177 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
179 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock *block = getBlockNoCreateNoEx(blockpos);
182 if (is_valid_position != NULL)
183 *is_valid_position = false;
184 return MapNode(CONTENT_IGNORE);
187 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
190 if (is_valid_position != NULL)
191 *is_valid_position = is_valid_p;
197 // throws InvalidPositionException if not found
198 // TODO: Now this is deprecated, getNodeNoEx should be renamed
199 MapNode Map::getNode(v3s16 p)
201 v3s16 blockpos = getNodeBlockPos(p);
202 MapBlock *block = getBlockNoCreateNoEx(blockpos);
204 throw InvalidPositionException();
205 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 bool is_valid_position;
207 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
208 if (!is_valid_position)
209 throw InvalidPositionException();
214 // throws InvalidPositionException if not found
215 void Map::setNode(v3s16 p, MapNode & n)
217 v3s16 blockpos = getNodeBlockPos(p);
218 MapBlock *block = getBlockNoCreate(blockpos);
219 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
220 // Never allow placing CONTENT_IGNORE, it fucks up stuff
221 if(n.getContent() == CONTENT_IGNORE){
223 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
224 <<" while trying to replace \""
225 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
226 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
227 debug_stacks_print_to(infostream);
230 block->setNodeNoCheck(relpos, n);
235 Goes recursively through the neighbours of the node.
237 Alters only transparent nodes.
239 If the lighting of the neighbour is lower than the lighting of
240 the node was (before changing it to 0 at the step before), the
241 lighting of the neighbour is set to 0 and then the same stuff
242 repeats for the neighbour.
244 The ending nodes of the routine are stored in light_sources.
245 This is useful when a light is removed. In such case, this
246 routine can be called for the light node and then again for
247 light_sources to re-light the area without the removed light.
249 values of from_nodes are lighting values.
251 void Map::unspreadLight(enum LightBank bank,
252 std::map<v3s16, u8> & from_nodes,
253 std::set<v3s16> & light_sources,
254 std::map<v3s16, MapBlock*> & modified_blocks)
256 INodeDefManager *nodemgr = m_gamedef->ndef();
259 v3s16(0,0,1), // back
261 v3s16(1,0,0), // right
262 v3s16(0,0,-1), // front
263 v3s16(0,-1,0), // bottom
264 v3s16(-1,0,0), // left
267 if(from_nodes.empty())
270 u32 blockchangecount = 0;
272 std::map<v3s16, u8> unlighted_nodes;
275 Initialize block cache
278 MapBlock *block = NULL;
279 // Cache this a bit, too
280 bool block_checked_in_modified = false;
282 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
283 j != from_nodes.end(); ++j)
285 v3s16 pos = j->first;
286 v3s16 blockpos = getNodeBlockPos(pos);
288 // Only fetch a new block if the block position has changed
290 if(block == NULL || blockpos != blockpos_last){
291 block = getBlockNoCreate(blockpos);
292 blockpos_last = blockpos;
294 block_checked_in_modified = false;
298 catch(InvalidPositionException &e)
306 // Calculate relative position in block
307 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
309 // Get node straight from the block
310 //MapNode n = block->getNode(relpos);
312 u8 oldlight = j->second;
314 // Loop through 6 neighbors
315 for(u16 i=0; i<6; i++)
317 // Get the position of the neighbor node
318 v3s16 n2pos = pos + dirs[i];
320 // Get the block where the node is located
321 v3s16 blockpos, relpos;
322 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
324 // Only fetch a new block if the block position has changed
326 if(block == NULL || blockpos != blockpos_last){
327 block = getBlockNoCreate(blockpos);
328 blockpos_last = blockpos;
330 block_checked_in_modified = false;
334 catch(InvalidPositionException &e) {
338 // Get node straight from the block
339 bool is_valid_position;
340 MapNode n2 = block->getNode(relpos, &is_valid_position);
341 if (!is_valid_position)
344 bool changed = false;
346 //TODO: Optimize output by optimizing light_sources?
349 If the neighbor is dimmer than what was specified
350 as oldlight (the light of the previous node)
352 if(n2.getLight(bank, nodemgr) < oldlight)
355 And the neighbor is transparent and it has some light
357 if(nodemgr->get(n2).light_propagates
358 && n2.getLight(bank, nodemgr) != 0)
361 Set light to 0 and add to queue
364 u8 current_light = n2.getLight(bank, nodemgr);
365 n2.setLight(bank, 0, nodemgr);
366 block->setNode(relpos, n2);
368 unlighted_nodes[n2pos] = current_light;
372 Remove from light_sources if it is there
373 NOTE: This doesn't happen nearly at all
375 /*if(light_sources.find(n2pos))
377 infostream<<"Removed from light_sources"<<std::endl;
378 light_sources.remove(n2pos);
383 if(light_sources.find(n2pos) != NULL)
384 light_sources.remove(n2pos);*/
387 light_sources.insert(n2pos);
390 // Add to modified_blocks
391 if(changed == true && block_checked_in_modified == false)
393 // If the block is not found in modified_blocks, add.
394 if(modified_blocks.find(blockpos) == modified_blocks.end())
396 modified_blocks[blockpos] = block;
398 block_checked_in_modified = true;
403 /*infostream<<"unspreadLight(): Changed block "
404 <<blockchangecount<<" times"
405 <<" for "<<from_nodes.size()<<" nodes"
408 if(!unlighted_nodes.empty())
409 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
413 A single-node wrapper of the above
415 void Map::unLightNeighbors(enum LightBank bank,
416 v3s16 pos, u8 lightwas,
417 std::set<v3s16> & light_sources,
418 std::map<v3s16, MapBlock*> & modified_blocks)
420 std::map<v3s16, u8> from_nodes;
421 from_nodes[pos] = lightwas;
423 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
427 Lights neighbors of from_nodes, collects all them and then
430 void Map::spreadLight(enum LightBank bank,
431 std::set<v3s16> & from_nodes,
432 std::map<v3s16, MapBlock*> & modified_blocks)
434 INodeDefManager *nodemgr = m_gamedef->ndef();
436 const v3s16 dirs[6] = {
437 v3s16(0,0,1), // back
439 v3s16(1,0,0), // right
440 v3s16(0,0,-1), // front
441 v3s16(0,-1,0), // bottom
442 v3s16(-1,0,0), // left
445 if(from_nodes.empty())
448 u32 blockchangecount = 0;
450 std::set<v3s16> lighted_nodes;
453 Initialize block cache
456 MapBlock *block = NULL;
457 // Cache this a bit, too
458 bool block_checked_in_modified = false;
460 for(std::set<v3s16>::iterator j = from_nodes.begin();
461 j != from_nodes.end(); ++j)
464 v3s16 blockpos, relpos;
466 getNodeBlockPosWithOffset(pos, blockpos, relpos);
468 // Only fetch a new block if the block position has changed
470 if(block == NULL || blockpos != blockpos_last){
471 block = getBlockNoCreate(blockpos);
472 blockpos_last = blockpos;
474 block_checked_in_modified = false;
478 catch(InvalidPositionException &e) {
485 // Get node straight from the block
486 bool is_valid_position;
487 MapNode n = block->getNode(relpos, &is_valid_position);
489 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
490 u8 newlight = diminish_light(oldlight);
492 // Loop through 6 neighbors
493 for(u16 i=0; i<6; i++){
494 // Get the position of the neighbor node
495 v3s16 n2pos = pos + dirs[i];
497 // Get the block where the node is located
498 v3s16 blockpos, relpos;
499 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
501 // Only fetch a new block if the block position has changed
503 if(block == NULL || blockpos != blockpos_last){
504 block = getBlockNoCreate(blockpos);
505 blockpos_last = blockpos;
507 block_checked_in_modified = false;
511 catch(InvalidPositionException &e) {
515 // Get node straight from the block
516 MapNode n2 = block->getNode(relpos, &is_valid_position);
517 if (!is_valid_position)
520 bool changed = false;
522 If the neighbor is brighter than the current node,
523 add to list (it will light up this node on its turn)
525 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
527 lighted_nodes.insert(n2pos);
531 If the neighbor is dimmer than how much light this node
532 would spread on it, add to list
534 if(n2.getLight(bank, nodemgr) < newlight)
536 if(nodemgr->get(n2).light_propagates)
538 n2.setLight(bank, newlight, nodemgr);
539 block->setNode(relpos, n2);
540 lighted_nodes.insert(n2pos);
545 // Add to modified_blocks
546 if(changed == true && block_checked_in_modified == false)
548 // If the block is not found in modified_blocks, add.
549 if(modified_blocks.find(blockpos) == modified_blocks.end())
551 modified_blocks[blockpos] = block;
553 block_checked_in_modified = true;
558 /*infostream<<"spreadLight(): Changed block "
559 <<blockchangecount<<" times"
560 <<" for "<<from_nodes.size()<<" nodes"
563 if(!lighted_nodes.empty())
564 spreadLight(bank, lighted_nodes, modified_blocks);
568 A single-node source variation of the above.
570 void Map::lightNeighbors(enum LightBank bank,
572 std::map<v3s16, MapBlock*> & modified_blocks)
574 std::set<v3s16> from_nodes;
575 from_nodes.insert(pos);
576 spreadLight(bank, from_nodes, modified_blocks);
579 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
581 INodeDefManager *nodemgr = m_gamedef->ndef();
584 v3s16(0,0,1), // back
586 v3s16(1,0,0), // right
587 v3s16(0,0,-1), // front
588 v3s16(0,-1,0), // bottom
589 v3s16(-1,0,0), // left
592 u8 brightest_light = 0;
593 v3s16 brightest_pos(0,0,0);
594 bool found_something = false;
596 // Loop through 6 neighbors
597 for(u16 i=0; i<6; i++){
598 // Get the position of the neighbor node
599 v3s16 n2pos = p + dirs[i];
601 bool is_valid_position;
602 n2 = getNodeNoEx(n2pos, &is_valid_position);
603 if (!is_valid_position)
606 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
607 brightest_light = n2.getLight(bank, nodemgr);
608 brightest_pos = n2pos;
609 found_something = true;
613 if(found_something == false)
614 throw InvalidPositionException();
616 return brightest_pos;
620 Propagates sunlight down from a node.
621 Starting point gets sunlight.
623 Returns the lowest y value of where the sunlight went.
625 Mud is turned into grass in where the sunlight stops.
627 s16 Map::propagateSunlight(v3s16 start,
628 std::map<v3s16, MapBlock*> & modified_blocks)
630 INodeDefManager *nodemgr = m_gamedef->ndef();
635 v3s16 pos(start.X, y, start.Z);
637 v3s16 blockpos = getNodeBlockPos(pos);
640 block = getBlockNoCreate(blockpos);
642 catch(InvalidPositionException &e)
647 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
648 bool is_valid_position;
649 MapNode n = block->getNode(relpos, &is_valid_position);
650 if (!is_valid_position)
653 if(nodemgr->get(n).sunlight_propagates)
655 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
656 block->setNode(relpos, n);
658 modified_blocks[blockpos] = block;
662 // Sunlight goes no further
669 void Map::updateLighting(enum LightBank bank,
670 std::map<v3s16, MapBlock*> & a_blocks,
671 std::map<v3s16, MapBlock*> & modified_blocks)
673 INodeDefManager *nodemgr = m_gamedef->ndef();
675 /*m_dout<<DTIME<<"Map::updateLighting(): "
676 <<a_blocks.size()<<" blocks."<<std::endl;*/
678 //TimeTaker timer("updateLighting");
682 //u32 count_was = modified_blocks.size();
684 //std::map<v3s16, MapBlock*> blocks_to_update;
686 std::set<v3s16> light_sources;
688 std::map<v3s16, u8> unlight_from;
690 int num_bottom_invalid = 0;
693 //TimeTaker t("first stuff");
695 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
696 i != a_blocks.end(); ++i)
698 MapBlock *block = i->second;
702 // Don't bother with dummy blocks.
706 v3s16 pos = block->getPos();
707 v3s16 posnodes = block->getPosRelative();
708 modified_blocks[pos] = block;
709 //blocks_to_update[pos] = block;
712 Clear all light from block
714 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
715 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
716 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
719 bool is_valid_position;
720 MapNode n = block->getNode(p, &is_valid_position);
721 if (!is_valid_position) {
722 /* This would happen when dealing with a
725 infostream<<"updateLighting(): InvalidPositionException"
729 u8 oldlight = n.getLight(bank, nodemgr);
730 n.setLight(bank, 0, nodemgr);
731 block->setNode(p, n);
733 // If node sources light, add to list
734 u8 source = nodemgr->get(n).light_source;
736 light_sources.insert(p + posnodes);
738 // Collect borders for unlighting
739 if((x==0 || x == MAP_BLOCKSIZE-1
740 || y==0 || y == MAP_BLOCKSIZE-1
741 || z==0 || z == MAP_BLOCKSIZE-1)
744 v3s16 p_map = p + posnodes;
745 unlight_from[p_map] = oldlight;
751 if(bank == LIGHTBANK_DAY)
753 bool bottom_valid = block->propagateSunlight(light_sources);
756 num_bottom_invalid++;
758 // If bottom is valid, we're done.
762 else if(bank == LIGHTBANK_NIGHT)
764 // For night lighting, sunlight is not propagated
769 assert("Invalid lighting bank" == NULL);
772 /*infostream<<"Bottom for sunlight-propagated block ("
773 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
776 // Bottom sunlight is not valid; get the block and loop to it
780 block = getBlockNoCreate(pos);
782 catch(InvalidPositionException &e)
784 FATAL_ERROR("Invalid position");
793 Enable this to disable proper lighting for speeding up map
794 generation for testing or whatever
797 //if(g_settings->get(""))
799 core::map<v3s16, MapBlock*>::Iterator i;
800 i = blocks_to_update.getIterator();
801 for(; i.atEnd() == false; i++)
803 MapBlock *block = i.getNode()->getValue();
804 v3s16 p = block->getPos();
805 block->setLightingExpired(false);
813 //TimeTaker timer("unspreadLight");
814 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
819 u32 diff = modified_blocks.size() - count_was;
820 count_was = modified_blocks.size();
821 infostream<<"unspreadLight modified "<<diff<<std::endl;
825 //TimeTaker timer("spreadLight");
826 spreadLight(bank, light_sources, modified_blocks);
831 u32 diff = modified_blocks.size() - count_was;
832 count_was = modified_blocks.size();
833 infostream<<"spreadLight modified "<<diff<<std::endl;
839 //MapVoxelManipulator vmanip(this);
841 // Make a manual voxel manipulator and load all the blocks
842 // that touch the requested blocks
843 ManualMapVoxelManipulator vmanip(this);
846 //TimeTaker timer("initialEmerge");
848 core::map<v3s16, MapBlock*>::Iterator i;
849 i = blocks_to_update.getIterator();
850 for(; i.atEnd() == false; i++)
852 MapBlock *block = i.getNode()->getValue();
853 v3s16 p = block->getPos();
855 // Add all surrounding blocks
856 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
859 Add all surrounding blocks that have up-to-date lighting
860 NOTE: This doesn't quite do the job (not everything
861 appropriate is lighted)
863 /*for(s16 z=-1; z<=1; z++)
864 for(s16 y=-1; y<=1; y++)
865 for(s16 x=-1; x<=1; x++)
867 v3s16 p2 = p + v3s16(x,y,z);
868 MapBlock *block = getBlockNoCreateNoEx(p2);
873 if(block->getLightingExpired())
875 vmanip.initialEmerge(p2, p2);
878 // Lighting of block will be updated completely
879 block->setLightingExpired(false);
884 //TimeTaker timer("unSpreadLight");
885 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
888 //TimeTaker timer("spreadLight");
889 vmanip.spreadLight(bank, light_sources, nodemgr);
892 //TimeTaker timer("blitBack");
893 vmanip.blitBack(modified_blocks);
895 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
900 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
903 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
904 std::map<v3s16, MapBlock*> & modified_blocks)
906 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
907 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
910 Update information about whether day and night light differ
912 for(std::map<v3s16, MapBlock*>::iterator
913 i = modified_blocks.begin();
914 i != modified_blocks.end(); ++i)
916 MapBlock *block = i->second;
917 block->expireDayNightDiff();
923 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
924 std::map<v3s16, MapBlock*> &modified_blocks,
925 bool remove_metadata)
927 INodeDefManager *ndef = m_gamedef->ndef();
930 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
931 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
934 From this node to nodes underneath:
935 If lighting is sunlight (1.0), unlight neighbours and
940 v3s16 toppos = p + v3s16(0,1,0);
941 //v3s16 bottompos = p + v3s16(0,-1,0);
943 bool node_under_sunlight = true;
944 std::set<v3s16> light_sources;
947 Collect old node for rollback
949 RollbackNode rollback_oldnode(this, p, m_gamedef);
952 If there is a node at top and it doesn't have sunlight,
953 there has not been any sunlight going down.
955 Otherwise there probably is.
958 bool is_valid_position;
959 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
961 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
962 node_under_sunlight = false;
965 Remove all light that has come out of this node
968 enum LightBank banks[] =
973 for(s32 i=0; i<2; i++)
975 enum LightBank bank = banks[i];
977 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
979 // Add the block of the added node to modified_blocks
980 v3s16 blockpos = getNodeBlockPos(p);
981 MapBlock * block = getBlockNoCreate(blockpos);
982 assert(block != NULL);
983 modified_blocks[blockpos] = block;
985 assert(isValidPosition(p));
987 // Unlight neighbours of node.
988 // This means setting light of all consequent dimmer nodes
990 // This also collects the nodes at the border which will spread
991 // light again into this.
992 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
994 n.setLight(bank, 0, ndef);
998 If node lets sunlight through and is under sunlight, it has
1001 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1003 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1007 Remove node metadata
1009 if (remove_metadata) {
1010 removeNodeMetadata(p);
1014 Set the node on the map
1020 If node is under sunlight and doesn't let sunlight through,
1021 take all sunlighted nodes under it and clear light from them
1022 and from where the light has been spread.
1023 TODO: This could be optimized by mass-unlighting instead
1026 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1030 //m_dout<<DTIME<<"y="<<y<<std::endl;
1031 v3s16 n2pos(p.X, y, p.Z);
1035 n2 = getNodeNoEx(n2pos, &is_valid_position);
1036 if (!is_valid_position)
1039 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1041 unLightNeighbors(LIGHTBANK_DAY,
1042 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1043 light_sources, modified_blocks);
1044 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1052 for(s32 i=0; i<2; i++)
1054 enum LightBank bank = banks[i];
1057 Spread light from all nodes that might be capable of doing so
1059 spreadLight(bank, light_sources, modified_blocks);
1063 Update information about whether day and night light differ
1065 for(std::map<v3s16, MapBlock*>::iterator
1066 i = modified_blocks.begin();
1067 i != modified_blocks.end(); ++i)
1069 i->second->expireDayNightDiff();
1075 if(m_gamedef->rollback())
1077 RollbackNode rollback_newnode(this, p, m_gamedef);
1078 RollbackAction action;
1079 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1080 m_gamedef->rollback()->reportAction(action);
1084 Add neighboring liquid nodes and the node itself if it is
1085 liquid (=water node was added) to transform queue.
1088 v3s16(0,0,0), // self
1089 v3s16(0,0,1), // back
1090 v3s16(0,1,0), // top
1091 v3s16(1,0,0), // right
1092 v3s16(0,0,-1), // front
1093 v3s16(0,-1,0), // bottom
1094 v3s16(-1,0,0), // left
1096 for(u16 i=0; i<7; i++)
1098 v3s16 p2 = p + dirs[i];
1100 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1101 if(is_valid_position
1102 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1104 m_transforming_liquid.push_back(p2);
1111 void Map::removeNodeAndUpdate(v3s16 p,
1112 std::map<v3s16, MapBlock*> &modified_blocks)
1114 INodeDefManager *ndef = m_gamedef->ndef();
1116 /*PrintInfo(m_dout);
1117 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1118 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1120 bool node_under_sunlight = true;
1122 v3s16 toppos = p + v3s16(0,1,0);
1124 // Node will be replaced with this
1125 content_t replace_material = CONTENT_AIR;
1128 Collect old node for rollback
1130 RollbackNode rollback_oldnode(this, p, m_gamedef);
1133 If there is a node at top and it doesn't have sunlight,
1134 there will be no sunlight going down.
1136 bool is_valid_position;
1137 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1139 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1140 node_under_sunlight = false;
1142 std::set<v3s16> light_sources;
1144 enum LightBank banks[] =
1149 for(s32 i=0; i<2; i++)
1151 enum LightBank bank = banks[i];
1154 Unlight neighbors (in case the node is a light source)
1156 unLightNeighbors(bank, p,
1157 getNodeNoEx(p).getLight(bank, ndef),
1158 light_sources, modified_blocks);
1162 Remove node metadata
1165 removeNodeMetadata(p);
1169 This also clears the lighting.
1172 MapNode n(replace_material);
1175 for(s32 i=0; i<2; i++)
1177 enum LightBank bank = banks[i];
1180 Recalculate lighting
1182 spreadLight(bank, light_sources, modified_blocks);
1185 // Add the block of the removed node to modified_blocks
1186 v3s16 blockpos = getNodeBlockPos(p);
1187 MapBlock * block = getBlockNoCreate(blockpos);
1188 assert(block != NULL);
1189 modified_blocks[blockpos] = block;
1192 If the removed node was under sunlight, propagate the
1193 sunlight down from it and then light all neighbors
1194 of the propagated blocks.
1196 if(node_under_sunlight)
1198 s16 ybottom = propagateSunlight(p, modified_blocks);
1199 /*m_dout<<DTIME<<"Node was under sunlight. "
1200 "Propagating sunlight";
1201 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1203 for(; y >= ybottom; y--)
1205 v3s16 p2(p.X, y, p.Z);
1206 /*m_dout<<DTIME<<"lighting neighbors of node ("
1207 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1209 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1214 // Set the lighting of this node to 0
1215 // TODO: Is this needed? Lighting is cleared up there already.
1216 MapNode n = getNodeNoEx(p, &is_valid_position);
1217 if (is_valid_position) {
1218 n.setLight(LIGHTBANK_DAY, 0, ndef);
1221 FATAL_ERROR("Invalid position");
1225 for(s32 i=0; i<2; i++)
1227 enum LightBank bank = banks[i];
1229 // Get the brightest neighbour node and propagate light from it
1230 v3s16 n2p = getBrightestNeighbour(bank, p);
1232 //MapNode n2 = getNode(n2p);
1233 lightNeighbors(bank, n2p, modified_blocks);
1235 catch(InvalidPositionException &e)
1241 Update information about whether day and night light differ
1243 for(std::map<v3s16, MapBlock*>::iterator
1244 i = modified_blocks.begin();
1245 i != modified_blocks.end(); ++i)
1247 i->second->expireDayNightDiff();
1253 if(m_gamedef->rollback())
1255 RollbackNode rollback_newnode(this, p, m_gamedef);
1256 RollbackAction action;
1257 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1258 m_gamedef->rollback()->reportAction(action);
1262 Add neighboring liquid nodes and this node to transform queue.
1263 (it's vital for the node itself to get updated last.)
1266 v3s16(0,0,1), // back
1267 v3s16(0,1,0), // top
1268 v3s16(1,0,0), // right
1269 v3s16(0,0,-1), // front
1270 v3s16(0,-1,0), // bottom
1271 v3s16(-1,0,0), // left
1272 v3s16(0,0,0), // self
1274 for(u16 i=0; i<7; i++)
1276 v3s16 p2 = p + dirs[i];
1278 bool is_position_valid;
1279 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1280 if (is_position_valid
1281 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1283 m_transforming_liquid.push_back(p2);
1288 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1291 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1295 bool succeeded = true;
1297 std::map<v3s16, MapBlock*> modified_blocks;
1298 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1300 // Copy modified_blocks to event
1301 for(std::map<v3s16, MapBlock*>::iterator
1302 i = modified_blocks.begin();
1303 i != modified_blocks.end(); ++i)
1305 event.modified_blocks.insert(i->first);
1308 catch(InvalidPositionException &e){
1312 dispatchEvent(&event);
1317 bool Map::removeNodeWithEvent(v3s16 p)
1320 event.type = MEET_REMOVENODE;
1323 bool succeeded = true;
1325 std::map<v3s16, MapBlock*> modified_blocks;
1326 removeNodeAndUpdate(p, modified_blocks);
1328 // Copy modified_blocks to event
1329 for(std::map<v3s16, MapBlock*>::iterator
1330 i = modified_blocks.begin();
1331 i != modified_blocks.end(); ++i)
1333 event.modified_blocks.insert(i->first);
1336 catch(InvalidPositionException &e){
1340 dispatchEvent(&event);
1345 bool Map::getDayNightDiff(v3s16 blockpos)
1348 v3s16 p = blockpos + v3s16(0,0,0);
1349 MapBlock *b = getBlockNoCreate(p);
1350 if(b->getDayNightDiff())
1353 catch(InvalidPositionException &e){}
1356 v3s16 p = blockpos + v3s16(-1,0,0);
1357 MapBlock *b = getBlockNoCreate(p);
1358 if(b->getDayNightDiff())
1361 catch(InvalidPositionException &e){}
1363 v3s16 p = blockpos + v3s16(0,-1,0);
1364 MapBlock *b = getBlockNoCreate(p);
1365 if(b->getDayNightDiff())
1368 catch(InvalidPositionException &e){}
1370 v3s16 p = blockpos + v3s16(0,0,-1);
1371 MapBlock *b = getBlockNoCreate(p);
1372 if(b->getDayNightDiff())
1375 catch(InvalidPositionException &e){}
1378 v3s16 p = blockpos + v3s16(1,0,0);
1379 MapBlock *b = getBlockNoCreate(p);
1380 if(b->getDayNightDiff())
1383 catch(InvalidPositionException &e){}
1385 v3s16 p = blockpos + v3s16(0,1,0);
1386 MapBlock *b = getBlockNoCreate(p);
1387 if(b->getDayNightDiff())
1390 catch(InvalidPositionException &e){}
1392 v3s16 p = blockpos + v3s16(0,0,1);
1393 MapBlock *b = getBlockNoCreate(p);
1394 if(b->getDayNightDiff())
1397 catch(InvalidPositionException &e){}
1403 Updates usage timers
1405 void Map::timerUpdate(float dtime, float unload_timeout,
1406 std::vector<v3s16> *unloaded_blocks)
1408 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1410 // Profile modified reasons
1411 Profiler modprofiler;
1413 std::vector<v2s16> sector_deletion_queue;
1414 u32 deleted_blocks_count = 0;
1415 u32 saved_blocks_count = 0;
1416 u32 block_count_all = 0;
1419 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1420 si != m_sectors.end(); ++si) {
1421 MapSector *sector = si->second;
1423 bool all_blocks_deleted = true;
1425 MapBlockVect blocks;
1426 sector->getBlocks(blocks);
1428 for(MapBlockVect::iterator i = blocks.begin();
1429 i != blocks.end(); ++i) {
1430 MapBlock *block = (*i);
1432 block->incrementUsageTimer(dtime);
1434 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) {
1435 v3s16 p = block->getPos();
1438 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1439 modprofiler.add(block->getModifiedReasonString(), 1);
1440 if (!saveBlock(block))
1442 saved_blocks_count++;
1445 // Delete from memory
1446 sector->deleteBlock(block);
1449 unloaded_blocks->push_back(p);
1451 deleted_blocks_count++;
1454 all_blocks_deleted = false;
1459 if(all_blocks_deleted) {
1460 sector_deletion_queue.push_back(si->first);
1465 // Finally delete the empty sectors
1466 deleteSectors(sector_deletion_queue);
1468 if(deleted_blocks_count != 0)
1470 PrintInfo(infostream); // ServerMap/ClientMap:
1471 infostream<<"Unloaded "<<deleted_blocks_count
1472 <<" blocks from memory";
1473 if(save_before_unloading)
1474 infostream<<", of which "<<saved_blocks_count<<" were written";
1475 infostream<<", "<<block_count_all<<" blocks in memory";
1476 infostream<<"."<<std::endl;
1477 if(saved_blocks_count != 0){
1478 PrintInfo(infostream); // ServerMap/ClientMap:
1479 infostream<<"Blocks modified by: "<<std::endl;
1480 modprofiler.print(infostream);
1485 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1487 timerUpdate(0.0, -1.0, unloaded_blocks);
1490 void Map::deleteSectors(std::vector<v2s16> §orList)
1492 for(std::vector<v2s16>::iterator j = sectorList.begin();
1493 j != sectorList.end(); ++j) {
1494 MapSector *sector = m_sectors[*j];
1495 // If sector is in sector cache, remove it from there
1496 if(m_sector_cache == sector)
1497 m_sector_cache = NULL;
1498 // Remove from map and delete
1499 m_sectors.erase(*j);
1504 void Map::PrintInfo(std::ostream &out)
1509 #define WATER_DROP_BOOST 4
1513 NEIGHBOR_SAME_LEVEL,
1516 struct NodeNeighbor {
1520 bool l; //can liquid
1526 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1533 void Map::transforming_liquid_add(v3s16 p) {
1534 m_transforming_liquid.push_back(p);
1537 s32 Map::transforming_liquid_size() {
1538 return m_transforming_liquid.size();
1541 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1544 INodeDefManager *nodemgr = m_gamedef->ndef();
1546 DSTACK(__FUNCTION_NAME);
1547 //TimeTaker timer("transformLiquids()");
1550 u32 initial_size = m_transforming_liquid.size();
1552 /*if(initial_size != 0)
1553 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1555 // list of nodes that due to viscosity have not reached their max level height
1556 std::deque<v3s16> must_reflow;
1558 // List of MapBlocks that will require a lighting update (due to lava)
1559 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1561 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1562 u32 loop_max = liquid_loop_max;
1566 /* If liquid_loop_max is not keeping up with the queue size increase
1567 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1569 if (m_transforming_liquid.size() > loop_max * 2) {
1571 float server_step = g_settings->getFloat("dedicated_server_step");
1572 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1573 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1575 m_transforming_liquid_loop_count_multiplier = 1.0;
1578 loop_max *= m_transforming_liquid_loop_count_multiplier;
1581 while(m_transforming_liquid.size() != 0)
1583 // This should be done here so that it is done when continue is used
1584 if(loopcount >= initial_size || loopcount >= loop_max)
1589 Get a queued transforming liquid node
1591 v3s16 p0 = m_transforming_liquid.front();
1592 m_transforming_liquid.pop_front();
1594 MapNode n0 = getNodeNoEx(p0);
1597 Collect information about current node
1599 s8 liquid_level = -1;
1600 content_t liquid_kind = CONTENT_IGNORE;
1601 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1602 switch (liquid_type) {
1604 liquid_level = LIQUID_LEVEL_SOURCE;
1605 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1607 case LIQUID_FLOWING:
1608 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1609 liquid_kind = n0.getContent();
1612 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1613 // continue with the next node.
1614 if (n0.getContent() != CONTENT_AIR)
1616 liquid_kind = CONTENT_AIR;
1621 Collect information about the environment
1623 const v3s16 *dirs = g_6dirs;
1624 NodeNeighbor sources[6]; // surrounding sources
1625 int num_sources = 0;
1626 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1628 NodeNeighbor airs[6]; // surrounding air
1630 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1631 int num_neutrals = 0;
1632 bool flowing_down = false;
1633 for (u16 i = 0; i < 6; i++) {
1634 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1637 nt = NEIGHBOR_UPPER;
1640 nt = NEIGHBOR_LOWER;
1643 v3s16 npos = p0 + dirs[i];
1644 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1645 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1647 if (nb.n.getContent() == CONTENT_AIR) {
1648 airs[num_airs++] = nb;
1649 // if the current node is a water source the neighbor
1650 // should be enqueded for transformation regardless of whether the
1651 // current node changes or not.
1652 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1653 m_transforming_liquid.push_back(npos);
1654 // if the current node happens to be a flowing node, it will start to flow down here.
1655 if (nb.t == NEIGHBOR_LOWER) {
1656 flowing_down = true;
1659 neutrals[num_neutrals++] = nb;
1663 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1664 if (liquid_kind == CONTENT_AIR)
1665 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1666 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1667 neutrals[num_neutrals++] = nb;
1669 // Do not count bottom source, it will screw things up
1671 sources[num_sources++] = nb;
1674 case LIQUID_FLOWING:
1675 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1676 if (liquid_kind == CONTENT_AIR)
1677 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1678 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1679 neutrals[num_neutrals++] = nb;
1681 flows[num_flows++] = nb;
1682 if (nb.t == NEIGHBOR_LOWER)
1683 flowing_down = true;
1690 decide on the type (and possibly level) of the current node
1692 content_t new_node_content;
1693 s8 new_node_level = -1;
1694 s8 max_node_level = -1;
1696 u8 range = nodemgr->get(liquid_kind).liquid_range;
1697 if (range > LIQUID_LEVEL_MAX+1)
1698 range = LIQUID_LEVEL_MAX+1;
1700 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1701 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1702 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1703 // it's perfectly safe to use liquid_kind here to determine the new node content.
1704 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1705 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1706 // liquid_kind is set properly, see above
1707 new_node_content = liquid_kind;
1708 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1709 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1710 new_node_content = CONTENT_AIR;
1712 // no surrounding sources, so get the maximum level that can flow into this node
1713 for (u16 i = 0; i < num_flows; i++) {
1714 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1715 switch (flows[i].t) {
1716 case NEIGHBOR_UPPER:
1717 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1718 max_node_level = LIQUID_LEVEL_MAX;
1719 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1720 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1721 } else if (nb_liquid_level > max_node_level)
1722 max_node_level = nb_liquid_level;
1724 case NEIGHBOR_LOWER:
1726 case NEIGHBOR_SAME_LEVEL:
1727 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1728 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1729 max_node_level = nb_liquid_level - 1;
1735 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1736 if (viscosity > 1 && max_node_level != liquid_level) {
1737 // amount to gain, limited by viscosity
1738 // must be at least 1 in absolute value
1739 s8 level_inc = max_node_level - liquid_level;
1740 if (level_inc < -viscosity || level_inc > viscosity)
1741 new_node_level = liquid_level + level_inc/viscosity;
1742 else if (level_inc < 0)
1743 new_node_level = liquid_level - 1;
1744 else if (level_inc > 0)
1745 new_node_level = liquid_level + 1;
1746 if (new_node_level != max_node_level)
1747 must_reflow.push_back(p0);
1749 new_node_level = max_node_level;
1751 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1752 new_node_content = liquid_kind;
1754 new_node_content = CONTENT_AIR;
1759 check if anything has changed. if not, just continue with the next node.
1761 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1762 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1763 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1769 update the current node
1772 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1773 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1774 // set level to last 3 bits, flowing down bit to 4th bit
1775 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1777 // set the liquid level and flow bit to 0
1778 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1780 n0.setContent(new_node_content);
1782 // Find out whether there is a suspect for this action
1783 std::string suspect;
1784 if(m_gamedef->rollback()) {
1785 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1788 if(m_gamedef->rollback() && !suspect.empty()){
1790 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1791 // Get old node for rollback
1792 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1796 RollbackNode rollback_newnode(this, p0, m_gamedef);
1797 RollbackAction action;
1798 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1799 m_gamedef->rollback()->reportAction(action);
1805 v3s16 blockpos = getNodeBlockPos(p0);
1806 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1808 modified_blocks[blockpos] = block;
1809 // If new or old node emits light, MapBlock requires lighting update
1810 if(nodemgr->get(n0).light_source != 0 ||
1811 nodemgr->get(n00).light_source != 0)
1812 lighting_modified_blocks[block->getPos()] = block;
1816 enqueue neighbors for update if neccessary
1818 switch (nodemgr->get(n0.getContent()).liquid_type) {
1820 case LIQUID_FLOWING:
1821 // make sure source flows into all neighboring nodes
1822 for (u16 i = 0; i < num_flows; i++)
1823 if (flows[i].t != NEIGHBOR_UPPER)
1824 m_transforming_liquid.push_back(flows[i].p);
1825 for (u16 i = 0; i < num_airs; i++)
1826 if (airs[i].t != NEIGHBOR_UPPER)
1827 m_transforming_liquid.push_back(airs[i].p);
1830 // this flow has turned to air; neighboring flows might need to do the same
1831 for (u16 i = 0; i < num_flows; i++)
1832 m_transforming_liquid.push_back(flows[i].p);
1836 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1838 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1839 m_transforming_liquid.push_back(*iter);
1841 updateLighting(lighting_modified_blocks, modified_blocks);
1844 /* ----------------------------------------------------------------------
1845 * Manage the queue so that it does not grow indefinately
1847 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1849 if (time_until_purge == 0)
1850 return; // Feature disabled
1852 time_until_purge *= 1000; // seconds -> milliseconds
1854 u32 curr_time = getTime(PRECISION_MILLI);
1855 u32 prev_unprocessed = m_unprocessed_count;
1856 m_unprocessed_count = m_transforming_liquid.size();
1858 // if unprocessed block count is decreasing or stable
1859 if (m_unprocessed_count <= prev_unprocessed) {
1860 m_queue_size_timer_started = false;
1862 if (!m_queue_size_timer_started)
1863 m_inc_trending_up_start_time = curr_time;
1864 m_queue_size_timer_started = true;
1867 // Account for curr_time overflowing
1868 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1869 m_queue_size_timer_started = false;
1871 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1872 * and the number of unprocessed blocks is still > liquid_loop_max then we
1873 * cannot keep up; dump the oldest blocks from the queue so that the queue
1874 * has liquid_loop_max items in it
1876 if (m_queue_size_timer_started
1877 && curr_time - m_inc_trending_up_start_time > time_until_purge
1878 && m_unprocessed_count > liquid_loop_max) {
1880 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1882 infostream << "transformLiquids(): DUMPING " << dump_qty
1883 << " blocks from the queue" << std::endl;
1886 m_transforming_liquid.pop_front();
1888 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1889 m_unprocessed_count = m_transforming_liquid.size();
1893 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1895 std::vector<v3s16> positions_with_meta;
1897 sortBoxVerticies(p1, p2);
1898 v3s16 bpmin = getNodeBlockPos(p1);
1899 v3s16 bpmax = getNodeBlockPos(p2);
1901 VoxelArea area(p1, p2);
1903 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1904 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1905 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1906 v3s16 blockpos(x, y, z);
1908 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1910 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1911 << PP(blockpos) << std::endl;
1912 block = emergeBlock(blockpos, false);
1915 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1920 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1921 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1922 for (size_t i = 0; i != keys.size(); i++) {
1923 v3s16 p(keys[i] + p_base);
1924 if (!area.contains(p))
1927 positions_with_meta.push_back(p);
1931 return positions_with_meta;
1934 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1936 v3s16 blockpos = getNodeBlockPos(p);
1937 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1938 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1940 infostream<<"Map::getNodeMetadata(): Need to emerge "
1941 <<PP(blockpos)<<std::endl;
1942 block = emergeBlock(blockpos, false);
1945 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1949 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1953 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1955 v3s16 blockpos = getNodeBlockPos(p);
1956 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1957 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1959 infostream<<"Map::setNodeMetadata(): Need to emerge "
1960 <<PP(blockpos)<<std::endl;
1961 block = emergeBlock(blockpos, false);
1964 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1968 block->m_node_metadata.set(p_rel, meta);
1972 void Map::removeNodeMetadata(v3s16 p)
1974 v3s16 blockpos = getNodeBlockPos(p);
1975 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1976 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1979 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1983 block->m_node_metadata.remove(p_rel);
1986 NodeTimer Map::getNodeTimer(v3s16 p)
1988 v3s16 blockpos = getNodeBlockPos(p);
1989 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1990 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1992 infostream<<"Map::getNodeTimer(): Need to emerge "
1993 <<PP(blockpos)<<std::endl;
1994 block = emergeBlock(blockpos, false);
1997 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2001 NodeTimer t = block->m_node_timers.get(p_rel);
2005 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2007 v3s16 blockpos = getNodeBlockPos(p);
2008 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2009 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2011 infostream<<"Map::setNodeTimer(): Need to emerge "
2012 <<PP(blockpos)<<std::endl;
2013 block = emergeBlock(blockpos, false);
2016 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2020 block->m_node_timers.set(p_rel, t);
2023 void Map::removeNodeTimer(v3s16 p)
2025 v3s16 blockpos = getNodeBlockPos(p);
2026 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2027 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2030 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2034 block->m_node_timers.remove(p_rel);
2040 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2041 Map(dout_server, gamedef),
2043 m_map_metadata_changed(true)
2045 verbosestream<<__FUNCTION_NAME<<std::endl;
2048 Try to load map; if not found, create a new one.
2051 // Determine which database backend to use
2052 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2054 bool succeeded = conf.readConfigFile(conf_path.c_str());
2055 if (!succeeded || !conf.exists("backend")) {
2056 // fall back to sqlite3
2057 conf.set("backend", "sqlite3");
2059 std::string backend = conf.get("backend");
2060 dbase = createDatabase(backend, savedir, conf);
2062 if (!conf.updateConfigFile(conf_path.c_str()))
2063 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
2065 m_savedir = savedir;
2066 m_map_saving_enabled = false;
2070 // If directory exists, check contents and load if possible
2071 if(fs::PathExists(m_savedir))
2073 // If directory is empty, it is safe to save into it.
2074 if(fs::GetDirListing(m_savedir).size() == 0)
2076 infostream<<"ServerMap: Empty save directory is valid."
2078 m_map_saving_enabled = true;
2083 // Load map metadata (seed, chunksize)
2086 catch(SettingNotFoundException &e){
2087 infostream<<"ServerMap: Some metadata not found."
2088 <<" Using default settings."<<std::endl;
2090 catch(FileNotGoodException &e){
2091 infostream<<"WARNING: Could not load map metadata"
2092 //<<" Disabling chunk-based generator."
2097 infostream<<"ServerMap: Successfully loaded map "
2098 <<"metadata from "<<savedir
2099 <<", assuming valid save directory."
2100 <<" seed="<< m_emerge->params.seed <<"."
2103 m_map_saving_enabled = true;
2104 // Map loaded, not creating new one
2108 // If directory doesn't exist, it is safe to save to it
2110 m_map_saving_enabled = true;
2113 catch(std::exception &e)
2115 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2116 <<", exception: "<<e.what()<<std::endl;
2117 infostream<<"Please remove the map or fix it."<<std::endl;
2118 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2121 infostream<<"Initializing new map."<<std::endl;
2123 // Create zero sector
2124 emergeSector(v2s16(0,0));
2126 // Initially write whole map
2127 save(MOD_STATE_CLEAN);
2130 ServerMap::~ServerMap()
2132 verbosestream<<__FUNCTION_NAME<<std::endl;
2136 if(m_map_saving_enabled)
2138 // Save only changed parts
2139 save(MOD_STATE_WRITE_AT_UNLOAD);
2140 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2144 infostream<<"ServerMap: Map not saved"<<std::endl;
2147 catch(std::exception &e)
2149 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2150 <<", exception: "<<e.what()<<std::endl;
2154 Close database if it was opened
2162 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2163 for(; i.atEnd() == false; i++)
2165 MapChunk *chunk = i.getNode()->getValue();
2171 u64 ServerMap::getSeed()
2173 return m_emerge->params.seed;
2176 s16 ServerMap::getWaterLevel()
2178 return m_emerge->params.water_level;
2181 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2183 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2184 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2186 s16 chunksize = m_emerge->params.chunksize;
2187 s16 coffset = -chunksize / 2;
2188 v3s16 chunk_offset(coffset, coffset, coffset);
2189 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2190 v3s16 blockpos_min = blockpos_div * chunksize;
2191 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2192 blockpos_min += chunk_offset;
2193 blockpos_max += chunk_offset;
2195 v3s16 extra_borders(1,1,1);
2197 // Do nothing if not inside limits (+-1 because of neighbors)
2198 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2199 blockpos_over_limit(blockpos_max + extra_borders))
2202 data->seed = m_emerge->params.seed;
2203 data->blockpos_min = blockpos_min;
2204 data->blockpos_max = blockpos_max;
2205 data->blockpos_requested = blockpos;
2206 data->nodedef = m_gamedef->ndef();
2209 Create the whole area of this and the neighboring blocks
2212 //TimeTaker timer("initBlockMake() create area");
2214 for(s16 x=blockpos_min.X-extra_borders.X;
2215 x<=blockpos_max.X+extra_borders.X; x++)
2216 for(s16 z=blockpos_min.Z-extra_borders.Z;
2217 z<=blockpos_max.Z+extra_borders.Z; z++)
2219 v2s16 sectorpos(x, z);
2220 // Sector metadata is loaded from disk if not already loaded.
2221 ServerMapSector *sector = createSector(sectorpos);
2222 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
2225 for(s16 y=blockpos_min.Y-extra_borders.Y;
2226 y<=blockpos_max.Y+extra_borders.Y; y++)
2229 //MapBlock *block = createBlock(p);
2230 // 1) get from memory, 2) load from disk
2231 MapBlock *block = emergeBlock(p, false);
2232 // 3) create a blank one
2235 block = createBlock(p);
2238 Block gets sunlight if this is true.
2240 Refer to the map generator heuristics.
2242 bool ug = m_emerge->isBlockUnderground(p);
2243 block->setIsUnderground(ug);
2246 // Lighting will not be valid after make_chunk is called
2247 block->setLightingExpired(true);
2248 // Lighting will be calculated
2249 //block->setLightingExpired(false);
2255 Now we have a big empty area.
2257 Make a ManualMapVoxelManipulator that contains this and the
2261 // The area that contains this block and it's neighbors
2262 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2263 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2265 data->vmanip = new MMVManip(this);
2266 //data->vmanip->setMap(this);
2270 //TimeTaker timer("initBlockMake() initialEmerge");
2271 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2274 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2275 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2276 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2277 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2278 core::map<v3s16, u8>::Node *n;
2279 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2282 u8 flags = n->getValue();
2283 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2289 // Data is ready now.
2293 void ServerMap::finishBlockMake(BlockMakeData *data,
2294 std::map<v3s16, MapBlock*> &changed_blocks)
2296 v3s16 blockpos_min = data->blockpos_min;
2297 v3s16 blockpos_max = data->blockpos_max;
2298 v3s16 blockpos_requested = data->blockpos_requested;
2299 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2300 <<blockpos_requested.Y<<","
2301 <<blockpos_requested.Z<<")"<<std::endl;*/
2303 v3s16 extra_borders(1,1,1);
2305 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2307 /*infostream<<"Resulting vmanip:"<<std::endl;
2308 data->vmanip.print(infostream);*/
2310 // Make sure affected blocks are loaded
2311 for(s16 x=blockpos_min.X-extra_borders.X;
2312 x<=blockpos_max.X+extra_borders.X; x++)
2313 for(s16 z=blockpos_min.Z-extra_borders.Z;
2314 z<=blockpos_max.Z+extra_borders.Z; z++)
2315 for(s16 y=blockpos_min.Y-extra_borders.Y;
2316 y<=blockpos_max.Y+extra_borders.Y; y++)
2319 // Load from disk if not already in memory
2320 emergeBlock(p, false);
2324 Blit generated stuff to map
2325 NOTE: blitBackAll adds nearly everything to changed_blocks
2329 //TimeTaker timer("finishBlockMake() blitBackAll");
2330 data->vmanip->blitBackAll(&changed_blocks);
2333 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2336 Copy transforming liquid information
2338 while(data->transforming_liquid.size() > 0)
2340 m_transforming_liquid.push_back(data->transforming_liquid.front());
2341 data->transforming_liquid.pop_front();
2345 Do stuff in central blocks
2353 TimeTaker t("finishBlockMake lighting update");
2355 core::map<v3s16, MapBlock*> lighting_update_blocks;
2358 for(s16 x=blockpos_min.X-extra_borders.X;
2359 x<=blockpos_max.X+extra_borders.X; x++)
2360 for(s16 z=blockpos_min.Z-extra_borders.Z;
2361 z<=blockpos_max.Z+extra_borders.Z; z++)
2362 for(s16 y=blockpos_min.Y-extra_borders.Y;
2363 y<=blockpos_max.Y+extra_borders.Y; y++)
2366 MapBlock *block = getBlockNoCreateNoEx(p);
2368 lighting_update_blocks.insert(block->getPos(), block);
2371 updateLighting(lighting_update_blocks, changed_blocks);
2375 Set lighting to non-expired state in all of them.
2376 This is cheating, but it is not fast enough if all of them
2377 would actually be updated.
2379 for(s16 x=blockpos_min.X-extra_borders.X;
2380 x<=blockpos_max.X+extra_borders.X; x++)
2381 for(s16 z=blockpos_min.Z-extra_borders.Z;
2382 z<=blockpos_max.Z+extra_borders.Z; z++)
2383 for(s16 y=blockpos_min.Y-extra_borders.Y;
2384 y<=blockpos_max.Y+extra_borders.Y; y++)
2387 MapBlock * block = getBlockNoCreateNoEx(p);
2389 block->setLightingExpired(false);
2393 if(enable_mapgen_debug_info == false)
2394 t.stop(true); // Hide output
2399 Go through changed blocks
2401 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2402 i != changed_blocks.end(); ++i)
2404 MapBlock *block = i->second;
2408 Update day/night difference cache of the MapBlocks
2410 block->expireDayNightDiff();
2412 Set block as modified
2414 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2415 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2419 Set central blocks as generated
2421 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2422 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2423 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2426 MapBlock *block = getBlockNoCreateNoEx(p);
2429 block->setGenerated(true);
2433 Save changed parts of map
2434 NOTE: Will be saved later.
2436 //save(MOD_STATE_WRITE_AT_UNLOAD);
2438 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2439 <<","<<blockpos_requested.Y<<","
2440 <<blockpos_requested.Z<<")"<<std::endl;*/
2444 if(enable_mapgen_debug_info)
2447 Analyze resulting blocks
2449 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2450 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2451 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2452 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2453 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2454 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2456 v3s16 p = v3s16(x,y,z);
2457 MapBlock *block = getBlockNoCreateNoEx(p);
2459 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2460 infostream<<"Generated "<<spos<<": "
2461 <<analyze_block(block)<<std::endl;
2466 getBlockNoCreateNoEx(blockpos_requested);
2469 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2471 DSTACKF("%s: p2d=(%d,%d)",
2476 Check if it exists already in memory
2478 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2483 Try to load it from disk (with blocks)
2485 //if(loadSectorFull(p2d) == true)
2488 Try to load metadata from disk
2491 if(loadSectorMeta(p2d) == true)
2493 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2496 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2497 throw InvalidPositionException("");
2503 Do not create over-limit
2505 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2506 g_settings->getU16("map_generation_limit"));
2507 if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2508 || p2d.X > map_gen_limit / MAP_BLOCKSIZE
2509 || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2510 || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
2511 throw InvalidPositionException("createSector(): pos. over limit");
2514 Generate blank sector
2517 sector = new ServerMapSector(this, p2d, m_gamedef);
2519 // Sector position on map in nodes
2520 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2525 m_sectors[p2d] = sector;
2532 This is a quick-hand function for calling makeBlock().
2534 MapBlock * ServerMap::generateBlock(
2536 std::map<v3s16, MapBlock*> &modified_blocks
2539 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2541 /*infostream<<"generateBlock(): "
2542 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2545 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2547 TimeTaker timer("generateBlock");
2549 //MapBlock *block = original_dummy;
2551 v2s16 p2d(p.X, p.Z);
2552 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2555 Do not generate over-limit
2557 if(blockpos_over_limit(p))
2559 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2560 throw InvalidPositionException("generateBlock(): pos. over limit");
2564 Create block make data
2567 initBlockMake(&data, p);
2573 TimeTaker t("mapgen::make_block()");
2574 mapgen->makeChunk(&data);
2575 //mapgen::make_block(&data);
2577 if(enable_mapgen_debug_info == false)
2578 t.stop(true); // Hide output
2582 Blit data back on map, update lighting, add mobs and whatever this does
2584 finishBlockMake(&data, modified_blocks);
2589 MapBlock *block = getBlockNoCreateNoEx(p);
2597 bool erroneus_content = false;
2598 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2599 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2600 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2603 MapNode n = block->getNode(p);
2604 if(n.getContent() == CONTENT_IGNORE)
2606 infostream<<"CONTENT_IGNORE at "
2607 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2609 erroneus_content = true;
2613 if(erroneus_content)
2622 Generate a completely empty block
2626 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2627 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2629 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2632 n.setContent(CONTENT_AIR);
2633 block->setNode(v3s16(x0,y0,z0), n);
2639 if(enable_mapgen_debug_info == false)
2640 timer.stop(true); // Hide output
2646 MapBlock * ServerMap::createBlock(v3s16 p)
2648 DSTACKF("%s: p=(%d,%d,%d)",
2649 __FUNCTION_NAME, p.X, p.Y, p.Z);
2652 Do not create over-limit
2654 if (blockpos_over_limit(p))
2655 throw InvalidPositionException("createBlock(): pos. over limit");
2657 v2s16 p2d(p.X, p.Z);
2660 This will create or load a sector if not found in memory.
2661 If block exists on disk, it will be loaded.
2663 NOTE: On old save formats, this will be slow, as it generates
2664 lighting on blocks for them.
2666 ServerMapSector *sector;
2668 sector = (ServerMapSector*)createSector(p2d);
2669 assert(sector->getId() == MAPSECTOR_SERVER);
2671 catch(InvalidPositionException &e)
2673 infostream<<"createBlock: createSector() failed"<<std::endl;
2677 NOTE: This should not be done, or at least the exception
2678 should not be passed on as std::exception, because it
2679 won't be catched at all.
2681 /*catch(std::exception &e)
2683 infostream<<"createBlock: createSector() failed: "
2684 <<e.what()<<std::endl;
2689 Try to get a block from the sector
2692 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2695 if(block->isDummy())
2700 block = sector->createBlankBlock(block_y);
2705 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2707 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2709 p.X, p.Y, p.Z, create_blank);
2712 MapBlock *block = getBlockNoCreateNoEx(p);
2713 if(block && block->isDummy() == false)
2718 MapBlock *block = loadBlock(p);
2724 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2725 MapBlock *block = sector->createBlankBlock(p.Y);
2733 std::map<v3s16, MapBlock*> modified_blocks;
2734 MapBlock *block = generateBlock(p, modified_blocks);
2738 event.type = MEET_OTHER;
2741 // Copy modified_blocks to event
2742 for(std::map<v3s16, MapBlock*>::iterator
2743 i = modified_blocks.begin();
2744 i != modified_blocks.end(); ++i)
2746 event.modified_blocks.insert(i->first);
2750 dispatchEvent(&event);
2760 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2762 MapBlock *block = getBlockNoCreateNoEx(p3d);
2764 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2769 void ServerMap::prepareBlock(MapBlock *block) {
2772 // N.B. This requires no synchronization, since data will not be modified unless
2773 // the VoxelManipulator being updated belongs to the same thread.
2774 void ServerMap::updateVManip(v3s16 pos)
2776 Mapgen *mg = m_emerge->getCurrentMapgen();
2780 MMVManip *vm = mg->vm;
2784 if (!vm->m_area.contains(pos))
2787 s32 idx = vm->m_area.index(pos);
2788 vm->m_data[idx] = getNodeNoEx(pos);
2789 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2791 vm->m_is_dirty = true;
2794 s16 ServerMap::findGroundLevel(v2s16 p2d)
2798 Uh, just do something random...
2800 // Find existing map from top to down
2803 v3s16 p(p2d.X, max, p2d.Y);
2804 for(; p.Y>min; p.Y--)
2806 MapNode n = getNodeNoEx(p);
2807 if(n.getContent() != CONTENT_IGNORE)
2812 // If this node is not air, go to plan b
2813 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2815 // Search existing walkable and return it
2816 for(; p.Y>min; p.Y--)
2818 MapNode n = getNodeNoEx(p);
2819 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2828 Determine from map generator noise functions
2831 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2834 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2835 //return (s16)level;
2838 bool ServerMap::loadFromFolders() {
2839 if (!dbase->initialized() &&
2840 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2845 void ServerMap::createDirs(std::string path)
2847 if(fs::CreateAllDirs(path) == false)
2849 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2850 <<"\""<<path<<"\""<<std::endl;
2851 throw BaseException("ServerMap failed to create directory");
2855 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2861 snprintf(cc, 9, "%.4x%.4x",
2862 (unsigned int) pos.X & 0xffff,
2863 (unsigned int) pos.Y & 0xffff);
2865 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2867 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2868 (unsigned int) pos.X & 0xfff,
2869 (unsigned int) pos.Y & 0xfff);
2871 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2878 v2s16 ServerMap::getSectorPos(std::string dirname)
2880 unsigned int x = 0, y = 0;
2882 std::string component;
2883 fs::RemoveLastPathComponent(dirname, &component, 1);
2884 if(component.size() == 8)
2887 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2889 else if(component.size() == 3)
2892 fs::RemoveLastPathComponent(dirname, &component, 2);
2893 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2894 // Sign-extend the 12 bit values up to 16 bits...
2895 if(x & 0x800) x |= 0xF000;
2896 if(y & 0x800) y |= 0xF000;
2903 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2904 v2s16 pos((s16)x, (s16)y);
2908 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2910 v2s16 p2d = getSectorPos(sectordir);
2912 if(blockfile.size() != 4){
2913 throw InvalidFilenameException("Invalid block filename");
2916 int r = sscanf(blockfile.c_str(), "%4x", &y);
2918 throw InvalidFilenameException("Invalid block filename");
2919 return v3s16(p2d.X, y, p2d.Y);
2922 std::string ServerMap::getBlockFilename(v3s16 p)
2925 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2929 void ServerMap::save(ModifiedState save_level)
2931 DSTACK(__FUNCTION_NAME);
2932 if(m_map_saving_enabled == false) {
2933 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2937 if(save_level == MOD_STATE_CLEAN)
2938 infostream<<"ServerMap: Saving whole map, this can take time."
2941 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2945 // Profile modified reasons
2946 Profiler modprofiler;
2948 u32 sector_meta_count = 0;
2949 u32 block_count = 0;
2950 u32 block_count_all = 0; // Number of blocks in memory
2952 // Don't do anything with sqlite unless something is really saved
2953 bool save_started = false;
2955 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2956 i != m_sectors.end(); ++i) {
2957 ServerMapSector *sector = (ServerMapSector*)i->second;
2958 assert(sector->getId() == MAPSECTOR_SERVER);
2960 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2961 saveSectorMeta(sector);
2962 sector_meta_count++;
2965 MapBlockVect blocks;
2966 sector->getBlocks(blocks);
2968 for(MapBlockVect::iterator j = blocks.begin();
2969 j != blocks.end(); ++j) {
2970 MapBlock *block = *j;
2974 if(block->getModified() >= (u32)save_level) {
2978 save_started = true;
2981 modprofiler.add(block->getModifiedReasonString(), 1);
2986 /*infostream<<"ServerMap: Written block ("
2987 <<block->getPos().X<<","
2988 <<block->getPos().Y<<","
2989 <<block->getPos().Z<<")"
2999 Only print if something happened or saved whole map
3001 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3002 || block_count != 0) {
3003 infostream<<"ServerMap: Written: "
3004 <<sector_meta_count<<" sector metadata files, "
3005 <<block_count<<" block files"
3006 <<", "<<block_count_all<<" blocks in memory."
3008 PrintInfo(infostream); // ServerMap/ClientMap:
3009 infostream<<"Blocks modified by: "<<std::endl;
3010 modprofiler.print(infostream);
3014 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
3016 if (loadFromFolders()) {
3017 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
3018 << "all blocks that are stored in flat files." << std::endl;
3020 dbase->listAllLoadableBlocks(dst);
3023 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
3025 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3026 si != m_sectors.end(); ++si)
3028 MapSector *sector = si->second;
3030 MapBlockVect blocks;
3031 sector->getBlocks(blocks);
3033 for(MapBlockVect::iterator i = blocks.begin();
3034 i != blocks.end(); ++i) {
3035 v3s16 p = (*i)->getPos();
3041 void ServerMap::saveMapMeta()
3043 DSTACK(__FUNCTION_NAME);
3045 createDirs(m_savedir);
3047 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3048 std::ostringstream oss(std::ios_base::binary);
3051 m_emerge->params.save(conf);
3052 conf.writeLines(oss);
3054 oss << "[end_of_params]\n";
3056 if(!fs::safeWriteToFile(fullpath, oss.str())) {
3057 errorstream << "ServerMap::saveMapMeta(): "
3058 << "could not write " << fullpath << std::endl;
3059 throw FileNotGoodException("Cannot save chunk metadata");
3062 m_map_metadata_changed = false;
3065 void ServerMap::loadMapMeta()
3067 DSTACK(__FUNCTION_NAME);
3070 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3072 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3074 errorstream << "ServerMap::loadMapMeta(): "
3075 "could not open " << fullpath << std::endl;
3076 throw FileNotGoodException("Cannot open map metadata");
3079 if (!conf.parseConfigLines(is, "[end_of_params]")) {
3080 throw SerializationError("ServerMap::loadMapMeta(): "
3081 "[end_of_params] not found!");
3084 m_emerge->params.load(conf);
3086 verbosestream << "ServerMap::loadMapMeta(): seed="
3087 << m_emerge->params.seed << std::endl;
3090 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3092 DSTACK(__FUNCTION_NAME);
3093 // Format used for writing
3094 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3096 v2s16 pos = sector->getPos();
3097 std::string dir = getSectorDir(pos);
3100 std::string fullpath = dir + DIR_DELIM + "meta";
3101 std::ostringstream ss(std::ios_base::binary);
3103 sector->serialize(ss, version);
3105 if(!fs::safeWriteToFile(fullpath, ss.str()))
3106 throw FileNotGoodException("Cannot write sector metafile");
3108 sector->differs_from_disk = false;
3111 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3113 DSTACK(__FUNCTION_NAME);
3115 v2s16 p2d = getSectorPos(sectordir);
3117 ServerMapSector *sector = NULL;
3119 std::string fullpath = sectordir + DIR_DELIM + "meta";
3120 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3121 if(is.good() == false)
3123 // If the directory exists anyway, it probably is in some old
3124 // format. Just go ahead and create the sector.
3125 if(fs::PathExists(sectordir))
3127 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3128 <<fullpath<<" doesn't exist but directory does."
3129 <<" Continuing with a sector with no metadata."
3131 sector = new ServerMapSector(this, p2d, m_gamedef);
3132 m_sectors[p2d] = sector;
3136 throw FileNotGoodException("Cannot open sector metafile");
3141 sector = ServerMapSector::deSerialize
3142 (is, this, p2d, m_sectors, m_gamedef);
3144 saveSectorMeta(sector);
3147 sector->differs_from_disk = false;
3152 bool ServerMap::loadSectorMeta(v2s16 p2d)
3154 DSTACK(__FUNCTION_NAME);
3156 // The directory layout we're going to load from.
3157 // 1 - original sectors/xxxxzzzz/
3158 // 2 - new sectors2/xxx/zzz/
3159 // If we load from anything but the latest structure, we will
3160 // immediately save to the new one, and remove the old.
3162 std::string sectordir1 = getSectorDir(p2d, 1);
3163 std::string sectordir;
3164 if(fs::PathExists(sectordir1))
3166 sectordir = sectordir1;
3171 sectordir = getSectorDir(p2d, 2);
3175 loadSectorMeta(sectordir, loadlayout != 2);
3177 catch(InvalidFilenameException &e)
3181 catch(FileNotGoodException &e)
3185 catch(std::exception &e)
3194 bool ServerMap::loadSectorFull(v2s16 p2d)
3196 DSTACK(__FUNCTION_NAME);
3198 MapSector *sector = NULL;
3200 // The directory layout we're going to load from.
3201 // 1 - original sectors/xxxxzzzz/
3202 // 2 - new sectors2/xxx/zzz/
3203 // If we load from anything but the latest structure, we will
3204 // immediately save to the new one, and remove the old.
3206 std::string sectordir1 = getSectorDir(p2d, 1);
3207 std::string sectordir;
3208 if(fs::PathExists(sectordir1))
3210 sectordir = sectordir1;
3215 sectordir = getSectorDir(p2d, 2);
3219 sector = loadSectorMeta(sectordir, loadlayout != 2);
3221 catch(InvalidFilenameException &e)
3225 catch(FileNotGoodException &e)
3229 catch(std::exception &e)
3237 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3239 std::vector<fs::DirListNode>::iterator i2;
3240 for(i2=list2.begin(); i2!=list2.end(); i2++)
3246 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3248 catch(InvalidFilenameException &e)
3250 // This catches unknown crap in directory
3256 infostream<<"Sector converted to new layout - deleting "<<
3257 sectordir1<<std::endl;
3258 fs::RecursiveDelete(sectordir1);
3265 Database *ServerMap::createDatabase(const std::string &name, const std::string &savedir, Settings &conf)
3267 if (name == "sqlite3")
3268 return new Database_SQLite3(savedir);
3269 if (name == "dummy")
3270 return new Database_Dummy();
3272 else if (name == "leveldb")
3273 return new Database_LevelDB(savedir);
3276 else if (name == "redis")
3277 return new Database_Redis(conf);
3280 throw BaseException(std::string("Database backend ") + name + " not supported.");
3283 void ServerMap::beginSave()
3288 void ServerMap::endSave()
3293 bool ServerMap::saveBlock(MapBlock *block)
3295 return saveBlock(block, dbase);
3298 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3300 v3s16 p3d = block->getPos();
3302 // Dummy blocks are not written
3303 if (block->isDummy()) {
3304 errorstream << "WARNING: saveBlock: Not writing dummy block "
3305 << PP(p3d) << std::endl;
3309 // Format used for writing
3310 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3313 [0] u8 serialization version
3316 std::ostringstream o(std::ios_base::binary);
3317 o.write((char*) &version, 1);
3318 block->serialize(o, version, true);
3320 std::string data = o.str();
3321 bool ret = db->saveBlock(p3d, data);
3323 // We just wrote it to the disk so clear modified flag
3324 block->resetModified();
3329 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3330 MapSector *sector, bool save_after_load)
3332 DSTACK(__FUNCTION_NAME);
3334 std::string fullpath = sectordir + DIR_DELIM + blockfile;
3337 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3338 if(is.good() == false)
3339 throw FileNotGoodException("Cannot open block file");
3341 v3s16 p3d = getBlockPos(sectordir, blockfile);
3342 v2s16 p2d(p3d.X, p3d.Z);
3344 assert(sector->getPos() == p2d);
3346 u8 version = SER_FMT_VER_INVALID;
3347 is.read((char*)&version, 1);
3350 throw SerializationError("ServerMap::loadBlock(): Failed"
3351 " to read MapBlock version");
3353 /*u32 block_size = MapBlock::serializedLength(version);
3354 SharedBuffer<u8> data(block_size);
3355 is.read((char*)*data, block_size);*/
3357 // This will always return a sector because we're the server
3358 //MapSector *sector = emergeSector(p2d);
3360 MapBlock *block = NULL;
3361 bool created_new = false;
3362 block = sector->getBlockNoCreateNoEx(p3d.Y);
3365 block = sector->createBlankBlockNoInsert(p3d.Y);
3370 block->deSerialize(is, version, true);
3372 // If it's a new block, insert it to the map
3374 sector->insertBlock(block);
3377 Save blocks loaded in old format in new format
3380 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3384 // Should be in database now, so delete the old file
3385 fs::RecursiveDelete(fullpath);
3388 // We just loaded it from the disk, so it's up-to-date.
3389 block->resetModified();
3392 catch(SerializationError &e)
3394 infostream<<"WARNING: Invalid block data on disk "
3395 <<"fullpath="<<fullpath
3396 <<" (SerializationError). "
3397 <<"what()="<<e.what()
3399 // Ignoring. A new one will be generated.
3402 // TODO: Backup file; name is in fullpath.
3406 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3408 DSTACK(__FUNCTION_NAME);
3411 std::istringstream is(*blob, std::ios_base::binary);
3413 u8 version = SER_FMT_VER_INVALID;
3414 is.read((char*)&version, 1);
3417 throw SerializationError("ServerMap::loadBlock(): Failed"
3418 " to read MapBlock version");
3420 /*u32 block_size = MapBlock::serializedLength(version);
3421 SharedBuffer<u8> data(block_size);
3422 is.read((char*)*data, block_size);*/
3424 // This will always return a sector because we're the server
3425 //MapSector *sector = emergeSector(p2d);
3427 MapBlock *block = NULL;
3428 bool created_new = false;
3429 block = sector->getBlockNoCreateNoEx(p3d.Y);
3432 block = sector->createBlankBlockNoInsert(p3d.Y);
3437 block->deSerialize(is, version, true);
3439 // If it's a new block, insert it to the map
3441 sector->insertBlock(block);
3444 Save blocks loaded in old format in new format
3447 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3448 // Only save if asked to; no need to update version
3452 // We just loaded it from, so it's up-to-date.
3453 block->resetModified();
3456 catch(SerializationError &e)
3458 errorstream<<"Invalid block data in database"
3459 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3460 <<" (SerializationError): "<<e.what()<<std::endl;
3462 // TODO: Block should be marked as invalid in memory so that it is
3463 // not touched but the game can run
3465 if(g_settings->getBool("ignore_world_load_errors")){
3466 errorstream<<"Ignoring block load error. Duck and cover! "
3467 <<"(ignore_world_load_errors)"<<std::endl;
3469 throw SerializationError("Invalid block data in database");
3474 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3476 DSTACK(__FUNCTION_NAME);
3478 v2s16 p2d(blockpos.X, blockpos.Z);
3482 ret = dbase->loadBlock(blockpos);
3484 loadBlock(&ret, blockpos, createSector(p2d), false);
3485 return getBlockNoCreateNoEx(blockpos);
3487 // Not found in database, try the files
3489 // The directory layout we're going to load from.
3490 // 1 - original sectors/xxxxzzzz/
3491 // 2 - new sectors2/xxx/zzz/
3492 // If we load from anything but the latest structure, we will
3493 // immediately save to the new one, and remove the old.
3495 std::string sectordir1 = getSectorDir(p2d, 1);
3496 std::string sectordir;
3497 if(fs::PathExists(sectordir1))
3499 sectordir = sectordir1;
3504 sectordir = getSectorDir(p2d, 2);
3508 Make sure sector is loaded
3510 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3514 sector = loadSectorMeta(sectordir, loadlayout != 2);
3516 catch(InvalidFilenameException &e)
3520 catch(FileNotGoodException &e)
3524 catch(std::exception &e)
3531 Make sure file exists
3534 std::string blockfilename = getBlockFilename(blockpos);
3535 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3539 Load block and save it to the database
3541 loadBlock(sectordir, blockfilename, sector, true);
3542 return getBlockNoCreateNoEx(blockpos);
3545 bool ServerMap::deleteBlock(v3s16 blockpos)
3547 if (!dbase->deleteBlock(blockpos))
3550 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3552 v2s16 p2d(blockpos.X, blockpos.Z);
3553 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3556 sector->deleteBlock(block);
3562 void ServerMap::PrintInfo(std::ostream &out)
3567 MMVManip::MMVManip(Map *map):
3570 m_create_area(false),
3575 MMVManip::~MMVManip()
3579 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3580 bool load_if_inexistent)
3582 TimeTaker timer1("initialEmerge", &emerge_time);
3584 // Units of these are MapBlocks
3585 v3s16 p_min = blockpos_min;
3586 v3s16 p_max = blockpos_max;
3588 VoxelArea block_area_nodes
3589 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3591 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3594 infostream<<"initialEmerge: area: ";
3595 block_area_nodes.print(infostream);
3596 infostream<<" ("<<size_MB<<"MB)";
3597 infostream<<std::endl;
3600 addArea(block_area_nodes);
3602 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3603 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3604 for(s32 x=p_min.X; x<=p_max.X; x++)
3609 std::map<v3s16, u8>::iterator n;
3610 n = m_loaded_blocks.find(p);
3611 if(n != m_loaded_blocks.end())
3614 bool block_data_inexistent = false;
3617 TimeTaker timer1("emerge load", &emerge_load_time);
3619 block = m_map->getBlockNoCreate(p);
3620 if(block->isDummy())
3621 block_data_inexistent = true;
3623 block->copyTo(*this);
3625 catch(InvalidPositionException &e)
3627 block_data_inexistent = true;
3630 if(block_data_inexistent)
3633 if (load_if_inexistent) {
3634 ServerMap *svrmap = (ServerMap *)m_map;
3635 block = svrmap->emergeBlock(p, false);
3637 block = svrmap->createBlock(p);
3638 block->copyTo(*this);
3640 flags |= VMANIP_BLOCK_DATA_INEXIST;
3643 Mark area inexistent
3645 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3646 // Fill with VOXELFLAG_NO_DATA
3647 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3648 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3650 s32 i = m_area.index(a.MinEdge.X,y,z);
3651 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3655 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3657 // Mark that block was loaded as blank
3658 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3661 m_loaded_blocks[p] = flags;
3667 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3668 bool overwrite_generated)
3670 if(m_area.getExtent() == v3s16(0,0,0))
3674 Copy data of all blocks
3676 for(std::map<v3s16, u8>::iterator
3677 i = m_loaded_blocks.begin();
3678 i != m_loaded_blocks.end(); ++i)
3681 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3682 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3683 if ((existed == false) || (block == NULL) ||
3684 (overwrite_generated == false && block->isGenerated() == true))
3687 block->copyFrom(*this);
3690 (*modified_blocks)[p] = block;