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"
48 #include "database-leveldb.h"
51 #include "database-redis.h"
54 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
61 Map::Map(std::ostream &dout, IGameDef *gamedef):
65 m_transforming_liquid_loop_count_multiplier(1.0f),
66 m_unprocessed_count(0),
67 m_inc_trending_up_start_time(0),
68 m_queue_size_timer_started(false)
77 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
78 i != m_sectors.end(); ++i)
84 void Map::addEventReceiver(MapEventReceiver *event_receiver)
86 m_event_receivers.insert(event_receiver);
89 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
91 m_event_receivers.erase(event_receiver);
94 void Map::dispatchEvent(MapEditEvent *event)
96 for(std::set<MapEventReceiver*>::iterator
97 i = m_event_receivers.begin();
98 i != m_event_receivers.end(); ++i)
100 (*i)->onMapEditEvent(event);
104 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
106 if(m_sector_cache != NULL && p == m_sector_cache_p){
107 MapSector * sector = m_sector_cache;
111 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
113 if(n == m_sectors.end())
116 MapSector *sector = n->second;
118 // Cache the last result
119 m_sector_cache_p = p;
120 m_sector_cache = sector;
125 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
127 return getSectorNoGenerateNoExNoLock(p);
130 MapSector * Map::getSectorNoGenerate(v2s16 p)
132 MapSector *sector = getSectorNoGenerateNoEx(p);
134 throw InvalidPositionException();
139 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
141 v2s16 p2d(p3d.X, p3d.Z);
142 MapSector * sector = getSectorNoGenerateNoEx(p2d);
145 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
149 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
151 MapBlock *block = getBlockNoCreateNoEx(p3d);
153 throw InvalidPositionException();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
170 bool Map::isValidPosition(v3s16 p)
172 v3s16 blockpos = getNodeBlockPos(p);
173 MapBlock *block = getBlockNoCreate(blockpos);
174 return (block != NULL);
177 // Returns a CONTENT_IGNORE node if not found
178 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreateNoEx(blockpos);
183 if (is_valid_position != NULL)
184 *is_valid_position = false;
185 return MapNode(CONTENT_IGNORE);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
190 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
191 if (is_valid_position != NULL)
192 *is_valid_position = is_valid_p;
198 // throws InvalidPositionException if not found
199 // TODO: Now this is deprecated, getNodeNoEx should be renamed
200 MapNode Map::getNode(v3s16 p)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreateNoEx(blockpos);
205 throw InvalidPositionException();
206 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
207 bool is_valid_position;
208 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
209 if (!is_valid_position)
210 throw InvalidPositionException();
215 // throws InvalidPositionException if not found
216 void Map::setNode(v3s16 p, MapNode & n)
218 v3s16 blockpos = getNodeBlockPos(p);
219 MapBlock *block = getBlockNoCreate(blockpos);
220 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
221 // Never allow placing CONTENT_IGNORE, it fucks up stuff
222 if(n.getContent() == CONTENT_IGNORE){
224 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
225 <<" while trying to replace \""
226 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
227 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
228 debug_stacks_print_to(infostream);
231 block->setNodeNoCheck(relpos, n);
236 Goes recursively through the neighbours of the node.
238 Alters only transparent nodes.
240 If the lighting of the neighbour is lower than the lighting of
241 the node was (before changing it to 0 at the step before), the
242 lighting of the neighbour is set to 0 and then the same stuff
243 repeats for the neighbour.
245 The ending nodes of the routine are stored in light_sources.
246 This is useful when a light is removed. In such case, this
247 routine can be called for the light node and then again for
248 light_sources to re-light the area without the removed light.
250 values of from_nodes are lighting values.
252 void Map::unspreadLight(enum LightBank bank,
253 std::map<v3s16, u8> & from_nodes,
254 std::set<v3s16> & light_sources,
255 std::map<v3s16, MapBlock*> & modified_blocks)
257 INodeDefManager *nodemgr = m_gamedef->ndef();
260 v3s16(0,0,1), // back
262 v3s16(1,0,0), // right
263 v3s16(0,0,-1), // front
264 v3s16(0,-1,0), // bottom
265 v3s16(-1,0,0), // left
268 if(from_nodes.empty())
271 u32 blockchangecount = 0;
273 std::map<v3s16, u8> unlighted_nodes;
276 Initialize block cache
279 MapBlock *block = NULL;
280 // Cache this a bit, too
281 bool block_checked_in_modified = false;
283 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
284 j != from_nodes.end(); ++j)
286 v3s16 pos = j->first;
287 v3s16 blockpos = getNodeBlockPos(pos);
289 // Only fetch a new block if the block position has changed
291 if(block == NULL || blockpos != blockpos_last){
292 block = getBlockNoCreate(blockpos);
293 blockpos_last = blockpos;
295 block_checked_in_modified = false;
299 catch(InvalidPositionException &e)
307 // Calculate relative position in block
308 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
310 // Get node straight from the block
311 //MapNode n = block->getNode(relpos);
313 u8 oldlight = j->second;
315 // Loop through 6 neighbors
316 for(u16 i=0; i<6; i++)
318 // Get the position of the neighbor node
319 v3s16 n2pos = pos + dirs[i];
321 // Get the block where the node is located
322 v3s16 blockpos, relpos;
323 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
325 // Only fetch a new block if the block position has changed
327 if(block == NULL || blockpos != blockpos_last){
328 block = getBlockNoCreate(blockpos);
329 blockpos_last = blockpos;
331 block_checked_in_modified = false;
335 catch(InvalidPositionException &e) {
339 // Get node straight from the block
340 bool is_valid_position;
341 MapNode n2 = block->getNode(relpos, &is_valid_position);
342 if (!is_valid_position)
345 bool changed = false;
347 //TODO: Optimize output by optimizing light_sources?
350 If the neighbor is dimmer than what was specified
351 as oldlight (the light of the previous node)
353 if(n2.getLight(bank, nodemgr) < oldlight)
356 And the neighbor is transparent and it has some light
358 if(nodemgr->get(n2).light_propagates
359 && n2.getLight(bank, nodemgr) != 0)
362 Set light to 0 and add to queue
365 u8 current_light = n2.getLight(bank, nodemgr);
366 n2.setLight(bank, 0, nodemgr);
367 block->setNode(relpos, n2);
369 unlighted_nodes[n2pos] = current_light;
373 Remove from light_sources if it is there
374 NOTE: This doesn't happen nearly at all
376 /*if(light_sources.find(n2pos))
378 infostream<<"Removed from light_sources"<<std::endl;
379 light_sources.remove(n2pos);
384 if(light_sources.find(n2pos) != NULL)
385 light_sources.remove(n2pos);*/
388 light_sources.insert(n2pos);
391 // Add to modified_blocks
392 if(changed == true && block_checked_in_modified == false)
394 // If the block is not found in modified_blocks, add.
395 if(modified_blocks.find(blockpos) == modified_blocks.end())
397 modified_blocks[blockpos] = block;
399 block_checked_in_modified = true;
404 /*infostream<<"unspreadLight(): Changed block "
405 <<blockchangecount<<" times"
406 <<" for "<<from_nodes.size()<<" nodes"
409 if(!unlighted_nodes.empty())
410 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
414 A single-node wrapper of the above
416 void Map::unLightNeighbors(enum LightBank bank,
417 v3s16 pos, u8 lightwas,
418 std::set<v3s16> & light_sources,
419 std::map<v3s16, MapBlock*> & modified_blocks)
421 std::map<v3s16, u8> from_nodes;
422 from_nodes[pos] = lightwas;
424 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
428 Lights neighbors of from_nodes, collects all them and then
431 void Map::spreadLight(enum LightBank bank,
432 std::set<v3s16> & from_nodes,
433 std::map<v3s16, MapBlock*> & modified_blocks)
435 INodeDefManager *nodemgr = m_gamedef->ndef();
437 const v3s16 dirs[6] = {
438 v3s16(0,0,1), // back
440 v3s16(1,0,0), // right
441 v3s16(0,0,-1), // front
442 v3s16(0,-1,0), // bottom
443 v3s16(-1,0,0), // left
446 if(from_nodes.empty())
449 u32 blockchangecount = 0;
451 std::set<v3s16> lighted_nodes;
454 Initialize block cache
457 MapBlock *block = NULL;
458 // Cache this a bit, too
459 bool block_checked_in_modified = false;
461 for(std::set<v3s16>::iterator j = from_nodes.begin();
462 j != from_nodes.end(); ++j)
465 v3s16 blockpos, relpos;
467 getNodeBlockPosWithOffset(pos, blockpos, relpos);
469 // Only fetch a new block if the block position has changed
471 if(block == NULL || blockpos != blockpos_last){
472 block = getBlockNoCreate(blockpos);
473 blockpos_last = blockpos;
475 block_checked_in_modified = false;
479 catch(InvalidPositionException &e) {
486 // Get node straight from the block
487 bool is_valid_position;
488 MapNode n = block->getNode(relpos, &is_valid_position);
490 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
491 u8 newlight = diminish_light(oldlight);
493 // Loop through 6 neighbors
494 for(u16 i=0; i<6; i++){
495 // Get the position of the neighbor node
496 v3s16 n2pos = pos + dirs[i];
498 // Get the block where the node is located
499 v3s16 blockpos, relpos;
500 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
502 // Only fetch a new block if the block position has changed
504 if(block == NULL || blockpos != blockpos_last){
505 block = getBlockNoCreate(blockpos);
506 blockpos_last = blockpos;
508 block_checked_in_modified = false;
512 catch(InvalidPositionException &e) {
516 // Get node straight from the block
517 MapNode n2 = block->getNode(relpos, &is_valid_position);
518 if (!is_valid_position)
521 bool changed = false;
523 If the neighbor is brighter than the current node,
524 add to list (it will light up this node on its turn)
526 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
528 lighted_nodes.insert(n2pos);
532 If the neighbor is dimmer than how much light this node
533 would spread on it, add to list
535 if(n2.getLight(bank, nodemgr) < newlight)
537 if(nodemgr->get(n2).light_propagates)
539 n2.setLight(bank, newlight, nodemgr);
540 block->setNode(relpos, n2);
541 lighted_nodes.insert(n2pos);
546 // Add to modified_blocks
547 if(changed == true && block_checked_in_modified == false)
549 // If the block is not found in modified_blocks, add.
550 if(modified_blocks.find(blockpos) == modified_blocks.end())
552 modified_blocks[blockpos] = block;
554 block_checked_in_modified = true;
559 /*infostream<<"spreadLight(): Changed block "
560 <<blockchangecount<<" times"
561 <<" for "<<from_nodes.size()<<" nodes"
564 if(!lighted_nodes.empty())
565 spreadLight(bank, lighted_nodes, modified_blocks);
569 A single-node source variation of the above.
571 void Map::lightNeighbors(enum LightBank bank,
573 std::map<v3s16, MapBlock*> & modified_blocks)
575 std::set<v3s16> from_nodes;
576 from_nodes.insert(pos);
577 spreadLight(bank, from_nodes, modified_blocks);
580 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
582 INodeDefManager *nodemgr = m_gamedef->ndef();
585 v3s16(0,0,1), // back
587 v3s16(1,0,0), // right
588 v3s16(0,0,-1), // front
589 v3s16(0,-1,0), // bottom
590 v3s16(-1,0,0), // left
593 u8 brightest_light = 0;
594 v3s16 brightest_pos(0,0,0);
595 bool found_something = false;
597 // Loop through 6 neighbors
598 for(u16 i=0; i<6; i++){
599 // Get the position of the neighbor node
600 v3s16 n2pos = p + dirs[i];
602 bool is_valid_position;
603 n2 = getNodeNoEx(n2pos, &is_valid_position);
604 if (!is_valid_position)
607 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
608 brightest_light = n2.getLight(bank, nodemgr);
609 brightest_pos = n2pos;
610 found_something = true;
614 if(found_something == false)
615 throw InvalidPositionException();
617 return brightest_pos;
621 Propagates sunlight down from a node.
622 Starting point gets sunlight.
624 Returns the lowest y value of where the sunlight went.
626 Mud is turned into grass in where the sunlight stops.
628 s16 Map::propagateSunlight(v3s16 start,
629 std::map<v3s16, MapBlock*> & modified_blocks)
631 INodeDefManager *nodemgr = m_gamedef->ndef();
636 v3s16 pos(start.X, y, start.Z);
638 v3s16 blockpos = getNodeBlockPos(pos);
641 block = getBlockNoCreate(blockpos);
643 catch(InvalidPositionException &e)
648 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
649 bool is_valid_position;
650 MapNode n = block->getNode(relpos, &is_valid_position);
651 if (!is_valid_position)
654 if(nodemgr->get(n).sunlight_propagates)
656 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
657 block->setNode(relpos, n);
659 modified_blocks[blockpos] = block;
663 // Sunlight goes no further
670 void Map::updateLighting(enum LightBank bank,
671 std::map<v3s16, MapBlock*> & a_blocks,
672 std::map<v3s16, MapBlock*> & modified_blocks)
674 INodeDefManager *nodemgr = m_gamedef->ndef();
676 /*m_dout<<DTIME<<"Map::updateLighting(): "
677 <<a_blocks.size()<<" blocks."<<std::endl;*/
679 //TimeTaker timer("updateLighting");
683 //u32 count_was = modified_blocks.size();
685 //std::map<v3s16, MapBlock*> blocks_to_update;
687 std::set<v3s16> light_sources;
689 std::map<v3s16, u8> unlight_from;
691 int num_bottom_invalid = 0;
694 //TimeTaker t("first stuff");
696 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
697 i != a_blocks.end(); ++i)
699 MapBlock *block = i->second;
703 // Don't bother with dummy blocks.
707 v3s16 pos = block->getPos();
708 v3s16 posnodes = block->getPosRelative();
709 modified_blocks[pos] = block;
710 //blocks_to_update[pos] = block;
713 Clear all light from block
715 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
716 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
717 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
720 bool is_valid_position;
721 MapNode n = block->getNode(p, &is_valid_position);
722 if (!is_valid_position) {
723 /* This would happen when dealing with a
726 infostream<<"updateLighting(): InvalidPositionException"
730 u8 oldlight = n.getLight(bank, nodemgr);
731 n.setLight(bank, 0, nodemgr);
732 block->setNode(p, n);
734 // If node sources light, add to list
735 u8 source = nodemgr->get(n).light_source;
737 light_sources.insert(p + posnodes);
739 // Collect borders for unlighting
740 if((x==0 || x == MAP_BLOCKSIZE-1
741 || y==0 || y == MAP_BLOCKSIZE-1
742 || z==0 || z == MAP_BLOCKSIZE-1)
745 v3s16 p_map = p + posnodes;
746 unlight_from[p_map] = oldlight;
752 if(bank == LIGHTBANK_DAY)
754 bool bottom_valid = block->propagateSunlight(light_sources);
757 num_bottom_invalid++;
759 // If bottom is valid, we're done.
763 else if(bank == LIGHTBANK_NIGHT)
765 // For night lighting, sunlight is not propagated
770 // Invalid lighting bank
774 /*infostream<<"Bottom for sunlight-propagated block ("
775 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
778 // Bottom sunlight is not valid; get the block and loop to it
782 block = getBlockNoCreate(pos);
784 catch(InvalidPositionException &e)
795 Enable this to disable proper lighting for speeding up map
796 generation for testing or whatever
799 //if(g_settings->get(""))
801 core::map<v3s16, MapBlock*>::Iterator i;
802 i = blocks_to_update.getIterator();
803 for(; i.atEnd() == false; i++)
805 MapBlock *block = i.getNode()->getValue();
806 v3s16 p = block->getPos();
807 block->setLightingExpired(false);
815 //TimeTaker timer("unspreadLight");
816 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
821 u32 diff = modified_blocks.size() - count_was;
822 count_was = modified_blocks.size();
823 infostream<<"unspreadLight modified "<<diff<<std::endl;
827 //TimeTaker timer("spreadLight");
828 spreadLight(bank, light_sources, modified_blocks);
833 u32 diff = modified_blocks.size() - count_was;
834 count_was = modified_blocks.size();
835 infostream<<"spreadLight modified "<<diff<<std::endl;
841 //MapVoxelManipulator vmanip(this);
843 // Make a manual voxel manipulator and load all the blocks
844 // that touch the requested blocks
845 ManualMapVoxelManipulator vmanip(this);
848 //TimeTaker timer("initialEmerge");
850 core::map<v3s16, MapBlock*>::Iterator i;
851 i = blocks_to_update.getIterator();
852 for(; i.atEnd() == false; i++)
854 MapBlock *block = i.getNode()->getValue();
855 v3s16 p = block->getPos();
857 // Add all surrounding blocks
858 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
861 Add all surrounding blocks that have up-to-date lighting
862 NOTE: This doesn't quite do the job (not everything
863 appropriate is lighted)
865 /*for(s16 z=-1; z<=1; z++)
866 for(s16 y=-1; y<=1; y++)
867 for(s16 x=-1; x<=1; x++)
869 v3s16 p2 = p + v3s16(x,y,z);
870 MapBlock *block = getBlockNoCreateNoEx(p2);
875 if(block->getLightingExpired())
877 vmanip.initialEmerge(p2, p2);
880 // Lighting of block will be updated completely
881 block->setLightingExpired(false);
886 //TimeTaker timer("unSpreadLight");
887 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
890 //TimeTaker timer("spreadLight");
891 vmanip.spreadLight(bank, light_sources, nodemgr);
894 //TimeTaker timer("blitBack");
895 vmanip.blitBack(modified_blocks);
897 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
902 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
905 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
906 std::map<v3s16, MapBlock*> & modified_blocks)
908 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
909 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
912 Update information about whether day and night light differ
914 for(std::map<v3s16, MapBlock*>::iterator
915 i = modified_blocks.begin();
916 i != modified_blocks.end(); ++i)
918 MapBlock *block = i->second;
919 block->expireDayNightDiff();
925 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
926 std::map<v3s16, MapBlock*> &modified_blocks,
927 bool remove_metadata)
929 INodeDefManager *ndef = m_gamedef->ndef();
932 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
933 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
936 From this node to nodes underneath:
937 If lighting is sunlight (1.0), unlight neighbours and
942 v3s16 toppos = p + v3s16(0,1,0);
943 //v3s16 bottompos = p + v3s16(0,-1,0);
945 bool node_under_sunlight = true;
946 std::set<v3s16> light_sources;
949 Collect old node for rollback
951 RollbackNode rollback_oldnode(this, p, m_gamedef);
954 If there is a node at top and it doesn't have sunlight,
955 there has not been any sunlight going down.
957 Otherwise there probably is.
960 bool is_valid_position;
961 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
963 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
964 node_under_sunlight = false;
967 Remove all light that has come out of this node
970 enum LightBank banks[] =
975 for(s32 i=0; i<2; i++)
977 enum LightBank bank = banks[i];
979 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
981 // Add the block of the added node to modified_blocks
982 v3s16 blockpos = getNodeBlockPos(p);
983 MapBlock * block = getBlockNoCreate(blockpos);
984 assert(block != NULL);
985 modified_blocks[blockpos] = block;
987 assert(isValidPosition(p));
989 // Unlight neighbours of node.
990 // This means setting light of all consequent dimmer nodes
992 // This also collects the nodes at the border which will spread
993 // light again into this.
994 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
996 n.setLight(bank, 0, ndef);
1000 If node lets sunlight through and is under sunlight, it has
1003 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1005 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1009 Remove node metadata
1011 if (remove_metadata) {
1012 removeNodeMetadata(p);
1016 Set the node on the map
1022 If node is under sunlight and doesn't let sunlight through,
1023 take all sunlighted nodes under it and clear light from them
1024 and from where the light has been spread.
1025 TODO: This could be optimized by mass-unlighting instead
1028 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1032 //m_dout<<DTIME<<"y="<<y<<std::endl;
1033 v3s16 n2pos(p.X, y, p.Z);
1037 n2 = getNodeNoEx(n2pos, &is_valid_position);
1038 if (!is_valid_position)
1041 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1043 unLightNeighbors(LIGHTBANK_DAY,
1044 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1045 light_sources, modified_blocks);
1046 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1054 for(s32 i=0; i<2; i++)
1056 enum LightBank bank = banks[i];
1059 Spread light from all nodes that might be capable of doing so
1061 spreadLight(bank, light_sources, modified_blocks);
1065 Update information about whether day and night light differ
1067 for(std::map<v3s16, MapBlock*>::iterator
1068 i = modified_blocks.begin();
1069 i != modified_blocks.end(); ++i)
1071 i->second->expireDayNightDiff();
1077 if(m_gamedef->rollback())
1079 RollbackNode rollback_newnode(this, p, m_gamedef);
1080 RollbackAction action;
1081 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1082 m_gamedef->rollback()->reportAction(action);
1086 Add neighboring liquid nodes and the node itself if it is
1087 liquid (=water node was added) to transform queue.
1090 v3s16(0,0,0), // self
1091 v3s16(0,0,1), // back
1092 v3s16(0,1,0), // top
1093 v3s16(1,0,0), // right
1094 v3s16(0,0,-1), // front
1095 v3s16(0,-1,0), // bottom
1096 v3s16(-1,0,0), // left
1098 for(u16 i=0; i<7; i++)
1100 v3s16 p2 = p + dirs[i];
1102 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1103 if(is_valid_position
1104 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1106 m_transforming_liquid.push_back(p2);
1113 void Map::removeNodeAndUpdate(v3s16 p,
1114 std::map<v3s16, MapBlock*> &modified_blocks)
1116 INodeDefManager *ndef = m_gamedef->ndef();
1118 /*PrintInfo(m_dout);
1119 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1120 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1122 bool node_under_sunlight = true;
1124 v3s16 toppos = p + v3s16(0,1,0);
1126 // Node will be replaced with this
1127 content_t replace_material = CONTENT_AIR;
1130 Collect old node for rollback
1132 RollbackNode rollback_oldnode(this, p, m_gamedef);
1135 If there is a node at top and it doesn't have sunlight,
1136 there will be no sunlight going down.
1138 bool is_valid_position;
1139 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1141 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1142 node_under_sunlight = false;
1144 std::set<v3s16> light_sources;
1146 enum LightBank banks[] =
1151 for(s32 i=0; i<2; i++)
1153 enum LightBank bank = banks[i];
1156 Unlight neighbors (in case the node is a light source)
1158 unLightNeighbors(bank, p,
1159 getNodeNoEx(p).getLight(bank, ndef),
1160 light_sources, modified_blocks);
1164 Remove node metadata
1167 removeNodeMetadata(p);
1171 This also clears the lighting.
1174 MapNode n(replace_material);
1177 for(s32 i=0; i<2; i++)
1179 enum LightBank bank = banks[i];
1182 Recalculate lighting
1184 spreadLight(bank, light_sources, modified_blocks);
1187 // Add the block of the removed node to modified_blocks
1188 v3s16 blockpos = getNodeBlockPos(p);
1189 MapBlock * block = getBlockNoCreate(blockpos);
1190 assert(block != NULL);
1191 modified_blocks[blockpos] = block;
1194 If the removed node was under sunlight, propagate the
1195 sunlight down from it and then light all neighbors
1196 of the propagated blocks.
1198 if(node_under_sunlight)
1200 s16 ybottom = propagateSunlight(p, modified_blocks);
1201 /*m_dout<<DTIME<<"Node was under sunlight. "
1202 "Propagating sunlight";
1203 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1205 for(; y >= ybottom; y--)
1207 v3s16 p2(p.X, y, p.Z);
1208 /*m_dout<<DTIME<<"lighting neighbors of node ("
1209 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1211 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1216 // Set the lighting of this node to 0
1217 // TODO: Is this needed? Lighting is cleared up there already.
1218 MapNode n = getNodeNoEx(p, &is_valid_position);
1219 if (is_valid_position) {
1220 n.setLight(LIGHTBANK_DAY, 0, ndef);
1227 for(s32 i=0; i<2; i++)
1229 enum LightBank bank = banks[i];
1231 // Get the brightest neighbour node and propagate light from it
1232 v3s16 n2p = getBrightestNeighbour(bank, p);
1234 //MapNode n2 = getNode(n2p);
1235 lightNeighbors(bank, n2p, modified_blocks);
1237 catch(InvalidPositionException &e)
1243 Update information about whether day and night light differ
1245 for(std::map<v3s16, MapBlock*>::iterator
1246 i = modified_blocks.begin();
1247 i != modified_blocks.end(); ++i)
1249 i->second->expireDayNightDiff();
1255 if(m_gamedef->rollback())
1257 RollbackNode rollback_newnode(this, p, m_gamedef);
1258 RollbackAction action;
1259 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1260 m_gamedef->rollback()->reportAction(action);
1264 Add neighboring liquid nodes and this node to transform queue.
1265 (it's vital for the node itself to get updated last.)
1268 v3s16(0,0,1), // back
1269 v3s16(0,1,0), // top
1270 v3s16(1,0,0), // right
1271 v3s16(0,0,-1), // front
1272 v3s16(0,-1,0), // bottom
1273 v3s16(-1,0,0), // left
1274 v3s16(0,0,0), // self
1276 for(u16 i=0; i<7; i++)
1278 v3s16 p2 = p + dirs[i];
1280 bool is_position_valid;
1281 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1282 if (is_position_valid
1283 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1285 m_transforming_liquid.push_back(p2);
1290 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1293 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1297 bool succeeded = true;
1299 std::map<v3s16, MapBlock*> modified_blocks;
1300 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1302 // Copy modified_blocks to event
1303 for(std::map<v3s16, MapBlock*>::iterator
1304 i = modified_blocks.begin();
1305 i != modified_blocks.end(); ++i)
1307 event.modified_blocks.insert(i->first);
1310 catch(InvalidPositionException &e){
1314 dispatchEvent(&event);
1319 bool Map::removeNodeWithEvent(v3s16 p)
1322 event.type = MEET_REMOVENODE;
1325 bool succeeded = true;
1327 std::map<v3s16, MapBlock*> modified_blocks;
1328 removeNodeAndUpdate(p, modified_blocks);
1330 // Copy modified_blocks to event
1331 for(std::map<v3s16, MapBlock*>::iterator
1332 i = modified_blocks.begin();
1333 i != modified_blocks.end(); ++i)
1335 event.modified_blocks.insert(i->first);
1338 catch(InvalidPositionException &e){
1342 dispatchEvent(&event);
1347 bool Map::getDayNightDiff(v3s16 blockpos)
1350 v3s16 p = blockpos + v3s16(0,0,0);
1351 MapBlock *b = getBlockNoCreate(p);
1352 if(b->getDayNightDiff())
1355 catch(InvalidPositionException &e){}
1358 v3s16 p = blockpos + v3s16(-1,0,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->getDayNightDiff())
1363 catch(InvalidPositionException &e){}
1365 v3s16 p = blockpos + v3s16(0,-1,0);
1366 MapBlock *b = getBlockNoCreate(p);
1367 if(b->getDayNightDiff())
1370 catch(InvalidPositionException &e){}
1372 v3s16 p = blockpos + v3s16(0,0,-1);
1373 MapBlock *b = getBlockNoCreate(p);
1374 if(b->getDayNightDiff())
1377 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(1,0,0);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->getDayNightDiff())
1385 catch(InvalidPositionException &e){}
1387 v3s16 p = blockpos + v3s16(0,1,0);
1388 MapBlock *b = getBlockNoCreate(p);
1389 if(b->getDayNightDiff())
1392 catch(InvalidPositionException &e){}
1394 v3s16 p = blockpos + v3s16(0,0,1);
1395 MapBlock *b = getBlockNoCreate(p);
1396 if(b->getDayNightDiff())
1399 catch(InvalidPositionException &e){}
1405 Updates usage timers
1407 void Map::timerUpdate(float dtime, float unload_timeout,
1408 std::vector<v3s16> *unloaded_blocks)
1410 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1412 // Profile modified reasons
1413 Profiler modprofiler;
1415 std::vector<v2s16> sector_deletion_queue;
1416 u32 deleted_blocks_count = 0;
1417 u32 saved_blocks_count = 0;
1418 u32 block_count_all = 0;
1421 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1422 si != m_sectors.end(); ++si) {
1423 MapSector *sector = si->second;
1425 bool all_blocks_deleted = true;
1427 MapBlockVect blocks;
1428 sector->getBlocks(blocks);
1430 for(MapBlockVect::iterator i = blocks.begin();
1431 i != blocks.end(); ++i) {
1432 MapBlock *block = (*i);
1434 block->incrementUsageTimer(dtime);
1436 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) {
1437 v3s16 p = block->getPos();
1440 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1441 modprofiler.add(block->getModifiedReason(), 1);
1442 if (!saveBlock(block))
1444 saved_blocks_count++;
1447 // Delete from memory
1448 sector->deleteBlock(block);
1451 unloaded_blocks->push_back(p);
1453 deleted_blocks_count++;
1456 all_blocks_deleted = false;
1461 if(all_blocks_deleted) {
1462 sector_deletion_queue.push_back(si->first);
1467 // Finally delete the empty sectors
1468 deleteSectors(sector_deletion_queue);
1470 if(deleted_blocks_count != 0)
1472 PrintInfo(infostream); // ServerMap/ClientMap:
1473 infostream<<"Unloaded "<<deleted_blocks_count
1474 <<" blocks from memory";
1475 if(save_before_unloading)
1476 infostream<<", of which "<<saved_blocks_count<<" were written";
1477 infostream<<", "<<block_count_all<<" blocks in memory";
1478 infostream<<"."<<std::endl;
1479 if(saved_blocks_count != 0){
1480 PrintInfo(infostream); // ServerMap/ClientMap:
1481 infostream<<"Blocks modified by: "<<std::endl;
1482 modprofiler.print(infostream);
1487 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1489 timerUpdate(0.0, -1.0, unloaded_blocks);
1492 void Map::deleteSectors(std::vector<v2s16> §orList)
1494 for(std::vector<v2s16>::iterator j = sectorList.begin();
1495 j != sectorList.end(); ++j) {
1496 MapSector *sector = m_sectors[*j];
1497 // If sector is in sector cache, remove it from there
1498 if(m_sector_cache == sector)
1499 m_sector_cache = NULL;
1500 // Remove from map and delete
1501 m_sectors.erase(*j);
1506 void Map::PrintInfo(std::ostream &out)
1511 #define WATER_DROP_BOOST 4
1515 NEIGHBOR_SAME_LEVEL,
1518 struct NodeNeighbor {
1522 bool l; //can liquid
1528 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1535 void Map::transforming_liquid_add(v3s16 p) {
1536 m_transforming_liquid.push_back(p);
1539 s32 Map::transforming_liquid_size() {
1540 return m_transforming_liquid.size();
1543 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1546 INodeDefManager *nodemgr = m_gamedef->ndef();
1548 DSTACK(__FUNCTION_NAME);
1549 //TimeTaker timer("transformLiquids()");
1552 u32 initial_size = m_transforming_liquid.size();
1554 /*if(initial_size != 0)
1555 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1557 // list of nodes that due to viscosity have not reached their max level height
1558 std::deque<v3s16> must_reflow;
1560 // List of MapBlocks that will require a lighting update (due to lava)
1561 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1563 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1564 u32 loop_max = liquid_loop_max;
1568 /* If liquid_loop_max is not keeping up with the queue size increase
1569 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1571 if (m_transforming_liquid.size() > loop_max * 2) {
1573 float server_step = g_settings->getFloat("dedicated_server_step");
1574 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1575 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1577 m_transforming_liquid_loop_count_multiplier = 1.0;
1580 loop_max *= m_transforming_liquid_loop_count_multiplier;
1583 while(m_transforming_liquid.size() != 0)
1585 // This should be done here so that it is done when continue is used
1586 if(loopcount >= initial_size || loopcount >= loop_max)
1591 Get a queued transforming liquid node
1593 v3s16 p0 = m_transforming_liquid.front();
1594 m_transforming_liquid.pop_front();
1596 MapNode n0 = getNodeNoEx(p0);
1599 Collect information about current node
1601 s8 liquid_level = -1;
1602 content_t liquid_kind = CONTENT_IGNORE;
1603 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1604 switch (liquid_type) {
1606 liquid_level = LIQUID_LEVEL_SOURCE;
1607 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1609 case LIQUID_FLOWING:
1610 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1611 liquid_kind = n0.getContent();
1614 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1615 // continue with the next node.
1616 if (n0.getContent() != CONTENT_AIR)
1618 liquid_kind = CONTENT_AIR;
1623 Collect information about the environment
1625 const v3s16 *dirs = g_6dirs;
1626 NodeNeighbor sources[6]; // surrounding sources
1627 int num_sources = 0;
1628 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1630 NodeNeighbor airs[6]; // surrounding air
1632 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1633 int num_neutrals = 0;
1634 bool flowing_down = false;
1635 for (u16 i = 0; i < 6; i++) {
1636 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1639 nt = NEIGHBOR_UPPER;
1642 nt = NEIGHBOR_LOWER;
1645 v3s16 npos = p0 + dirs[i];
1646 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1647 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1649 if (nb.n.getContent() == CONTENT_AIR) {
1650 airs[num_airs++] = nb;
1651 // if the current node is a water source the neighbor
1652 // should be enqueded for transformation regardless of whether the
1653 // current node changes or not.
1654 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1655 m_transforming_liquid.push_back(npos);
1656 // if the current node happens to be a flowing node, it will start to flow down here.
1657 if (nb.t == NEIGHBOR_LOWER) {
1658 flowing_down = true;
1661 neutrals[num_neutrals++] = nb;
1665 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1666 if (liquid_kind == CONTENT_AIR)
1667 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1668 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1669 neutrals[num_neutrals++] = nb;
1671 // Do not count bottom source, it will screw things up
1673 sources[num_sources++] = nb;
1676 case LIQUID_FLOWING:
1677 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1678 if (liquid_kind == CONTENT_AIR)
1679 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1680 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1681 neutrals[num_neutrals++] = nb;
1683 flows[num_flows++] = nb;
1684 if (nb.t == NEIGHBOR_LOWER)
1685 flowing_down = true;
1692 decide on the type (and possibly level) of the current node
1694 content_t new_node_content;
1695 s8 new_node_level = -1;
1696 s8 max_node_level = -1;
1698 u8 range = nodemgr->get(liquid_kind).liquid_range;
1699 if (range > LIQUID_LEVEL_MAX+1)
1700 range = LIQUID_LEVEL_MAX+1;
1702 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1703 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1704 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1705 // it's perfectly safe to use liquid_kind here to determine the new node content.
1706 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1707 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1708 // liquid_kind is set properly, see above
1709 new_node_content = liquid_kind;
1710 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1711 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1712 new_node_content = CONTENT_AIR;
1714 // no surrounding sources, so get the maximum level that can flow into this node
1715 for (u16 i = 0; i < num_flows; i++) {
1716 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1717 switch (flows[i].t) {
1718 case NEIGHBOR_UPPER:
1719 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1720 max_node_level = LIQUID_LEVEL_MAX;
1721 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1722 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1723 } else if (nb_liquid_level > max_node_level)
1724 max_node_level = nb_liquid_level;
1726 case NEIGHBOR_LOWER:
1728 case NEIGHBOR_SAME_LEVEL:
1729 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1730 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1731 max_node_level = nb_liquid_level - 1;
1737 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1738 if (viscosity > 1 && max_node_level != liquid_level) {
1739 // amount to gain, limited by viscosity
1740 // must be at least 1 in absolute value
1741 s8 level_inc = max_node_level - liquid_level;
1742 if (level_inc < -viscosity || level_inc > viscosity)
1743 new_node_level = liquid_level + level_inc/viscosity;
1744 else if (level_inc < 0)
1745 new_node_level = liquid_level - 1;
1746 else if (level_inc > 0)
1747 new_node_level = liquid_level + 1;
1748 if (new_node_level != max_node_level)
1749 must_reflow.push_back(p0);
1751 new_node_level = max_node_level;
1753 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1754 new_node_content = liquid_kind;
1756 new_node_content = CONTENT_AIR;
1761 check if anything has changed. if not, just continue with the next node.
1763 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1764 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1765 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1771 update the current node
1774 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1775 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1776 // set level to last 3 bits, flowing down bit to 4th bit
1777 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1779 // set the liquid level and flow bit to 0
1780 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1782 n0.setContent(new_node_content);
1784 // Find out whether there is a suspect for this action
1785 std::string suspect;
1786 if(m_gamedef->rollback()) {
1787 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1790 if(m_gamedef->rollback() && !suspect.empty()){
1792 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1793 // Get old node for rollback
1794 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1798 RollbackNode rollback_newnode(this, p0, m_gamedef);
1799 RollbackAction action;
1800 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1801 m_gamedef->rollback()->reportAction(action);
1807 v3s16 blockpos = getNodeBlockPos(p0);
1808 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1810 modified_blocks[blockpos] = block;
1811 // If new or old node emits light, MapBlock requires lighting update
1812 if(nodemgr->get(n0).light_source != 0 ||
1813 nodemgr->get(n00).light_source != 0)
1814 lighting_modified_blocks[block->getPos()] = block;
1818 enqueue neighbors for update if neccessary
1820 switch (nodemgr->get(n0.getContent()).liquid_type) {
1822 case LIQUID_FLOWING:
1823 // make sure source flows into all neighboring nodes
1824 for (u16 i = 0; i < num_flows; i++)
1825 if (flows[i].t != NEIGHBOR_UPPER)
1826 m_transforming_liquid.push_back(flows[i].p);
1827 for (u16 i = 0; i < num_airs; i++)
1828 if (airs[i].t != NEIGHBOR_UPPER)
1829 m_transforming_liquid.push_back(airs[i].p);
1832 // this flow has turned to air; neighboring flows might need to do the same
1833 for (u16 i = 0; i < num_flows; i++)
1834 m_transforming_liquid.push_back(flows[i].p);
1838 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1840 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1841 m_transforming_liquid.push_back(*iter);
1843 updateLighting(lighting_modified_blocks, modified_blocks);
1846 /* ----------------------------------------------------------------------
1847 * Manage the queue so that it does not grow indefinately
1849 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1851 if (time_until_purge == 0)
1852 return; // Feature disabled
1854 time_until_purge *= 1000; // seconds -> milliseconds
1856 u32 curr_time = getTime(PRECISION_MILLI);
1857 u32 prev_unprocessed = m_unprocessed_count;
1858 m_unprocessed_count = m_transforming_liquid.size();
1860 // if unprocessed block count is decreasing or stable
1861 if (m_unprocessed_count <= prev_unprocessed) {
1862 m_queue_size_timer_started = false;
1864 if (!m_queue_size_timer_started)
1865 m_inc_trending_up_start_time = curr_time;
1866 m_queue_size_timer_started = true;
1869 // Account for curr_time overflowing
1870 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1871 m_queue_size_timer_started = false;
1873 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1874 * and the number of unprocessed blocks is still > liquid_loop_max then we
1875 * cannot keep up; dump the oldest blocks from the queue so that the queue
1876 * has liquid_loop_max items in it
1878 if (m_queue_size_timer_started
1879 && curr_time - m_inc_trending_up_start_time > time_until_purge
1880 && m_unprocessed_count > liquid_loop_max) {
1882 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1884 infostream << "transformLiquids(): DUMPING " << dump_qty
1885 << " blocks from the queue" << std::endl;
1888 m_transforming_liquid.pop_front();
1890 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1891 m_unprocessed_count = m_transforming_liquid.size();
1895 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1897 v3s16 blockpos = getNodeBlockPos(p);
1898 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1899 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1901 infostream<<"Map::getNodeMetadata(): Need to emerge "
1902 <<PP(blockpos)<<std::endl;
1903 block = emergeBlock(blockpos, false);
1906 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1910 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1914 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1916 v3s16 blockpos = getNodeBlockPos(p);
1917 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1918 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1920 infostream<<"Map::setNodeMetadata(): Need to emerge "
1921 <<PP(blockpos)<<std::endl;
1922 block = emergeBlock(blockpos, false);
1925 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1929 block->m_node_metadata.set(p_rel, meta);
1933 void Map::removeNodeMetadata(v3s16 p)
1935 v3s16 blockpos = getNodeBlockPos(p);
1936 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1937 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1940 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1944 block->m_node_metadata.remove(p_rel);
1947 NodeTimer Map::getNodeTimer(v3s16 p)
1949 v3s16 blockpos = getNodeBlockPos(p);
1950 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1951 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1953 infostream<<"Map::getNodeTimer(): Need to emerge "
1954 <<PP(blockpos)<<std::endl;
1955 block = emergeBlock(blockpos, false);
1958 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1962 NodeTimer t = block->m_node_timers.get(p_rel);
1966 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1968 v3s16 blockpos = getNodeBlockPos(p);
1969 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1970 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1972 infostream<<"Map::setNodeTimer(): Need to emerge "
1973 <<PP(blockpos)<<std::endl;
1974 block = emergeBlock(blockpos, false);
1977 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1981 block->m_node_timers.set(p_rel, t);
1984 void Map::removeNodeTimer(v3s16 p)
1986 v3s16 blockpos = getNodeBlockPos(p);
1987 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1988 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1991 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
1995 block->m_node_timers.remove(p_rel);
2001 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2002 Map(dout_server, gamedef),
2004 m_map_metadata_changed(true)
2006 verbosestream<<__FUNCTION_NAME<<std::endl;
2009 Try to load map; if not found, create a new one.
2012 // Determine which database backend to use
2013 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2015 bool succeeded = conf.readConfigFile(conf_path.c_str());
2016 if (!succeeded || !conf.exists("backend")) {
2017 // fall back to sqlite3
2018 conf.set("backend", "sqlite3");
2020 std::string backend = conf.get("backend");
2021 if (backend == "dummy")
2022 dbase = new Database_Dummy();
2023 else if (backend == "sqlite3")
2024 dbase = new Database_SQLite3(savedir);
2026 else if (backend == "leveldb")
2027 dbase = new Database_LevelDB(savedir);
2030 else if (backend == "redis")
2031 dbase = new Database_Redis(conf);
2034 throw BaseException("Unknown map backend");
2036 if (!conf.updateConfigFile(conf_path.c_str()))
2037 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
2039 m_savedir = savedir;
2040 m_map_saving_enabled = false;
2044 // If directory exists, check contents and load if possible
2045 if(fs::PathExists(m_savedir))
2047 // If directory is empty, it is safe to save into it.
2048 if(fs::GetDirListing(m_savedir).size() == 0)
2050 infostream<<"ServerMap: Empty save directory is valid."
2052 m_map_saving_enabled = true;
2057 // Load map metadata (seed, chunksize)
2060 catch(SettingNotFoundException &e){
2061 infostream<<"ServerMap: Some metadata not found."
2062 <<" Using default settings."<<std::endl;
2064 catch(FileNotGoodException &e){
2065 infostream<<"WARNING: Could not load map metadata"
2066 //<<" Disabling chunk-based generator."
2071 infostream<<"ServerMap: Successfully loaded map "
2072 <<"metadata from "<<savedir
2073 <<", assuming valid save directory."
2074 <<" seed="<< m_emerge->params.seed <<"."
2077 m_map_saving_enabled = true;
2078 // Map loaded, not creating new one
2082 // If directory doesn't exist, it is safe to save to it
2084 m_map_saving_enabled = true;
2087 catch(std::exception &e)
2089 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2090 <<", exception: "<<e.what()<<std::endl;
2091 infostream<<"Please remove the map or fix it."<<std::endl;
2092 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2095 infostream<<"Initializing new map."<<std::endl;
2097 // Create zero sector
2098 emergeSector(v2s16(0,0));
2100 // Initially write whole map
2101 save(MOD_STATE_CLEAN);
2104 ServerMap::~ServerMap()
2106 verbosestream<<__FUNCTION_NAME<<std::endl;
2110 if(m_map_saving_enabled)
2112 // Save only changed parts
2113 save(MOD_STATE_WRITE_AT_UNLOAD);
2114 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2118 infostream<<"ServerMap: Map not saved"<<std::endl;
2121 catch(std::exception &e)
2123 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2124 <<", exception: "<<e.what()<<std::endl;
2128 Close database if it was opened
2136 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2137 for(; i.atEnd() == false; i++)
2139 MapChunk *chunk = i.getNode()->getValue();
2145 u64 ServerMap::getSeed()
2147 return m_emerge->params.seed;
2150 s16 ServerMap::getWaterLevel()
2152 return m_emerge->params.water_level;
2155 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2157 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2158 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2160 s16 chunksize = m_emerge->params.chunksize;
2161 s16 coffset = -chunksize / 2;
2162 v3s16 chunk_offset(coffset, coffset, coffset);
2163 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2164 v3s16 blockpos_min = blockpos_div * chunksize;
2165 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2166 blockpos_min += chunk_offset;
2167 blockpos_max += chunk_offset;
2169 v3s16 extra_borders(1,1,1);
2171 // Do nothing if not inside limits (+-1 because of neighbors)
2172 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2173 blockpos_over_limit(blockpos_max + extra_borders))
2176 data->seed = m_emerge->params.seed;
2177 data->blockpos_min = blockpos_min;
2178 data->blockpos_max = blockpos_max;
2179 data->blockpos_requested = blockpos;
2180 data->nodedef = m_gamedef->ndef();
2183 Create the whole area of this and the neighboring blocks
2186 //TimeTaker timer("initBlockMake() create area");
2188 for(s16 x=blockpos_min.X-extra_borders.X;
2189 x<=blockpos_max.X+extra_borders.X; x++)
2190 for(s16 z=blockpos_min.Z-extra_borders.Z;
2191 z<=blockpos_max.Z+extra_borders.Z; z++)
2193 v2s16 sectorpos(x, z);
2194 // Sector metadata is loaded from disk if not already loaded.
2195 ServerMapSector *sector = createSector(sectorpos);
2199 for(s16 y=blockpos_min.Y-extra_borders.Y;
2200 y<=blockpos_max.Y+extra_borders.Y; y++)
2203 //MapBlock *block = createBlock(p);
2204 // 1) get from memory, 2) load from disk
2205 MapBlock *block = emergeBlock(p, false);
2206 // 3) create a blank one
2209 block = createBlock(p);
2212 Block gets sunlight if this is true.
2214 Refer to the map generator heuristics.
2216 bool ug = m_emerge->isBlockUnderground(p);
2217 block->setIsUnderground(ug);
2220 // Lighting will not be valid after make_chunk is called
2221 block->setLightingExpired(true);
2222 // Lighting will be calculated
2223 //block->setLightingExpired(false);
2229 Now we have a big empty area.
2231 Make a ManualMapVoxelManipulator that contains this and the
2235 // The area that contains this block and it's neighbors
2236 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2237 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2239 data->vmanip = new MMVManip(this);
2240 //data->vmanip->setMap(this);
2244 //TimeTaker timer("initBlockMake() initialEmerge");
2245 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2248 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2249 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2250 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2251 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2252 core::map<v3s16, u8>::Node *n;
2253 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2256 u8 flags = n->getValue();
2257 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2263 // Data is ready now.
2267 void ServerMap::finishBlockMake(BlockMakeData *data,
2268 std::map<v3s16, MapBlock*> &changed_blocks)
2270 v3s16 blockpos_min = data->blockpos_min;
2271 v3s16 blockpos_max = data->blockpos_max;
2272 v3s16 blockpos_requested = data->blockpos_requested;
2273 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2274 <<blockpos_requested.Y<<","
2275 <<blockpos_requested.Z<<")"<<std::endl;*/
2277 v3s16 extra_borders(1,1,1);
2279 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2281 /*infostream<<"Resulting vmanip:"<<std::endl;
2282 data->vmanip.print(infostream);*/
2284 // Make sure affected blocks are loaded
2285 for(s16 x=blockpos_min.X-extra_borders.X;
2286 x<=blockpos_max.X+extra_borders.X; x++)
2287 for(s16 z=blockpos_min.Z-extra_borders.Z;
2288 z<=blockpos_max.Z+extra_borders.Z; z++)
2289 for(s16 y=blockpos_min.Y-extra_borders.Y;
2290 y<=blockpos_max.Y+extra_borders.Y; y++)
2293 // Load from disk if not already in memory
2294 emergeBlock(p, false);
2298 Blit generated stuff to map
2299 NOTE: blitBackAll adds nearly everything to changed_blocks
2303 //TimeTaker timer("finishBlockMake() blitBackAll");
2304 data->vmanip->blitBackAll(&changed_blocks);
2307 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2310 Copy transforming liquid information
2312 while(data->transforming_liquid.size() > 0)
2314 m_transforming_liquid.push_back(data->transforming_liquid.front());
2315 data->transforming_liquid.pop_front();
2319 Do stuff in central blocks
2327 TimeTaker t("finishBlockMake lighting update");
2329 core::map<v3s16, MapBlock*> lighting_update_blocks;
2332 for(s16 x=blockpos_min.X-extra_borders.X;
2333 x<=blockpos_max.X+extra_borders.X; x++)
2334 for(s16 z=blockpos_min.Z-extra_borders.Z;
2335 z<=blockpos_max.Z+extra_borders.Z; z++)
2336 for(s16 y=blockpos_min.Y-extra_borders.Y;
2337 y<=blockpos_max.Y+extra_borders.Y; y++)
2340 MapBlock *block = getBlockNoCreateNoEx(p);
2342 lighting_update_blocks.insert(block->getPos(), block);
2345 updateLighting(lighting_update_blocks, changed_blocks);
2349 Set lighting to non-expired state in all of them.
2350 This is cheating, but it is not fast enough if all of them
2351 would actually be updated.
2353 for(s16 x=blockpos_min.X-extra_borders.X;
2354 x<=blockpos_max.X+extra_borders.X; x++)
2355 for(s16 z=blockpos_min.Z-extra_borders.Z;
2356 z<=blockpos_max.Z+extra_borders.Z; z++)
2357 for(s16 y=blockpos_min.Y-extra_borders.Y;
2358 y<=blockpos_max.Y+extra_borders.Y; y++)
2361 MapBlock * block = getBlockNoCreateNoEx(p);
2363 block->setLightingExpired(false);
2367 if(enable_mapgen_debug_info == false)
2368 t.stop(true); // Hide output
2373 Go through changed blocks
2375 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2376 i != changed_blocks.end(); ++i)
2378 MapBlock *block = i->second;
2382 Update day/night difference cache of the MapBlocks
2384 block->expireDayNightDiff();
2386 Set block as modified
2388 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2389 "finishBlockMake expireDayNightDiff");
2393 Set central blocks as generated
2395 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2396 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2397 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2400 MapBlock *block = getBlockNoCreateNoEx(p);
2403 block->setGenerated(true);
2407 Save changed parts of map
2408 NOTE: Will be saved later.
2410 //save(MOD_STATE_WRITE_AT_UNLOAD);
2412 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2413 <<","<<blockpos_requested.Y<<","
2414 <<blockpos_requested.Z<<")"<<std::endl;*/
2418 if(enable_mapgen_debug_info)
2421 Analyze resulting blocks
2423 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2424 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2425 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2426 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2427 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2428 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2430 v3s16 p = v3s16(x,y,z);
2431 MapBlock *block = getBlockNoCreateNoEx(p);
2433 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2434 infostream<<"Generated "<<spos<<": "
2435 <<analyze_block(block)<<std::endl;
2440 getBlockNoCreateNoEx(blockpos_requested);
2443 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2445 DSTACKF("%s: p2d=(%d,%d)",
2450 Check if it exists already in memory
2452 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2457 Try to load it from disk (with blocks)
2459 //if(loadSectorFull(p2d) == true)
2462 Try to load metadata from disk
2465 if(loadSectorMeta(p2d) == true)
2467 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2470 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2471 throw InvalidPositionException("");
2477 Do not create over-limit
2479 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2480 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2481 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2482 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2483 throw InvalidPositionException("createSector(): pos. over limit");
2486 Generate blank sector
2489 sector = new ServerMapSector(this, p2d, m_gamedef);
2491 // Sector position on map in nodes
2492 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2497 m_sectors[p2d] = sector;
2504 This is a quick-hand function for calling makeBlock().
2506 MapBlock * ServerMap::generateBlock(
2508 std::map<v3s16, MapBlock*> &modified_blocks
2511 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2513 /*infostream<<"generateBlock(): "
2514 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2517 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2519 TimeTaker timer("generateBlock");
2521 //MapBlock *block = original_dummy;
2523 v2s16 p2d(p.X, p.Z);
2524 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2527 Do not generate over-limit
2529 if(blockpos_over_limit(p))
2531 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2532 throw InvalidPositionException("generateBlock(): pos. over limit");
2536 Create block make data
2539 initBlockMake(&data, p);
2545 TimeTaker t("mapgen::make_block()");
2546 mapgen->makeChunk(&data);
2547 //mapgen::make_block(&data);
2549 if(enable_mapgen_debug_info == false)
2550 t.stop(true); // Hide output
2554 Blit data back on map, update lighting, add mobs and whatever this does
2556 finishBlockMake(&data, modified_blocks);
2561 MapBlock *block = getBlockNoCreateNoEx(p);
2569 bool erroneus_content = false;
2570 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2571 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2572 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2575 MapNode n = block->getNode(p);
2576 if(n.getContent() == CONTENT_IGNORE)
2578 infostream<<"CONTENT_IGNORE at "
2579 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2581 erroneus_content = true;
2585 if(erroneus_content)
2594 Generate a completely empty block
2598 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2599 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2601 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2604 n.setContent(CONTENT_AIR);
2605 block->setNode(v3s16(x0,y0,z0), n);
2611 if(enable_mapgen_debug_info == false)
2612 timer.stop(true); // Hide output
2618 MapBlock * ServerMap::createBlock(v3s16 p)
2620 DSTACKF("%s: p=(%d,%d,%d)",
2621 __FUNCTION_NAME, p.X, p.Y, p.Z);
2624 Do not create over-limit
2626 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2627 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2628 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2629 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2630 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2631 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2632 throw InvalidPositionException("createBlock(): pos. over limit");
2634 v2s16 p2d(p.X, p.Z);
2637 This will create or load a sector if not found in memory.
2638 If block exists on disk, it will be loaded.
2640 NOTE: On old save formats, this will be slow, as it generates
2641 lighting on blocks for them.
2643 ServerMapSector *sector;
2645 sector = (ServerMapSector*)createSector(p2d);
2646 assert(sector->getId() == MAPSECTOR_SERVER);
2648 catch(InvalidPositionException &e)
2650 infostream<<"createBlock: createSector() failed"<<std::endl;
2654 NOTE: This should not be done, or at least the exception
2655 should not be passed on as std::exception, because it
2656 won't be catched at all.
2658 /*catch(std::exception &e)
2660 infostream<<"createBlock: createSector() failed: "
2661 <<e.what()<<std::endl;
2666 Try to get a block from the sector
2669 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2672 if(block->isDummy())
2677 block = sector->createBlankBlock(block_y);
2682 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2684 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2686 p.X, p.Y, p.Z, create_blank);
2689 MapBlock *block = getBlockNoCreateNoEx(p);
2690 if(block && block->isDummy() == false)
2695 MapBlock *block = loadBlock(p);
2701 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2702 MapBlock *block = sector->createBlankBlock(p.Y);
2710 std::map<v3s16, MapBlock*> modified_blocks;
2711 MapBlock *block = generateBlock(p, modified_blocks);
2715 event.type = MEET_OTHER;
2718 // Copy modified_blocks to event
2719 for(std::map<v3s16, MapBlock*>::iterator
2720 i = modified_blocks.begin();
2721 i != modified_blocks.end(); ++i)
2723 event.modified_blocks.insert(i->first);
2727 dispatchEvent(&event);
2737 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2739 MapBlock *block = getBlockNoCreateNoEx(p3d);
2741 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2746 void ServerMap::prepareBlock(MapBlock *block) {
2749 // N.B. This requires no synchronization, since data will not be modified unless
2750 // the VoxelManipulator being updated belongs to the same thread.
2751 void ServerMap::updateVManip(v3s16 pos)
2753 Mapgen *mg = m_emerge->getCurrentMapgen();
2757 MMVManip *vm = mg->vm;
2761 if (!vm->m_area.contains(pos))
2764 s32 idx = vm->m_area.index(pos);
2765 vm->m_data[idx] = getNodeNoEx(pos);
2766 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2768 vm->m_is_dirty = true;
2771 s16 ServerMap::findGroundLevel(v2s16 p2d)
2775 Uh, just do something random...
2777 // Find existing map from top to down
2780 v3s16 p(p2d.X, max, p2d.Y);
2781 for(; p.Y>min; p.Y--)
2783 MapNode n = getNodeNoEx(p);
2784 if(n.getContent() != CONTENT_IGNORE)
2789 // If this node is not air, go to plan b
2790 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2792 // Search existing walkable and return it
2793 for(; p.Y>min; p.Y--)
2795 MapNode n = getNodeNoEx(p);
2796 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2805 Determine from map generator noise functions
2808 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2811 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2812 //return (s16)level;
2815 bool ServerMap::loadFromFolders() {
2816 if(!dbase->initialized() &&
2817 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2822 void ServerMap::createDirs(std::string path)
2824 if(fs::CreateAllDirs(path) == false)
2826 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2827 <<"\""<<path<<"\""<<std::endl;
2828 throw BaseException("ServerMap failed to create directory");
2832 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2838 snprintf(cc, 9, "%.4x%.4x",
2839 (unsigned int) pos.X & 0xffff,
2840 (unsigned int) pos.Y & 0xffff);
2842 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2844 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2845 (unsigned int) pos.X & 0xfff,
2846 (unsigned int) pos.Y & 0xfff);
2848 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2855 v2s16 ServerMap::getSectorPos(std::string dirname)
2857 unsigned int x = 0, y = 0;
2859 std::string component;
2860 fs::RemoveLastPathComponent(dirname, &component, 1);
2861 if(component.size() == 8)
2864 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2866 else if(component.size() == 3)
2869 fs::RemoveLastPathComponent(dirname, &component, 2);
2870 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2871 // Sign-extend the 12 bit values up to 16 bits...
2872 if(x & 0x800) x |= 0xF000;
2873 if(y & 0x800) y |= 0xF000;
2880 v2s16 pos((s16)x, (s16)y);
2884 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2886 v2s16 p2d = getSectorPos(sectordir);
2888 if(blockfile.size() != 4){
2889 throw InvalidFilenameException("Invalid block filename");
2892 int r = sscanf(blockfile.c_str(), "%4x", &y);
2894 throw InvalidFilenameException("Invalid block filename");
2895 return v3s16(p2d.X, y, p2d.Y);
2898 std::string ServerMap::getBlockFilename(v3s16 p)
2901 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2905 void ServerMap::save(ModifiedState save_level)
2907 DSTACK(__FUNCTION_NAME);
2908 if(m_map_saving_enabled == false) {
2909 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2913 if(save_level == MOD_STATE_CLEAN)
2914 infostream<<"ServerMap: Saving whole map, this can take time."
2917 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2921 // Profile modified reasons
2922 Profiler modprofiler;
2924 u32 sector_meta_count = 0;
2925 u32 block_count = 0;
2926 u32 block_count_all = 0; // Number of blocks in memory
2928 // Don't do anything with sqlite unless something is really saved
2929 bool save_started = false;
2931 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2932 i != m_sectors.end(); ++i) {
2933 ServerMapSector *sector = (ServerMapSector*)i->second;
2934 assert(sector->getId() == MAPSECTOR_SERVER);
2936 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2937 saveSectorMeta(sector);
2938 sector_meta_count++;
2941 MapBlockVect blocks;
2942 sector->getBlocks(blocks);
2944 for(MapBlockVect::iterator j = blocks.begin();
2945 j != blocks.end(); ++j) {
2946 MapBlock *block = *j;
2950 if(block->getModified() >= (u32)save_level) {
2954 save_started = true;
2957 modprofiler.add(block->getModifiedReason(), 1);
2962 /*infostream<<"ServerMap: Written block ("
2963 <<block->getPos().X<<","
2964 <<block->getPos().Y<<","
2965 <<block->getPos().Z<<")"
2975 Only print if something happened or saved whole map
2977 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2978 || block_count != 0) {
2979 infostream<<"ServerMap: Written: "
2980 <<sector_meta_count<<" sector metadata files, "
2981 <<block_count<<" block files"
2982 <<", "<<block_count_all<<" blocks in memory."
2984 PrintInfo(infostream); // ServerMap/ClientMap:
2985 infostream<<"Blocks modified by: "<<std::endl;
2986 modprofiler.print(infostream);
2990 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2992 if(loadFromFolders()){
2993 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2994 <<"all blocks that are stored in flat files"<<std::endl;
2996 dbase->listAllLoadableBlocks(dst);
2999 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
3001 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3002 si != m_sectors.end(); ++si)
3004 MapSector *sector = si->second;
3006 MapBlockVect blocks;
3007 sector->getBlocks(blocks);
3009 for(MapBlockVect::iterator i = blocks.begin();
3010 i != blocks.end(); ++i) {
3011 v3s16 p = (*i)->getPos();
3017 void ServerMap::saveMapMeta()
3019 DSTACK(__FUNCTION_NAME);
3021 /*infostream<<"ServerMap::saveMapMeta(): "
3025 createDirs(m_savedir);
3027 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3028 std::ostringstream ss(std::ios_base::binary);
3032 m_emerge->saveParamsToSettings(¶ms);
3033 params.writeLines(ss);
3035 ss<<"[end_of_params]\n";
3037 if(!fs::safeWriteToFile(fullpath, ss.str()))
3039 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3040 <<"could not write "<<fullpath<<std::endl;
3041 throw FileNotGoodException("Cannot save chunk metadata");
3044 m_map_metadata_changed = false;
3047 void ServerMap::loadMapMeta()
3049 DSTACK(__FUNCTION_NAME);
3052 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3054 if (fs::PathExists(fullpath)) {
3055 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3057 errorstream << "ServerMap::loadMapMeta(): "
3058 "could not open " << fullpath << std::endl;
3059 throw FileNotGoodException("Cannot open map metadata");
3062 if (!params.parseConfigLines(is, "[end_of_params]")) {
3063 throw SerializationError("ServerMap::loadMapMeta(): "
3064 "[end_of_params] not found!");
3068 m_emerge->loadParamsFromSettings(¶ms);
3070 verbosestream << "ServerMap::loadMapMeta(): seed="
3071 << m_emerge->params.seed << std::endl;
3074 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3076 DSTACK(__FUNCTION_NAME);
3077 // Format used for writing
3078 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3080 v2s16 pos = sector->getPos();
3081 std::string dir = getSectorDir(pos);
3084 std::string fullpath = dir + DIR_DELIM + "meta";
3085 std::ostringstream ss(std::ios_base::binary);
3087 sector->serialize(ss, version);
3089 if(!fs::safeWriteToFile(fullpath, ss.str()))
3090 throw FileNotGoodException("Cannot write sector metafile");
3092 sector->differs_from_disk = false;
3095 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3097 DSTACK(__FUNCTION_NAME);
3099 v2s16 p2d = getSectorPos(sectordir);
3101 ServerMapSector *sector = NULL;
3103 std::string fullpath = sectordir + DIR_DELIM + "meta";
3104 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3105 if(is.good() == false)
3107 // If the directory exists anyway, it probably is in some old
3108 // format. Just go ahead and create the sector.
3109 if(fs::PathExists(sectordir))
3111 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3112 <<fullpath<<" doesn't exist but directory does."
3113 <<" Continuing with a sector with no metadata."
3115 sector = new ServerMapSector(this, p2d, m_gamedef);
3116 m_sectors[p2d] = sector;
3120 throw FileNotGoodException("Cannot open sector metafile");
3125 sector = ServerMapSector::deSerialize
3126 (is, this, p2d, m_sectors, m_gamedef);
3128 saveSectorMeta(sector);
3131 sector->differs_from_disk = false;
3136 bool ServerMap::loadSectorMeta(v2s16 p2d)
3138 DSTACK(__FUNCTION_NAME);
3140 // The directory layout we're going to load from.
3141 // 1 - original sectors/xxxxzzzz/
3142 // 2 - new sectors2/xxx/zzz/
3143 // If we load from anything but the latest structure, we will
3144 // immediately save to the new one, and remove the old.
3146 std::string sectordir1 = getSectorDir(p2d, 1);
3147 std::string sectordir;
3148 if(fs::PathExists(sectordir1))
3150 sectordir = sectordir1;
3155 sectordir = getSectorDir(p2d, 2);
3159 loadSectorMeta(sectordir, loadlayout != 2);
3161 catch(InvalidFilenameException &e)
3165 catch(FileNotGoodException &e)
3169 catch(std::exception &e)
3178 bool ServerMap::loadSectorFull(v2s16 p2d)
3180 DSTACK(__FUNCTION_NAME);
3182 MapSector *sector = NULL;
3184 // The directory layout we're going to load from.
3185 // 1 - original sectors/xxxxzzzz/
3186 // 2 - new sectors2/xxx/zzz/
3187 // If we load from anything but the latest structure, we will
3188 // immediately save to the new one, and remove the old.
3190 std::string sectordir1 = getSectorDir(p2d, 1);
3191 std::string sectordir;
3192 if(fs::PathExists(sectordir1))
3194 sectordir = sectordir1;
3199 sectordir = getSectorDir(p2d, 2);
3203 sector = loadSectorMeta(sectordir, loadlayout != 2);
3205 catch(InvalidFilenameException &e)
3209 catch(FileNotGoodException &e)
3213 catch(std::exception &e)
3221 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3223 std::vector<fs::DirListNode>::iterator i2;
3224 for(i2=list2.begin(); i2!=list2.end(); i2++)
3230 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3232 catch(InvalidFilenameException &e)
3234 // This catches unknown crap in directory
3240 infostream<<"Sector converted to new layout - deleting "<<
3241 sectordir1<<std::endl;
3242 fs::RecursiveDelete(sectordir1);
3249 void ServerMap::beginSave()
3254 void ServerMap::endSave()
3259 bool ServerMap::saveBlock(MapBlock *block)
3261 return saveBlock(block, dbase);
3264 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3266 v3s16 p3d = block->getPos();
3268 // Dummy blocks are not written
3269 if (block->isDummy()) {
3270 errorstream << "WARNING: saveBlock: Not writing dummy block "
3271 << PP(p3d) << std::endl;
3275 // Format used for writing
3276 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3279 [0] u8 serialization version
3282 std::ostringstream o(std::ios_base::binary);
3283 o.write((char*) &version, 1);
3284 block->serialize(o, version, true);
3286 std::string data = o.str();
3287 bool ret = db->saveBlock(p3d, data);
3289 // We just wrote it to the disk so clear modified flag
3290 block->resetModified();
3295 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3296 MapSector *sector, bool save_after_load)
3298 DSTACK(__FUNCTION_NAME);
3300 std::string fullpath = sectordir + DIR_DELIM + blockfile;
3303 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3304 if(is.good() == false)
3305 throw FileNotGoodException("Cannot open block file");
3307 v3s16 p3d = getBlockPos(sectordir, blockfile);
3308 v2s16 p2d(p3d.X, p3d.Z);
3310 assert(sector->getPos() == p2d);
3312 u8 version = SER_FMT_VER_INVALID;
3313 is.read((char*)&version, 1);
3316 throw SerializationError("ServerMap::loadBlock(): Failed"
3317 " to read MapBlock version");
3319 /*u32 block_size = MapBlock::serializedLength(version);
3320 SharedBuffer<u8> data(block_size);
3321 is.read((char*)*data, block_size);*/
3323 // This will always return a sector because we're the server
3324 //MapSector *sector = emergeSector(p2d);
3326 MapBlock *block = NULL;
3327 bool created_new = false;
3328 block = sector->getBlockNoCreateNoEx(p3d.Y);
3331 block = sector->createBlankBlockNoInsert(p3d.Y);
3336 block->deSerialize(is, version, true);
3338 // If it's a new block, insert it to the map
3340 sector->insertBlock(block);
3343 Save blocks loaded in old format in new format
3346 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3350 // Should be in database now, so delete the old file
3351 fs::RecursiveDelete(fullpath);
3354 // We just loaded it from the disk, so it's up-to-date.
3355 block->resetModified();
3358 catch(SerializationError &e)
3360 infostream<<"WARNING: Invalid block data on disk "
3361 <<"fullpath="<<fullpath
3362 <<" (SerializationError). "
3363 <<"what()="<<e.what()
3365 // Ignoring. A new one will be generated.
3368 // TODO: Backup file; name is in fullpath.
3372 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3374 DSTACK(__FUNCTION_NAME);
3377 std::istringstream is(*blob, std::ios_base::binary);
3379 u8 version = SER_FMT_VER_INVALID;
3380 is.read((char*)&version, 1);
3383 throw SerializationError("ServerMap::loadBlock(): Failed"
3384 " to read MapBlock version");
3386 /*u32 block_size = MapBlock::serializedLength(version);
3387 SharedBuffer<u8> data(block_size);
3388 is.read((char*)*data, block_size);*/
3390 // This will always return a sector because we're the server
3391 //MapSector *sector = emergeSector(p2d);
3393 MapBlock *block = NULL;
3394 bool created_new = false;
3395 block = sector->getBlockNoCreateNoEx(p3d.Y);
3398 block = sector->createBlankBlockNoInsert(p3d.Y);
3403 block->deSerialize(is, version, true);
3405 // If it's a new block, insert it to the map
3407 sector->insertBlock(block);
3410 Save blocks loaded in old format in new format
3413 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3414 // Only save if asked to; no need to update version
3418 // We just loaded it from, so it's up-to-date.
3419 block->resetModified();
3422 catch(SerializationError &e)
3424 errorstream<<"Invalid block data in database"
3425 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3426 <<" (SerializationError): "<<e.what()<<std::endl;
3428 // TODO: Block should be marked as invalid in memory so that it is
3429 // not touched but the game can run
3431 if(g_settings->getBool("ignore_world_load_errors")){
3432 errorstream<<"Ignoring block load error. Duck and cover! "
3433 <<"(ignore_world_load_errors)"<<std::endl;
3435 throw SerializationError("Invalid block data in database");
3441 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3443 DSTACK(__FUNCTION_NAME);
3445 v2s16 p2d(blockpos.X, blockpos.Z);
3449 ret = dbase->loadBlock(blockpos);
3451 loadBlock(&ret, blockpos, createSector(p2d), false);
3452 return getBlockNoCreateNoEx(blockpos);
3454 // Not found in database, try the files
3456 // The directory layout we're going to load from.
3457 // 1 - original sectors/xxxxzzzz/
3458 // 2 - new sectors2/xxx/zzz/
3459 // If we load from anything but the latest structure, we will
3460 // immediately save to the new one, and remove the old.
3462 std::string sectordir1 = getSectorDir(p2d, 1);
3463 std::string sectordir;
3464 if(fs::PathExists(sectordir1))
3466 sectordir = sectordir1;
3471 sectordir = getSectorDir(p2d, 2);
3475 Make sure sector is loaded
3477 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3481 sector = loadSectorMeta(sectordir, loadlayout != 2);
3483 catch(InvalidFilenameException &e)
3487 catch(FileNotGoodException &e)
3491 catch(std::exception &e)
3498 Make sure file exists
3501 std::string blockfilename = getBlockFilename(blockpos);
3502 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3506 Load block and save it to the database
3508 loadBlock(sectordir, blockfilename, sector, true);
3509 return getBlockNoCreateNoEx(blockpos);
3512 bool ServerMap::deleteBlock(v3s16 blockpos)
3514 if (!dbase->deleteBlock(blockpos))
3517 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3519 v2s16 p2d(blockpos.X, blockpos.Z);
3520 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3523 sector->deleteBlock(block);
3529 void ServerMap::PrintInfo(std::ostream &out)
3534 MMVManip::MMVManip(Map *map):
3537 m_create_area(false),
3542 MMVManip::~MMVManip()
3546 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3547 bool load_if_inexistent)
3549 TimeTaker timer1("initialEmerge", &emerge_time);
3551 // Units of these are MapBlocks
3552 v3s16 p_min = blockpos_min;
3553 v3s16 p_max = blockpos_max;
3555 VoxelArea block_area_nodes
3556 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3558 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3561 infostream<<"initialEmerge: area: ";
3562 block_area_nodes.print(infostream);
3563 infostream<<" ("<<size_MB<<"MB)";
3564 infostream<<std::endl;
3567 addArea(block_area_nodes);
3569 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3570 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3571 for(s32 x=p_min.X; x<=p_max.X; x++)
3576 std::map<v3s16, u8>::iterator n;
3577 n = m_loaded_blocks.find(p);
3578 if(n != m_loaded_blocks.end())
3581 bool block_data_inexistent = false;
3584 TimeTaker timer1("emerge load", &emerge_load_time);
3586 block = m_map->getBlockNoCreate(p);
3587 if(block->isDummy())
3588 block_data_inexistent = true;
3590 block->copyTo(*this);
3592 catch(InvalidPositionException &e)
3594 block_data_inexistent = true;
3597 if(block_data_inexistent)
3600 if (load_if_inexistent) {
3601 ServerMap *svrmap = (ServerMap *)m_map;
3602 block = svrmap->emergeBlock(p, false);
3604 block = svrmap->createBlock(p);
3605 block->copyTo(*this);
3607 flags |= VMANIP_BLOCK_DATA_INEXIST;
3610 Mark area inexistent
3612 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3613 // Fill with VOXELFLAG_NO_DATA
3614 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3615 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3617 s32 i = m_area.index(a.MinEdge.X,y,z);
3618 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3622 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3624 // Mark that block was loaded as blank
3625 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3628 m_loaded_blocks[p] = flags;
3634 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3635 bool overwrite_generated)
3637 if(m_area.getExtent() == v3s16(0,0,0))
3641 Copy data of all blocks
3643 for(std::map<v3s16, u8>::iterator
3644 i = m_loaded_blocks.begin();
3645 i != m_loaded_blocks.end(); ++i)
3648 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3649 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3650 if ((existed == false) || (block == NULL) ||
3651 (overwrite_generated == false && block->isGenerated() == true))
3654 block->copyFrom(*this);
3657 (*modified_blocks)[p] = block;