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 "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
44 #include "database-dummy.h"
45 #include "database-sqlite3.h"
49 #include "database-leveldb.h"
52 #include "database-redis.h"
55 #include "database-postgresql.h"
58 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
65 Map::Map(std::ostream &dout, IGameDef *gamedef):
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 = getBlockNoCreate(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_gamedef->ndef()->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)
260 INodeDefManager *nodemgr = m_gamedef->ndef();
263 v3s16(0,0,1), // back
265 v3s16(1,0,0), // right
266 v3s16(0,0,-1), // front
267 v3s16(0,-1,0), // bottom
268 v3s16(-1,0,0), // left
271 if(from_nodes.empty())
274 u32 blockchangecount = 0;
276 std::map<v3s16, u8> unlighted_nodes;
279 Initialize block cache
282 MapBlock *block = NULL;
283 // Cache this a bit, too
284 bool block_checked_in_modified = false;
286 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
287 j != from_nodes.end(); ++j)
289 v3s16 pos = j->first;
290 v3s16 blockpos = getNodeBlockPos(pos);
292 // Only fetch a new block if the block position has changed
294 if(block == NULL || blockpos != blockpos_last){
295 block = getBlockNoCreate(blockpos);
296 blockpos_last = blockpos;
298 block_checked_in_modified = false;
302 catch(InvalidPositionException &e)
310 // Calculate relative position in block
311 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
313 // Get node straight from the block
314 //MapNode n = block->getNode(relpos);
316 u8 oldlight = j->second;
318 // Loop through 6 neighbors
319 for(u16 i=0; i<6; i++)
321 // Get the position of the neighbor node
322 v3s16 n2pos = pos + dirs[i];
324 // Get the block where the node is located
325 v3s16 blockpos, relpos;
326 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
328 // Only fetch a new block if the block position has changed
330 if(block == NULL || blockpos != blockpos_last){
331 block = getBlockNoCreate(blockpos);
332 blockpos_last = blockpos;
334 block_checked_in_modified = false;
338 catch(InvalidPositionException &e) {
342 // Get node straight from the block
343 bool is_valid_position;
344 MapNode n2 = block->getNode(relpos, &is_valid_position);
345 if (!is_valid_position)
348 bool changed = false;
350 //TODO: Optimize output by optimizing light_sources?
353 If the neighbor is dimmer than what was specified
354 as oldlight (the light of the previous node)
356 if(n2.getLight(bank, nodemgr) < oldlight)
359 And the neighbor is transparent and it has some light
361 if(nodemgr->get(n2).light_propagates
362 && n2.getLight(bank, nodemgr) != 0)
365 Set light to 0 and add to queue
368 u8 current_light = n2.getLight(bank, nodemgr);
369 n2.setLight(bank, 0, nodemgr);
370 block->setNode(relpos, n2);
372 unlighted_nodes[n2pos] = current_light;
376 Remove from light_sources if it is there
377 NOTE: This doesn't happen nearly at all
379 /*if(light_sources.find(n2pos))
381 infostream<<"Removed from light_sources"<<std::endl;
382 light_sources.remove(n2pos);
387 if(light_sources.find(n2pos) != NULL)
388 light_sources.remove(n2pos);*/
391 light_sources.insert(n2pos);
394 // Add to modified_blocks
395 if(changed == true && block_checked_in_modified == false)
397 // If the block is not found in modified_blocks, add.
398 if(modified_blocks.find(blockpos) == modified_blocks.end())
400 modified_blocks[blockpos] = block;
402 block_checked_in_modified = true;
407 /*infostream<<"unspreadLight(): Changed block "
408 <<blockchangecount<<" times"
409 <<" for "<<from_nodes.size()<<" nodes"
412 if(!unlighted_nodes.empty())
413 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
417 Lights neighbors of from_nodes, collects all them and then
420 void Map::spreadLight(enum LightBank bank,
421 std::set<v3s16> & from_nodes,
422 std::map<v3s16, MapBlock*> & modified_blocks)
424 INodeDefManager *nodemgr = m_gamedef->ndef();
426 const v3s16 dirs[6] = {
427 v3s16(0,0,1), // back
429 v3s16(1,0,0), // right
430 v3s16(0,0,-1), // front
431 v3s16(0,-1,0), // bottom
432 v3s16(-1,0,0), // left
435 if(from_nodes.empty())
438 u32 blockchangecount = 0;
440 std::set<v3s16> lighted_nodes;
443 Initialize block cache
446 MapBlock *block = NULL;
447 // Cache this a bit, too
448 bool block_checked_in_modified = false;
450 for(std::set<v3s16>::iterator j = from_nodes.begin();
451 j != from_nodes.end(); ++j)
454 v3s16 blockpos, relpos;
456 getNodeBlockPosWithOffset(pos, blockpos, relpos);
458 // Only fetch a new block if the block position has changed
460 if(block == NULL || blockpos != blockpos_last){
461 block = getBlockNoCreate(blockpos);
462 blockpos_last = blockpos;
464 block_checked_in_modified = false;
468 catch(InvalidPositionException &e) {
475 // Get node straight from the block
476 bool is_valid_position;
477 MapNode n = block->getNode(relpos, &is_valid_position);
479 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
480 u8 newlight = diminish_light(oldlight);
482 // Loop through 6 neighbors
483 for(u16 i=0; i<6; i++){
484 // Get the position of the neighbor node
485 v3s16 n2pos = pos + dirs[i];
487 // Get the block where the node is located
488 v3s16 blockpos, relpos;
489 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
491 // Only fetch a new block if the block position has changed
493 if(block == NULL || blockpos != blockpos_last){
494 block = getBlockNoCreate(blockpos);
495 blockpos_last = blockpos;
497 block_checked_in_modified = false;
501 catch(InvalidPositionException &e) {
505 // Get node straight from the block
506 MapNode n2 = block->getNode(relpos, &is_valid_position);
507 if (!is_valid_position)
510 bool changed = false;
512 If the neighbor is brighter than the current node,
513 add to list (it will light up this node on its turn)
515 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
517 lighted_nodes.insert(n2pos);
521 If the neighbor is dimmer than how much light this node
522 would spread on it, add to list
524 if(n2.getLight(bank, nodemgr) < newlight)
526 if(nodemgr->get(n2).light_propagates)
528 n2.setLight(bank, newlight, nodemgr);
529 block->setNode(relpos, n2);
530 lighted_nodes.insert(n2pos);
535 // Add to modified_blocks
536 if(changed == true && block_checked_in_modified == false)
538 // If the block is not found in modified_blocks, add.
539 if(modified_blocks.find(blockpos) == modified_blocks.end())
541 modified_blocks[blockpos] = block;
543 block_checked_in_modified = true;
548 /*infostream<<"spreadLight(): Changed block "
549 <<blockchangecount<<" times"
550 <<" for "<<from_nodes.size()<<" nodes"
553 if(!lighted_nodes.empty())
554 spreadLight(bank, lighted_nodes, modified_blocks);
557 void Map::updateLighting(enum LightBank bank,
558 std::map<v3s16, MapBlock*> & a_blocks,
559 std::map<v3s16, MapBlock*> & modified_blocks)
561 INodeDefManager *nodemgr = m_gamedef->ndef();
563 /*m_dout<<"Map::updateLighting(): "
564 <<a_blocks.size()<<" blocks."<<std::endl;*/
566 //TimeTaker timer("updateLighting");
570 //u32 count_was = modified_blocks.size();
572 //std::map<v3s16, MapBlock*> blocks_to_update;
574 std::set<v3s16> light_sources;
576 std::map<v3s16, u8> unlight_from;
578 int num_bottom_invalid = 0;
581 //TimeTaker t("first stuff");
583 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
584 i != a_blocks.end(); ++i)
586 MapBlock *block = i->second;
590 // Don't bother with dummy blocks.
594 v3s16 pos = block->getPos();
595 v3s16 posnodes = block->getPosRelative();
596 modified_blocks[pos] = block;
597 //blocks_to_update[pos] = block;
600 Clear all light from block
602 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
603 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
604 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
607 bool is_valid_position;
608 MapNode n = block->getNode(p, &is_valid_position);
609 if (!is_valid_position) {
610 /* This would happen when dealing with a
613 infostream<<"updateLighting(): InvalidPositionException"
617 u8 oldlight = n.getLight(bank, nodemgr);
618 n.setLight(bank, 0, nodemgr);
619 block->setNode(p, n);
621 // If node sources light, add to list
622 u8 source = nodemgr->get(n).light_source;
624 light_sources.insert(p + posnodes);
626 // Collect borders for unlighting
627 if((x==0 || x == MAP_BLOCKSIZE-1
628 || y==0 || y == MAP_BLOCKSIZE-1
629 || z==0 || z == MAP_BLOCKSIZE-1)
632 v3s16 p_map = p + posnodes;
633 unlight_from[p_map] = oldlight;
639 if(bank == LIGHTBANK_DAY)
641 bool bottom_valid = block->propagateSunlight(light_sources);
644 num_bottom_invalid++;
646 // If bottom is valid, we're done.
650 else if(bank == LIGHTBANK_NIGHT)
652 // For night lighting, sunlight is not propagated
657 assert("Invalid lighting bank" == NULL);
660 /*infostream<<"Bottom for sunlight-propagated block ("
661 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
664 // Bottom sunlight is not valid; get the block and loop to it
668 block = getBlockNoCreate(pos);
670 catch(InvalidPositionException &e)
672 FATAL_ERROR("Invalid position");
681 Enable this to disable proper lighting for speeding up map
682 generation for testing or whatever
685 //if(g_settings->get(""))
687 core::map<v3s16, MapBlock*>::Iterator i;
688 i = blocks_to_update.getIterator();
689 for(; i.atEnd() == false; i++)
691 MapBlock *block = i.getNode()->getValue();
692 v3s16 p = block->getPos();
693 block->setLightingExpired(false);
701 //TimeTaker timer("unspreadLight");
702 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
707 u32 diff = modified_blocks.size() - count_was;
708 count_was = modified_blocks.size();
709 infostream<<"unspreadLight modified "<<diff<<std::endl;
713 //TimeTaker timer("spreadLight");
714 spreadLight(bank, light_sources, modified_blocks);
719 u32 diff = modified_blocks.size() - count_was;
720 count_was = modified_blocks.size();
721 infostream<<"spreadLight modified "<<diff<<std::endl;
727 //MapVoxelManipulator vmanip(this);
729 // Make a manual voxel manipulator and load all the blocks
730 // that touch the requested blocks
731 ManualMapVoxelManipulator vmanip(this);
734 //TimeTaker timer("initialEmerge");
736 core::map<v3s16, MapBlock*>::Iterator i;
737 i = blocks_to_update.getIterator();
738 for(; i.atEnd() == false; i++)
740 MapBlock *block = i.getNode()->getValue();
741 v3s16 p = block->getPos();
743 // Add all surrounding blocks
744 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
747 Add all surrounding blocks that have up-to-date lighting
748 NOTE: This doesn't quite do the job (not everything
749 appropriate is lighted)
751 /*for(s16 z=-1; z<=1; z++)
752 for(s16 y=-1; y<=1; y++)
753 for(s16 x=-1; x<=1; x++)
755 v3s16 p2 = p + v3s16(x,y,z);
756 MapBlock *block = getBlockNoCreateNoEx(p2);
761 if(block->getLightingExpired())
763 vmanip.initialEmerge(p2, p2);
766 // Lighting of block will be updated completely
767 block->setLightingExpired(false);
772 //TimeTaker timer("unSpreadLight");
773 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
776 //TimeTaker timer("spreadLight");
777 vmanip.spreadLight(bank, light_sources, nodemgr);
780 //TimeTaker timer("blitBack");
781 vmanip.blitBack(modified_blocks);
783 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
788 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
791 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
792 std::map<v3s16, MapBlock*> & modified_blocks)
794 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
795 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
798 Update information about whether day and night light differ
800 for(std::map<v3s16, MapBlock*>::iterator
801 i = modified_blocks.begin();
802 i != modified_blocks.end(); ++i)
804 MapBlock *block = i->second;
805 block->expireDayNightDiff();
809 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
810 std::map<v3s16, MapBlock*> &modified_blocks,
811 bool remove_metadata)
813 INodeDefManager *ndef = m_gamedef->ndef();
815 // Collect old node for rollback
816 RollbackNode rollback_oldnode(this, p, m_gamedef);
818 // This is needed for updating the lighting
819 MapNode oldnode = getNodeNoEx(p);
821 // Remove node metadata
822 if (remove_metadata) {
823 removeNodeMetadata(p);
826 // Set the node on the map
830 voxalgo::update_lighting_node(this, ndef, p, oldnode, modified_blocks);
832 for(std::map<v3s16, MapBlock*>::iterator
833 i = modified_blocks.begin();
834 i != modified_blocks.end(); ++i)
836 i->second->expireDayNightDiff();
839 // Report for rollback
840 if(m_gamedef->rollback())
842 RollbackNode rollback_newnode(this, p, m_gamedef);
843 RollbackAction action;
844 action.setSetNode(p, rollback_oldnode, rollback_newnode);
845 m_gamedef->rollback()->reportAction(action);
849 Add neighboring liquid nodes and this node to transform queue.
850 (it's vital for the node itself to get updated last, if it was removed.)
853 v3s16(0,0,1), // back
855 v3s16(1,0,0), // right
856 v3s16(0,0,-1), // front
857 v3s16(0,-1,0), // bottom
858 v3s16(-1,0,0), // left
859 v3s16(0,0,0), // self
861 for(u16 i=0; i<7; i++)
863 v3s16 p2 = p + dirs[i];
865 bool is_valid_position;
866 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
868 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
870 m_transforming_liquid.push_back(p2);
875 void Map::removeNodeAndUpdate(v3s16 p,
876 std::map<v3s16, MapBlock*> &modified_blocks)
878 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
881 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
884 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
888 bool succeeded = true;
890 std::map<v3s16, MapBlock*> modified_blocks;
891 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
893 // Copy modified_blocks to event
894 for(std::map<v3s16, MapBlock*>::iterator
895 i = modified_blocks.begin();
896 i != modified_blocks.end(); ++i)
898 event.modified_blocks.insert(i->first);
901 catch(InvalidPositionException &e){
905 dispatchEvent(&event);
910 bool Map::removeNodeWithEvent(v3s16 p)
913 event.type = MEET_REMOVENODE;
916 bool succeeded = true;
918 std::map<v3s16, MapBlock*> modified_blocks;
919 removeNodeAndUpdate(p, modified_blocks);
921 // Copy modified_blocks to event
922 for(std::map<v3s16, MapBlock*>::iterator
923 i = modified_blocks.begin();
924 i != modified_blocks.end(); ++i)
926 event.modified_blocks.insert(i->first);
929 catch(InvalidPositionException &e){
933 dispatchEvent(&event);
938 bool Map::getDayNightDiff(v3s16 blockpos)
941 v3s16 p = blockpos + v3s16(0,0,0);
942 MapBlock *b = getBlockNoCreate(p);
943 if(b->getDayNightDiff())
946 catch(InvalidPositionException &e){}
949 v3s16 p = blockpos + v3s16(-1,0,0);
950 MapBlock *b = getBlockNoCreate(p);
951 if(b->getDayNightDiff())
954 catch(InvalidPositionException &e){}
956 v3s16 p = blockpos + v3s16(0,-1,0);
957 MapBlock *b = getBlockNoCreate(p);
958 if(b->getDayNightDiff())
961 catch(InvalidPositionException &e){}
963 v3s16 p = blockpos + v3s16(0,0,-1);
964 MapBlock *b = getBlockNoCreate(p);
965 if(b->getDayNightDiff())
968 catch(InvalidPositionException &e){}
971 v3s16 p = blockpos + v3s16(1,0,0);
972 MapBlock *b = getBlockNoCreate(p);
973 if(b->getDayNightDiff())
976 catch(InvalidPositionException &e){}
978 v3s16 p = blockpos + v3s16(0,1,0);
979 MapBlock *b = getBlockNoCreate(p);
980 if(b->getDayNightDiff())
983 catch(InvalidPositionException &e){}
985 v3s16 p = blockpos + v3s16(0,0,1);
986 MapBlock *b = getBlockNoCreate(p);
987 if(b->getDayNightDiff())
990 catch(InvalidPositionException &e){}
995 struct TimeOrderedMapBlock {
999 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
1004 bool operator<(const TimeOrderedMapBlock &b) const
1006 return block->getUsageTimer() < b.block->getUsageTimer();
1011 Updates usage timers
1013 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
1014 std::vector<v3s16> *unloaded_blocks)
1016 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1018 // Profile modified reasons
1019 Profiler modprofiler;
1021 std::vector<v2s16> sector_deletion_queue;
1022 u32 deleted_blocks_count = 0;
1023 u32 saved_blocks_count = 0;
1024 u32 block_count_all = 0;
1028 // If there is no practical limit, we spare creation of mapblock_queue
1029 if (max_loaded_blocks == U32_MAX) {
1030 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1031 si != m_sectors.end(); ++si) {
1032 MapSector *sector = si->second;
1034 bool all_blocks_deleted = true;
1036 MapBlockVect blocks;
1037 sector->getBlocks(blocks);
1039 for (MapBlockVect::iterator i = blocks.begin();
1040 i != blocks.end(); ++i) {
1041 MapBlock *block = (*i);
1043 block->incrementUsageTimer(dtime);
1045 if (block->refGet() == 0
1046 && block->getUsageTimer() > unload_timeout) {
1047 v3s16 p = block->getPos();
1050 if (block->getModified() != MOD_STATE_CLEAN
1051 && save_before_unloading) {
1052 modprofiler.add(block->getModifiedReasonString(), 1);
1053 if (!saveBlock(block))
1055 saved_blocks_count++;
1058 // Delete from memory
1059 sector->deleteBlock(block);
1061 if (unloaded_blocks)
1062 unloaded_blocks->push_back(p);
1064 deleted_blocks_count++;
1066 all_blocks_deleted = false;
1071 if (all_blocks_deleted) {
1072 sector_deletion_queue.push_back(si->first);
1076 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
1077 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1078 si != m_sectors.end(); ++si) {
1079 MapSector *sector = si->second;
1081 MapBlockVect blocks;
1082 sector->getBlocks(blocks);
1084 for(MapBlockVect::iterator i = blocks.begin();
1085 i != blocks.end(); ++i) {
1086 MapBlock *block = (*i);
1088 block->incrementUsageTimer(dtime);
1089 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
1092 block_count_all = mapblock_queue.size();
1093 // Delete old blocks, and blocks over the limit from the memory
1094 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
1095 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
1096 TimeOrderedMapBlock b = mapblock_queue.top();
1097 mapblock_queue.pop();
1099 MapBlock *block = b.block;
1101 if (block->refGet() != 0)
1104 v3s16 p = block->getPos();
1107 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1108 modprofiler.add(block->getModifiedReasonString(), 1);
1109 if (!saveBlock(block))
1111 saved_blocks_count++;
1114 // Delete from memory
1115 b.sect->deleteBlock(block);
1117 if (unloaded_blocks)
1118 unloaded_blocks->push_back(p);
1120 deleted_blocks_count++;
1123 // Delete empty sectors
1124 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1125 si != m_sectors.end(); ++si) {
1126 if (si->second->empty()) {
1127 sector_deletion_queue.push_back(si->first);
1133 // Finally delete the empty sectors
1134 deleteSectors(sector_deletion_queue);
1136 if(deleted_blocks_count != 0)
1138 PrintInfo(infostream); // ServerMap/ClientMap:
1139 infostream<<"Unloaded "<<deleted_blocks_count
1140 <<" blocks from memory";
1141 if(save_before_unloading)
1142 infostream<<", of which "<<saved_blocks_count<<" were written";
1143 infostream<<", "<<block_count_all<<" blocks in memory";
1144 infostream<<"."<<std::endl;
1145 if(saved_blocks_count != 0){
1146 PrintInfo(infostream); // ServerMap/ClientMap:
1147 infostream<<"Blocks modified by: "<<std::endl;
1148 modprofiler.print(infostream);
1153 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1155 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
1158 void Map::deleteSectors(std::vector<v2s16> §orList)
1160 for(std::vector<v2s16>::iterator j = sectorList.begin();
1161 j != sectorList.end(); ++j) {
1162 MapSector *sector = m_sectors[*j];
1163 // If sector is in sector cache, remove it from there
1164 if(m_sector_cache == sector)
1165 m_sector_cache = NULL;
1166 // Remove from map and delete
1167 m_sectors.erase(*j);
1172 void Map::PrintInfo(std::ostream &out)
1177 #define WATER_DROP_BOOST 4
1181 NEIGHBOR_SAME_LEVEL,
1184 struct NodeNeighbor {
1188 bool l; //can liquid
1194 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1201 void Map::transforming_liquid_add(v3s16 p) {
1202 m_transforming_liquid.push_back(p);
1205 s32 Map::transforming_liquid_size() {
1206 return m_transforming_liquid.size();
1209 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
1212 INodeDefManager *nodemgr = m_gamedef->ndef();
1214 DSTACK(FUNCTION_NAME);
1215 //TimeTaker timer("transformLiquids()");
1218 u32 initial_size = m_transforming_liquid.size();
1220 /*if(initial_size != 0)
1221 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1223 // list of nodes that due to viscosity have not reached their max level height
1224 std::deque<v3s16> must_reflow;
1226 // List of MapBlocks that will require a lighting update (due to lava)
1227 std::map<v3s16, MapBlock *> lighting_modified_blocks;
1229 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1230 u32 loop_max = liquid_loop_max;
1234 /* If liquid_loop_max is not keeping up with the queue size increase
1235 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1237 if (m_transforming_liquid.size() > loop_max * 2) {
1239 float server_step = g_settings->getFloat("dedicated_server_step");
1240 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1241 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1243 m_transforming_liquid_loop_count_multiplier = 1.0;
1246 loop_max *= m_transforming_liquid_loop_count_multiplier;
1249 while (m_transforming_liquid.size() != 0)
1251 // This should be done here so that it is done when continue is used
1252 if (loopcount >= initial_size || loopcount >= loop_max)
1257 Get a queued transforming liquid node
1259 v3s16 p0 = m_transforming_liquid.front();
1260 m_transforming_liquid.pop_front();
1262 MapNode n0 = getNodeNoEx(p0);
1265 Collect information about current node
1267 s8 liquid_level = -1;
1268 content_t liquid_kind = CONTENT_IGNORE;
1269 content_t floodable_node = CONTENT_AIR;
1270 const ContentFeatures &cf = nodemgr->get(n0);
1271 LiquidType liquid_type = cf.liquid_type;
1272 switch (liquid_type) {
1274 liquid_level = LIQUID_LEVEL_SOURCE;
1275 liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing);
1277 case LIQUID_FLOWING:
1278 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1279 liquid_kind = n0.getContent();
1282 // if this node is 'floodable', it *could* be transformed
1283 // into a liquid, otherwise, continue with the next node.
1286 floodable_node = n0.getContent();
1287 liquid_kind = CONTENT_AIR;
1292 Collect information about the environment
1294 const v3s16 *dirs = g_6dirs;
1295 NodeNeighbor sources[6]; // surrounding sources
1296 int num_sources = 0;
1297 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1299 NodeNeighbor airs[6]; // surrounding air
1301 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1302 int num_neutrals = 0;
1303 bool flowing_down = false;
1304 for (u16 i = 0; i < 6; i++) {
1305 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1308 nt = NEIGHBOR_UPPER;
1311 nt = NEIGHBOR_LOWER;
1314 v3s16 npos = p0 + dirs[i];
1315 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1316 const ContentFeatures &cfnb = nodemgr->get(nb.n);
1317 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1319 if (cfnb.floodable) {
1320 airs[num_airs++] = nb;
1321 // if the current node is a water source the neighbor
1322 // should be enqueded for transformation regardless of whether the
1323 // current node changes or not.
1324 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1325 m_transforming_liquid.push_back(npos);
1326 // if the current node happens to be a flowing node, it will start to flow down here.
1327 if (nb.t == NEIGHBOR_LOWER)
1328 flowing_down = true;
1330 neutrals[num_neutrals++] = nb;
1331 // If neutral below is ignore prevent water spreading outwards
1332 if (nb.t == NEIGHBOR_LOWER &&
1333 nb.n.getContent() == CONTENT_IGNORE)
1334 flowing_down = true;
1338 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1339 if (liquid_kind == CONTENT_AIR)
1340 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1341 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1342 neutrals[num_neutrals++] = nb;
1344 // Do not count bottom source, it will screw things up
1346 sources[num_sources++] = nb;
1349 case LIQUID_FLOWING:
1350 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1351 if (liquid_kind == CONTENT_AIR)
1352 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1353 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1354 neutrals[num_neutrals++] = nb;
1356 flows[num_flows++] = nb;
1357 if (nb.t == NEIGHBOR_LOWER)
1358 flowing_down = true;
1365 decide on the type (and possibly level) of the current node
1367 content_t new_node_content;
1368 s8 new_node_level = -1;
1369 s8 max_node_level = -1;
1371 u8 range = nodemgr->get(liquid_kind).liquid_range;
1372 if (range > LIQUID_LEVEL_MAX + 1)
1373 range = LIQUID_LEVEL_MAX + 1;
1375 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1376 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1377 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1378 // it's perfectly safe to use liquid_kind here to determine the new node content.
1379 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1380 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1381 // liquid_kind is set properly, see above
1382 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1383 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1384 new_node_content = liquid_kind;
1386 new_node_content = floodable_node;
1388 // no surrounding sources, so get the maximum level that can flow into this node
1389 for (u16 i = 0; i < num_flows; i++) {
1390 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1391 switch (flows[i].t) {
1392 case NEIGHBOR_UPPER:
1393 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1394 max_node_level = LIQUID_LEVEL_MAX;
1395 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1396 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1397 } else if (nb_liquid_level > max_node_level) {
1398 max_node_level = nb_liquid_level;
1401 case NEIGHBOR_LOWER:
1403 case NEIGHBOR_SAME_LEVEL:
1404 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1405 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
1406 max_node_level = nb_liquid_level - 1;
1411 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1412 if (viscosity > 1 && max_node_level != liquid_level) {
1413 // amount to gain, limited by viscosity
1414 // must be at least 1 in absolute value
1415 s8 level_inc = max_node_level - liquid_level;
1416 if (level_inc < -viscosity || level_inc > viscosity)
1417 new_node_level = liquid_level + level_inc/viscosity;
1418 else if (level_inc < 0)
1419 new_node_level = liquid_level - 1;
1420 else if (level_inc > 0)
1421 new_node_level = liquid_level + 1;
1422 if (new_node_level != max_node_level)
1423 must_reflow.push_back(p0);
1425 new_node_level = max_node_level;
1428 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1429 new_node_content = liquid_kind;
1431 new_node_content = floodable_node;
1436 check if anything has changed. if not, just continue with the next node.
1438 if (new_node_content == n0.getContent() &&
1439 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1440 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1441 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1447 update the current node
1450 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1451 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1452 // set level to last 3 bits, flowing down bit to 4th bit
1453 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1455 // set the liquid level and flow bit to 0
1456 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1458 n0.setContent(new_node_content);
1460 // Find out whether there is a suspect for this action
1461 std::string suspect;
1462 if (m_gamedef->rollback())
1463 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1465 if (m_gamedef->rollback() && !suspect.empty()) {
1467 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1468 // Get old node for rollback
1469 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1473 RollbackNode rollback_newnode(this, p0, m_gamedef);
1474 RollbackAction action;
1475 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1476 m_gamedef->rollback()->reportAction(action);
1482 v3s16 blockpos = getNodeBlockPos(p0);
1483 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1484 if (block != NULL) {
1485 modified_blocks[blockpos] = block;
1486 // If new or old node emits light, MapBlock requires lighting update
1487 if (nodemgr->get(n0).light_source != 0 ||
1488 nodemgr->get(n00).light_source != 0)
1489 lighting_modified_blocks[block->getPos()] = block;
1493 enqueue neighbors for update if neccessary
1495 switch (nodemgr->get(n0.getContent()).liquid_type) {
1497 case LIQUID_FLOWING:
1498 // make sure source flows into all neighboring nodes
1499 for (u16 i = 0; i < num_flows; i++)
1500 if (flows[i].t != NEIGHBOR_UPPER)
1501 m_transforming_liquid.push_back(flows[i].p);
1502 for (u16 i = 0; i < num_airs; i++)
1503 if (airs[i].t != NEIGHBOR_UPPER)
1504 m_transforming_liquid.push_back(airs[i].p);
1507 // this flow has turned to air; neighboring flows might need to do the same
1508 for (u16 i = 0; i < num_flows; i++)
1509 m_transforming_liquid.push_back(flows[i].p);
1513 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1515 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1516 m_transforming_liquid.push_back(*iter);
1518 updateLighting(lighting_modified_blocks, modified_blocks);
1521 /* ----------------------------------------------------------------------
1522 * Manage the queue so that it does not grow indefinately
1524 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1526 if (time_until_purge == 0)
1527 return; // Feature disabled
1529 time_until_purge *= 1000; // seconds -> milliseconds
1531 u32 curr_time = getTime(PRECISION_MILLI);
1532 u32 prev_unprocessed = m_unprocessed_count;
1533 m_unprocessed_count = m_transforming_liquid.size();
1535 // if unprocessed block count is decreasing or stable
1536 if (m_unprocessed_count <= prev_unprocessed) {
1537 m_queue_size_timer_started = false;
1539 if (!m_queue_size_timer_started)
1540 m_inc_trending_up_start_time = curr_time;
1541 m_queue_size_timer_started = true;
1544 // Account for curr_time overflowing
1545 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1546 m_queue_size_timer_started = false;
1548 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1549 * and the number of unprocessed blocks is still > liquid_loop_max then we
1550 * cannot keep up; dump the oldest blocks from the queue so that the queue
1551 * has liquid_loop_max items in it
1553 if (m_queue_size_timer_started
1554 && curr_time - m_inc_trending_up_start_time > time_until_purge
1555 && m_unprocessed_count > liquid_loop_max) {
1557 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1559 infostream << "transformLiquids(): DUMPING " << dump_qty
1560 << " blocks from the queue" << std::endl;
1563 m_transforming_liquid.pop_front();
1565 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1566 m_unprocessed_count = m_transforming_liquid.size();
1570 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1572 std::vector<v3s16> positions_with_meta;
1574 sortBoxVerticies(p1, p2);
1575 v3s16 bpmin = getNodeBlockPos(p1);
1576 v3s16 bpmax = getNodeBlockPos(p2);
1578 VoxelArea area(p1, p2);
1580 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1581 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1582 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1583 v3s16 blockpos(x, y, z);
1585 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1587 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1588 << PP(blockpos) << std::endl;
1589 block = emergeBlock(blockpos, false);
1592 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1597 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1598 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1599 for (size_t i = 0; i != keys.size(); i++) {
1600 v3s16 p(keys[i] + p_base);
1601 if (!area.contains(p))
1604 positions_with_meta.push_back(p);
1608 return positions_with_meta;
1611 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1613 v3s16 blockpos = getNodeBlockPos(p);
1614 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1615 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1617 infostream<<"Map::getNodeMetadata(): Need to emerge "
1618 <<PP(blockpos)<<std::endl;
1619 block = emergeBlock(blockpos, false);
1622 warningstream<<"Map::getNodeMetadata(): Block not found"
1626 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1630 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1632 v3s16 blockpos = getNodeBlockPos(p);
1633 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1634 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1636 infostream<<"Map::setNodeMetadata(): Need to emerge "
1637 <<PP(blockpos)<<std::endl;
1638 block = emergeBlock(blockpos, false);
1641 warningstream<<"Map::setNodeMetadata(): Block not found"
1645 block->m_node_metadata.set(p_rel, meta);
1649 void Map::removeNodeMetadata(v3s16 p)
1651 v3s16 blockpos = getNodeBlockPos(p);
1652 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1653 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1656 warningstream<<"Map::removeNodeMetadata(): Block not found"
1660 block->m_node_metadata.remove(p_rel);
1663 NodeTimer Map::getNodeTimer(v3s16 p)
1665 v3s16 blockpos = getNodeBlockPos(p);
1666 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1667 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1669 infostream<<"Map::getNodeTimer(): Need to emerge "
1670 <<PP(blockpos)<<std::endl;
1671 block = emergeBlock(blockpos, false);
1674 warningstream<<"Map::getNodeTimer(): Block not found"
1678 NodeTimer t = block->m_node_timers.get(p_rel);
1679 NodeTimer nt(t.timeout, t.elapsed, p);
1683 void Map::setNodeTimer(const NodeTimer &t)
1685 v3s16 p = t.position;
1686 v3s16 blockpos = getNodeBlockPos(p);
1687 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1688 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1690 infostream<<"Map::setNodeTimer(): Need to emerge "
1691 <<PP(blockpos)<<std::endl;
1692 block = emergeBlock(blockpos, false);
1695 warningstream<<"Map::setNodeTimer(): Block not found"
1699 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1700 block->m_node_timers.set(nt);
1703 void Map::removeNodeTimer(v3s16 p)
1705 v3s16 blockpos = getNodeBlockPos(p);
1706 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1707 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1710 warningstream<<"Map::removeNodeTimer(): Block not found"
1714 block->m_node_timers.remove(p_rel);
1720 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1721 Map(dout_server, gamedef),
1722 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1724 m_map_metadata_changed(true)
1726 verbosestream<<FUNCTION_NAME<<std::endl;
1728 // Tell the EmergeManager about our MapSettingsManager
1729 emerge->map_settings_mgr = &settings_mgr;
1732 Try to load map; if not found, create a new one.
1735 // Determine which database backend to use
1736 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1738 bool succeeded = conf.readConfigFile(conf_path.c_str());
1739 if (!succeeded || !conf.exists("backend")) {
1740 // fall back to sqlite3
1741 conf.set("backend", "sqlite3");
1743 std::string backend = conf.get("backend");
1744 dbase = createDatabase(backend, savedir, conf);
1746 if (!conf.updateConfigFile(conf_path.c_str()))
1747 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1749 m_savedir = savedir;
1750 m_map_saving_enabled = false;
1754 // If directory exists, check contents and load if possible
1755 if(fs::PathExists(m_savedir))
1757 // If directory is empty, it is safe to save into it.
1758 if(fs::GetDirListing(m_savedir).size() == 0)
1760 infostream<<"ServerMap: Empty save directory is valid."
1762 m_map_saving_enabled = true;
1767 if (settings_mgr.loadMapMeta()) {
1768 infostream << "ServerMap: Metadata loaded from "
1769 << savedir << std::endl;
1771 infostream << "ServerMap: Metadata could not be loaded "
1772 "from " << savedir << ", assuming valid save "
1773 "directory." << std::endl;
1776 m_map_saving_enabled = true;
1777 // Map loaded, not creating new one
1781 // If directory doesn't exist, it is safe to save to it
1783 m_map_saving_enabled = true;
1786 catch(std::exception &e)
1788 warningstream<<"ServerMap: Failed to load map from "<<savedir
1789 <<", exception: "<<e.what()<<std::endl;
1790 infostream<<"Please remove the map or fix it."<<std::endl;
1791 warningstream<<"Map saving will be disabled."<<std::endl;
1794 infostream<<"Initializing new map."<<std::endl;
1796 // Create zero sector
1797 emergeSector(v2s16(0,0));
1799 // Initially write whole map
1800 save(MOD_STATE_CLEAN);
1803 ServerMap::~ServerMap()
1805 verbosestream<<FUNCTION_NAME<<std::endl;
1809 if(m_map_saving_enabled)
1811 // Save only changed parts
1812 save(MOD_STATE_WRITE_AT_UNLOAD);
1813 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
1817 infostream<<"ServerMap: Map not saved"<<std::endl;
1820 catch(std::exception &e)
1822 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1823 <<", exception: "<<e.what()<<std::endl;
1827 Close database if it was opened
1835 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1836 for(; i.atEnd() == false; i++)
1838 MapChunk *chunk = i.getNode()->getValue();
1844 MapgenParams *ServerMap::getMapgenParams()
1846 // getMapgenParams() should only ever be called after Server is initialized
1847 assert(settings_mgr.mapgen_params != NULL);
1848 return settings_mgr.mapgen_params;
1851 u64 ServerMap::getSeed()
1853 return getMapgenParams()->seed;
1856 s16 ServerMap::getWaterLevel()
1858 return getMapgenParams()->water_level;
1861 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1863 s16 csize = getMapgenParams()->chunksize;
1864 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1865 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1867 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1868 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1870 v3s16 extra_borders(1, 1, 1);
1871 v3s16 full_bpmin = bpmin - extra_borders;
1872 v3s16 full_bpmax = bpmax + extra_borders;
1874 // Do nothing if not inside limits (+-1 because of neighbors)
1875 if (blockpos_over_limit(full_bpmin) ||
1876 blockpos_over_limit(full_bpmax))
1879 data->seed = getSeed();
1880 data->blockpos_min = bpmin;
1881 data->blockpos_max = bpmax;
1882 data->blockpos_requested = blockpos;
1883 data->nodedef = m_gamedef->ndef();
1886 Create the whole area of this and the neighboring blocks
1888 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1889 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1890 v2s16 sectorpos(x, z);
1891 // Sector metadata is loaded from disk if not already loaded.
1892 ServerMapSector *sector = createSector(sectorpos);
1893 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1895 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1898 MapBlock *block = emergeBlock(p, false);
1899 if (block == NULL) {
1900 block = createBlock(p);
1902 // Block gets sunlight if this is true.
1903 // Refer to the map generator heuristics.
1904 bool ug = m_emerge->isBlockUnderground(p);
1905 block->setIsUnderground(ug);
1911 Now we have a big empty area.
1913 Make a ManualMapVoxelManipulator that contains this and the
1917 data->vmanip = new MMVManip(this);
1918 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1920 // Note: we may need this again at some point.
1922 // Ensure none of the blocks to be generated were marked as
1923 // containing CONTENT_IGNORE
1924 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1925 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1926 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1927 core::map<v3s16, u8>::Node *n;
1928 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1931 u8 flags = n->getValue();
1932 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1939 // Data is ready now.
1943 void ServerMap::finishBlockMake(BlockMakeData *data,
1944 std::map<v3s16, MapBlock*> *changed_blocks)
1946 v3s16 bpmin = data->blockpos_min;
1947 v3s16 bpmax = data->blockpos_max;
1949 v3s16 extra_borders(1, 1, 1);
1950 v3s16 full_bpmin = bpmin - extra_borders;
1951 v3s16 full_bpmax = bpmax + extra_borders;
1953 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1954 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1957 Set lighting to non-expired state in all of them.
1958 This is cheating, but it is not fast enough if all of them
1959 would actually be updated.
1961 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1962 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
1963 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1964 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
1968 block->setLightingExpired(false);
1972 Blit generated stuff to map
1973 NOTE: blitBackAll adds nearly everything to changed_blocks
1975 data->vmanip->blitBackAll(changed_blocks);
1977 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1978 << changed_blocks->size());
1981 Copy transforming liquid information
1983 while (data->transforming_liquid.size()) {
1984 m_transforming_liquid.push_back(data->transforming_liquid.front());
1985 data->transforming_liquid.pop_front();
1988 for (std::map<v3s16, MapBlock *>::iterator
1989 it = changed_blocks->begin();
1990 it != changed_blocks->end(); ++it) {
1991 MapBlock *block = it->second;
1995 Update day/night difference cache of the MapBlocks
1997 block->expireDayNightDiff();
1999 Set block as modified
2001 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2002 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2006 Set central blocks as generated
2008 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2009 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2010 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2011 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2015 block->setGenerated(true);
2019 Save changed parts of map
2020 NOTE: Will be saved later.
2022 //save(MOD_STATE_WRITE_AT_UNLOAD);
2025 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2027 DSTACKF("%s: p2d=(%d,%d)",
2032 Check if it exists already in memory
2034 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2039 Try to load it from disk (with blocks)
2041 //if(loadSectorFull(p2d) == true)
2044 Try to load metadata from disk
2047 if(loadSectorMeta(p2d) == true)
2049 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2052 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2053 throw InvalidPositionException("");
2059 Do not create over-limit
2061 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2062 g_settings->getU16("map_generation_limit"));
2063 if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2064 || p2d.X > map_gen_limit / MAP_BLOCKSIZE
2065 || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2066 || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
2067 throw InvalidPositionException("createSector(): pos. over limit");
2070 Generate blank sector
2073 sector = new ServerMapSector(this, p2d, m_gamedef);
2075 // Sector position on map in nodes
2076 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2081 m_sectors[p2d] = sector;
2088 This is a quick-hand function for calling makeBlock().
2090 MapBlock * ServerMap::generateBlock(
2092 std::map<v3s16, MapBlock*> &modified_blocks
2095 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2097 /*infostream<<"generateBlock(): "
2098 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2101 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2103 TimeTaker timer("generateBlock");
2105 //MapBlock *block = original_dummy;
2107 v2s16 p2d(p.X, p.Z);
2108 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2111 Do not generate over-limit
2113 if(blockpos_over_limit(p))
2115 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2116 throw InvalidPositionException("generateBlock(): pos. over limit");
2120 Create block make data
2123 initBlockMake(&data, p);
2129 TimeTaker t("mapgen::make_block()");
2130 mapgen->makeChunk(&data);
2131 //mapgen::make_block(&data);
2133 if(enable_mapgen_debug_info == false)
2134 t.stop(true); // Hide output
2138 Blit data back on map, update lighting, add mobs and whatever this does
2140 finishBlockMake(&data, modified_blocks);
2145 MapBlock *block = getBlockNoCreateNoEx(p);
2153 bool erroneus_content = false;
2154 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2155 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2156 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2159 MapNode n = block->getNode(p);
2160 if(n.getContent() == CONTENT_IGNORE)
2162 infostream<<"CONTENT_IGNORE at "
2163 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2165 erroneus_content = true;
2169 if(erroneus_content)
2178 Generate a completely empty block
2182 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2183 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2185 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2188 n.setContent(CONTENT_AIR);
2189 block->setNode(v3s16(x0,y0,z0), n);
2195 if(enable_mapgen_debug_info == false)
2196 timer.stop(true); // Hide output
2202 MapBlock * ServerMap::createBlock(v3s16 p)
2204 DSTACKF("%s: p=(%d,%d,%d)",
2205 FUNCTION_NAME, p.X, p.Y, p.Z);
2208 Do not create over-limit
2210 if (blockpos_over_limit(p))
2211 throw InvalidPositionException("createBlock(): pos. over limit");
2213 v2s16 p2d(p.X, p.Z);
2216 This will create or load a sector if not found in memory.
2217 If block exists on disk, it will be loaded.
2219 NOTE: On old save formats, this will be slow, as it generates
2220 lighting on blocks for them.
2222 ServerMapSector *sector;
2224 sector = (ServerMapSector*)createSector(p2d);
2225 assert(sector->getId() == MAPSECTOR_SERVER);
2227 catch(InvalidPositionException &e)
2229 infostream<<"createBlock: createSector() failed"<<std::endl;
2233 NOTE: This should not be done, or at least the exception
2234 should not be passed on as std::exception, because it
2235 won't be catched at all.
2237 /*catch(std::exception &e)
2239 infostream<<"createBlock: createSector() failed: "
2240 <<e.what()<<std::endl;
2245 Try to get a block from the sector
2248 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2251 if(block->isDummy())
2256 block = sector->createBlankBlock(block_y);
2261 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2263 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2265 p.X, p.Y, p.Z, create_blank);
2268 MapBlock *block = getBlockNoCreateNoEx(p);
2269 if(block && block->isDummy() == false)
2274 MapBlock *block = loadBlock(p);
2280 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2281 MapBlock *block = sector->createBlankBlock(p.Y);
2289 std::map<v3s16, MapBlock*> modified_blocks;
2290 MapBlock *block = generateBlock(p, modified_blocks);
2294 event.type = MEET_OTHER;
2297 // Copy modified_blocks to event
2298 for(std::map<v3s16, MapBlock*>::iterator
2299 i = modified_blocks.begin();
2300 i != modified_blocks.end(); ++i)
2302 event.modified_blocks.insert(i->first);
2306 dispatchEvent(&event);
2316 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2318 MapBlock *block = getBlockNoCreateNoEx(p3d);
2320 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2325 void ServerMap::prepareBlock(MapBlock *block) {
2328 // N.B. This requires no synchronization, since data will not be modified unless
2329 // the VoxelManipulator being updated belongs to the same thread.
2330 void ServerMap::updateVManip(v3s16 pos)
2332 Mapgen *mg = m_emerge->getCurrentMapgen();
2336 MMVManip *vm = mg->vm;
2340 if (!vm->m_area.contains(pos))
2343 s32 idx = vm->m_area.index(pos);
2344 vm->m_data[idx] = getNodeNoEx(pos);
2345 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2347 vm->m_is_dirty = true;
2350 s16 ServerMap::findGroundLevel(v2s16 p2d)
2354 Uh, just do something random...
2356 // Find existing map from top to down
2359 v3s16 p(p2d.X, max, p2d.Y);
2360 for(; p.Y>min; p.Y--)
2362 MapNode n = getNodeNoEx(p);
2363 if(n.getContent() != CONTENT_IGNORE)
2368 // If this node is not air, go to plan b
2369 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2371 // Search existing walkable and return it
2372 for(; p.Y>min; p.Y--)
2374 MapNode n = getNodeNoEx(p);
2375 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2384 Determine from map generator noise functions
2387 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2390 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2391 //return (s16)level;
2394 bool ServerMap::loadFromFolders() {
2395 if (!dbase->initialized() &&
2396 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2401 void ServerMap::createDirs(std::string path)
2403 if(fs::CreateAllDirs(path) == false)
2405 m_dout<<"ServerMap: Failed to create directory "
2406 <<"\""<<path<<"\""<<std::endl;
2407 throw BaseException("ServerMap failed to create directory");
2411 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2417 snprintf(cc, 9, "%.4x%.4x",
2418 (unsigned int) pos.X & 0xffff,
2419 (unsigned int) pos.Y & 0xffff);
2421 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2423 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2424 (unsigned int) pos.X & 0xfff,
2425 (unsigned int) pos.Y & 0xfff);
2427 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2434 v2s16 ServerMap::getSectorPos(std::string dirname)
2436 unsigned int x = 0, y = 0;
2438 std::string component;
2439 fs::RemoveLastPathComponent(dirname, &component, 1);
2440 if(component.size() == 8)
2443 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2445 else if(component.size() == 3)
2448 fs::RemoveLastPathComponent(dirname, &component, 2);
2449 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2450 // Sign-extend the 12 bit values up to 16 bits...
2451 if(x & 0x800) x |= 0xF000;
2452 if(y & 0x800) y |= 0xF000;
2459 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2460 v2s16 pos((s16)x, (s16)y);
2464 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2466 v2s16 p2d = getSectorPos(sectordir);
2468 if(blockfile.size() != 4){
2469 throw InvalidFilenameException("Invalid block filename");
2472 int r = sscanf(blockfile.c_str(), "%4x", &y);
2474 throw InvalidFilenameException("Invalid block filename");
2475 return v3s16(p2d.X, y, p2d.Y);
2478 std::string ServerMap::getBlockFilename(v3s16 p)
2481 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2485 void ServerMap::save(ModifiedState save_level)
2487 DSTACK(FUNCTION_NAME);
2488 if(m_map_saving_enabled == false) {
2489 warningstream<<"Not saving map, saving disabled."<<std::endl;
2493 if(save_level == MOD_STATE_CLEAN)
2494 infostream<<"ServerMap: Saving whole map, this can take time."
2497 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2498 if (settings_mgr.saveMapMeta())
2499 m_map_metadata_changed = false;
2502 // Profile modified reasons
2503 Profiler modprofiler;
2505 u32 sector_meta_count = 0;
2506 u32 block_count = 0;
2507 u32 block_count_all = 0; // Number of blocks in memory
2509 // Don't do anything with sqlite unless something is really saved
2510 bool save_started = false;
2512 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2513 i != m_sectors.end(); ++i) {
2514 ServerMapSector *sector = (ServerMapSector*)i->second;
2515 assert(sector->getId() == MAPSECTOR_SERVER);
2517 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2518 saveSectorMeta(sector);
2519 sector_meta_count++;
2522 MapBlockVect blocks;
2523 sector->getBlocks(blocks);
2525 for(MapBlockVect::iterator j = blocks.begin();
2526 j != blocks.end(); ++j) {
2527 MapBlock *block = *j;
2531 if(block->getModified() >= (u32)save_level) {
2535 save_started = true;
2538 modprofiler.add(block->getModifiedReasonString(), 1);
2543 /*infostream<<"ServerMap: Written block ("
2544 <<block->getPos().X<<","
2545 <<block->getPos().Y<<","
2546 <<block->getPos().Z<<")"
2556 Only print if something happened or saved whole map
2558 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2559 || block_count != 0) {
2560 infostream<<"ServerMap: Written: "
2561 <<sector_meta_count<<" sector metadata files, "
2562 <<block_count<<" block files"
2563 <<", "<<block_count_all<<" blocks in memory."
2565 PrintInfo(infostream); // ServerMap/ClientMap:
2566 infostream<<"Blocks modified by: "<<std::endl;
2567 modprofiler.print(infostream);
2571 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2573 if (loadFromFolders()) {
2574 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2575 << "all blocks that are stored in flat files." << std::endl;
2577 dbase->listAllLoadableBlocks(dst);
2580 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2582 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2583 si != m_sectors.end(); ++si)
2585 MapSector *sector = si->second;
2587 MapBlockVect blocks;
2588 sector->getBlocks(blocks);
2590 for(MapBlockVect::iterator i = blocks.begin();
2591 i != blocks.end(); ++i) {
2592 v3s16 p = (*i)->getPos();
2598 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2600 DSTACK(FUNCTION_NAME);
2601 // Format used for writing
2602 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2604 v2s16 pos = sector->getPos();
2605 std::string dir = getSectorDir(pos);
2608 std::string fullpath = dir + DIR_DELIM + "meta";
2609 std::ostringstream ss(std::ios_base::binary);
2611 sector->serialize(ss, version);
2613 if(!fs::safeWriteToFile(fullpath, ss.str()))
2614 throw FileNotGoodException("Cannot write sector metafile");
2616 sector->differs_from_disk = false;
2619 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2621 DSTACK(FUNCTION_NAME);
2623 v2s16 p2d = getSectorPos(sectordir);
2625 ServerMapSector *sector = NULL;
2627 std::string fullpath = sectordir + DIR_DELIM + "meta";
2628 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2629 if(is.good() == false)
2631 // If the directory exists anyway, it probably is in some old
2632 // format. Just go ahead and create the sector.
2633 if(fs::PathExists(sectordir))
2635 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
2636 <<fullpath<<" doesn't exist but directory does."
2637 <<" Continuing with a sector with no metadata."
2639 sector = new ServerMapSector(this, p2d, m_gamedef);
2640 m_sectors[p2d] = sector;
2644 throw FileNotGoodException("Cannot open sector metafile");
2649 sector = ServerMapSector::deSerialize
2650 (is, this, p2d, m_sectors, m_gamedef);
2652 saveSectorMeta(sector);
2655 sector->differs_from_disk = false;
2660 bool ServerMap::loadSectorMeta(v2s16 p2d)
2662 DSTACK(FUNCTION_NAME);
2664 // The directory layout we're going to load from.
2665 // 1 - original sectors/xxxxzzzz/
2666 // 2 - new sectors2/xxx/zzz/
2667 // If we load from anything but the latest structure, we will
2668 // immediately save to the new one, and remove the old.
2670 std::string sectordir1 = getSectorDir(p2d, 1);
2671 std::string sectordir;
2672 if(fs::PathExists(sectordir1))
2674 sectordir = sectordir1;
2679 sectordir = getSectorDir(p2d, 2);
2683 loadSectorMeta(sectordir, loadlayout != 2);
2685 catch(InvalidFilenameException &e)
2689 catch(FileNotGoodException &e)
2693 catch(std::exception &e)
2702 bool ServerMap::loadSectorFull(v2s16 p2d)
2704 DSTACK(FUNCTION_NAME);
2706 MapSector *sector = NULL;
2708 // The directory layout we're going to load from.
2709 // 1 - original sectors/xxxxzzzz/
2710 // 2 - new sectors2/xxx/zzz/
2711 // If we load from anything but the latest structure, we will
2712 // immediately save to the new one, and remove the old.
2714 std::string sectordir1 = getSectorDir(p2d, 1);
2715 std::string sectordir;
2716 if(fs::PathExists(sectordir1))
2718 sectordir = sectordir1;
2723 sectordir = getSectorDir(p2d, 2);
2727 sector = loadSectorMeta(sectordir, loadlayout != 2);
2729 catch(InvalidFilenameException &e)
2733 catch(FileNotGoodException &e)
2737 catch(std::exception &e)
2745 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2747 std::vector<fs::DirListNode>::iterator i2;
2748 for(i2=list2.begin(); i2!=list2.end(); i2++)
2754 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2756 catch(InvalidFilenameException &e)
2758 // This catches unknown crap in directory
2764 infostream<<"Sector converted to new layout - deleting "<<
2765 sectordir1<<std::endl;
2766 fs::RecursiveDelete(sectordir1);
2773 Database *ServerMap::createDatabase(
2774 const std::string &name,
2775 const std::string &savedir,
2778 if (name == "sqlite3")
2779 return new Database_SQLite3(savedir);
2780 if (name == "dummy")
2781 return new Database_Dummy();
2783 else if (name == "leveldb")
2784 return new Database_LevelDB(savedir);
2787 else if (name == "redis")
2788 return new Database_Redis(conf);
2791 else if (name == "postgresql")
2792 return new Database_PostgreSQL(conf);
2795 throw BaseException(std::string("Database backend ") + name + " not supported.");
2798 void ServerMap::beginSave()
2803 void ServerMap::endSave()
2808 bool ServerMap::saveBlock(MapBlock *block)
2810 return saveBlock(block, dbase);
2813 bool ServerMap::saveBlock(MapBlock *block, Database *db)
2815 v3s16 p3d = block->getPos();
2817 // Dummy blocks are not written
2818 if (block->isDummy()) {
2819 warningstream << "saveBlock: Not writing dummy block "
2820 << PP(p3d) << std::endl;
2824 // Format used for writing
2825 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2828 [0] u8 serialization version
2831 std::ostringstream o(std::ios_base::binary);
2832 o.write((char*) &version, 1);
2833 block->serialize(o, version, true);
2835 std::string data = o.str();
2836 bool ret = db->saveBlock(p3d, data);
2838 // We just wrote it to the disk so clear modified flag
2839 block->resetModified();
2844 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
2845 MapSector *sector, bool save_after_load)
2847 DSTACK(FUNCTION_NAME);
2849 std::string fullpath = sectordir + DIR_DELIM + blockfile;
2852 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2853 if(is.good() == false)
2854 throw FileNotGoodException("Cannot open block file");
2856 v3s16 p3d = getBlockPos(sectordir, blockfile);
2857 v2s16 p2d(p3d.X, p3d.Z);
2859 assert(sector->getPos() == p2d);
2861 u8 version = SER_FMT_VER_INVALID;
2862 is.read((char*)&version, 1);
2865 throw SerializationError("ServerMap::loadBlock(): Failed"
2866 " to read MapBlock version");
2868 /*u32 block_size = MapBlock::serializedLength(version);
2869 SharedBuffer<u8> data(block_size);
2870 is.read((char*)*data, block_size);*/
2872 // This will always return a sector because we're the server
2873 //MapSector *sector = emergeSector(p2d);
2875 MapBlock *block = NULL;
2876 bool created_new = false;
2877 block = sector->getBlockNoCreateNoEx(p3d.Y);
2880 block = sector->createBlankBlockNoInsert(p3d.Y);
2885 block->deSerialize(is, version, true);
2887 // If it's a new block, insert it to the map
2889 sector->insertBlock(block);
2892 Save blocks loaded in old format in new format
2895 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
2899 // Should be in database now, so delete the old file
2900 fs::RecursiveDelete(fullpath);
2903 // We just loaded it from the disk, so it's up-to-date.
2904 block->resetModified();
2907 catch(SerializationError &e)
2909 warningstream<<"Invalid block data on disk "
2910 <<"fullpath="<<fullpath
2911 <<" (SerializationError). "
2912 <<"what()="<<e.what()
2914 // Ignoring. A new one will be generated.
2917 // TODO: Backup file; name is in fullpath.
2921 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
2923 DSTACK(FUNCTION_NAME);
2926 std::istringstream is(*blob, std::ios_base::binary);
2928 u8 version = SER_FMT_VER_INVALID;
2929 is.read((char*)&version, 1);
2932 throw SerializationError("ServerMap::loadBlock(): Failed"
2933 " to read MapBlock version");
2935 /*u32 block_size = MapBlock::serializedLength(version);
2936 SharedBuffer<u8> data(block_size);
2937 is.read((char*)*data, block_size);*/
2939 // This will always return a sector because we're the server
2940 //MapSector *sector = emergeSector(p2d);
2942 MapBlock *block = NULL;
2943 bool created_new = false;
2944 block = sector->getBlockNoCreateNoEx(p3d.Y);
2947 block = sector->createBlankBlockNoInsert(p3d.Y);
2952 block->deSerialize(is, version, true);
2954 // If it's a new block, insert it to the map
2956 sector->insertBlock(block);
2959 Save blocks loaded in old format in new format
2962 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
2963 // Only save if asked to; no need to update version
2967 // We just loaded it from, so it's up-to-date.
2968 block->resetModified();
2971 catch(SerializationError &e)
2973 errorstream<<"Invalid block data in database"
2974 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2975 <<" (SerializationError): "<<e.what()<<std::endl;
2977 // TODO: Block should be marked as invalid in memory so that it is
2978 // not touched but the game can run
2980 if(g_settings->getBool("ignore_world_load_errors")){
2981 errorstream<<"Ignoring block load error. Duck and cover! "
2982 <<"(ignore_world_load_errors)"<<std::endl;
2984 throw SerializationError("Invalid block data in database");
2989 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
2991 DSTACK(FUNCTION_NAME);
2993 v2s16 p2d(blockpos.X, blockpos.Z);
2996 dbase->loadBlock(blockpos, &ret);
2998 loadBlock(&ret, blockpos, createSector(p2d), false);
2999 return getBlockNoCreateNoEx(blockpos);
3001 // Not found in database, try the files
3003 // The directory layout we're going to load from.
3004 // 1 - original sectors/xxxxzzzz/
3005 // 2 - new sectors2/xxx/zzz/
3006 // If we load from anything but the latest structure, we will
3007 // immediately save to the new one, and remove the old.
3009 std::string sectordir1 = getSectorDir(p2d, 1);
3010 std::string sectordir;
3011 if(fs::PathExists(sectordir1))
3013 sectordir = sectordir1;
3018 sectordir = getSectorDir(p2d, 2);
3022 Make sure sector is loaded
3025 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3029 sector = loadSectorMeta(sectordir, loadlayout != 2);
3031 catch(InvalidFilenameException &e)
3035 catch(FileNotGoodException &e)
3039 catch(std::exception &e)
3046 Make sure file exists
3049 std::string blockfilename = getBlockFilename(blockpos);
3050 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3054 Load block and save it to the database
3056 loadBlock(sectordir, blockfilename, sector, true);
3057 return getBlockNoCreateNoEx(blockpos);
3060 bool ServerMap::deleteBlock(v3s16 blockpos)
3062 if (!dbase->deleteBlock(blockpos))
3065 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3067 v2s16 p2d(blockpos.X, blockpos.Z);
3068 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3071 sector->deleteBlock(block);
3077 void ServerMap::PrintInfo(std::ostream &out)
3082 MMVManip::MMVManip(Map *map):
3085 m_create_area(false),
3090 MMVManip::~MMVManip()
3094 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3095 bool load_if_inexistent)
3097 TimeTaker timer1("initialEmerge", &emerge_time);
3099 // Units of these are MapBlocks
3100 v3s16 p_min = blockpos_min;
3101 v3s16 p_max = blockpos_max;
3103 VoxelArea block_area_nodes
3104 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3106 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3109 infostream<<"initialEmerge: area: ";
3110 block_area_nodes.print(infostream);
3111 infostream<<" ("<<size_MB<<"MB)";
3112 infostream<<std::endl;
3115 addArea(block_area_nodes);
3117 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3118 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3119 for(s32 x=p_min.X; x<=p_max.X; x++)
3124 std::map<v3s16, u8>::iterator n;
3125 n = m_loaded_blocks.find(p);
3126 if(n != m_loaded_blocks.end())
3129 bool block_data_inexistent = false;
3132 TimeTaker timer1("emerge load", &emerge_load_time);
3134 block = m_map->getBlockNoCreate(p);
3135 if(block->isDummy())
3136 block_data_inexistent = true;
3138 block->copyTo(*this);
3140 catch(InvalidPositionException &e)
3142 block_data_inexistent = true;
3145 if(block_data_inexistent)
3148 if (load_if_inexistent) {
3149 ServerMap *svrmap = (ServerMap *)m_map;
3150 block = svrmap->emergeBlock(p, false);
3152 block = svrmap->createBlock(p);
3153 block->copyTo(*this);
3155 flags |= VMANIP_BLOCK_DATA_INEXIST;
3158 Mark area inexistent
3160 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3161 // Fill with VOXELFLAG_NO_DATA
3162 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3163 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3165 s32 i = m_area.index(a.MinEdge.X,y,z);
3166 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3170 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3172 // Mark that block was loaded as blank
3173 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3176 m_loaded_blocks[p] = flags;
3182 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3183 bool overwrite_generated)
3185 if(m_area.getExtent() == v3s16(0,0,0))
3189 Copy data of all blocks
3191 for(std::map<v3s16, u8>::iterator
3192 i = m_loaded_blocks.begin();
3193 i != m_loaded_blocks.end(); ++i)
3196 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3197 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3198 if ((existed == false) || (block == NULL) ||
3199 (overwrite_generated == false && block->isGenerated() == true))
3202 block->copyFrom(*this);
3205 (*modified_blocks)[p] = block;