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/mathconstants.h"
36 #include "util/basic_macros.h"
37 #include "rollback_interface.h"
38 #include "environment.h"
39 #include "reflowscan.h"
41 #include "mapgen_v6.h"
46 #include "database-dummy.h"
47 #include "database-sqlite3.h"
51 #include "database-leveldb.h"
54 #include "database-redis.h"
57 #include "database-postgresql.h"
65 Map::Map(std::ostream &dout, IGameDef *gamedef):
69 m_nodedef(gamedef->ndef()),
70 m_transforming_liquid_loop_count_multiplier(1.0f),
71 m_unprocessed_count(0),
72 m_inc_trending_up_start_time(0),
73 m_queue_size_timer_started(false)
82 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
83 i != m_sectors.end(); ++i)
89 void Map::addEventReceiver(MapEventReceiver *event_receiver)
91 m_event_receivers.insert(event_receiver);
94 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
96 m_event_receivers.erase(event_receiver);
99 void Map::dispatchEvent(MapEditEvent *event)
101 for(std::set<MapEventReceiver*>::iterator
102 i = m_event_receivers.begin();
103 i != m_event_receivers.end(); ++i)
105 (*i)->onMapEditEvent(event);
109 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
111 if(m_sector_cache != NULL && p == m_sector_cache_p){
112 MapSector * sector = m_sector_cache;
116 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
118 if(n == m_sectors.end())
121 MapSector *sector = n->second;
123 // Cache the last result
124 m_sector_cache_p = p;
125 m_sector_cache = sector;
130 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
132 return getSectorNoGenerateNoExNoLock(p);
135 MapSector * Map::getSectorNoGenerate(v2s16 p)
137 MapSector *sector = getSectorNoGenerateNoEx(p);
139 throw InvalidPositionException();
144 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
146 v2s16 p2d(p3d.X, p3d.Z);
147 MapSector * sector = getSectorNoGenerateNoEx(p2d);
150 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
154 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
156 MapBlock *block = getBlockNoCreateNoEx(p3d);
158 throw InvalidPositionException();
162 bool Map::isNodeUnderground(v3s16 p)
164 v3s16 blockpos = getNodeBlockPos(p);
166 MapBlock * block = getBlockNoCreate(blockpos);
167 return block->getIsUnderground();
169 catch(InvalidPositionException &e)
175 bool Map::isValidPosition(v3s16 p)
177 v3s16 blockpos = getNodeBlockPos(p);
178 MapBlock *block = getBlockNoCreateNoEx(blockpos);
179 return (block != NULL);
182 // Returns a CONTENT_IGNORE node if not found
183 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
185 v3s16 blockpos = getNodeBlockPos(p);
186 MapBlock *block = getBlockNoCreateNoEx(blockpos);
188 if (is_valid_position != NULL)
189 *is_valid_position = false;
190 return MapNode(CONTENT_IGNORE);
193 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
195 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
196 if (is_valid_position != NULL)
197 *is_valid_position = is_valid_p;
203 // throws InvalidPositionException if not found
204 // TODO: Now this is deprecated, getNodeNoEx should be renamed
205 MapNode Map::getNode(v3s16 p)
207 v3s16 blockpos = getNodeBlockPos(p);
208 MapBlock *block = getBlockNoCreateNoEx(blockpos);
210 throw InvalidPositionException();
211 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
212 bool is_valid_position;
213 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
214 if (!is_valid_position)
215 throw InvalidPositionException();
220 // throws InvalidPositionException if not found
221 void Map::setNode(v3s16 p, MapNode & n)
223 v3s16 blockpos = getNodeBlockPos(p);
224 MapBlock *block = getBlockNoCreate(blockpos);
225 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
226 // Never allow placing CONTENT_IGNORE, it fucks up stuff
227 if(n.getContent() == CONTENT_IGNORE){
229 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
230 <<" while trying to replace \""
231 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
232 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
233 debug_stacks_print_to(infostream);
236 block->setNodeNoCheck(relpos, n);
240 Goes recursively through the neighbours of the node.
242 Alters only transparent nodes.
244 If the lighting of the neighbour is lower than the lighting of
245 the node was (before changing it to 0 at the step before), the
246 lighting of the neighbour is set to 0 and then the same stuff
247 repeats for the neighbour.
249 The ending nodes of the routine are stored in light_sources.
250 This is useful when a light is removed. In such case, this
251 routine can be called for the light node and then again for
252 light_sources to re-light the area without the removed light.
254 values of from_nodes are lighting values.
256 void Map::unspreadLight(enum LightBank bank,
257 std::map<v3s16, u8> & from_nodes,
258 std::set<v3s16> & light_sources,
259 std::map<v3s16, MapBlock*> & modified_blocks)
262 v3s16(0,0,1), // back
264 v3s16(1,0,0), // right
265 v3s16(0,0,-1), // front
266 v3s16(0,-1,0), // bottom
267 v3s16(-1,0,0), // left
270 if(from_nodes.empty())
273 u32 blockchangecount = 0;
275 std::map<v3s16, u8> unlighted_nodes;
278 Initialize block cache
281 MapBlock *block = NULL;
282 // Cache this a bit, too
283 bool block_checked_in_modified = false;
285 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
286 j != from_nodes.end(); ++j)
288 v3s16 pos = j->first;
289 v3s16 blockpos = getNodeBlockPos(pos);
291 // Only fetch a new block if the block position has changed
293 if(block == NULL || blockpos != blockpos_last){
294 block = getBlockNoCreate(blockpos);
295 blockpos_last = blockpos;
297 block_checked_in_modified = false;
301 catch(InvalidPositionException &e)
309 // Calculate relative position in block
310 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
312 // Get node straight from the block
313 //MapNode n = block->getNode(relpos);
315 u8 oldlight = j->second;
317 // Loop through 6 neighbors
318 for(u16 i=0; i<6; i++)
320 // Get the position of the neighbor node
321 v3s16 n2pos = pos + dirs[i];
323 // Get the block where the node is located
324 v3s16 blockpos, relpos;
325 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
327 // Only fetch a new block if the block position has changed
329 if(block == NULL || blockpos != blockpos_last){
330 block = getBlockNoCreate(blockpos);
331 blockpos_last = blockpos;
333 block_checked_in_modified = false;
337 catch(InvalidPositionException &e) {
341 // Get node straight from the block
342 bool is_valid_position;
343 MapNode n2 = block->getNode(relpos, &is_valid_position);
344 if (!is_valid_position)
347 bool changed = false;
349 //TODO: Optimize output by optimizing light_sources?
352 If the neighbor is dimmer than what was specified
353 as oldlight (the light of the previous node)
355 if(n2.getLight(bank, m_nodedef) < oldlight)
358 And the neighbor is transparent and it has some light
360 if(m_nodedef->get(n2).light_propagates
361 && n2.getLight(bank, m_nodedef) != 0)
364 Set light to 0 and add to queue
367 u8 current_light = n2.getLight(bank, m_nodedef);
368 n2.setLight(bank, 0, m_nodedef);
369 block->setNode(relpos, n2);
371 unlighted_nodes[n2pos] = current_light;
375 Remove from light_sources if it is there
376 NOTE: This doesn't happen nearly at all
378 /*if(light_sources.find(n2pos))
380 infostream<<"Removed from light_sources"<<std::endl;
381 light_sources.remove(n2pos);
386 if(light_sources.find(n2pos) != NULL)
387 light_sources.remove(n2pos);*/
390 light_sources.insert(n2pos);
393 // Add to modified_blocks
394 if(changed == true && block_checked_in_modified == false)
396 // If the block is not found in modified_blocks, add.
397 if(modified_blocks.find(blockpos) == modified_blocks.end())
399 modified_blocks[blockpos] = block;
401 block_checked_in_modified = true;
406 /*infostream<<"unspreadLight(): Changed block "
407 <<blockchangecount<<" times"
408 <<" for "<<from_nodes.size()<<" nodes"
411 if(!unlighted_nodes.empty())
412 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
416 Lights neighbors of from_nodes, collects all them and then
419 void Map::spreadLight(enum LightBank bank,
420 std::set<v3s16> & from_nodes,
421 std::map<v3s16, MapBlock*> & modified_blocks)
423 const v3s16 dirs[6] = {
424 v3s16(0,0,1), // back
426 v3s16(1,0,0), // right
427 v3s16(0,0,-1), // front
428 v3s16(0,-1,0), // bottom
429 v3s16(-1,0,0), // left
432 if(from_nodes.empty())
435 u32 blockchangecount = 0;
437 std::set<v3s16> lighted_nodes;
440 Initialize block cache
443 MapBlock *block = NULL;
444 // Cache this a bit, too
445 bool block_checked_in_modified = false;
447 for(std::set<v3s16>::iterator j = from_nodes.begin();
448 j != from_nodes.end(); ++j)
451 v3s16 blockpos, relpos;
453 getNodeBlockPosWithOffset(pos, blockpos, relpos);
455 // Only fetch a new block if the block position has changed
457 if(block == NULL || blockpos != blockpos_last){
458 block = getBlockNoCreate(blockpos);
459 blockpos_last = blockpos;
461 block_checked_in_modified = false;
465 catch(InvalidPositionException &e) {
472 // Get node straight from the block
473 bool is_valid_position;
474 MapNode n = block->getNode(relpos, &is_valid_position);
476 u8 oldlight = is_valid_position ? n.getLight(bank, m_nodedef) : 0;
477 u8 newlight = diminish_light(oldlight);
479 // Loop through 6 neighbors
480 for(u16 i=0; i<6; i++){
481 // Get the position of the neighbor node
482 v3s16 n2pos = pos + dirs[i];
484 // Get the block where the node is located
485 v3s16 blockpos, relpos;
486 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
488 // Only fetch a new block if the block position has changed
490 if(block == NULL || blockpos != blockpos_last){
491 block = getBlockNoCreate(blockpos);
492 blockpos_last = blockpos;
494 block_checked_in_modified = false;
498 catch(InvalidPositionException &e) {
502 // Get node straight from the block
503 MapNode n2 = block->getNode(relpos, &is_valid_position);
504 if (!is_valid_position)
507 bool changed = false;
509 If the neighbor is brighter than the current node,
510 add to list (it will light up this node on its turn)
512 if(n2.getLight(bank, m_nodedef) > undiminish_light(oldlight))
514 lighted_nodes.insert(n2pos);
518 If the neighbor is dimmer than how much light this node
519 would spread on it, add to list
521 if(n2.getLight(bank, m_nodedef) < newlight)
523 if(m_nodedef->get(n2).light_propagates)
525 n2.setLight(bank, newlight, m_nodedef);
526 block->setNode(relpos, n2);
527 lighted_nodes.insert(n2pos);
532 // Add to modified_blocks
533 if(changed == true && block_checked_in_modified == false)
535 // If the block is not found in modified_blocks, add.
536 if(modified_blocks.find(blockpos) == modified_blocks.end())
538 modified_blocks[blockpos] = block;
540 block_checked_in_modified = true;
545 /*infostream<<"spreadLight(): Changed block "
546 <<blockchangecount<<" times"
547 <<" for "<<from_nodes.size()<<" nodes"
550 if(!lighted_nodes.empty())
551 spreadLight(bank, lighted_nodes, modified_blocks);
554 void Map::updateLighting(enum LightBank bank,
555 std::map<v3s16, MapBlock*> & a_blocks,
556 std::map<v3s16, MapBlock*> & modified_blocks)
558 /*m_dout<<"Map::updateLighting(): "
559 <<a_blocks.size()<<" blocks."<<std::endl;*/
561 //TimeTaker timer("updateLighting");
565 //u32 count_was = modified_blocks.size();
567 //std::map<v3s16, MapBlock*> blocks_to_update;
569 std::set<v3s16> light_sources;
571 std::map<v3s16, u8> unlight_from;
573 int num_bottom_invalid = 0;
576 //TimeTaker t("first stuff");
578 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
579 i != a_blocks.end(); ++i)
581 MapBlock *block = i->second;
585 // Don't bother with dummy blocks.
589 v3s16 pos = block->getPos();
590 v3s16 posnodes = block->getPosRelative();
591 modified_blocks[pos] = block;
592 //blocks_to_update[pos] = block;
595 Clear all light from block
597 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
598 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
599 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
602 bool is_valid_position;
603 MapNode n = block->getNode(p, &is_valid_position);
604 if (!is_valid_position) {
605 /* This would happen when dealing with a
608 infostream<<"updateLighting(): InvalidPositionException"
612 u8 oldlight = n.getLight(bank, m_nodedef);
613 n.setLight(bank, 0, m_nodedef);
614 block->setNode(p, n);
616 // If node sources light, add to list
617 u8 source = m_nodedef->get(n).light_source;
619 light_sources.insert(p + posnodes);
621 // Collect borders for unlighting
622 if((x==0 || x == MAP_BLOCKSIZE-1
623 || y==0 || y == MAP_BLOCKSIZE-1
624 || z==0 || z == MAP_BLOCKSIZE-1)
627 v3s16 p_map = p + posnodes;
628 unlight_from[p_map] = oldlight;
634 if(bank == LIGHTBANK_DAY)
636 bool bottom_valid = block->propagateSunlight(light_sources);
639 num_bottom_invalid++;
641 // If bottom is valid, we're done.
645 else if(bank == LIGHTBANK_NIGHT)
647 // For night lighting, sunlight is not propagated
652 assert("Invalid lighting bank" == NULL);
655 /*infostream<<"Bottom for sunlight-propagated block ("
656 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
659 // Bottom sunlight is not valid; get the block and loop to it
663 block = getBlockNoCreate(pos);
665 catch(InvalidPositionException &e)
667 FATAL_ERROR("Invalid position");
676 Enable this to disable proper lighting for speeding up map
677 generation for testing or whatever
680 //if(g_settings->get(""))
682 core::map<v3s16, MapBlock*>::Iterator i;
683 i = blocks_to_update.getIterator();
684 for(; i.atEnd() == false; i++)
686 MapBlock *block = i.getNode()->getValue();
687 v3s16 p = block->getPos();
688 block->setLightingExpired(false);
696 //TimeTaker timer("unspreadLight");
697 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
702 u32 diff = modified_blocks.size() - count_was;
703 count_was = modified_blocks.size();
704 infostream<<"unspreadLight modified "<<diff<<std::endl;
708 //TimeTaker timer("spreadLight");
709 spreadLight(bank, light_sources, modified_blocks);
714 u32 diff = modified_blocks.size() - count_was;
715 count_was = modified_blocks.size();
716 infostream<<"spreadLight modified "<<diff<<std::endl;
722 //MapVoxelManipulator vmanip(this);
724 // Make a manual voxel manipulator and load all the blocks
725 // that touch the requested blocks
726 ManualMapVoxelManipulator vmanip(this);
729 //TimeTaker timer("initialEmerge");
731 core::map<v3s16, MapBlock*>::Iterator i;
732 i = blocks_to_update.getIterator();
733 for(; i.atEnd() == false; i++)
735 MapBlock *block = i.getNode()->getValue();
736 v3s16 p = block->getPos();
738 // Add all surrounding blocks
739 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
742 Add all surrounding blocks that have up-to-date lighting
743 NOTE: This doesn't quite do the job (not everything
744 appropriate is lighted)
746 /*for(s16 z=-1; z<=1; z++)
747 for(s16 y=-1; y<=1; y++)
748 for(s16 x=-1; x<=1; x++)
750 v3s16 p2 = p + v3s16(x,y,z);
751 MapBlock *block = getBlockNoCreateNoEx(p2);
756 if(block->getLightingExpired())
758 vmanip.initialEmerge(p2, p2);
761 // Lighting of block will be updated completely
762 block->setLightingExpired(false);
767 //TimeTaker timer("unSpreadLight");
768 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
771 //TimeTaker timer("spreadLight");
772 vmanip.spreadLight(bank, light_sources, nodemgr);
775 //TimeTaker timer("blitBack");
776 vmanip.blitBack(modified_blocks);
778 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
783 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
786 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
787 std::map<v3s16, MapBlock*> & modified_blocks)
789 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
790 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
793 Update information about whether day and night light differ
795 for(std::map<v3s16, MapBlock*>::iterator
796 i = modified_blocks.begin();
797 i != modified_blocks.end(); ++i)
799 MapBlock *block = i->second;
800 block->expireDayNightDiff();
804 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
805 std::map<v3s16, MapBlock*> &modified_blocks,
806 bool remove_metadata)
808 // Collect old node for rollback
809 RollbackNode rollback_oldnode(this, p, m_gamedef);
811 // This is needed for updating the lighting
812 MapNode oldnode = getNodeNoEx(p);
814 // Remove node metadata
815 if (remove_metadata) {
816 removeNodeMetadata(p);
819 // Set the node on the map
820 // Ignore light (because calling voxalgo::update_lighting_nodes)
821 n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
822 n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
826 std::vector<std::pair<v3s16, MapNode> > oldnodes;
827 oldnodes.push_back(std::pair<v3s16, MapNode>(p, oldnode));
828 voxalgo::update_lighting_nodes(this, m_nodedef, oldnodes, modified_blocks);
830 for(std::map<v3s16, MapBlock*>::iterator
831 i = modified_blocks.begin();
832 i != modified_blocks.end(); ++i)
834 i->second->expireDayNightDiff();
837 // Report for rollback
838 if(m_gamedef->rollback())
840 RollbackNode rollback_newnode(this, p, m_gamedef);
841 RollbackAction action;
842 action.setSetNode(p, rollback_oldnode, rollback_newnode);
843 m_gamedef->rollback()->reportAction(action);
847 Add neighboring liquid nodes and this node to transform queue.
848 (it's vital for the node itself to get updated last, if it was removed.)
851 v3s16(0,0,1), // back
853 v3s16(1,0,0), // right
854 v3s16(0,0,-1), // front
855 v3s16(0,-1,0), // bottom
856 v3s16(-1,0,0), // left
857 v3s16(0,0,0), // self
859 for(u16 i=0; i<7; i++)
861 v3s16 p2 = p + dirs[i];
863 bool is_valid_position;
864 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
865 if(is_valid_position &&
866 (m_nodedef->get(n2).isLiquid() ||
867 n2.getContent() == CONTENT_AIR))
868 m_transforming_liquid.push_back(p2);
872 void Map::removeNodeAndUpdate(v3s16 p,
873 std::map<v3s16, MapBlock*> &modified_blocks)
875 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
878 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
881 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
885 bool succeeded = true;
887 std::map<v3s16, MapBlock*> modified_blocks;
888 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
890 // Copy modified_blocks to event
891 for(std::map<v3s16, MapBlock*>::iterator
892 i = modified_blocks.begin();
893 i != modified_blocks.end(); ++i)
895 event.modified_blocks.insert(i->first);
898 catch(InvalidPositionException &e){
902 dispatchEvent(&event);
907 bool Map::removeNodeWithEvent(v3s16 p)
910 event.type = MEET_REMOVENODE;
913 bool succeeded = true;
915 std::map<v3s16, MapBlock*> modified_blocks;
916 removeNodeAndUpdate(p, modified_blocks);
918 // Copy modified_blocks to event
919 for(std::map<v3s16, MapBlock*>::iterator
920 i = modified_blocks.begin();
921 i != modified_blocks.end(); ++i)
923 event.modified_blocks.insert(i->first);
926 catch(InvalidPositionException &e){
930 dispatchEvent(&event);
935 bool Map::getDayNightDiff(v3s16 blockpos)
938 v3s16 p = blockpos + v3s16(0,0,0);
939 MapBlock *b = getBlockNoCreate(p);
940 if(b->getDayNightDiff())
943 catch(InvalidPositionException &e){}
946 v3s16 p = blockpos + v3s16(-1,0,0);
947 MapBlock *b = getBlockNoCreate(p);
948 if(b->getDayNightDiff())
951 catch(InvalidPositionException &e){}
953 v3s16 p = blockpos + v3s16(0,-1,0);
954 MapBlock *b = getBlockNoCreate(p);
955 if(b->getDayNightDiff())
958 catch(InvalidPositionException &e){}
960 v3s16 p = blockpos + v3s16(0,0,-1);
961 MapBlock *b = getBlockNoCreate(p);
962 if(b->getDayNightDiff())
965 catch(InvalidPositionException &e){}
968 v3s16 p = blockpos + v3s16(1,0,0);
969 MapBlock *b = getBlockNoCreate(p);
970 if(b->getDayNightDiff())
973 catch(InvalidPositionException &e){}
975 v3s16 p = blockpos + v3s16(0,1,0);
976 MapBlock *b = getBlockNoCreate(p);
977 if(b->getDayNightDiff())
980 catch(InvalidPositionException &e){}
982 v3s16 p = blockpos + v3s16(0,0,1);
983 MapBlock *b = getBlockNoCreate(p);
984 if(b->getDayNightDiff())
987 catch(InvalidPositionException &e){}
992 struct TimeOrderedMapBlock {
996 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
1001 bool operator<(const TimeOrderedMapBlock &b) const
1003 return block->getUsageTimer() < b.block->getUsageTimer();
1008 Updates usage timers
1010 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
1011 std::vector<v3s16> *unloaded_blocks)
1013 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1015 // Profile modified reasons
1016 Profiler modprofiler;
1018 std::vector<v2s16> sector_deletion_queue;
1019 u32 deleted_blocks_count = 0;
1020 u32 saved_blocks_count = 0;
1021 u32 block_count_all = 0;
1025 // If there is no practical limit, we spare creation of mapblock_queue
1026 if (max_loaded_blocks == U32_MAX) {
1027 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1028 si != m_sectors.end(); ++si) {
1029 MapSector *sector = si->second;
1031 bool all_blocks_deleted = true;
1033 MapBlockVect blocks;
1034 sector->getBlocks(blocks);
1036 for (MapBlockVect::iterator i = blocks.begin();
1037 i != blocks.end(); ++i) {
1038 MapBlock *block = (*i);
1040 block->incrementUsageTimer(dtime);
1042 if (block->refGet() == 0
1043 && block->getUsageTimer() > unload_timeout) {
1044 v3s16 p = block->getPos();
1047 if (block->getModified() != MOD_STATE_CLEAN
1048 && save_before_unloading) {
1049 modprofiler.add(block->getModifiedReasonString(), 1);
1050 if (!saveBlock(block))
1052 saved_blocks_count++;
1055 // Delete from memory
1056 sector->deleteBlock(block);
1058 if (unloaded_blocks)
1059 unloaded_blocks->push_back(p);
1061 deleted_blocks_count++;
1063 all_blocks_deleted = false;
1068 if (all_blocks_deleted) {
1069 sector_deletion_queue.push_back(si->first);
1073 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
1074 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1075 si != m_sectors.end(); ++si) {
1076 MapSector *sector = si->second;
1078 MapBlockVect blocks;
1079 sector->getBlocks(blocks);
1081 for(MapBlockVect::iterator i = blocks.begin();
1082 i != blocks.end(); ++i) {
1083 MapBlock *block = (*i);
1085 block->incrementUsageTimer(dtime);
1086 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
1089 block_count_all = mapblock_queue.size();
1090 // Delete old blocks, and blocks over the limit from the memory
1091 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
1092 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
1093 TimeOrderedMapBlock b = mapblock_queue.top();
1094 mapblock_queue.pop();
1096 MapBlock *block = b.block;
1098 if (block->refGet() != 0)
1101 v3s16 p = block->getPos();
1104 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1105 modprofiler.add(block->getModifiedReasonString(), 1);
1106 if (!saveBlock(block))
1108 saved_blocks_count++;
1111 // Delete from memory
1112 b.sect->deleteBlock(block);
1114 if (unloaded_blocks)
1115 unloaded_blocks->push_back(p);
1117 deleted_blocks_count++;
1120 // Delete empty sectors
1121 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1122 si != m_sectors.end(); ++si) {
1123 if (si->second->empty()) {
1124 sector_deletion_queue.push_back(si->first);
1130 // Finally delete the empty sectors
1131 deleteSectors(sector_deletion_queue);
1133 if(deleted_blocks_count != 0)
1135 PrintInfo(infostream); // ServerMap/ClientMap:
1136 infostream<<"Unloaded "<<deleted_blocks_count
1137 <<" blocks from memory";
1138 if(save_before_unloading)
1139 infostream<<", of which "<<saved_blocks_count<<" were written";
1140 infostream<<", "<<block_count_all<<" blocks in memory";
1141 infostream<<"."<<std::endl;
1142 if(saved_blocks_count != 0){
1143 PrintInfo(infostream); // ServerMap/ClientMap:
1144 infostream<<"Blocks modified by: "<<std::endl;
1145 modprofiler.print(infostream);
1150 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1152 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
1155 void Map::deleteSectors(std::vector<v2s16> §orList)
1157 for(std::vector<v2s16>::iterator j = sectorList.begin();
1158 j != sectorList.end(); ++j) {
1159 MapSector *sector = m_sectors[*j];
1160 // If sector is in sector cache, remove it from there
1161 if(m_sector_cache == sector)
1162 m_sector_cache = NULL;
1163 // Remove from map and delete
1164 m_sectors.erase(*j);
1169 void Map::PrintInfo(std::ostream &out)
1174 #define WATER_DROP_BOOST 4
1178 NEIGHBOR_SAME_LEVEL,
1181 struct NodeNeighbor {
1185 bool l; //can liquid
1191 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1198 void Map::transforming_liquid_add(v3s16 p) {
1199 m_transforming_liquid.push_back(p);
1202 s32 Map::transforming_liquid_size() {
1203 return m_transforming_liquid.size();
1206 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
1208 DSTACK(FUNCTION_NAME);
1209 //TimeTaker timer("transformLiquids()");
1212 u32 initial_size = m_transforming_liquid.size();
1214 /*if(initial_size != 0)
1215 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1217 // list of nodes that due to viscosity have not reached their max level height
1218 std::deque<v3s16> must_reflow;
1220 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
1222 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1223 u32 loop_max = liquid_loop_max;
1227 /* If liquid_loop_max is not keeping up with the queue size increase
1228 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1230 if (m_transforming_liquid.size() > loop_max * 2) {
1232 float server_step = g_settings->getFloat("dedicated_server_step");
1233 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1234 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1236 m_transforming_liquid_loop_count_multiplier = 1.0;
1239 loop_max *= m_transforming_liquid_loop_count_multiplier;
1242 while (m_transforming_liquid.size() != 0)
1244 // This should be done here so that it is done when continue is used
1245 if (loopcount >= initial_size || loopcount >= loop_max)
1250 Get a queued transforming liquid node
1252 v3s16 p0 = m_transforming_liquid.front();
1253 m_transforming_liquid.pop_front();
1255 MapNode n0 = getNodeNoEx(p0);
1258 Collect information about current node
1260 s8 liquid_level = -1;
1261 // The liquid node which will be placed there if
1262 // the liquid flows into this node.
1263 content_t liquid_kind = CONTENT_IGNORE;
1264 // The node which will be placed there if liquid
1265 // can't flow into this node.
1266 content_t floodable_node = CONTENT_AIR;
1267 const ContentFeatures &cf = m_nodedef->get(n0);
1268 LiquidType liquid_type = cf.liquid_type;
1269 switch (liquid_type) {
1271 liquid_level = LIQUID_LEVEL_SOURCE;
1272 liquid_kind = m_nodedef->getId(cf.liquid_alternative_flowing);
1274 case LIQUID_FLOWING:
1275 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1276 liquid_kind = n0.getContent();
1279 // if this node is 'floodable', it *could* be transformed
1280 // into a liquid, otherwise, continue with the next node.
1283 floodable_node = n0.getContent();
1284 liquid_kind = CONTENT_AIR;
1289 Collect information about the environment
1291 const v3s16 *dirs = g_6dirs;
1292 NodeNeighbor sources[6]; // surrounding sources
1293 int num_sources = 0;
1294 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1296 NodeNeighbor airs[6]; // surrounding air
1298 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1299 int num_neutrals = 0;
1300 bool flowing_down = false;
1301 bool ignored_sources = false;
1302 for (u16 i = 0; i < 6; i++) {
1303 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1306 nt = NEIGHBOR_UPPER;
1309 nt = NEIGHBOR_LOWER;
1312 v3s16 npos = p0 + dirs[i];
1313 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1314 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
1315 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
1317 if (cfnb.floodable) {
1318 airs[num_airs++] = nb;
1319 // if the current node is a water source the neighbor
1320 // should be enqueded for transformation regardless of whether the
1321 // current node changes or not.
1322 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1323 m_transforming_liquid.push_back(npos);
1324 // if the current node happens to be a flowing node, it will start to flow down here.
1325 if (nb.t == NEIGHBOR_LOWER)
1326 flowing_down = true;
1328 neutrals[num_neutrals++] = nb;
1329 if (nb.n.getContent() == CONTENT_IGNORE) {
1330 // If node below is ignore prevent water from
1331 // spreading outwards and otherwise prevent from
1332 // flowing away as ignore node might be the source
1333 if (nb.t == NEIGHBOR_LOWER)
1334 flowing_down = true;
1336 ignored_sources = true;
1341 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1342 if (liquid_kind == CONTENT_AIR)
1343 liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing);
1344 if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1345 neutrals[num_neutrals++] = nb;
1347 // Do not count bottom source, it will screw things up
1349 sources[num_sources++] = nb;
1352 case LIQUID_FLOWING:
1353 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1354 if (liquid_kind == CONTENT_AIR)
1355 liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing);
1356 if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1357 neutrals[num_neutrals++] = nb;
1359 flows[num_flows++] = nb;
1360 if (nb.t == NEIGHBOR_LOWER)
1361 flowing_down = true;
1368 decide on the type (and possibly level) of the current node
1370 content_t new_node_content;
1371 s8 new_node_level = -1;
1372 s8 max_node_level = -1;
1374 u8 range = m_nodedef->get(liquid_kind).liquid_range;
1375 if (range > LIQUID_LEVEL_MAX + 1)
1376 range = LIQUID_LEVEL_MAX + 1;
1378 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1379 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1380 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1381 // it's perfectly safe to use liquid_kind here to determine the new node content.
1382 new_node_content = m_nodedef->getId(m_nodedef->get(liquid_kind).liquid_alternative_source);
1383 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1384 // liquid_kind is set properly, see above
1385 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1386 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1387 new_node_content = liquid_kind;
1389 new_node_content = floodable_node;
1390 } else if (ignored_sources && liquid_level >= 0) {
1391 // Maybe there are neighbouring sources that aren't loaded yet
1392 // so prevent flowing away.
1393 new_node_level = liquid_level;
1394 new_node_content = liquid_kind;
1396 // no surrounding sources, so get the maximum level that can flow into this node
1397 for (u16 i = 0; i < num_flows; i++) {
1398 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1399 switch (flows[i].t) {
1400 case NEIGHBOR_UPPER:
1401 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1402 max_node_level = LIQUID_LEVEL_MAX;
1403 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1404 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1405 } else if (nb_liquid_level > max_node_level) {
1406 max_node_level = nb_liquid_level;
1409 case NEIGHBOR_LOWER:
1411 case NEIGHBOR_SAME_LEVEL:
1412 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1413 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
1414 max_node_level = nb_liquid_level - 1;
1419 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
1420 if (viscosity > 1 && max_node_level != liquid_level) {
1421 // amount to gain, limited by viscosity
1422 // must be at least 1 in absolute value
1423 s8 level_inc = max_node_level - liquid_level;
1424 if (level_inc < -viscosity || level_inc > viscosity)
1425 new_node_level = liquid_level + level_inc/viscosity;
1426 else if (level_inc < 0)
1427 new_node_level = liquid_level - 1;
1428 else if (level_inc > 0)
1429 new_node_level = liquid_level + 1;
1430 if (new_node_level != max_node_level)
1431 must_reflow.push_back(p0);
1433 new_node_level = max_node_level;
1436 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1437 new_node_content = liquid_kind;
1439 new_node_content = floodable_node;
1444 check if anything has changed. if not, just continue with the next node.
1446 if (new_node_content == n0.getContent() &&
1447 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1448 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1449 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1455 update the current node
1458 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1459 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1460 // set level to last 3 bits, flowing down bit to 4th bit
1461 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1463 // set the liquid level and flow bit to 0
1464 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1466 n0.setContent(new_node_content);
1468 // Ignore light (because calling voxalgo::update_lighting_nodes)
1469 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
1470 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
1472 // Find out whether there is a suspect for this action
1473 std::string suspect;
1474 if (m_gamedef->rollback())
1475 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1477 if (m_gamedef->rollback() && !suspect.empty()) {
1479 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1480 // Get old node for rollback
1481 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1485 RollbackNode rollback_newnode(this, p0, m_gamedef);
1486 RollbackAction action;
1487 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1488 m_gamedef->rollback()->reportAction(action);
1494 v3s16 blockpos = getNodeBlockPos(p0);
1495 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1496 if (block != NULL) {
1497 modified_blocks[blockpos] = block;
1498 changed_nodes.push_back(std::pair<v3s16, MapNode>(p0, n00));
1502 enqueue neighbors for update if neccessary
1504 switch (m_nodedef->get(n0.getContent()).liquid_type) {
1506 case LIQUID_FLOWING:
1507 // make sure source flows into all neighboring nodes
1508 for (u16 i = 0; i < num_flows; i++)
1509 if (flows[i].t != NEIGHBOR_UPPER)
1510 m_transforming_liquid.push_back(flows[i].p);
1511 for (u16 i = 0; i < num_airs; i++)
1512 if (airs[i].t != NEIGHBOR_UPPER)
1513 m_transforming_liquid.push_back(airs[i].p);
1516 // this flow has turned to air; neighboring flows might need to do the same
1517 for (u16 i = 0; i < num_flows; i++)
1518 m_transforming_liquid.push_back(flows[i].p);
1522 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1524 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1525 m_transforming_liquid.push_back(*iter);
1527 voxalgo::update_lighting_nodes(this, m_nodedef, changed_nodes, modified_blocks);
1530 /* ----------------------------------------------------------------------
1531 * Manage the queue so that it does not grow indefinately
1533 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1535 if (time_until_purge == 0)
1536 return; // Feature disabled
1538 time_until_purge *= 1000; // seconds -> milliseconds
1540 u32 curr_time = getTime(PRECISION_MILLI);
1541 u32 prev_unprocessed = m_unprocessed_count;
1542 m_unprocessed_count = m_transforming_liquid.size();
1544 // if unprocessed block count is decreasing or stable
1545 if (m_unprocessed_count <= prev_unprocessed) {
1546 m_queue_size_timer_started = false;
1548 if (!m_queue_size_timer_started)
1549 m_inc_trending_up_start_time = curr_time;
1550 m_queue_size_timer_started = true;
1553 // Account for curr_time overflowing
1554 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1555 m_queue_size_timer_started = false;
1557 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1558 * and the number of unprocessed blocks is still > liquid_loop_max then we
1559 * cannot keep up; dump the oldest blocks from the queue so that the queue
1560 * has liquid_loop_max items in it
1562 if (m_queue_size_timer_started
1563 && curr_time - m_inc_trending_up_start_time > time_until_purge
1564 && m_unprocessed_count > liquid_loop_max) {
1566 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1568 infostream << "transformLiquids(): DUMPING " << dump_qty
1569 << " blocks from the queue" << std::endl;
1572 m_transforming_liquid.pop_front();
1574 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1575 m_unprocessed_count = m_transforming_liquid.size();
1579 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1581 std::vector<v3s16> positions_with_meta;
1583 sortBoxVerticies(p1, p2);
1584 v3s16 bpmin = getNodeBlockPos(p1);
1585 v3s16 bpmax = getNodeBlockPos(p2);
1587 VoxelArea area(p1, p2);
1589 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1590 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1591 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1592 v3s16 blockpos(x, y, z);
1594 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1596 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1597 << PP(blockpos) << std::endl;
1598 block = emergeBlock(blockpos, false);
1601 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1606 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1607 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1608 for (size_t i = 0; i != keys.size(); i++) {
1609 v3s16 p(keys[i] + p_base);
1610 if (!area.contains(p))
1613 positions_with_meta.push_back(p);
1617 return positions_with_meta;
1620 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1622 v3s16 blockpos = getNodeBlockPos(p);
1623 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1624 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1626 infostream<<"Map::getNodeMetadata(): Need to emerge "
1627 <<PP(blockpos)<<std::endl;
1628 block = emergeBlock(blockpos, false);
1631 warningstream<<"Map::getNodeMetadata(): Block not found"
1635 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1639 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1641 v3s16 blockpos = getNodeBlockPos(p);
1642 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1643 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1645 infostream<<"Map::setNodeMetadata(): Need to emerge "
1646 <<PP(blockpos)<<std::endl;
1647 block = emergeBlock(blockpos, false);
1650 warningstream<<"Map::setNodeMetadata(): Block not found"
1654 block->m_node_metadata.set(p_rel, meta);
1658 void Map::removeNodeMetadata(v3s16 p)
1660 v3s16 blockpos = getNodeBlockPos(p);
1661 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1662 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1665 warningstream<<"Map::removeNodeMetadata(): Block not found"
1669 block->m_node_metadata.remove(p_rel);
1672 NodeTimer Map::getNodeTimer(v3s16 p)
1674 v3s16 blockpos = getNodeBlockPos(p);
1675 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1676 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1678 infostream<<"Map::getNodeTimer(): Need to emerge "
1679 <<PP(blockpos)<<std::endl;
1680 block = emergeBlock(blockpos, false);
1683 warningstream<<"Map::getNodeTimer(): Block not found"
1687 NodeTimer t = block->m_node_timers.get(p_rel);
1688 NodeTimer nt(t.timeout, t.elapsed, p);
1692 void Map::setNodeTimer(const NodeTimer &t)
1694 v3s16 p = t.position;
1695 v3s16 blockpos = getNodeBlockPos(p);
1696 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1697 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1699 infostream<<"Map::setNodeTimer(): Need to emerge "
1700 <<PP(blockpos)<<std::endl;
1701 block = emergeBlock(blockpos, false);
1704 warningstream<<"Map::setNodeTimer(): Block not found"
1708 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1709 block->m_node_timers.set(nt);
1712 void Map::removeNodeTimer(v3s16 p)
1714 v3s16 blockpos = getNodeBlockPos(p);
1715 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1716 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1719 warningstream<<"Map::removeNodeTimer(): Block not found"
1723 block->m_node_timers.remove(p_rel);
1729 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1730 Map(dout_server, gamedef),
1731 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1733 m_map_metadata_changed(true)
1735 verbosestream<<FUNCTION_NAME<<std::endl;
1737 // Tell the EmergeManager about our MapSettingsManager
1738 emerge->map_settings_mgr = &settings_mgr;
1741 Try to load map; if not found, create a new one.
1744 // Determine which database backend to use
1745 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1747 bool succeeded = conf.readConfigFile(conf_path.c_str());
1748 if (!succeeded || !conf.exists("backend")) {
1749 // fall back to sqlite3
1750 conf.set("backend", "sqlite3");
1752 std::string backend = conf.get("backend");
1753 dbase = createDatabase(backend, savedir, conf);
1755 if (!conf.updateConfigFile(conf_path.c_str()))
1756 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1758 m_savedir = savedir;
1759 m_map_saving_enabled = false;
1763 // If directory exists, check contents and load if possible
1764 if(fs::PathExists(m_savedir))
1766 // If directory is empty, it is safe to save into it.
1767 if(fs::GetDirListing(m_savedir).size() == 0)
1769 infostream<<"ServerMap: Empty save directory is valid."
1771 m_map_saving_enabled = true;
1776 if (settings_mgr.loadMapMeta()) {
1777 infostream << "ServerMap: Metadata loaded from "
1778 << savedir << std::endl;
1780 infostream << "ServerMap: Metadata could not be loaded "
1781 "from " << savedir << ", assuming valid save "
1782 "directory." << std::endl;
1785 m_map_saving_enabled = true;
1786 // Map loaded, not creating new one
1790 // If directory doesn't exist, it is safe to save to it
1792 m_map_saving_enabled = true;
1795 catch(std::exception &e)
1797 warningstream<<"ServerMap: Failed to load map from "<<savedir
1798 <<", exception: "<<e.what()<<std::endl;
1799 infostream<<"Please remove the map or fix it."<<std::endl;
1800 warningstream<<"Map saving will be disabled."<<std::endl;
1803 infostream<<"Initializing new map."<<std::endl;
1805 // Create zero sector
1806 emergeSector(v2s16(0,0));
1808 // Initially write whole map
1809 save(MOD_STATE_CLEAN);
1812 ServerMap::~ServerMap()
1814 verbosestream<<FUNCTION_NAME<<std::endl;
1818 if(m_map_saving_enabled)
1820 // Save only changed parts
1821 save(MOD_STATE_WRITE_AT_UNLOAD);
1822 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
1826 infostream<<"ServerMap: Map not saved"<<std::endl;
1829 catch(std::exception &e)
1831 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1832 <<", exception: "<<e.what()<<std::endl;
1836 Close database if it was opened
1844 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1845 for(; i.atEnd() == false; i++)
1847 MapChunk *chunk = i.getNode()->getValue();
1853 MapgenParams *ServerMap::getMapgenParams()
1855 // getMapgenParams() should only ever be called after Server is initialized
1856 assert(settings_mgr.mapgen_params != NULL);
1857 return settings_mgr.mapgen_params;
1860 u64 ServerMap::getSeed()
1862 return getMapgenParams()->seed;
1865 s16 ServerMap::getWaterLevel()
1867 return getMapgenParams()->water_level;
1870 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1872 s16 csize = getMapgenParams()->chunksize;
1873 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1874 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1876 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1877 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1879 v3s16 extra_borders(1, 1, 1);
1880 v3s16 full_bpmin = bpmin - extra_borders;
1881 v3s16 full_bpmax = bpmax + extra_borders;
1883 // Do nothing if not inside limits (+-1 because of neighbors)
1884 if (blockpos_over_limit(full_bpmin) ||
1885 blockpos_over_limit(full_bpmax))
1888 data->seed = getSeed();
1889 data->blockpos_min = bpmin;
1890 data->blockpos_max = bpmax;
1891 data->blockpos_requested = blockpos;
1892 data->nodedef = m_nodedef;
1895 Create the whole area of this and the neighboring blocks
1897 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1898 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1899 v2s16 sectorpos(x, z);
1900 // Sector metadata is loaded from disk if not already loaded.
1901 ServerMapSector *sector = createSector(sectorpos);
1902 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1904 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1907 MapBlock *block = emergeBlock(p, false);
1908 if (block == NULL) {
1909 block = createBlock(p);
1911 // Block gets sunlight if this is true.
1912 // Refer to the map generator heuristics.
1913 bool ug = m_emerge->isBlockUnderground(p);
1914 block->setIsUnderground(ug);
1920 Now we have a big empty area.
1922 Make a ManualMapVoxelManipulator that contains this and the
1926 data->vmanip = new MMVManip(this);
1927 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1929 // Note: we may need this again at some point.
1931 // Ensure none of the blocks to be generated were marked as
1932 // containing CONTENT_IGNORE
1933 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1934 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1935 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1936 core::map<v3s16, u8>::Node *n;
1937 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1940 u8 flags = n->getValue();
1941 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1948 // Data is ready now.
1952 void ServerMap::finishBlockMake(BlockMakeData *data,
1953 std::map<v3s16, MapBlock*> *changed_blocks)
1955 v3s16 bpmin = data->blockpos_min;
1956 v3s16 bpmax = data->blockpos_max;
1958 v3s16 extra_borders(1, 1, 1);
1959 v3s16 full_bpmin = bpmin - extra_borders;
1960 v3s16 full_bpmax = bpmax + extra_borders;
1962 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1963 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1966 Set lighting to non-expired state in all of them.
1967 This is cheating, but it is not fast enough if all of them
1968 would actually be updated.
1970 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1971 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
1972 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1973 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
1977 block->setLightingExpired(false);
1981 Blit generated stuff to map
1982 NOTE: blitBackAll adds nearly everything to changed_blocks
1984 data->vmanip->blitBackAll(changed_blocks);
1986 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1987 << changed_blocks->size());
1990 Copy transforming liquid information
1992 while (data->transforming_liquid.size()) {
1993 m_transforming_liquid.push_back(data->transforming_liquid.front());
1994 data->transforming_liquid.pop_front();
1997 for (std::map<v3s16, MapBlock *>::iterator
1998 it = changed_blocks->begin();
1999 it != changed_blocks->end(); ++it) {
2000 MapBlock *block = it->second;
2004 Update day/night difference cache of the MapBlocks
2006 block->expireDayNightDiff();
2008 Set block as modified
2010 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2011 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2015 Set central blocks as generated
2017 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2018 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2019 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2020 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2024 block->setGenerated(true);
2028 Save changed parts of map
2029 NOTE: Will be saved later.
2031 //save(MOD_STATE_WRITE_AT_UNLOAD);
2034 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2036 DSTACKF("%s: p2d=(%d,%d)",
2041 Check if it exists already in memory
2043 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2048 Try to load it from disk (with blocks)
2050 //if(loadSectorFull(p2d) == true)
2053 Try to load metadata from disk
2056 if(loadSectorMeta(p2d) == true)
2058 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2061 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2062 throw InvalidPositionException("");
2069 Do not create over-limit.
2070 We are checking for any nodes of the mapblocks of the sector being beyond the limit.
2071 A sector is a vertical column of mapblocks, so sectorpos is like a 2D blockpos.
2073 At the negative limit we are checking for
2074 block minimum nodepos < -mapgenlimit.
2075 At the positive limit we are checking for
2076 block maximum nodepos > mapgenlimit.
2078 Block minimum nodepos = blockpos * mapblocksize.
2079 Block maximum nodepos = (blockpos + 1) * mapblocksize - 1.
2081 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2082 g_settings->getU16("map_generation_limit"));
2083 if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit
2084 || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit
2085 || p2d.Y * MAP_BLOCKSIZE < -map_gen_limit
2086 || (p2d.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit)
2087 throw InvalidPositionException("createSector(): pos. over limit");
2090 Generate blank sector
2093 sector = new ServerMapSector(this, p2d, m_gamedef);
2095 // Sector position on map in nodes
2096 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2101 m_sectors[p2d] = sector;
2108 This is a quick-hand function for calling makeBlock().
2110 MapBlock * ServerMap::generateBlock(
2112 std::map<v3s16, MapBlock*> &modified_blocks
2115 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2117 /*infostream<<"generateBlock(): "
2118 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2121 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2123 TimeTaker timer("generateBlock");
2125 //MapBlock *block = original_dummy;
2127 v2s16 p2d(p.X, p.Z);
2128 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2131 Do not generate over-limit
2133 if(blockpos_over_limit(p))
2135 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2136 throw InvalidPositionException("generateBlock(): pos. over limit");
2140 Create block make data
2143 initBlockMake(&data, p);
2149 TimeTaker t("mapgen::make_block()");
2150 mapgen->makeChunk(&data);
2151 //mapgen::make_block(&data);
2153 if(enable_mapgen_debug_info == false)
2154 t.stop(true); // Hide output
2158 Blit data back on map, update lighting, add mobs and whatever this does
2160 finishBlockMake(&data, modified_blocks);
2165 MapBlock *block = getBlockNoCreateNoEx(p);
2173 bool erroneus_content = false;
2174 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2175 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2176 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2179 MapNode n = block->getNode(p);
2180 if(n.getContent() == CONTENT_IGNORE)
2182 infostream<<"CONTENT_IGNORE at "
2183 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2185 erroneus_content = true;
2189 if(erroneus_content)
2198 Generate a completely empty block
2202 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2203 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2205 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2208 n.setContent(CONTENT_AIR);
2209 block->setNode(v3s16(x0,y0,z0), n);
2215 if(enable_mapgen_debug_info == false)
2216 timer.stop(true); // Hide output
2222 MapBlock * ServerMap::createBlock(v3s16 p)
2224 DSTACKF("%s: p=(%d,%d,%d)",
2225 FUNCTION_NAME, p.X, p.Y, p.Z);
2228 Do not create over-limit
2230 if (blockpos_over_limit(p))
2231 throw InvalidPositionException("createBlock(): pos. over limit");
2233 v2s16 p2d(p.X, p.Z);
2236 This will create or load a sector if not found in memory.
2237 If block exists on disk, it will be loaded.
2239 NOTE: On old save formats, this will be slow, as it generates
2240 lighting on blocks for them.
2242 ServerMapSector *sector;
2244 sector = (ServerMapSector*)createSector(p2d);
2245 assert(sector->getId() == MAPSECTOR_SERVER);
2247 catch(InvalidPositionException &e)
2249 infostream<<"createBlock: createSector() failed"<<std::endl;
2253 NOTE: This should not be done, or at least the exception
2254 should not be passed on as std::exception, because it
2255 won't be catched at all.
2257 /*catch(std::exception &e)
2259 infostream<<"createBlock: createSector() failed: "
2260 <<e.what()<<std::endl;
2265 Try to get a block from the sector
2268 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2271 if(block->isDummy())
2276 block = sector->createBlankBlock(block_y);
2281 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2283 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2285 p.X, p.Y, p.Z, create_blank);
2288 MapBlock *block = getBlockNoCreateNoEx(p);
2289 if(block && block->isDummy() == false)
2294 MapBlock *block = loadBlock(p);
2300 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2301 MapBlock *block = sector->createBlankBlock(p.Y);
2309 std::map<v3s16, MapBlock*> modified_blocks;
2310 MapBlock *block = generateBlock(p, modified_blocks);
2314 event.type = MEET_OTHER;
2317 // Copy modified_blocks to event
2318 for(std::map<v3s16, MapBlock*>::iterator
2319 i = modified_blocks.begin();
2320 i != modified_blocks.end(); ++i)
2322 event.modified_blocks.insert(i->first);
2326 dispatchEvent(&event);
2336 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2338 MapBlock *block = getBlockNoCreateNoEx(p3d);
2340 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2345 void ServerMap::prepareBlock(MapBlock *block) {
2348 // N.B. This requires no synchronization, since data will not be modified unless
2349 // the VoxelManipulator being updated belongs to the same thread.
2350 void ServerMap::updateVManip(v3s16 pos)
2352 Mapgen *mg = m_emerge->getCurrentMapgen();
2356 MMVManip *vm = mg->vm;
2360 if (!vm->m_area.contains(pos))
2363 s32 idx = vm->m_area.index(pos);
2364 vm->m_data[idx] = getNodeNoEx(pos);
2365 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2367 vm->m_is_dirty = true;
2370 s16 ServerMap::findGroundLevel(v2s16 p2d)
2374 Uh, just do something random...
2376 // Find existing map from top to down
2379 v3s16 p(p2d.X, max, p2d.Y);
2380 for(; p.Y>min; p.Y--)
2382 MapNode n = getNodeNoEx(p);
2383 if(n.getContent() != CONTENT_IGNORE)
2388 // If this node is not air, go to plan b
2389 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2391 // Search existing walkable and return it
2392 for(; p.Y>min; p.Y--)
2394 MapNode n = getNodeNoEx(p);
2395 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2404 Determine from map generator noise functions
2407 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2410 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2411 //return (s16)level;
2414 bool ServerMap::loadFromFolders() {
2415 if (!dbase->initialized() &&
2416 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2421 void ServerMap::createDirs(std::string path)
2423 if(fs::CreateAllDirs(path) == false)
2425 m_dout<<"ServerMap: Failed to create directory "
2426 <<"\""<<path<<"\""<<std::endl;
2427 throw BaseException("ServerMap failed to create directory");
2431 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2437 snprintf(cc, 9, "%.4x%.4x",
2438 (unsigned int) pos.X & 0xffff,
2439 (unsigned int) pos.Y & 0xffff);
2441 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2443 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2444 (unsigned int) pos.X & 0xfff,
2445 (unsigned int) pos.Y & 0xfff);
2447 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2454 v2s16 ServerMap::getSectorPos(std::string dirname)
2456 unsigned int x = 0, y = 0;
2458 std::string component;
2459 fs::RemoveLastPathComponent(dirname, &component, 1);
2460 if(component.size() == 8)
2463 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2465 else if(component.size() == 3)
2468 fs::RemoveLastPathComponent(dirname, &component, 2);
2469 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2470 // Sign-extend the 12 bit values up to 16 bits...
2471 if(x & 0x800) x |= 0xF000;
2472 if(y & 0x800) y |= 0xF000;
2479 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2480 v2s16 pos((s16)x, (s16)y);
2484 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2486 v2s16 p2d = getSectorPos(sectordir);
2488 if(blockfile.size() != 4){
2489 throw InvalidFilenameException("Invalid block filename");
2492 int r = sscanf(blockfile.c_str(), "%4x", &y);
2494 throw InvalidFilenameException("Invalid block filename");
2495 return v3s16(p2d.X, y, p2d.Y);
2498 std::string ServerMap::getBlockFilename(v3s16 p)
2501 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2505 void ServerMap::save(ModifiedState save_level)
2507 DSTACK(FUNCTION_NAME);
2508 if(m_map_saving_enabled == false) {
2509 warningstream<<"Not saving map, saving disabled."<<std::endl;
2513 if(save_level == MOD_STATE_CLEAN)
2514 infostream<<"ServerMap: Saving whole map, this can take time."
2517 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2518 if (settings_mgr.saveMapMeta())
2519 m_map_metadata_changed = false;
2522 // Profile modified reasons
2523 Profiler modprofiler;
2525 u32 sector_meta_count = 0;
2526 u32 block_count = 0;
2527 u32 block_count_all = 0; // Number of blocks in memory
2529 // Don't do anything with sqlite unless something is really saved
2530 bool save_started = false;
2532 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2533 i != m_sectors.end(); ++i) {
2534 ServerMapSector *sector = (ServerMapSector*)i->second;
2535 assert(sector->getId() == MAPSECTOR_SERVER);
2537 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2538 saveSectorMeta(sector);
2539 sector_meta_count++;
2542 MapBlockVect blocks;
2543 sector->getBlocks(blocks);
2545 for(MapBlockVect::iterator j = blocks.begin();
2546 j != blocks.end(); ++j) {
2547 MapBlock *block = *j;
2551 if(block->getModified() >= (u32)save_level) {
2555 save_started = true;
2558 modprofiler.add(block->getModifiedReasonString(), 1);
2563 /*infostream<<"ServerMap: Written block ("
2564 <<block->getPos().X<<","
2565 <<block->getPos().Y<<","
2566 <<block->getPos().Z<<")"
2576 Only print if something happened or saved whole map
2578 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2579 || block_count != 0) {
2580 infostream<<"ServerMap: Written: "
2581 <<sector_meta_count<<" sector metadata files, "
2582 <<block_count<<" block files"
2583 <<", "<<block_count_all<<" blocks in memory."
2585 PrintInfo(infostream); // ServerMap/ClientMap:
2586 infostream<<"Blocks modified by: "<<std::endl;
2587 modprofiler.print(infostream);
2591 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2593 if (loadFromFolders()) {
2594 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2595 << "all blocks that are stored in flat files." << std::endl;
2597 dbase->listAllLoadableBlocks(dst);
2600 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2602 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2603 si != m_sectors.end(); ++si)
2605 MapSector *sector = si->second;
2607 MapBlockVect blocks;
2608 sector->getBlocks(blocks);
2610 for(MapBlockVect::iterator i = blocks.begin();
2611 i != blocks.end(); ++i) {
2612 v3s16 p = (*i)->getPos();
2618 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2620 DSTACK(FUNCTION_NAME);
2621 // Format used for writing
2622 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2624 v2s16 pos = sector->getPos();
2625 std::string dir = getSectorDir(pos);
2628 std::string fullpath = dir + DIR_DELIM + "meta";
2629 std::ostringstream ss(std::ios_base::binary);
2631 sector->serialize(ss, version);
2633 if(!fs::safeWriteToFile(fullpath, ss.str()))
2634 throw FileNotGoodException("Cannot write sector metafile");
2636 sector->differs_from_disk = false;
2639 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2641 DSTACK(FUNCTION_NAME);
2643 v2s16 p2d = getSectorPos(sectordir);
2645 ServerMapSector *sector = NULL;
2647 std::string fullpath = sectordir + DIR_DELIM + "meta";
2648 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2649 if(is.good() == false)
2651 // If the directory exists anyway, it probably is in some old
2652 // format. Just go ahead and create the sector.
2653 if(fs::PathExists(sectordir))
2655 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
2656 <<fullpath<<" doesn't exist but directory does."
2657 <<" Continuing with a sector with no metadata."
2659 sector = new ServerMapSector(this, p2d, m_gamedef);
2660 m_sectors[p2d] = sector;
2664 throw FileNotGoodException("Cannot open sector metafile");
2669 sector = ServerMapSector::deSerialize
2670 (is, this, p2d, m_sectors, m_gamedef);
2672 saveSectorMeta(sector);
2675 sector->differs_from_disk = false;
2680 bool ServerMap::loadSectorMeta(v2s16 p2d)
2682 DSTACK(FUNCTION_NAME);
2684 // The directory layout we're going to load from.
2685 // 1 - original sectors/xxxxzzzz/
2686 // 2 - new sectors2/xxx/zzz/
2687 // If we load from anything but the latest structure, we will
2688 // immediately save to the new one, and remove the old.
2690 std::string sectordir1 = getSectorDir(p2d, 1);
2691 std::string sectordir;
2692 if(fs::PathExists(sectordir1))
2694 sectordir = sectordir1;
2699 sectordir = getSectorDir(p2d, 2);
2703 loadSectorMeta(sectordir, loadlayout != 2);
2705 catch(InvalidFilenameException &e)
2709 catch(FileNotGoodException &e)
2713 catch(std::exception &e)
2722 bool ServerMap::loadSectorFull(v2s16 p2d)
2724 DSTACK(FUNCTION_NAME);
2726 MapSector *sector = NULL;
2728 // The directory layout we're going to load from.
2729 // 1 - original sectors/xxxxzzzz/
2730 // 2 - new sectors2/xxx/zzz/
2731 // If we load from anything but the latest structure, we will
2732 // immediately save to the new one, and remove the old.
2734 std::string sectordir1 = getSectorDir(p2d, 1);
2735 std::string sectordir;
2736 if(fs::PathExists(sectordir1))
2738 sectordir = sectordir1;
2743 sectordir = getSectorDir(p2d, 2);
2747 sector = loadSectorMeta(sectordir, loadlayout != 2);
2749 catch(InvalidFilenameException &e)
2753 catch(FileNotGoodException &e)
2757 catch(std::exception &e)
2765 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2767 std::vector<fs::DirListNode>::iterator i2;
2768 for(i2=list2.begin(); i2!=list2.end(); i2++)
2774 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2776 catch(InvalidFilenameException &e)
2778 // This catches unknown crap in directory
2784 infostream<<"Sector converted to new layout - deleting "<<
2785 sectordir1<<std::endl;
2786 fs::RecursiveDelete(sectordir1);
2793 Database *ServerMap::createDatabase(
2794 const std::string &name,
2795 const std::string &savedir,
2798 if (name == "sqlite3")
2799 return new Database_SQLite3(savedir);
2800 if (name == "dummy")
2801 return new Database_Dummy();
2803 else if (name == "leveldb")
2804 return new Database_LevelDB(savedir);
2807 else if (name == "redis")
2808 return new Database_Redis(conf);
2811 else if (name == "postgresql")
2812 return new Database_PostgreSQL(conf);
2815 throw BaseException(std::string("Database backend ") + name + " not supported.");
2818 void ServerMap::beginSave()
2823 void ServerMap::endSave()
2828 bool ServerMap::saveBlock(MapBlock *block)
2830 return saveBlock(block, dbase);
2833 bool ServerMap::saveBlock(MapBlock *block, Database *db)
2835 v3s16 p3d = block->getPos();
2837 // Dummy blocks are not written
2838 if (block->isDummy()) {
2839 warningstream << "saveBlock: Not writing dummy block "
2840 << PP(p3d) << std::endl;
2844 // Format used for writing
2845 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2848 [0] u8 serialization version
2851 std::ostringstream o(std::ios_base::binary);
2852 o.write((char*) &version, 1);
2853 block->serialize(o, version, true);
2855 std::string data = o.str();
2856 bool ret = db->saveBlock(p3d, data);
2858 // We just wrote it to the disk so clear modified flag
2859 block->resetModified();
2864 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
2865 MapSector *sector, bool save_after_load)
2867 DSTACK(FUNCTION_NAME);
2869 std::string fullpath = sectordir + DIR_DELIM + blockfile;
2872 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2873 if(is.good() == false)
2874 throw FileNotGoodException("Cannot open block file");
2876 v3s16 p3d = getBlockPos(sectordir, blockfile);
2877 v2s16 p2d(p3d.X, p3d.Z);
2879 assert(sector->getPos() == p2d);
2881 u8 version = SER_FMT_VER_INVALID;
2882 is.read((char*)&version, 1);
2885 throw SerializationError("ServerMap::loadBlock(): Failed"
2886 " to read MapBlock version");
2888 /*u32 block_size = MapBlock::serializedLength(version);
2889 SharedBuffer<u8> data(block_size);
2890 is.read((char*)*data, block_size);*/
2892 // This will always return a sector because we're the server
2893 //MapSector *sector = emergeSector(p2d);
2895 MapBlock *block = NULL;
2896 bool created_new = false;
2897 block = sector->getBlockNoCreateNoEx(p3d.Y);
2900 block = sector->createBlankBlockNoInsert(p3d.Y);
2905 block->deSerialize(is, version, true);
2907 // If it's a new block, insert it to the map
2909 sector->insertBlock(block);
2910 ReflowScan scanner(this, m_emerge->ndef);
2911 scanner.scan(block, &m_transforming_liquid);
2915 Save blocks loaded in old format in new format
2918 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
2922 // Should be in database now, so delete the old file
2923 fs::RecursiveDelete(fullpath);
2926 // We just loaded it from the disk, so it's up-to-date.
2927 block->resetModified();
2930 catch(SerializationError &e)
2932 warningstream<<"Invalid block data on disk "
2933 <<"fullpath="<<fullpath
2934 <<" (SerializationError). "
2935 <<"what()="<<e.what()
2937 // Ignoring. A new one will be generated.
2940 // TODO: Backup file; name is in fullpath.
2944 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
2946 DSTACK(FUNCTION_NAME);
2949 std::istringstream is(*blob, std::ios_base::binary);
2951 u8 version = SER_FMT_VER_INVALID;
2952 is.read((char*)&version, 1);
2955 throw SerializationError("ServerMap::loadBlock(): Failed"
2956 " to read MapBlock version");
2958 /*u32 block_size = MapBlock::serializedLength(version);
2959 SharedBuffer<u8> data(block_size);
2960 is.read((char*)*data, block_size);*/
2962 // This will always return a sector because we're the server
2963 //MapSector *sector = emergeSector(p2d);
2965 MapBlock *block = NULL;
2966 bool created_new = false;
2967 block = sector->getBlockNoCreateNoEx(p3d.Y);
2970 block = sector->createBlankBlockNoInsert(p3d.Y);
2975 block->deSerialize(is, version, true);
2977 // If it's a new block, insert it to the map
2979 sector->insertBlock(block);
2980 ReflowScan scanner(this, m_emerge->ndef);
2981 scanner.scan(block, &m_transforming_liquid);
2985 Save blocks loaded in old format in new format
2988 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
2989 // Only save if asked to; no need to update version
2993 // We just loaded it from, so it's up-to-date.
2994 block->resetModified();
2997 catch(SerializationError &e)
2999 errorstream<<"Invalid block data in database"
3000 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3001 <<" (SerializationError): "<<e.what()<<std::endl;
3003 // TODO: Block should be marked as invalid in memory so that it is
3004 // not touched but the game can run
3006 if(g_settings->getBool("ignore_world_load_errors")){
3007 errorstream<<"Ignoring block load error. Duck and cover! "
3008 <<"(ignore_world_load_errors)"<<std::endl;
3010 throw SerializationError("Invalid block data in database");
3015 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3017 DSTACK(FUNCTION_NAME);
3019 v2s16 p2d(blockpos.X, blockpos.Z);
3022 dbase->loadBlock(blockpos, &ret);
3024 loadBlock(&ret, blockpos, createSector(p2d), false);
3025 return getBlockNoCreateNoEx(blockpos);
3027 // Not found in database, try the files
3029 // The directory layout we're going to load from.
3030 // 1 - original sectors/xxxxzzzz/
3031 // 2 - new sectors2/xxx/zzz/
3032 // If we load from anything but the latest structure, we will
3033 // immediately save to the new one, and remove the old.
3035 std::string sectordir1 = getSectorDir(p2d, 1);
3036 std::string sectordir;
3037 if(fs::PathExists(sectordir1))
3039 sectordir = sectordir1;
3044 sectordir = getSectorDir(p2d, 2);
3048 Make sure sector is loaded
3051 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3055 sector = loadSectorMeta(sectordir, loadlayout != 2);
3057 catch(InvalidFilenameException &e)
3061 catch(FileNotGoodException &e)
3065 catch(std::exception &e)
3072 Make sure file exists
3075 std::string blockfilename = getBlockFilename(blockpos);
3076 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3080 Load block and save it to the database
3082 loadBlock(sectordir, blockfilename, sector, true);
3083 return getBlockNoCreateNoEx(blockpos);
3086 bool ServerMap::deleteBlock(v3s16 blockpos)
3088 if (!dbase->deleteBlock(blockpos))
3091 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3093 v2s16 p2d(blockpos.X, blockpos.Z);
3094 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3097 sector->deleteBlock(block);
3103 void ServerMap::PrintInfo(std::ostream &out)
3108 MMVManip::MMVManip(Map *map):
3111 m_create_area(false),
3116 MMVManip::~MMVManip()
3120 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3121 bool load_if_inexistent)
3123 TimeTaker timer1("initialEmerge", &emerge_time);
3125 // Units of these are MapBlocks
3126 v3s16 p_min = blockpos_min;
3127 v3s16 p_max = blockpos_max;
3129 VoxelArea block_area_nodes
3130 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3132 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3135 infostream<<"initialEmerge: area: ";
3136 block_area_nodes.print(infostream);
3137 infostream<<" ("<<size_MB<<"MB)";
3138 infostream<<std::endl;
3141 addArea(block_area_nodes);
3143 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3144 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3145 for(s32 x=p_min.X; x<=p_max.X; x++)
3150 std::map<v3s16, u8>::iterator n;
3151 n = m_loaded_blocks.find(p);
3152 if(n != m_loaded_blocks.end())
3155 bool block_data_inexistent = false;
3158 TimeTaker timer1("emerge load", &emerge_load_time);
3160 block = m_map->getBlockNoCreate(p);
3161 if(block->isDummy())
3162 block_data_inexistent = true;
3164 block->copyTo(*this);
3166 catch(InvalidPositionException &e)
3168 block_data_inexistent = true;
3171 if(block_data_inexistent)
3174 if (load_if_inexistent) {
3175 ServerMap *svrmap = (ServerMap *)m_map;
3176 block = svrmap->emergeBlock(p, false);
3178 block = svrmap->createBlock(p);
3179 block->copyTo(*this);
3181 flags |= VMANIP_BLOCK_DATA_INEXIST;
3184 Mark area inexistent
3186 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3187 // Fill with VOXELFLAG_NO_DATA
3188 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3189 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3191 s32 i = m_area.index(a.MinEdge.X,y,z);
3192 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3196 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3198 // Mark that block was loaded as blank
3199 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3202 m_loaded_blocks[p] = flags;
3208 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3209 bool overwrite_generated)
3211 if(m_area.getExtent() == v3s16(0,0,0))
3215 Copy data of all blocks
3217 for(std::map<v3s16, u8>::iterator
3218 i = m_loaded_blocks.begin();
3219 i != m_loaded_blocks.end(); ++i)
3222 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3223 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3224 if ((existed == false) || (block == NULL) ||
3225 (overwrite_generated == false && block->isGenerated() == true))
3228 block->copyFrom(*this);
3231 (*modified_blocks)[p] = block;