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, m_nodedef, 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, m_nodedef, 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);
1958 v3s16 full_bpmin = bpmin - extra_borders;
1959 v3s16 full_bpmax = bpmax + extra_borders;
1961 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1962 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1965 Set lighting to non-expired state in all of them.
1966 This is cheating, but it is not fast enough if all of them
1967 would actually be updated.
1969 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1970 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
1971 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1972 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
1976 block->setLightingExpired(false);
1980 Blit generated stuff to map
1981 NOTE: blitBackAll adds nearly everything to changed_blocks
1983 data->vmanip->blitBackAll(changed_blocks);
1985 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1986 << changed_blocks->size());
1989 Copy transforming liquid information
1991 while (data->transforming_liquid.size()) {
1992 m_transforming_liquid.push_back(data->transforming_liquid.front());
1993 data->transforming_liquid.pop_front();
1996 for (std::map<v3s16, MapBlock *>::iterator
1997 it = changed_blocks->begin();
1998 it != changed_blocks->end(); ++it) {
1999 MapBlock *block = it->second;
2003 Update day/night difference cache of the MapBlocks
2005 block->expireDayNightDiff();
2007 Set block as modified
2009 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2010 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2014 Set central blocks as generated
2016 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2017 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2018 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2019 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2023 block->setGenerated(true);
2027 Save changed parts of map
2028 NOTE: Will be saved later.
2030 //save(MOD_STATE_WRITE_AT_UNLOAD);
2033 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2035 DSTACKF("%s: p2d=(%d,%d)",
2040 Check if it exists already in memory
2042 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2047 Try to load it from disk (with blocks)
2049 //if(loadSectorFull(p2d) == true)
2052 Try to load metadata from disk
2055 if(loadSectorMeta(p2d) == true)
2057 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2060 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2061 throw InvalidPositionException("");
2068 Do not create over-limit.
2069 We are checking for any nodes of the mapblocks of the sector being beyond the limit.
2070 A sector is a vertical column of mapblocks, so sectorpos is like a 2D blockpos.
2072 At the negative limit we are checking for
2073 block minimum nodepos < -mapgenlimit.
2074 At the positive limit we are checking for
2075 block maximum nodepos > mapgenlimit.
2077 Block minimum nodepos = blockpos * mapblocksize.
2078 Block maximum nodepos = (blockpos + 1) * mapblocksize - 1.
2080 const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2081 g_settings->getU16("map_generation_limit"));
2082 if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit
2083 || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit
2084 || p2d.Y * MAP_BLOCKSIZE < -map_gen_limit
2085 || (p2d.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit)
2086 throw InvalidPositionException("createSector(): pos. over limit");
2089 Generate blank sector
2092 sector = new ServerMapSector(this, p2d, m_gamedef);
2094 // Sector position on map in nodes
2095 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2100 m_sectors[p2d] = sector;
2107 This is a quick-hand function for calling makeBlock().
2109 MapBlock * ServerMap::generateBlock(
2111 std::map<v3s16, MapBlock*> &modified_blocks
2114 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2116 /*infostream<<"generateBlock(): "
2117 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2120 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2122 TimeTaker timer("generateBlock");
2124 //MapBlock *block = original_dummy;
2126 v2s16 p2d(p.X, p.Z);
2127 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2130 Do not generate over-limit
2132 if(blockpos_over_limit(p))
2134 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2135 throw InvalidPositionException("generateBlock(): pos. over limit");
2139 Create block make data
2142 initBlockMake(&data, p);
2148 TimeTaker t("mapgen::make_block()");
2149 mapgen->makeChunk(&data);
2150 //mapgen::make_block(&data);
2152 if(enable_mapgen_debug_info == false)
2153 t.stop(true); // Hide output
2157 Blit data back on map, update lighting, add mobs and whatever this does
2159 finishBlockMake(&data, modified_blocks);
2164 MapBlock *block = getBlockNoCreateNoEx(p);
2172 bool erroneus_content = false;
2173 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2174 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2175 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2178 MapNode n = block->getNode(p);
2179 if(n.getContent() == CONTENT_IGNORE)
2181 infostream<<"CONTENT_IGNORE at "
2182 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2184 erroneus_content = true;
2188 if(erroneus_content)
2197 Generate a completely empty block
2201 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2202 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2204 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2207 n.setContent(CONTENT_AIR);
2208 block->setNode(v3s16(x0,y0,z0), n);
2214 if(enable_mapgen_debug_info == false)
2215 timer.stop(true); // Hide output
2221 MapBlock * ServerMap::createBlock(v3s16 p)
2223 DSTACKF("%s: p=(%d,%d,%d)",
2224 FUNCTION_NAME, p.X, p.Y, p.Z);
2227 Do not create over-limit
2229 if (blockpos_over_limit(p))
2230 throw InvalidPositionException("createBlock(): pos. over limit");
2232 v2s16 p2d(p.X, p.Z);
2235 This will create or load a sector if not found in memory.
2236 If block exists on disk, it will be loaded.
2238 NOTE: On old save formats, this will be slow, as it generates
2239 lighting on blocks for them.
2241 ServerMapSector *sector;
2243 sector = (ServerMapSector*)createSector(p2d);
2244 assert(sector->getId() == MAPSECTOR_SERVER);
2246 catch(InvalidPositionException &e)
2248 infostream<<"createBlock: createSector() failed"<<std::endl;
2252 NOTE: This should not be done, or at least the exception
2253 should not be passed on as std::exception, because it
2254 won't be catched at all.
2256 /*catch(std::exception &e)
2258 infostream<<"createBlock: createSector() failed: "
2259 <<e.what()<<std::endl;
2264 Try to get a block from the sector
2267 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2270 if(block->isDummy())
2275 block = sector->createBlankBlock(block_y);
2280 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2282 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2284 p.X, p.Y, p.Z, create_blank);
2287 MapBlock *block = getBlockNoCreateNoEx(p);
2288 if(block && block->isDummy() == false)
2293 MapBlock *block = loadBlock(p);
2299 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2300 MapBlock *block = sector->createBlankBlock(p.Y);
2308 std::map<v3s16, MapBlock*> modified_blocks;
2309 MapBlock *block = generateBlock(p, modified_blocks);
2313 event.type = MEET_OTHER;
2316 // Copy modified_blocks to event
2317 for(std::map<v3s16, MapBlock*>::iterator
2318 i = modified_blocks.begin();
2319 i != modified_blocks.end(); ++i)
2321 event.modified_blocks.insert(i->first);
2325 dispatchEvent(&event);
2335 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2337 MapBlock *block = getBlockNoCreateNoEx(p3d);
2339 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2344 void ServerMap::prepareBlock(MapBlock *block) {
2347 // N.B. This requires no synchronization, since data will not be modified unless
2348 // the VoxelManipulator being updated belongs to the same thread.
2349 void ServerMap::updateVManip(v3s16 pos)
2351 Mapgen *mg = m_emerge->getCurrentMapgen();
2355 MMVManip *vm = mg->vm;
2359 if (!vm->m_area.contains(pos))
2362 s32 idx = vm->m_area.index(pos);
2363 vm->m_data[idx] = getNodeNoEx(pos);
2364 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2366 vm->m_is_dirty = true;
2369 s16 ServerMap::findGroundLevel(v2s16 p2d)
2373 Uh, just do something random...
2375 // Find existing map from top to down
2378 v3s16 p(p2d.X, max, p2d.Y);
2379 for(; p.Y>min; p.Y--)
2381 MapNode n = getNodeNoEx(p);
2382 if(n.getContent() != CONTENT_IGNORE)
2387 // If this node is not air, go to plan b
2388 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2390 // Search existing walkable and return it
2391 for(; p.Y>min; p.Y--)
2393 MapNode n = getNodeNoEx(p);
2394 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2403 Determine from map generator noise functions
2406 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2409 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2410 //return (s16)level;
2413 bool ServerMap::loadFromFolders() {
2414 if (!dbase->initialized() &&
2415 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2420 void ServerMap::createDirs(std::string path)
2422 if(fs::CreateAllDirs(path) == false)
2424 m_dout<<"ServerMap: Failed to create directory "
2425 <<"\""<<path<<"\""<<std::endl;
2426 throw BaseException("ServerMap failed to create directory");
2430 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2436 snprintf(cc, 9, "%.4x%.4x",
2437 (unsigned int) pos.X & 0xffff,
2438 (unsigned int) pos.Y & 0xffff);
2440 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2442 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2443 (unsigned int) pos.X & 0xfff,
2444 (unsigned int) pos.Y & 0xfff);
2446 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2453 v2s16 ServerMap::getSectorPos(std::string dirname)
2455 unsigned int x = 0, y = 0;
2457 std::string component;
2458 fs::RemoveLastPathComponent(dirname, &component, 1);
2459 if(component.size() == 8)
2462 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2464 else if(component.size() == 3)
2467 fs::RemoveLastPathComponent(dirname, &component, 2);
2468 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2469 // Sign-extend the 12 bit values up to 16 bits...
2470 if(x & 0x800) x |= 0xF000;
2471 if(y & 0x800) y |= 0xF000;
2478 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2479 v2s16 pos((s16)x, (s16)y);
2483 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2485 v2s16 p2d = getSectorPos(sectordir);
2487 if(blockfile.size() != 4){
2488 throw InvalidFilenameException("Invalid block filename");
2491 int r = sscanf(blockfile.c_str(), "%4x", &y);
2493 throw InvalidFilenameException("Invalid block filename");
2494 return v3s16(p2d.X, y, p2d.Y);
2497 std::string ServerMap::getBlockFilename(v3s16 p)
2500 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2504 void ServerMap::save(ModifiedState save_level)
2506 DSTACK(FUNCTION_NAME);
2507 if(m_map_saving_enabled == false) {
2508 warningstream<<"Not saving map, saving disabled."<<std::endl;
2512 if(save_level == MOD_STATE_CLEAN)
2513 infostream<<"ServerMap: Saving whole map, this can take time."
2516 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2517 if (settings_mgr.saveMapMeta())
2518 m_map_metadata_changed = false;
2521 // Profile modified reasons
2522 Profiler modprofiler;
2524 u32 sector_meta_count = 0;
2525 u32 block_count = 0;
2526 u32 block_count_all = 0; // Number of blocks in memory
2528 // Don't do anything with sqlite unless something is really saved
2529 bool save_started = false;
2531 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2532 i != m_sectors.end(); ++i) {
2533 ServerMapSector *sector = (ServerMapSector*)i->second;
2534 assert(sector->getId() == MAPSECTOR_SERVER);
2536 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2537 saveSectorMeta(sector);
2538 sector_meta_count++;
2541 MapBlockVect blocks;
2542 sector->getBlocks(blocks);
2544 for(MapBlockVect::iterator j = blocks.begin();
2545 j != blocks.end(); ++j) {
2546 MapBlock *block = *j;
2550 if(block->getModified() >= (u32)save_level) {
2554 save_started = true;
2557 modprofiler.add(block->getModifiedReasonString(), 1);
2562 /*infostream<<"ServerMap: Written block ("
2563 <<block->getPos().X<<","
2564 <<block->getPos().Y<<","
2565 <<block->getPos().Z<<")"
2575 Only print if something happened or saved whole map
2577 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2578 || block_count != 0) {
2579 infostream<<"ServerMap: Written: "
2580 <<sector_meta_count<<" sector metadata files, "
2581 <<block_count<<" block files"
2582 <<", "<<block_count_all<<" blocks in memory."
2584 PrintInfo(infostream); // ServerMap/ClientMap:
2585 infostream<<"Blocks modified by: "<<std::endl;
2586 modprofiler.print(infostream);
2590 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2592 if (loadFromFolders()) {
2593 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2594 << "all blocks that are stored in flat files." << std::endl;
2596 dbase->listAllLoadableBlocks(dst);
2599 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2601 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2602 si != m_sectors.end(); ++si)
2604 MapSector *sector = si->second;
2606 MapBlockVect blocks;
2607 sector->getBlocks(blocks);
2609 for(MapBlockVect::iterator i = blocks.begin();
2610 i != blocks.end(); ++i) {
2611 v3s16 p = (*i)->getPos();
2617 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2619 DSTACK(FUNCTION_NAME);
2620 // Format used for writing
2621 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2623 v2s16 pos = sector->getPos();
2624 std::string dir = getSectorDir(pos);
2627 std::string fullpath = dir + DIR_DELIM + "meta";
2628 std::ostringstream ss(std::ios_base::binary);
2630 sector->serialize(ss, version);
2632 if(!fs::safeWriteToFile(fullpath, ss.str()))
2633 throw FileNotGoodException("Cannot write sector metafile");
2635 sector->differs_from_disk = false;
2638 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2640 DSTACK(FUNCTION_NAME);
2642 v2s16 p2d = getSectorPos(sectordir);
2644 ServerMapSector *sector = NULL;
2646 std::string fullpath = sectordir + DIR_DELIM + "meta";
2647 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2648 if(is.good() == false)
2650 // If the directory exists anyway, it probably is in some old
2651 // format. Just go ahead and create the sector.
2652 if(fs::PathExists(sectordir))
2654 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
2655 <<fullpath<<" doesn't exist but directory does."
2656 <<" Continuing with a sector with no metadata."
2658 sector = new ServerMapSector(this, p2d, m_gamedef);
2659 m_sectors[p2d] = sector;
2663 throw FileNotGoodException("Cannot open sector metafile");
2668 sector = ServerMapSector::deSerialize
2669 (is, this, p2d, m_sectors, m_gamedef);
2671 saveSectorMeta(sector);
2674 sector->differs_from_disk = false;
2679 bool ServerMap::loadSectorMeta(v2s16 p2d)
2681 DSTACK(FUNCTION_NAME);
2683 // The directory layout we're going to load from.
2684 // 1 - original sectors/xxxxzzzz/
2685 // 2 - new sectors2/xxx/zzz/
2686 // If we load from anything but the latest structure, we will
2687 // immediately save to the new one, and remove the old.
2689 std::string sectordir1 = getSectorDir(p2d, 1);
2690 std::string sectordir;
2691 if(fs::PathExists(sectordir1))
2693 sectordir = sectordir1;
2698 sectordir = getSectorDir(p2d, 2);
2702 loadSectorMeta(sectordir, loadlayout != 2);
2704 catch(InvalidFilenameException &e)
2708 catch(FileNotGoodException &e)
2712 catch(std::exception &e)
2721 bool ServerMap::loadSectorFull(v2s16 p2d)
2723 DSTACK(FUNCTION_NAME);
2725 MapSector *sector = NULL;
2727 // The directory layout we're going to load from.
2728 // 1 - original sectors/xxxxzzzz/
2729 // 2 - new sectors2/xxx/zzz/
2730 // If we load from anything but the latest structure, we will
2731 // immediately save to the new one, and remove the old.
2733 std::string sectordir1 = getSectorDir(p2d, 1);
2734 std::string sectordir;
2735 if(fs::PathExists(sectordir1))
2737 sectordir = sectordir1;
2742 sectordir = getSectorDir(p2d, 2);
2746 sector = loadSectorMeta(sectordir, loadlayout != 2);
2748 catch(InvalidFilenameException &e)
2752 catch(FileNotGoodException &e)
2756 catch(std::exception &e)
2764 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2766 std::vector<fs::DirListNode>::iterator i2;
2767 for(i2=list2.begin(); i2!=list2.end(); i2++)
2773 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2775 catch(InvalidFilenameException &e)
2777 // This catches unknown crap in directory
2783 infostream<<"Sector converted to new layout - deleting "<<
2784 sectordir1<<std::endl;
2785 fs::RecursiveDelete(sectordir1);
2792 Database *ServerMap::createDatabase(
2793 const std::string &name,
2794 const std::string &savedir,
2797 if (name == "sqlite3")
2798 return new Database_SQLite3(savedir);
2799 if (name == "dummy")
2800 return new Database_Dummy();
2802 else if (name == "leveldb")
2803 return new Database_LevelDB(savedir);
2806 else if (name == "redis")
2807 return new Database_Redis(conf);
2810 else if (name == "postgresql")
2811 return new Database_PostgreSQL(conf);
2814 throw BaseException(std::string("Database backend ") + name + " not supported.");
2817 void ServerMap::beginSave()
2822 void ServerMap::endSave()
2827 bool ServerMap::saveBlock(MapBlock *block)
2829 return saveBlock(block, dbase);
2832 bool ServerMap::saveBlock(MapBlock *block, Database *db)
2834 v3s16 p3d = block->getPos();
2836 // Dummy blocks are not written
2837 if (block->isDummy()) {
2838 warningstream << "saveBlock: Not writing dummy block "
2839 << PP(p3d) << std::endl;
2843 // Format used for writing
2844 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2847 [0] u8 serialization version
2850 std::ostringstream o(std::ios_base::binary);
2851 o.write((char*) &version, 1);
2852 block->serialize(o, version, true);
2854 std::string data = o.str();
2855 bool ret = db->saveBlock(p3d, data);
2857 // We just wrote it to the disk so clear modified flag
2858 block->resetModified();
2863 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
2864 MapSector *sector, bool save_after_load)
2866 DSTACK(FUNCTION_NAME);
2868 std::string fullpath = sectordir + DIR_DELIM + blockfile;
2871 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2872 if(is.good() == false)
2873 throw FileNotGoodException("Cannot open block file");
2875 v3s16 p3d = getBlockPos(sectordir, blockfile);
2876 v2s16 p2d(p3d.X, p3d.Z);
2878 assert(sector->getPos() == p2d);
2880 u8 version = SER_FMT_VER_INVALID;
2881 is.read((char*)&version, 1);
2884 throw SerializationError("ServerMap::loadBlock(): Failed"
2885 " to read MapBlock version");
2887 /*u32 block_size = MapBlock::serializedLength(version);
2888 SharedBuffer<u8> data(block_size);
2889 is.read((char*)*data, block_size);*/
2891 // This will always return a sector because we're the server
2892 //MapSector *sector = emergeSector(p2d);
2894 MapBlock *block = NULL;
2895 bool created_new = false;
2896 block = sector->getBlockNoCreateNoEx(p3d.Y);
2899 block = sector->createBlankBlockNoInsert(p3d.Y);
2904 block->deSerialize(is, version, true);
2906 // If it's a new block, insert it to the map
2908 sector->insertBlock(block);
2909 ReflowScan scanner(this, m_emerge->ndef);
2910 scanner.scan(block, &m_transforming_liquid);
2914 Save blocks loaded in old format in new format
2917 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
2921 // Should be in database now, so delete the old file
2922 fs::RecursiveDelete(fullpath);
2925 // We just loaded it from the disk, so it's up-to-date.
2926 block->resetModified();
2929 catch(SerializationError &e)
2931 warningstream<<"Invalid block data on disk "
2932 <<"fullpath="<<fullpath
2933 <<" (SerializationError). "
2934 <<"what()="<<e.what()
2936 // Ignoring. A new one will be generated.
2939 // TODO: Backup file; name is in fullpath.
2943 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
2945 DSTACK(FUNCTION_NAME);
2948 std::istringstream is(*blob, std::ios_base::binary);
2950 u8 version = SER_FMT_VER_INVALID;
2951 is.read((char*)&version, 1);
2954 throw SerializationError("ServerMap::loadBlock(): Failed"
2955 " to read MapBlock version");
2957 /*u32 block_size = MapBlock::serializedLength(version);
2958 SharedBuffer<u8> data(block_size);
2959 is.read((char*)*data, block_size);*/
2961 // This will always return a sector because we're the server
2962 //MapSector *sector = emergeSector(p2d);
2964 MapBlock *block = NULL;
2965 bool created_new = false;
2966 block = sector->getBlockNoCreateNoEx(p3d.Y);
2969 block = sector->createBlankBlockNoInsert(p3d.Y);
2974 block->deSerialize(is, version, true);
2976 // If it's a new block, insert it to the map
2978 sector->insertBlock(block);
2979 ReflowScan scanner(this, m_emerge->ndef);
2980 scanner.scan(block, &m_transforming_liquid);
2984 Save blocks loaded in old format in new format
2987 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
2988 // Only save if asked to; no need to update version
2992 // We just loaded it from, so it's up-to-date.
2993 block->resetModified();
2996 catch(SerializationError &e)
2998 errorstream<<"Invalid block data in database"
2999 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3000 <<" (SerializationError): "<<e.what()<<std::endl;
3002 // TODO: Block should be marked as invalid in memory so that it is
3003 // not touched but the game can run
3005 if(g_settings->getBool("ignore_world_load_errors")){
3006 errorstream<<"Ignoring block load error. Duck and cover! "
3007 <<"(ignore_world_load_errors)"<<std::endl;
3009 throw SerializationError("Invalid block data in database");
3014 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3016 DSTACK(FUNCTION_NAME);
3018 v2s16 p2d(blockpos.X, blockpos.Z);
3021 dbase->loadBlock(blockpos, &ret);
3023 loadBlock(&ret, blockpos, createSector(p2d), false);
3024 return getBlockNoCreateNoEx(blockpos);
3026 // Not found in database, try the files
3028 // The directory layout we're going to load from.
3029 // 1 - original sectors/xxxxzzzz/
3030 // 2 - new sectors2/xxx/zzz/
3031 // If we load from anything but the latest structure, we will
3032 // immediately save to the new one, and remove the old.
3034 std::string sectordir1 = getSectorDir(p2d, 1);
3035 std::string sectordir;
3036 if(fs::PathExists(sectordir1))
3038 sectordir = sectordir1;
3043 sectordir = getSectorDir(p2d, 2);
3047 Make sure sector is loaded
3050 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3054 sector = loadSectorMeta(sectordir, loadlayout != 2);
3056 catch(InvalidFilenameException &e)
3060 catch(FileNotGoodException &e)
3064 catch(std::exception &e)
3071 Make sure file exists
3074 std::string blockfilename = getBlockFilename(blockpos);
3075 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3079 Load block and save it to the database
3081 loadBlock(sectordir, blockfilename, sector, true);
3082 return getBlockNoCreateNoEx(blockpos);
3085 bool ServerMap::deleteBlock(v3s16 blockpos)
3087 if (!dbase->deleteBlock(blockpos))
3090 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3092 v2s16 p2d(blockpos.X, blockpos.Z);
3093 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3096 sector->deleteBlock(block);
3102 void ServerMap::PrintInfo(std::ostream &out)
3107 MMVManip::MMVManip(Map *map):
3110 m_create_area(false),
3115 MMVManip::~MMVManip()
3119 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3120 bool load_if_inexistent)
3122 TimeTaker timer1("initialEmerge", &emerge_time);
3124 // Units of these are MapBlocks
3125 v3s16 p_min = blockpos_min;
3126 v3s16 p_max = blockpos_max;
3128 VoxelArea block_area_nodes
3129 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3131 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3134 infostream<<"initialEmerge: area: ";
3135 block_area_nodes.print(infostream);
3136 infostream<<" ("<<size_MB<<"MB)";
3137 infostream<<std::endl;
3140 addArea(block_area_nodes);
3142 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3143 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3144 for(s32 x=p_min.X; x<=p_max.X; x++)
3149 std::map<v3s16, u8>::iterator n;
3150 n = m_loaded_blocks.find(p);
3151 if(n != m_loaded_blocks.end())
3154 bool block_data_inexistent = false;
3157 TimeTaker timer1("emerge load", &emerge_load_time);
3159 block = m_map->getBlockNoCreate(p);
3160 if(block->isDummy())
3161 block_data_inexistent = true;
3163 block->copyTo(*this);
3165 catch(InvalidPositionException &e)
3167 block_data_inexistent = true;
3170 if(block_data_inexistent)
3173 if (load_if_inexistent) {
3174 ServerMap *svrmap = (ServerMap *)m_map;
3175 block = svrmap->emergeBlock(p, false);
3177 block = svrmap->createBlock(p);
3178 block->copyTo(*this);
3180 flags |= VMANIP_BLOCK_DATA_INEXIST;
3183 Mark area inexistent
3185 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3186 // Fill with VOXELFLAG_NO_DATA
3187 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3188 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3190 s32 i = m_area.index(a.MinEdge.X,y,z);
3191 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3195 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3197 // Mark that block was loaded as blank
3198 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3201 m_loaded_blocks[p] = flags;
3207 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3208 bool overwrite_generated)
3210 if(m_area.getExtent() == v3s16(0,0,0))
3214 Copy data of all blocks
3216 for(std::map<v3s16, u8>::iterator
3217 i = m_loaded_blocks.begin();
3218 i != m_loaded_blocks.end(); ++i)
3221 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3222 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3223 if ((existed == false) || (block == NULL) ||
3224 (overwrite_generated == false && block->isGenerated() == true))
3227 block->copyFrom(*this);
3230 (*modified_blocks)[p] = block;