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"
25 #include "voxelalgorithms.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/basic_macros.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
38 #include "reflowscan.h"
40 #include "mapgen_v6.h"
45 #include "database-dummy.h"
46 #include "database-sqlite3.h"
50 #include "database-leveldb.h"
53 #include "database-redis.h"
56 #include "database-postgresql.h"
64 Map::Map(std::ostream &dout, IGameDef *gamedef):
68 m_nodedef(gamedef->ndef()),
69 m_transforming_liquid_loop_count_multiplier(1.0f),
70 m_unprocessed_count(0),
71 m_inc_trending_up_start_time(0),
72 m_queue_size_timer_started(false)
81 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
82 i != m_sectors.end(); ++i)
88 void Map::addEventReceiver(MapEventReceiver *event_receiver)
90 m_event_receivers.insert(event_receiver);
93 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
95 m_event_receivers.erase(event_receiver);
98 void Map::dispatchEvent(MapEditEvent *event)
100 for(std::set<MapEventReceiver*>::iterator
101 i = m_event_receivers.begin();
102 i != m_event_receivers.end(); ++i)
104 (*i)->onMapEditEvent(event);
108 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
110 if(m_sector_cache != NULL && p == m_sector_cache_p){
111 MapSector * sector = m_sector_cache;
115 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
117 if(n == m_sectors.end())
120 MapSector *sector = n->second;
122 // Cache the last result
123 m_sector_cache_p = p;
124 m_sector_cache = sector;
129 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
131 return getSectorNoGenerateNoExNoLock(p);
134 MapSector * Map::getSectorNoGenerate(v2s16 p)
136 MapSector *sector = getSectorNoGenerateNoEx(p);
138 throw InvalidPositionException();
143 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
145 v2s16 p2d(p3d.X, p3d.Z);
146 MapSector * sector = getSectorNoGenerateNoEx(p2d);
149 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
153 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
155 MapBlock *block = getBlockNoCreateNoEx(p3d);
157 throw InvalidPositionException();
161 bool Map::isNodeUnderground(v3s16 p)
163 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock * block = getBlockNoCreate(blockpos);
166 return block->getIsUnderground();
168 catch(InvalidPositionException &e)
174 bool Map::isValidPosition(v3s16 p)
176 v3s16 blockpos = getNodeBlockPos(p);
177 MapBlock *block = getBlockNoCreateNoEx(blockpos);
178 return (block != NULL);
181 // Returns a CONTENT_IGNORE node if not found
182 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
184 v3s16 blockpos = getNodeBlockPos(p);
185 MapBlock *block = getBlockNoCreateNoEx(blockpos);
187 if (is_valid_position != NULL)
188 *is_valid_position = false;
189 return MapNode(CONTENT_IGNORE);
192 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
194 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
195 if (is_valid_position != NULL)
196 *is_valid_position = is_valid_p;
202 // throws InvalidPositionException if not found
203 // TODO: Now this is deprecated, getNodeNoEx should be renamed
204 MapNode Map::getNode(v3s16 p)
206 v3s16 blockpos = getNodeBlockPos(p);
207 MapBlock *block = getBlockNoCreateNoEx(blockpos);
209 throw InvalidPositionException();
210 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
211 bool is_valid_position;
212 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
213 if (!is_valid_position)
214 throw InvalidPositionException();
219 // throws InvalidPositionException if not found
220 void Map::setNode(v3s16 p, MapNode & n)
222 v3s16 blockpos = getNodeBlockPos(p);
223 MapBlock *block = getBlockNoCreate(blockpos);
224 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
225 // Never allow placing CONTENT_IGNORE, it fucks up stuff
226 if(n.getContent() == CONTENT_IGNORE){
228 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
229 <<" while trying to replace \""
230 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
231 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
232 debug_stacks_print_to(infostream);
235 block->setNodeNoCheck(relpos, n);
239 Goes recursively through the neighbours of the node.
241 Alters only transparent nodes.
243 If the lighting of the neighbour is lower than the lighting of
244 the node was (before changing it to 0 at the step before), the
245 lighting of the neighbour is set to 0 and then the same stuff
246 repeats for the neighbour.
248 The ending nodes of the routine are stored in light_sources.
249 This is useful when a light is removed. In such case, this
250 routine can be called for the light node and then again for
251 light_sources to re-light the area without the removed light.
253 values of from_nodes are lighting values.
255 void Map::unspreadLight(enum LightBank bank,
256 std::map<v3s16, u8> & from_nodes,
257 std::set<v3s16> & light_sources,
258 std::map<v3s16, MapBlock*> & modified_blocks)
261 v3s16(0,0,1), // back
263 v3s16(1,0,0), // right
264 v3s16(0,0,-1), // front
265 v3s16(0,-1,0), // bottom
266 v3s16(-1,0,0), // left
269 if(from_nodes.empty())
272 u32 blockchangecount = 0;
274 std::map<v3s16, u8> unlighted_nodes;
277 Initialize block cache
280 MapBlock *block = NULL;
281 // Cache this a bit, too
282 bool block_checked_in_modified = false;
284 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
285 j != from_nodes.end(); ++j)
287 v3s16 pos = j->first;
288 v3s16 blockpos = getNodeBlockPos(pos);
290 // Only fetch a new block if the block position has changed
292 if(block == NULL || blockpos != blockpos_last){
293 block = getBlockNoCreate(blockpos);
294 blockpos_last = blockpos;
296 block_checked_in_modified = false;
300 catch(InvalidPositionException &e)
308 // Calculate relative position in block
309 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
311 // Get node straight from the block
312 //MapNode n = block->getNode(relpos);
314 u8 oldlight = j->second;
316 // Loop through 6 neighbors
317 for(u16 i=0; i<6; i++)
319 // Get the position of the neighbor node
320 v3s16 n2pos = pos + dirs[i];
322 // Get the block where the node is located
323 v3s16 blockpos, relpos;
324 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
326 // Only fetch a new block if the block position has changed
328 if(block == NULL || blockpos != blockpos_last){
329 block = getBlockNoCreate(blockpos);
330 blockpos_last = blockpos;
332 block_checked_in_modified = false;
336 catch(InvalidPositionException &e) {
340 // Get node straight from the block
341 bool is_valid_position;
342 MapNode n2 = block->getNode(relpos, &is_valid_position);
343 if (!is_valid_position)
346 bool changed = false;
348 //TODO: Optimize output by optimizing light_sources?
351 If the neighbor is dimmer than what was specified
352 as oldlight (the light of the previous node)
354 if(n2.getLight(bank, m_nodedef) < oldlight)
357 And the neighbor is transparent and it has some light
359 if(m_nodedef->get(n2).light_propagates
360 && n2.getLight(bank, m_nodedef) != 0)
363 Set light to 0 and add to queue
366 u8 current_light = n2.getLight(bank, m_nodedef);
367 n2.setLight(bank, 0, m_nodedef);
368 block->setNode(relpos, n2);
370 unlighted_nodes[n2pos] = current_light;
374 Remove from light_sources if it is there
375 NOTE: This doesn't happen nearly at all
377 /*if(light_sources.find(n2pos))
379 infostream<<"Removed from light_sources"<<std::endl;
380 light_sources.remove(n2pos);
385 if(light_sources.find(n2pos) != NULL)
386 light_sources.remove(n2pos);*/
389 light_sources.insert(n2pos);
392 // Add to modified_blocks
393 if(changed == true && block_checked_in_modified == false)
395 // If the block is not found in modified_blocks, add.
396 if(modified_blocks.find(blockpos) == modified_blocks.end())
398 modified_blocks[blockpos] = block;
400 block_checked_in_modified = true;
405 /*infostream<<"unspreadLight(): Changed block "
406 <<blockchangecount<<" times"
407 <<" for "<<from_nodes.size()<<" nodes"
410 if(!unlighted_nodes.empty())
411 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
415 Lights neighbors of from_nodes, collects all them and then
418 void Map::spreadLight(enum LightBank bank,
419 std::set<v3s16> & from_nodes,
420 std::map<v3s16, MapBlock*> & modified_blocks)
422 const v3s16 dirs[6] = {
423 v3s16(0,0,1), // back
425 v3s16(1,0,0), // right
426 v3s16(0,0,-1), // front
427 v3s16(0,-1,0), // bottom
428 v3s16(-1,0,0), // left
431 if(from_nodes.empty())
434 u32 blockchangecount = 0;
436 std::set<v3s16> lighted_nodes;
439 Initialize block cache
442 MapBlock *block = NULL;
443 // Cache this a bit, too
444 bool block_checked_in_modified = false;
446 for(std::set<v3s16>::iterator j = from_nodes.begin();
447 j != from_nodes.end(); ++j)
450 v3s16 blockpos, relpos;
452 getNodeBlockPosWithOffset(pos, blockpos, relpos);
454 // Only fetch a new block if the block position has changed
456 if(block == NULL || blockpos != blockpos_last){
457 block = getBlockNoCreate(blockpos);
458 blockpos_last = blockpos;
460 block_checked_in_modified = false;
464 catch(InvalidPositionException &e) {
471 // Get node straight from the block
472 bool is_valid_position;
473 MapNode n = block->getNode(relpos, &is_valid_position);
475 u8 oldlight = is_valid_position ? n.getLight(bank, m_nodedef) : 0;
476 u8 newlight = diminish_light(oldlight);
478 // Loop through 6 neighbors
479 for(u16 i=0; i<6; i++){
480 // Get the position of the neighbor node
481 v3s16 n2pos = pos + dirs[i];
483 // Get the block where the node is located
484 v3s16 blockpos, relpos;
485 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
487 // Only fetch a new block if the block position has changed
489 if(block == NULL || blockpos != blockpos_last){
490 block = getBlockNoCreate(blockpos);
491 blockpos_last = blockpos;
493 block_checked_in_modified = false;
497 catch(InvalidPositionException &e) {
501 // Get node straight from the block
502 MapNode n2 = block->getNode(relpos, &is_valid_position);
503 if (!is_valid_position)
506 bool changed = false;
508 If the neighbor is brighter than the current node,
509 add to list (it will light up this node on its turn)
511 if(n2.getLight(bank, m_nodedef) > undiminish_light(oldlight))
513 lighted_nodes.insert(n2pos);
517 If the neighbor is dimmer than how much light this node
518 would spread on it, add to list
520 if(n2.getLight(bank, m_nodedef) < newlight)
522 if(m_nodedef->get(n2).light_propagates)
524 n2.setLight(bank, newlight, m_nodedef);
525 block->setNode(relpos, n2);
526 lighted_nodes.insert(n2pos);
531 // Add to modified_blocks
532 if(changed == true && block_checked_in_modified == false)
534 // If the block is not found in modified_blocks, add.
535 if(modified_blocks.find(blockpos) == modified_blocks.end())
537 modified_blocks[blockpos] = block;
539 block_checked_in_modified = true;
544 /*infostream<<"spreadLight(): Changed block "
545 <<blockchangecount<<" times"
546 <<" for "<<from_nodes.size()<<" nodes"
549 if(!lighted_nodes.empty())
550 spreadLight(bank, lighted_nodes, modified_blocks);
553 void Map::updateLighting(enum LightBank bank,
554 std::map<v3s16, MapBlock*> & a_blocks,
555 std::map<v3s16, MapBlock*> & modified_blocks)
557 /*m_dout<<"Map::updateLighting(): "
558 <<a_blocks.size()<<" blocks."<<std::endl;*/
560 //TimeTaker timer("updateLighting");
564 //u32 count_was = modified_blocks.size();
566 //std::map<v3s16, MapBlock*> blocks_to_update;
568 std::set<v3s16> light_sources;
570 std::map<v3s16, u8> unlight_from;
572 int num_bottom_invalid = 0;
575 //TimeTaker t("first stuff");
577 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
578 i != a_blocks.end(); ++i)
580 MapBlock *block = i->second;
584 // Don't bother with dummy blocks.
588 v3s16 pos = block->getPos();
589 v3s16 posnodes = block->getPosRelative();
590 modified_blocks[pos] = block;
591 //blocks_to_update[pos] = block;
594 Clear all light from block
596 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
597 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
598 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
601 bool is_valid_position;
602 MapNode n = block->getNode(p, &is_valid_position);
603 if (!is_valid_position) {
604 /* This would happen when dealing with a
607 infostream<<"updateLighting(): InvalidPositionException"
611 u8 oldlight = n.getLight(bank, m_nodedef);
612 n.setLight(bank, 0, m_nodedef);
613 block->setNode(p, n);
615 // If node sources light, add to list
616 u8 source = m_nodedef->get(n).light_source;
618 light_sources.insert(p + posnodes);
620 // Collect borders for unlighting
621 if((x==0 || x == MAP_BLOCKSIZE-1
622 || y==0 || y == MAP_BLOCKSIZE-1
623 || z==0 || z == MAP_BLOCKSIZE-1)
626 v3s16 p_map = p + posnodes;
627 unlight_from[p_map] = oldlight;
633 if(bank == LIGHTBANK_DAY)
635 bool bottom_valid = block->propagateSunlight(light_sources);
638 num_bottom_invalid++;
640 // If bottom is valid, we're done.
644 else if(bank == LIGHTBANK_NIGHT)
646 // For night lighting, sunlight is not propagated
651 assert("Invalid lighting bank" == NULL);
654 /*infostream<<"Bottom for sunlight-propagated block ("
655 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
658 // Bottom sunlight is not valid; get the block and loop to it
662 block = getBlockNoCreate(pos);
664 catch(InvalidPositionException &e)
666 FATAL_ERROR("Invalid position");
675 Enable this to disable proper lighting for speeding up map
676 generation for testing or whatever
679 //if(g_settings->get(""))
681 core::map<v3s16, MapBlock*>::Iterator i;
682 i = blocks_to_update.getIterator();
683 for(; i.atEnd() == false; i++)
685 MapBlock *block = i.getNode()->getValue();
686 v3s16 p = block->getPos();
687 block->setLightingExpired(false);
695 //TimeTaker timer("unspreadLight");
696 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
701 u32 diff = modified_blocks.size() - count_was;
702 count_was = modified_blocks.size();
703 infostream<<"unspreadLight modified "<<diff<<std::endl;
707 //TimeTaker timer("spreadLight");
708 spreadLight(bank, light_sources, modified_blocks);
713 u32 diff = modified_blocks.size() - count_was;
714 count_was = modified_blocks.size();
715 infostream<<"spreadLight modified "<<diff<<std::endl;
721 //MapVoxelManipulator vmanip(this);
723 // Make a manual voxel manipulator and load all the blocks
724 // that touch the requested blocks
725 ManualMapVoxelManipulator vmanip(this);
728 //TimeTaker timer("initialEmerge");
730 core::map<v3s16, MapBlock*>::Iterator i;
731 i = blocks_to_update.getIterator();
732 for(; i.atEnd() == false; i++)
734 MapBlock *block = i.getNode()->getValue();
735 v3s16 p = block->getPos();
737 // Add all surrounding blocks
738 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
741 Add all surrounding blocks that have up-to-date lighting
742 NOTE: This doesn't quite do the job (not everything
743 appropriate is lighted)
745 /*for(s16 z=-1; z<=1; z++)
746 for(s16 y=-1; y<=1; y++)
747 for(s16 x=-1; x<=1; x++)
749 v3s16 p2 = p + v3s16(x,y,z);
750 MapBlock *block = getBlockNoCreateNoEx(p2);
755 if(block->getLightingExpired())
757 vmanip.initialEmerge(p2, p2);
760 // Lighting of block will be updated completely
761 block->setLightingExpired(false);
766 //TimeTaker timer("unSpreadLight");
767 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
770 //TimeTaker timer("spreadLight");
771 vmanip.spreadLight(bank, light_sources, nodemgr);
774 //TimeTaker timer("blitBack");
775 vmanip.blitBack(modified_blocks);
777 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
782 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
785 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
786 std::map<v3s16, MapBlock*> & modified_blocks)
788 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
789 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
792 Update information about whether day and night light differ
794 for(std::map<v3s16, MapBlock*>::iterator
795 i = modified_blocks.begin();
796 i != modified_blocks.end(); ++i)
798 MapBlock *block = i->second;
799 block->expireDayNightDiff();
803 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
804 std::map<v3s16, MapBlock*> &modified_blocks,
805 bool remove_metadata)
807 // Collect old node for rollback
808 RollbackNode rollback_oldnode(this, p, m_gamedef);
810 // This is needed for updating the lighting
811 MapNode oldnode = getNodeNoEx(p);
813 // Remove node metadata
814 if (remove_metadata) {
815 removeNodeMetadata(p);
818 // Set the node on the map
819 // Ignore light (because calling voxalgo::update_lighting_nodes)
820 n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
821 n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
825 std::vector<std::pair<v3s16, MapNode> > oldnodes;
826 oldnodes.push_back(std::pair<v3s16, MapNode>(p, oldnode));
827 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
829 for(std::map<v3s16, MapBlock*>::iterator
830 i = modified_blocks.begin();
831 i != modified_blocks.end(); ++i)
833 i->second->expireDayNightDiff();
836 // Report for rollback
837 if(m_gamedef->rollback())
839 RollbackNode rollback_newnode(this, p, m_gamedef);
840 RollbackAction action;
841 action.setSetNode(p, rollback_oldnode, rollback_newnode);
842 m_gamedef->rollback()->reportAction(action);
846 Add neighboring liquid nodes and this node to transform queue.
847 (it's vital for the node itself to get updated last, if it was removed.)
850 v3s16(0,0,1), // back
852 v3s16(1,0,0), // right
853 v3s16(0,0,-1), // front
854 v3s16(0,-1,0), // bottom
855 v3s16(-1,0,0), // left
856 v3s16(0,0,0), // self
858 for(u16 i=0; i<7; i++)
860 v3s16 p2 = p + dirs[i];
862 bool is_valid_position;
863 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
864 if(is_valid_position &&
865 (m_nodedef->get(n2).isLiquid() ||
866 n2.getContent() == CONTENT_AIR))
867 m_transforming_liquid.push_back(p2);
871 void Map::removeNodeAndUpdate(v3s16 p,
872 std::map<v3s16, MapBlock*> &modified_blocks)
874 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
877 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
880 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
884 bool succeeded = true;
886 std::map<v3s16, MapBlock*> modified_blocks;
887 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
889 // Copy modified_blocks to event
890 for(std::map<v3s16, MapBlock*>::iterator
891 i = modified_blocks.begin();
892 i != modified_blocks.end(); ++i)
894 event.modified_blocks.insert(i->first);
897 catch(InvalidPositionException &e){
901 dispatchEvent(&event);
906 bool Map::removeNodeWithEvent(v3s16 p)
909 event.type = MEET_REMOVENODE;
912 bool succeeded = true;
914 std::map<v3s16, MapBlock*> modified_blocks;
915 removeNodeAndUpdate(p, modified_blocks);
917 // Copy modified_blocks to event
918 for(std::map<v3s16, MapBlock*>::iterator
919 i = modified_blocks.begin();
920 i != modified_blocks.end(); ++i)
922 event.modified_blocks.insert(i->first);
925 catch(InvalidPositionException &e){
929 dispatchEvent(&event);
934 bool Map::getDayNightDiff(v3s16 blockpos)
937 v3s16 p = blockpos + v3s16(0,0,0);
938 MapBlock *b = getBlockNoCreate(p);
939 if(b->getDayNightDiff())
942 catch(InvalidPositionException &e){}
945 v3s16 p = blockpos + v3s16(-1,0,0);
946 MapBlock *b = getBlockNoCreate(p);
947 if(b->getDayNightDiff())
950 catch(InvalidPositionException &e){}
952 v3s16 p = blockpos + v3s16(0,-1,0);
953 MapBlock *b = getBlockNoCreate(p);
954 if(b->getDayNightDiff())
957 catch(InvalidPositionException &e){}
959 v3s16 p = blockpos + v3s16(0,0,-1);
960 MapBlock *b = getBlockNoCreate(p);
961 if(b->getDayNightDiff())
964 catch(InvalidPositionException &e){}
967 v3s16 p = blockpos + v3s16(1,0,0);
968 MapBlock *b = getBlockNoCreate(p);
969 if(b->getDayNightDiff())
972 catch(InvalidPositionException &e){}
974 v3s16 p = blockpos + v3s16(0,1,0);
975 MapBlock *b = getBlockNoCreate(p);
976 if(b->getDayNightDiff())
979 catch(InvalidPositionException &e){}
981 v3s16 p = blockpos + v3s16(0,0,1);
982 MapBlock *b = getBlockNoCreate(p);
983 if(b->getDayNightDiff())
986 catch(InvalidPositionException &e){}
991 struct TimeOrderedMapBlock {
995 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
1000 bool operator<(const TimeOrderedMapBlock &b) const
1002 return block->getUsageTimer() < b.block->getUsageTimer();
1007 Updates usage timers
1009 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
1010 std::vector<v3s16> *unloaded_blocks)
1012 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1014 // Profile modified reasons
1015 Profiler modprofiler;
1017 std::vector<v2s16> sector_deletion_queue;
1018 u32 deleted_blocks_count = 0;
1019 u32 saved_blocks_count = 0;
1020 u32 block_count_all = 0;
1024 // If there is no practical limit, we spare creation of mapblock_queue
1025 if (max_loaded_blocks == U32_MAX) {
1026 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1027 si != m_sectors.end(); ++si) {
1028 MapSector *sector = si->second;
1030 bool all_blocks_deleted = true;
1032 MapBlockVect blocks;
1033 sector->getBlocks(blocks);
1035 for (MapBlockVect::iterator i = blocks.begin();
1036 i != blocks.end(); ++i) {
1037 MapBlock *block = (*i);
1039 block->incrementUsageTimer(dtime);
1041 if (block->refGet() == 0
1042 && block->getUsageTimer() > unload_timeout) {
1043 v3s16 p = block->getPos();
1046 if (block->getModified() != MOD_STATE_CLEAN
1047 && save_before_unloading) {
1048 modprofiler.add(block->getModifiedReasonString(), 1);
1049 if (!saveBlock(block))
1051 saved_blocks_count++;
1054 // Delete from memory
1055 sector->deleteBlock(block);
1057 if (unloaded_blocks)
1058 unloaded_blocks->push_back(p);
1060 deleted_blocks_count++;
1062 all_blocks_deleted = false;
1067 if (all_blocks_deleted) {
1068 sector_deletion_queue.push_back(si->first);
1072 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
1073 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1074 si != m_sectors.end(); ++si) {
1075 MapSector *sector = si->second;
1077 MapBlockVect blocks;
1078 sector->getBlocks(blocks);
1080 for(MapBlockVect::iterator i = blocks.begin();
1081 i != blocks.end(); ++i) {
1082 MapBlock *block = (*i);
1084 block->incrementUsageTimer(dtime);
1085 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
1088 block_count_all = mapblock_queue.size();
1089 // Delete old blocks, and blocks over the limit from the memory
1090 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
1091 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
1092 TimeOrderedMapBlock b = mapblock_queue.top();
1093 mapblock_queue.pop();
1095 MapBlock *block = b.block;
1097 if (block->refGet() != 0)
1100 v3s16 p = block->getPos();
1103 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1104 modprofiler.add(block->getModifiedReasonString(), 1);
1105 if (!saveBlock(block))
1107 saved_blocks_count++;
1110 // Delete from memory
1111 b.sect->deleteBlock(block);
1113 if (unloaded_blocks)
1114 unloaded_blocks->push_back(p);
1116 deleted_blocks_count++;
1119 // Delete empty sectors
1120 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1121 si != m_sectors.end(); ++si) {
1122 if (si->second->empty()) {
1123 sector_deletion_queue.push_back(si->first);
1129 // Finally delete the empty sectors
1130 deleteSectors(sector_deletion_queue);
1132 if(deleted_blocks_count != 0)
1134 PrintInfo(infostream); // ServerMap/ClientMap:
1135 infostream<<"Unloaded "<<deleted_blocks_count
1136 <<" blocks from memory";
1137 if(save_before_unloading)
1138 infostream<<", of which "<<saved_blocks_count<<" were written";
1139 infostream<<", "<<block_count_all<<" blocks in memory";
1140 infostream<<"."<<std::endl;
1141 if(saved_blocks_count != 0){
1142 PrintInfo(infostream); // ServerMap/ClientMap:
1143 infostream<<"Blocks modified by: "<<std::endl;
1144 modprofiler.print(infostream);
1149 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1151 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
1154 void Map::deleteSectors(std::vector<v2s16> §orList)
1156 for(std::vector<v2s16>::iterator j = sectorList.begin();
1157 j != sectorList.end(); ++j) {
1158 MapSector *sector = m_sectors[*j];
1159 // If sector is in sector cache, remove it from there
1160 if(m_sector_cache == sector)
1161 m_sector_cache = NULL;
1162 // Remove from map and delete
1163 m_sectors.erase(*j);
1168 void Map::PrintInfo(std::ostream &out)
1173 #define WATER_DROP_BOOST 4
1177 NEIGHBOR_SAME_LEVEL,
1180 struct NodeNeighbor {
1184 bool l; //can liquid
1190 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1197 void Map::transforming_liquid_add(v3s16 p) {
1198 m_transforming_liquid.push_back(p);
1201 s32 Map::transforming_liquid_size() {
1202 return m_transforming_liquid.size();
1205 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
1207 DSTACK(FUNCTION_NAME);
1208 //TimeTaker timer("transformLiquids()");
1211 u32 initial_size = m_transforming_liquid.size();
1213 /*if(initial_size != 0)
1214 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1216 // list of nodes that due to viscosity have not reached their max level height
1217 std::deque<v3s16> must_reflow;
1219 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
1221 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1222 u32 loop_max = liquid_loop_max;
1226 /* If liquid_loop_max is not keeping up with the queue size increase
1227 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1229 if (m_transforming_liquid.size() > loop_max * 2) {
1231 float server_step = g_settings->getFloat("dedicated_server_step");
1232 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1233 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1235 m_transforming_liquid_loop_count_multiplier = 1.0;
1238 loop_max *= m_transforming_liquid_loop_count_multiplier;
1241 while (m_transforming_liquid.size() != 0)
1243 // This should be done here so that it is done when continue is used
1244 if (loopcount >= initial_size || loopcount >= loop_max)
1249 Get a queued transforming liquid node
1251 v3s16 p0 = m_transforming_liquid.front();
1252 m_transforming_liquid.pop_front();
1254 MapNode n0 = getNodeNoEx(p0);
1257 Collect information about current node
1259 s8 liquid_level = -1;
1260 // The liquid node which will be placed there if
1261 // the liquid flows into this node.
1262 content_t liquid_kind = CONTENT_IGNORE;
1263 // The node which will be placed there if liquid
1264 // can't flow into this node.
1265 content_t floodable_node = CONTENT_AIR;
1266 const ContentFeatures &cf = m_nodedef->get(n0);
1267 LiquidType liquid_type = cf.liquid_type;
1268 switch (liquid_type) {
1270 liquid_level = LIQUID_LEVEL_SOURCE;
1271 liquid_kind = m_nodedef->getId(cf.liquid_alternative_flowing);
1273 case LIQUID_FLOWING:
1274 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1275 liquid_kind = n0.getContent();
1278 // if this node is 'floodable', it *could* be transformed
1279 // into a liquid, otherwise, continue with the next node.
1282 floodable_node = n0.getContent();
1283 liquid_kind = CONTENT_AIR;
1288 Collect information about the environment
1290 const v3s16 *dirs = g_6dirs;
1291 NodeNeighbor sources[6]; // surrounding sources
1292 int num_sources = 0;
1293 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1295 NodeNeighbor airs[6]; // surrounding air
1297 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1298 int num_neutrals = 0;
1299 bool flowing_down = false;
1300 bool ignored_sources = false;
1301 for (u16 i = 0; i < 6; i++) {
1302 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1305 nt = NEIGHBOR_UPPER;
1308 nt = NEIGHBOR_LOWER;
1311 v3s16 npos = p0 + dirs[i];
1312 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1313 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
1314 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
1316 if (cfnb.floodable) {
1317 airs[num_airs++] = nb;
1318 // if the current node is a water source the neighbor
1319 // should be enqueded for transformation regardless of whether the
1320 // current node changes or not.
1321 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1322 m_transforming_liquid.push_back(npos);
1323 // if the current node happens to be a flowing node, it will start to flow down here.
1324 if (nb.t == NEIGHBOR_LOWER)
1325 flowing_down = true;
1327 neutrals[num_neutrals++] = nb;
1328 if (nb.n.getContent() == CONTENT_IGNORE) {
1329 // If node below is ignore prevent water from
1330 // spreading outwards and otherwise prevent from
1331 // flowing away as ignore node might be the source
1332 if (nb.t == NEIGHBOR_LOWER)
1333 flowing_down = true;
1335 ignored_sources = true;
1340 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1341 if (liquid_kind == CONTENT_AIR)
1342 liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing);
1343 if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1344 neutrals[num_neutrals++] = nb;
1346 // Do not count bottom source, it will screw things up
1348 sources[num_sources++] = nb;
1351 case LIQUID_FLOWING:
1352 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1353 if (liquid_kind == CONTENT_AIR)
1354 liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing);
1355 if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1356 neutrals[num_neutrals++] = nb;
1358 flows[num_flows++] = nb;
1359 if (nb.t == NEIGHBOR_LOWER)
1360 flowing_down = true;
1367 decide on the type (and possibly level) of the current node
1369 content_t new_node_content;
1370 s8 new_node_level = -1;
1371 s8 max_node_level = -1;
1373 u8 range = m_nodedef->get(liquid_kind).liquid_range;
1374 if (range > LIQUID_LEVEL_MAX + 1)
1375 range = LIQUID_LEVEL_MAX + 1;
1377 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1378 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1379 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1380 // it's perfectly safe to use liquid_kind here to determine the new node content.
1381 new_node_content = m_nodedef->getId(m_nodedef->get(liquid_kind).liquid_alternative_source);
1382 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1383 // liquid_kind is set properly, see above
1384 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1385 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1386 new_node_content = liquid_kind;
1388 new_node_content = floodable_node;
1389 } else if (ignored_sources && liquid_level >= 0) {
1390 // Maybe there are neighbouring sources that aren't loaded yet
1391 // so prevent flowing away.
1392 new_node_level = liquid_level;
1393 new_node_content = liquid_kind;
1395 // no surrounding sources, so get the maximum level that can flow into this node
1396 for (u16 i = 0; i < num_flows; i++) {
1397 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1398 switch (flows[i].t) {
1399 case NEIGHBOR_UPPER:
1400 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1401 max_node_level = LIQUID_LEVEL_MAX;
1402 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1403 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1404 } else if (nb_liquid_level > max_node_level) {
1405 max_node_level = nb_liquid_level;
1408 case NEIGHBOR_LOWER:
1410 case NEIGHBOR_SAME_LEVEL:
1411 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1412 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
1413 max_node_level = nb_liquid_level - 1;
1418 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
1419 if (viscosity > 1 && max_node_level != liquid_level) {
1420 // amount to gain, limited by viscosity
1421 // must be at least 1 in absolute value
1422 s8 level_inc = max_node_level - liquid_level;
1423 if (level_inc < -viscosity || level_inc > viscosity)
1424 new_node_level = liquid_level + level_inc/viscosity;
1425 else if (level_inc < 0)
1426 new_node_level = liquid_level - 1;
1427 else if (level_inc > 0)
1428 new_node_level = liquid_level + 1;
1429 if (new_node_level != max_node_level)
1430 must_reflow.push_back(p0);
1432 new_node_level = max_node_level;
1435 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1436 new_node_content = liquid_kind;
1438 new_node_content = floodable_node;
1443 check if anything has changed. if not, just continue with the next node.
1445 if (new_node_content == n0.getContent() &&
1446 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1447 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1448 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1454 update the current node
1457 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1458 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1459 // set level to last 3 bits, flowing down bit to 4th bit
1460 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1462 // set the liquid level and flow bit to 0
1463 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1465 n0.setContent(new_node_content);
1467 // Ignore light (because calling voxalgo::update_lighting_nodes)
1468 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
1469 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
1471 // Find out whether there is a suspect for this action
1472 std::string suspect;
1473 if (m_gamedef->rollback())
1474 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1476 if (m_gamedef->rollback() && !suspect.empty()) {
1478 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1479 // Get old node for rollback
1480 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1484 RollbackNode rollback_newnode(this, p0, m_gamedef);
1485 RollbackAction action;
1486 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1487 m_gamedef->rollback()->reportAction(action);
1493 v3s16 blockpos = getNodeBlockPos(p0);
1494 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1495 if (block != NULL) {
1496 modified_blocks[blockpos] = block;
1497 changed_nodes.push_back(std::pair<v3s16, MapNode>(p0, n00));
1501 enqueue neighbors for update if neccessary
1503 switch (m_nodedef->get(n0.getContent()).liquid_type) {
1505 case LIQUID_FLOWING:
1506 // make sure source flows into all neighboring nodes
1507 for (u16 i = 0; i < num_flows; i++)
1508 if (flows[i].t != NEIGHBOR_UPPER)
1509 m_transforming_liquid.push_back(flows[i].p);
1510 for (u16 i = 0; i < num_airs; i++)
1511 if (airs[i].t != NEIGHBOR_UPPER)
1512 m_transforming_liquid.push_back(airs[i].p);
1515 // this flow has turned to air; neighboring flows might need to do the same
1516 for (u16 i = 0; i < num_flows; i++)
1517 m_transforming_liquid.push_back(flows[i].p);
1521 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1523 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1524 m_transforming_liquid.push_back(*iter);
1526 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
1529 /* ----------------------------------------------------------------------
1530 * Manage the queue so that it does not grow indefinately
1532 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1534 if (time_until_purge == 0)
1535 return; // Feature disabled
1537 time_until_purge *= 1000; // seconds -> milliseconds
1539 u32 curr_time = getTime(PRECISION_MILLI);
1540 u32 prev_unprocessed = m_unprocessed_count;
1541 m_unprocessed_count = m_transforming_liquid.size();
1543 // if unprocessed block count is decreasing or stable
1544 if (m_unprocessed_count <= prev_unprocessed) {
1545 m_queue_size_timer_started = false;
1547 if (!m_queue_size_timer_started)
1548 m_inc_trending_up_start_time = curr_time;
1549 m_queue_size_timer_started = true;
1552 // Account for curr_time overflowing
1553 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1554 m_queue_size_timer_started = false;
1556 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1557 * and the number of unprocessed blocks is still > liquid_loop_max then we
1558 * cannot keep up; dump the oldest blocks from the queue so that the queue
1559 * has liquid_loop_max items in it
1561 if (m_queue_size_timer_started
1562 && curr_time - m_inc_trending_up_start_time > time_until_purge
1563 && m_unprocessed_count > liquid_loop_max) {
1565 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1567 infostream << "transformLiquids(): DUMPING " << dump_qty
1568 << " blocks from the queue" << std::endl;
1571 m_transforming_liquid.pop_front();
1573 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1574 m_unprocessed_count = m_transforming_liquid.size();
1578 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1580 std::vector<v3s16> positions_with_meta;
1582 sortBoxVerticies(p1, p2);
1583 v3s16 bpmin = getNodeBlockPos(p1);
1584 v3s16 bpmax = getNodeBlockPos(p2);
1586 VoxelArea area(p1, p2);
1588 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1589 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1590 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1591 v3s16 blockpos(x, y, z);
1593 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1595 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1596 << PP(blockpos) << std::endl;
1597 block = emergeBlock(blockpos, false);
1600 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1605 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1606 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1607 for (size_t i = 0; i != keys.size(); i++) {
1608 v3s16 p(keys[i] + p_base);
1609 if (!area.contains(p))
1612 positions_with_meta.push_back(p);
1616 return positions_with_meta;
1619 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1621 v3s16 blockpos = getNodeBlockPos(p);
1622 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1623 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1625 infostream<<"Map::getNodeMetadata(): Need to emerge "
1626 <<PP(blockpos)<<std::endl;
1627 block = emergeBlock(blockpos, false);
1630 warningstream<<"Map::getNodeMetadata(): Block not found"
1634 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1638 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1640 v3s16 blockpos = getNodeBlockPos(p);
1641 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1642 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1644 infostream<<"Map::setNodeMetadata(): Need to emerge "
1645 <<PP(blockpos)<<std::endl;
1646 block = emergeBlock(blockpos, false);
1649 warningstream<<"Map::setNodeMetadata(): Block not found"
1653 block->m_node_metadata.set(p_rel, meta);
1657 void Map::removeNodeMetadata(v3s16 p)
1659 v3s16 blockpos = getNodeBlockPos(p);
1660 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1661 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1664 warningstream<<"Map::removeNodeMetadata(): Block not found"
1668 block->m_node_metadata.remove(p_rel);
1671 NodeTimer Map::getNodeTimer(v3s16 p)
1673 v3s16 blockpos = getNodeBlockPos(p);
1674 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1675 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1677 infostream<<"Map::getNodeTimer(): Need to emerge "
1678 <<PP(blockpos)<<std::endl;
1679 block = emergeBlock(blockpos, false);
1682 warningstream<<"Map::getNodeTimer(): Block not found"
1686 NodeTimer t = block->m_node_timers.get(p_rel);
1687 NodeTimer nt(t.timeout, t.elapsed, p);
1691 void Map::setNodeTimer(const NodeTimer &t)
1693 v3s16 p = t.position;
1694 v3s16 blockpos = getNodeBlockPos(p);
1695 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1696 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1698 infostream<<"Map::setNodeTimer(): Need to emerge "
1699 <<PP(blockpos)<<std::endl;
1700 block = emergeBlock(blockpos, false);
1703 warningstream<<"Map::setNodeTimer(): Block not found"
1707 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1708 block->m_node_timers.set(nt);
1711 void Map::removeNodeTimer(v3s16 p)
1713 v3s16 blockpos = getNodeBlockPos(p);
1714 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1715 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1718 warningstream<<"Map::removeNodeTimer(): Block not found"
1722 block->m_node_timers.remove(p_rel);
1728 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1729 Map(dout_server, gamedef),
1730 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1732 m_map_metadata_changed(true)
1734 verbosestream<<FUNCTION_NAME<<std::endl;
1736 // Tell the EmergeManager about our MapSettingsManager
1737 emerge->map_settings_mgr = &settings_mgr;
1740 Try to load map; if not found, create a new one.
1743 // Determine which database backend to use
1744 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1746 bool succeeded = conf.readConfigFile(conf_path.c_str());
1747 if (!succeeded || !conf.exists("backend")) {
1748 // fall back to sqlite3
1749 conf.set("backend", "sqlite3");
1751 std::string backend = conf.get("backend");
1752 dbase = createDatabase(backend, savedir, conf);
1754 if (!conf.updateConfigFile(conf_path.c_str()))
1755 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1757 m_savedir = savedir;
1758 m_map_saving_enabled = false;
1762 // If directory exists, check contents and load if possible
1763 if(fs::PathExists(m_savedir))
1765 // If directory is empty, it is safe to save into it.
1766 if(fs::GetDirListing(m_savedir).size() == 0)
1768 infostream<<"ServerMap: Empty save directory is valid."
1770 m_map_saving_enabled = true;
1775 if (settings_mgr.loadMapMeta()) {
1776 infostream << "ServerMap: Metadata loaded from "
1777 << savedir << std::endl;
1779 infostream << "ServerMap: Metadata could not be loaded "
1780 "from " << savedir << ", assuming valid save "
1781 "directory." << std::endl;
1784 m_map_saving_enabled = true;
1785 // Map loaded, not creating new one
1789 // If directory doesn't exist, it is safe to save to it
1791 m_map_saving_enabled = true;
1794 catch(std::exception &e)
1796 warningstream<<"ServerMap: Failed to load map from "<<savedir
1797 <<", exception: "<<e.what()<<std::endl;
1798 infostream<<"Please remove the map or fix it."<<std::endl;
1799 warningstream<<"Map saving will be disabled."<<std::endl;
1802 infostream<<"Initializing new map."<<std::endl;
1804 // Create zero sector
1805 emergeSector(v2s16(0,0));
1807 // Initially write whole map
1808 save(MOD_STATE_CLEAN);
1811 ServerMap::~ServerMap()
1813 verbosestream<<FUNCTION_NAME<<std::endl;
1817 if(m_map_saving_enabled)
1819 // Save only changed parts
1820 save(MOD_STATE_WRITE_AT_UNLOAD);
1821 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
1825 infostream<<"ServerMap: Map not saved"<<std::endl;
1828 catch(std::exception &e)
1830 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1831 <<", exception: "<<e.what()<<std::endl;
1835 Close database if it was opened
1843 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1844 for(; i.atEnd() == false; i++)
1846 MapChunk *chunk = i.getNode()->getValue();
1852 MapgenParams *ServerMap::getMapgenParams()
1854 // getMapgenParams() should only ever be called after Server is initialized
1855 assert(settings_mgr.mapgen_params != NULL);
1856 return settings_mgr.mapgen_params;
1859 u64 ServerMap::getSeed()
1861 return getMapgenParams()->seed;
1864 s16 ServerMap::getWaterLevel()
1866 return getMapgenParams()->water_level;
1869 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1871 s16 csize = getMapgenParams()->chunksize;
1872 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1873 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1875 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1876 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1878 v3s16 extra_borders(1, 1, 1);
1879 v3s16 full_bpmin = bpmin - extra_borders;
1880 v3s16 full_bpmax = bpmax + extra_borders;
1882 // Do nothing if not inside limits (+-1 because of neighbors)
1883 if (blockpos_over_limit(full_bpmin) ||
1884 blockpos_over_limit(full_bpmax))
1887 data->seed = getSeed();
1888 data->blockpos_min = bpmin;
1889 data->blockpos_max = bpmax;
1890 data->blockpos_requested = blockpos;
1891 data->nodedef = m_nodedef;
1894 Create the whole area of this and the neighboring blocks
1896 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1897 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1898 v2s16 sectorpos(x, z);
1899 // Sector metadata is loaded from disk if not already loaded.
1900 ServerMapSector *sector = createSector(sectorpos);
1901 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1903 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1906 MapBlock *block = emergeBlock(p, false);
1907 if (block == NULL) {
1908 block = createBlock(p);
1910 // Block gets sunlight if this is true.
1911 // Refer to the map generator heuristics.
1912 bool ug = m_emerge->isBlockUnderground(p);
1913 block->setIsUnderground(ug);
1919 Now we have a big empty area.
1921 Make a ManualMapVoxelManipulator that contains this and the
1925 data->vmanip = new MMVManip(this);
1926 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1928 // Note: we may need this again at some point.
1930 // Ensure none of the blocks to be generated were marked as
1931 // containing CONTENT_IGNORE
1932 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1933 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1934 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1935 core::map<v3s16, u8>::Node *n;
1936 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1939 u8 flags = n->getValue();
1940 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1947 // Data is ready now.
1951 void ServerMap::finishBlockMake(BlockMakeData *data,
1952 std::map<v3s16, MapBlock*> *changed_blocks)
1954 v3s16 bpmin = data->blockpos_min;
1955 v3s16 bpmax = data->blockpos_max;
1957 v3s16 extra_borders(1, 1, 1);
1959 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1960 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1963 Blit generated stuff to map
1964 NOTE: blitBackAll adds nearly everything to changed_blocks
1966 data->vmanip->blitBackAll(changed_blocks);
1968 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1969 << changed_blocks->size());
1972 Copy transforming liquid information
1974 while (data->transforming_liquid.size()) {
1975 m_transforming_liquid.push_back(data->transforming_liquid.front());
1976 data->transforming_liquid.pop_front();
1979 for (std::map<v3s16, MapBlock *>::iterator
1980 it = changed_blocks->begin();
1981 it != changed_blocks->end(); ++it) {
1982 MapBlock *block = it->second;
1986 Update day/night difference cache of the MapBlocks
1988 block->expireDayNightDiff();
1990 Set block as modified
1992 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1993 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1997 Set central blocks as generated
1999 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2000 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2001 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2002 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2006 block->setGenerated(true);
2010 Save changed parts of map
2011 NOTE: Will be saved later.
2013 //save(MOD_STATE_WRITE_AT_UNLOAD);
2016 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2018 DSTACKF("%s: p2d=(%d,%d)",
2023 Check if it exists already in memory
2025 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2030 Try to load it from disk (with blocks)
2032 //if(loadSectorFull(p2d) == true)
2035 Try to load metadata from disk
2038 if(loadSectorMeta(p2d) == true)
2040 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2043 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2044 throw InvalidPositionException("");
2051 Do not create over-limit.
2052 We are checking for any nodes of the mapblocks of the sector being beyond the limit.
2053 A sector is a vertical column of mapblocks, so sectorpos is like a 2D blockpos.
2055 At the negative limit we are checking for
2056 block minimum nodepos < -mapgenlimit.
2057 At the positive limit we are checking for
2058 block maximum nodepos > mapgenlimit.
2060 Block minimum nodepos = blockpos * mapblocksize.
2061 Block maximum nodepos = (blockpos + 1) * mapblocksize - 1.
2063 const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2064 g_settings->getU16("map_generation_limit"));
2065 if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit
2066 || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit
2067 || p2d.Y * MAP_BLOCKSIZE < -map_gen_limit
2068 || (p2d.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit)
2069 throw InvalidPositionException("createSector(): pos. over limit");
2072 Generate blank sector
2075 sector = new ServerMapSector(this, p2d, m_gamedef);
2077 // Sector position on map in nodes
2078 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2083 m_sectors[p2d] = sector;
2090 This is a quick-hand function for calling makeBlock().
2092 MapBlock * ServerMap::generateBlock(
2094 std::map<v3s16, MapBlock*> &modified_blocks
2097 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2099 /*infostream<<"generateBlock(): "
2100 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2103 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2105 TimeTaker timer("generateBlock");
2107 //MapBlock *block = original_dummy;
2109 v2s16 p2d(p.X, p.Z);
2110 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2113 Do not generate over-limit
2115 if(blockpos_over_limit(p))
2117 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2118 throw InvalidPositionException("generateBlock(): pos. over limit");
2122 Create block make data
2125 initBlockMake(&data, p);
2131 TimeTaker t("mapgen::make_block()");
2132 mapgen->makeChunk(&data);
2133 //mapgen::make_block(&data);
2135 if(enable_mapgen_debug_info == false)
2136 t.stop(true); // Hide output
2140 Blit data back on map, update lighting, add mobs and whatever this does
2142 finishBlockMake(&data, modified_blocks);
2147 MapBlock *block = getBlockNoCreateNoEx(p);
2155 bool erroneus_content = false;
2156 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2157 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2158 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2161 MapNode n = block->getNode(p);
2162 if(n.getContent() == CONTENT_IGNORE)
2164 infostream<<"CONTENT_IGNORE at "
2165 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2167 erroneus_content = true;
2171 if(erroneus_content)
2180 Generate a completely empty block
2184 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2185 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2187 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2190 n.setContent(CONTENT_AIR);
2191 block->setNode(v3s16(x0,y0,z0), n);
2197 if(enable_mapgen_debug_info == false)
2198 timer.stop(true); // Hide output
2204 MapBlock * ServerMap::createBlock(v3s16 p)
2206 DSTACKF("%s: p=(%d,%d,%d)",
2207 FUNCTION_NAME, p.X, p.Y, p.Z);
2210 Do not create over-limit
2212 if (blockpos_over_limit(p))
2213 throw InvalidPositionException("createBlock(): pos. over limit");
2215 v2s16 p2d(p.X, p.Z);
2218 This will create or load a sector if not found in memory.
2219 If block exists on disk, it will be loaded.
2221 NOTE: On old save formats, this will be slow, as it generates
2222 lighting on blocks for them.
2224 ServerMapSector *sector;
2226 sector = (ServerMapSector*)createSector(p2d);
2227 assert(sector->getId() == MAPSECTOR_SERVER);
2229 catch(InvalidPositionException &e)
2231 infostream<<"createBlock: createSector() failed"<<std::endl;
2235 NOTE: This should not be done, or at least the exception
2236 should not be passed on as std::exception, because it
2237 won't be catched at all.
2239 /*catch(std::exception &e)
2241 infostream<<"createBlock: createSector() failed: "
2242 <<e.what()<<std::endl;
2247 Try to get a block from the sector
2250 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2253 if(block->isDummy())
2258 block = sector->createBlankBlock(block_y);
2263 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2265 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2267 p.X, p.Y, p.Z, create_blank);
2270 MapBlock *block = getBlockNoCreateNoEx(p);
2271 if(block && block->isDummy() == false)
2276 MapBlock *block = loadBlock(p);
2282 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2283 MapBlock *block = sector->createBlankBlock(p.Y);
2291 std::map<v3s16, MapBlock*> modified_blocks;
2292 MapBlock *block = generateBlock(p, modified_blocks);
2296 event.type = MEET_OTHER;
2299 // Copy modified_blocks to event
2300 for(std::map<v3s16, MapBlock*>::iterator
2301 i = modified_blocks.begin();
2302 i != modified_blocks.end(); ++i)
2304 event.modified_blocks.insert(i->first);
2308 dispatchEvent(&event);
2318 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2320 MapBlock *block = getBlockNoCreateNoEx(p3d);
2322 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2327 void ServerMap::prepareBlock(MapBlock *block) {
2330 // N.B. This requires no synchronization, since data will not be modified unless
2331 // the VoxelManipulator being updated belongs to the same thread.
2332 void ServerMap::updateVManip(v3s16 pos)
2334 Mapgen *mg = m_emerge->getCurrentMapgen();
2338 MMVManip *vm = mg->vm;
2342 if (!vm->m_area.contains(pos))
2345 s32 idx = vm->m_area.index(pos);
2346 vm->m_data[idx] = getNodeNoEx(pos);
2347 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2349 vm->m_is_dirty = true;
2352 s16 ServerMap::findGroundLevel(v2s16 p2d)
2356 Uh, just do something random...
2358 // Find existing map from top to down
2361 v3s16 p(p2d.X, max, p2d.Y);
2362 for(; p.Y>min; p.Y--)
2364 MapNode n = getNodeNoEx(p);
2365 if(n.getContent() != CONTENT_IGNORE)
2370 // If this node is not air, go to plan b
2371 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2373 // Search existing walkable and return it
2374 for(; p.Y>min; p.Y--)
2376 MapNode n = getNodeNoEx(p);
2377 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2386 Determine from map generator noise functions
2389 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2392 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2393 //return (s16)level;
2396 bool ServerMap::loadFromFolders() {
2397 if (!dbase->initialized() &&
2398 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2403 void ServerMap::createDirs(std::string path)
2405 if(fs::CreateAllDirs(path) == false)
2407 m_dout<<"ServerMap: Failed to create directory "
2408 <<"\""<<path<<"\""<<std::endl;
2409 throw BaseException("ServerMap failed to create directory");
2413 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2419 snprintf(cc, 9, "%.4x%.4x",
2420 (unsigned int) pos.X & 0xffff,
2421 (unsigned int) pos.Y & 0xffff);
2423 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2425 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2426 (unsigned int) pos.X & 0xfff,
2427 (unsigned int) pos.Y & 0xfff);
2429 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2436 v2s16 ServerMap::getSectorPos(std::string dirname)
2438 unsigned int x = 0, y = 0;
2440 std::string component;
2441 fs::RemoveLastPathComponent(dirname, &component, 1);
2442 if(component.size() == 8)
2445 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2447 else if(component.size() == 3)
2450 fs::RemoveLastPathComponent(dirname, &component, 2);
2451 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2452 // Sign-extend the 12 bit values up to 16 bits...
2453 if(x & 0x800) x |= 0xF000;
2454 if(y & 0x800) y |= 0xF000;
2461 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2462 v2s16 pos((s16)x, (s16)y);
2466 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2468 v2s16 p2d = getSectorPos(sectordir);
2470 if(blockfile.size() != 4){
2471 throw InvalidFilenameException("Invalid block filename");
2474 int r = sscanf(blockfile.c_str(), "%4x", &y);
2476 throw InvalidFilenameException("Invalid block filename");
2477 return v3s16(p2d.X, y, p2d.Y);
2480 std::string ServerMap::getBlockFilename(v3s16 p)
2483 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2487 void ServerMap::save(ModifiedState save_level)
2489 DSTACK(FUNCTION_NAME);
2490 if(m_map_saving_enabled == false) {
2491 warningstream<<"Not saving map, saving disabled."<<std::endl;
2495 if(save_level == MOD_STATE_CLEAN)
2496 infostream<<"ServerMap: Saving whole map, this can take time."
2499 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2500 if (settings_mgr.saveMapMeta())
2501 m_map_metadata_changed = false;
2504 // Profile modified reasons
2505 Profiler modprofiler;
2507 u32 sector_meta_count = 0;
2508 u32 block_count = 0;
2509 u32 block_count_all = 0; // Number of blocks in memory
2511 // Don't do anything with sqlite unless something is really saved
2512 bool save_started = false;
2514 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2515 i != m_sectors.end(); ++i) {
2516 ServerMapSector *sector = (ServerMapSector*)i->second;
2517 assert(sector->getId() == MAPSECTOR_SERVER);
2519 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2520 saveSectorMeta(sector);
2521 sector_meta_count++;
2524 MapBlockVect blocks;
2525 sector->getBlocks(blocks);
2527 for(MapBlockVect::iterator j = blocks.begin();
2528 j != blocks.end(); ++j) {
2529 MapBlock *block = *j;
2533 if(block->getModified() >= (u32)save_level) {
2537 save_started = true;
2540 modprofiler.add(block->getModifiedReasonString(), 1);
2545 /*infostream<<"ServerMap: Written block ("
2546 <<block->getPos().X<<","
2547 <<block->getPos().Y<<","
2548 <<block->getPos().Z<<")"
2558 Only print if something happened or saved whole map
2560 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2561 || block_count != 0) {
2562 infostream<<"ServerMap: Written: "
2563 <<sector_meta_count<<" sector metadata files, "
2564 <<block_count<<" block files"
2565 <<", "<<block_count_all<<" blocks in memory."
2567 PrintInfo(infostream); // ServerMap/ClientMap:
2568 infostream<<"Blocks modified by: "<<std::endl;
2569 modprofiler.print(infostream);
2573 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2575 if (loadFromFolders()) {
2576 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2577 << "all blocks that are stored in flat files." << std::endl;
2579 dbase->listAllLoadableBlocks(dst);
2582 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2584 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2585 si != m_sectors.end(); ++si)
2587 MapSector *sector = si->second;
2589 MapBlockVect blocks;
2590 sector->getBlocks(blocks);
2592 for(MapBlockVect::iterator i = blocks.begin();
2593 i != blocks.end(); ++i) {
2594 v3s16 p = (*i)->getPos();
2600 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2602 DSTACK(FUNCTION_NAME);
2603 // Format used for writing
2604 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2606 v2s16 pos = sector->getPos();
2607 std::string dir = getSectorDir(pos);
2610 std::string fullpath = dir + DIR_DELIM + "meta";
2611 std::ostringstream ss(std::ios_base::binary);
2613 sector->serialize(ss, version);
2615 if(!fs::safeWriteToFile(fullpath, ss.str()))
2616 throw FileNotGoodException("Cannot write sector metafile");
2618 sector->differs_from_disk = false;
2621 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2623 DSTACK(FUNCTION_NAME);
2625 v2s16 p2d = getSectorPos(sectordir);
2627 ServerMapSector *sector = NULL;
2629 std::string fullpath = sectordir + DIR_DELIM + "meta";
2630 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2631 if(is.good() == false)
2633 // If the directory exists anyway, it probably is in some old
2634 // format. Just go ahead and create the sector.
2635 if(fs::PathExists(sectordir))
2637 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
2638 <<fullpath<<" doesn't exist but directory does."
2639 <<" Continuing with a sector with no metadata."
2641 sector = new ServerMapSector(this, p2d, m_gamedef);
2642 m_sectors[p2d] = sector;
2646 throw FileNotGoodException("Cannot open sector metafile");
2651 sector = ServerMapSector::deSerialize
2652 (is, this, p2d, m_sectors, m_gamedef);
2654 saveSectorMeta(sector);
2657 sector->differs_from_disk = false;
2662 bool ServerMap::loadSectorMeta(v2s16 p2d)
2664 DSTACK(FUNCTION_NAME);
2666 // The directory layout we're going to load from.
2667 // 1 - original sectors/xxxxzzzz/
2668 // 2 - new sectors2/xxx/zzz/
2669 // If we load from anything but the latest structure, we will
2670 // immediately save to the new one, and remove the old.
2672 std::string sectordir1 = getSectorDir(p2d, 1);
2673 std::string sectordir;
2674 if(fs::PathExists(sectordir1))
2676 sectordir = sectordir1;
2681 sectordir = getSectorDir(p2d, 2);
2685 loadSectorMeta(sectordir, loadlayout != 2);
2687 catch(InvalidFilenameException &e)
2691 catch(FileNotGoodException &e)
2695 catch(std::exception &e)
2704 bool ServerMap::loadSectorFull(v2s16 p2d)
2706 DSTACK(FUNCTION_NAME);
2708 MapSector *sector = NULL;
2710 // The directory layout we're going to load from.
2711 // 1 - original sectors/xxxxzzzz/
2712 // 2 - new sectors2/xxx/zzz/
2713 // If we load from anything but the latest structure, we will
2714 // immediately save to the new one, and remove the old.
2716 std::string sectordir1 = getSectorDir(p2d, 1);
2717 std::string sectordir;
2718 if(fs::PathExists(sectordir1))
2720 sectordir = sectordir1;
2725 sectordir = getSectorDir(p2d, 2);
2729 sector = loadSectorMeta(sectordir, loadlayout != 2);
2731 catch(InvalidFilenameException &e)
2735 catch(FileNotGoodException &e)
2739 catch(std::exception &e)
2747 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2749 std::vector<fs::DirListNode>::iterator i2;
2750 for(i2=list2.begin(); i2!=list2.end(); i2++)
2756 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2758 catch(InvalidFilenameException &e)
2760 // This catches unknown crap in directory
2766 infostream<<"Sector converted to new layout - deleting "<<
2767 sectordir1<<std::endl;
2768 fs::RecursiveDelete(sectordir1);
2775 Database *ServerMap::createDatabase(
2776 const std::string &name,
2777 const std::string &savedir,
2780 if (name == "sqlite3")
2781 return new Database_SQLite3(savedir);
2782 if (name == "dummy")
2783 return new Database_Dummy();
2785 else if (name == "leveldb")
2786 return new Database_LevelDB(savedir);
2789 else if (name == "redis")
2790 return new Database_Redis(conf);
2793 else if (name == "postgresql")
2794 return new Database_PostgreSQL(conf);
2797 throw BaseException(std::string("Database backend ") + name + " not supported.");
2800 void ServerMap::beginSave()
2805 void ServerMap::endSave()
2810 bool ServerMap::saveBlock(MapBlock *block)
2812 return saveBlock(block, dbase);
2815 bool ServerMap::saveBlock(MapBlock *block, Database *db)
2817 v3s16 p3d = block->getPos();
2819 // Dummy blocks are not written
2820 if (block->isDummy()) {
2821 warningstream << "saveBlock: Not writing dummy block "
2822 << PP(p3d) << std::endl;
2826 // Format used for writing
2827 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2830 [0] u8 serialization version
2833 std::ostringstream o(std::ios_base::binary);
2834 o.write((char*) &version, 1);
2835 block->serialize(o, version, true);
2837 std::string data = o.str();
2838 bool ret = db->saveBlock(p3d, data);
2840 // We just wrote it to the disk so clear modified flag
2841 block->resetModified();
2846 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
2847 MapSector *sector, bool save_after_load)
2849 DSTACK(FUNCTION_NAME);
2851 std::string fullpath = sectordir + DIR_DELIM + blockfile;
2854 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2855 if(is.good() == false)
2856 throw FileNotGoodException("Cannot open block file");
2858 v3s16 p3d = getBlockPos(sectordir, blockfile);
2859 v2s16 p2d(p3d.X, p3d.Z);
2861 assert(sector->getPos() == p2d);
2863 u8 version = SER_FMT_VER_INVALID;
2864 is.read((char*)&version, 1);
2867 throw SerializationError("ServerMap::loadBlock(): Failed"
2868 " to read MapBlock version");
2870 /*u32 block_size = MapBlock::serializedLength(version);
2871 SharedBuffer<u8> data(block_size);
2872 is.read((char*)*data, block_size);*/
2874 // This will always return a sector because we're the server
2875 //MapSector *sector = emergeSector(p2d);
2877 MapBlock *block = NULL;
2878 bool created_new = false;
2879 block = sector->getBlockNoCreateNoEx(p3d.Y);
2882 block = sector->createBlankBlockNoInsert(p3d.Y);
2887 block->deSerialize(is, version, true);
2889 // If it's a new block, insert it to the map
2891 sector->insertBlock(block);
2892 ReflowScan scanner(this, m_emerge->ndef);
2893 scanner.scan(block, &m_transforming_liquid);
2897 Save blocks loaded in old format in new format
2900 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
2904 // Should be in database now, so delete the old file
2905 fs::RecursiveDelete(fullpath);
2908 // We just loaded it from the disk, so it's up-to-date.
2909 block->resetModified();
2912 catch(SerializationError &e)
2914 warningstream<<"Invalid block data on disk "
2915 <<"fullpath="<<fullpath
2916 <<" (SerializationError). "
2917 <<"what()="<<e.what()
2919 // Ignoring. A new one will be generated.
2922 // TODO: Backup file; name is in fullpath.
2926 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
2928 DSTACK(FUNCTION_NAME);
2931 std::istringstream is(*blob, std::ios_base::binary);
2933 u8 version = SER_FMT_VER_INVALID;
2934 is.read((char*)&version, 1);
2937 throw SerializationError("ServerMap::loadBlock(): Failed"
2938 " to read MapBlock version");
2940 /*u32 block_size = MapBlock::serializedLength(version);
2941 SharedBuffer<u8> data(block_size);
2942 is.read((char*)*data, block_size);*/
2944 // This will always return a sector because we're the server
2945 //MapSector *sector = emergeSector(p2d);
2947 MapBlock *block = NULL;
2948 bool created_new = false;
2949 block = sector->getBlockNoCreateNoEx(p3d.Y);
2952 block = sector->createBlankBlockNoInsert(p3d.Y);
2957 block->deSerialize(is, version, true);
2959 // If it's a new block, insert it to the map
2961 sector->insertBlock(block);
2962 ReflowScan scanner(this, m_emerge->ndef);
2963 scanner.scan(block, &m_transforming_liquid);
2967 Save blocks loaded in old format in new format
2970 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
2971 // Only save if asked to; no need to update version
2975 // We just loaded it from, so it's up-to-date.
2976 block->resetModified();
2978 catch(SerializationError &e)
2980 errorstream<<"Invalid block data in database"
2981 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2982 <<" (SerializationError): "<<e.what()<<std::endl;
2984 // TODO: Block should be marked as invalid in memory so that it is
2985 // not touched but the game can run
2987 if(g_settings->getBool("ignore_world_load_errors")){
2988 errorstream<<"Ignoring block load error. Duck and cover! "
2989 <<"(ignore_world_load_errors)"<<std::endl;
2991 throw SerializationError("Invalid block data in database");
2996 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
2998 DSTACK(FUNCTION_NAME);
3000 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
3002 v2s16 p2d(blockpos.X, blockpos.Z);
3005 dbase->loadBlock(blockpos, &ret);
3007 loadBlock(&ret, blockpos, createSector(p2d), false);
3009 // Not found in database, try the files
3011 // The directory layout we're going to load from.
3012 // 1 - original sectors/xxxxzzzz/
3013 // 2 - new sectors2/xxx/zzz/
3014 // If we load from anything but the latest structure, we will
3015 // immediately save to the new one, and remove the old.
3017 std::string sectordir1 = getSectorDir(p2d, 1);
3018 std::string sectordir;
3019 if (fs::PathExists(sectordir1)) {
3020 sectordir = sectordir1;
3023 sectordir = getSectorDir(p2d, 2);
3027 Make sure sector is loaded
3030 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3031 if (sector == NULL) {
3033 sector = loadSectorMeta(sectordir, loadlayout != 2);
3034 } catch(InvalidFilenameException &e) {
3036 } catch(FileNotGoodException &e) {
3038 } catch(std::exception &e) {
3045 Make sure file exists
3048 std::string blockfilename = getBlockFilename(blockpos);
3049 if (fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3053 Load block and save it to the database
3055 loadBlock(sectordir, blockfilename, sector, true);
3057 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3058 if (created_new && (block != NULL)) {
3059 std::map<v3s16, MapBlock*> modified_blocks;
3060 // Fix lighting if necessary
3061 voxalgo::update_block_border_lighting(this, block, modified_blocks);
3062 if (!modified_blocks.empty()) {
3063 //Modified lighting, send event
3065 event.type = MEET_OTHER;
3066 std::map<v3s16, MapBlock *>::iterator it;
3067 for (it = modified_blocks.begin();
3068 it != modified_blocks.end(); ++it)
3069 event.modified_blocks.insert(it->first);
3070 dispatchEvent(&event);
3076 bool ServerMap::deleteBlock(v3s16 blockpos)
3078 if (!dbase->deleteBlock(blockpos))
3081 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3083 v2s16 p2d(blockpos.X, blockpos.Z);
3084 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3087 sector->deleteBlock(block);
3093 void ServerMap::PrintInfo(std::ostream &out)
3098 MMVManip::MMVManip(Map *map):
3101 m_create_area(false),
3106 MMVManip::~MMVManip()
3110 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3111 bool load_if_inexistent)
3113 TimeTaker timer1("initialEmerge", &emerge_time);
3115 // Units of these are MapBlocks
3116 v3s16 p_min = blockpos_min;
3117 v3s16 p_max = blockpos_max;
3119 VoxelArea block_area_nodes
3120 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3122 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3125 infostream<<"initialEmerge: area: ";
3126 block_area_nodes.print(infostream);
3127 infostream<<" ("<<size_MB<<"MB)";
3128 infostream<<std::endl;
3131 addArea(block_area_nodes);
3133 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3134 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3135 for(s32 x=p_min.X; x<=p_max.X; x++)
3140 std::map<v3s16, u8>::iterator n;
3141 n = m_loaded_blocks.find(p);
3142 if(n != m_loaded_blocks.end())
3145 bool block_data_inexistent = false;
3148 TimeTaker timer1("emerge load", &emerge_load_time);
3150 block = m_map->getBlockNoCreate(p);
3151 if(block->isDummy())
3152 block_data_inexistent = true;
3154 block->copyTo(*this);
3156 catch(InvalidPositionException &e)
3158 block_data_inexistent = true;
3161 if(block_data_inexistent)
3164 if (load_if_inexistent) {
3165 ServerMap *svrmap = (ServerMap *)m_map;
3166 block = svrmap->emergeBlock(p, false);
3168 block = svrmap->createBlock(p);
3169 block->copyTo(*this);
3171 flags |= VMANIP_BLOCK_DATA_INEXIST;
3174 Mark area inexistent
3176 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3177 // Fill with VOXELFLAG_NO_DATA
3178 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3179 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3181 s32 i = m_area.index(a.MinEdge.X,y,z);
3182 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3186 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3188 // Mark that block was loaded as blank
3189 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3192 m_loaded_blocks[p] = flags;
3198 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3199 bool overwrite_generated)
3201 if(m_area.getExtent() == v3s16(0,0,0))
3205 Copy data of all blocks
3207 for(std::map<v3s16, u8>::iterator
3208 i = m_loaded_blocks.begin();
3209 i != m_loaded_blocks.end(); ++i)
3212 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3213 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3214 if ((existed == false) || (block == NULL) ||
3215 (overwrite_generated == false && block->isGenerated() == true))
3218 block->copyFrom(*this);
3221 (*modified_blocks)[p] = block;