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"
38 #include "reflowscan.h"
40 #include "mapgen_v6.h"
45 #include "database-dummy.h"
46 #include "database-sqlite3.h"
50 #include "database-leveldb.h"
53 #include "database-redis.h"
56 #include "database-postgresql.h"
59 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
66 Map::Map(std::ostream &dout, IGameDef *gamedef):
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_gamedef->ndef()->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)
261 INodeDefManager *nodemgr = m_gamedef->ndef();
264 v3s16(0,0,1), // back
266 v3s16(1,0,0), // right
267 v3s16(0,0,-1), // front
268 v3s16(0,-1,0), // bottom
269 v3s16(-1,0,0), // left
272 if(from_nodes.empty())
275 u32 blockchangecount = 0;
277 std::map<v3s16, u8> unlighted_nodes;
280 Initialize block cache
283 MapBlock *block = NULL;
284 // Cache this a bit, too
285 bool block_checked_in_modified = false;
287 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
288 j != from_nodes.end(); ++j)
290 v3s16 pos = j->first;
291 v3s16 blockpos = getNodeBlockPos(pos);
293 // Only fetch a new block if the block position has changed
295 if(block == NULL || blockpos != blockpos_last){
296 block = getBlockNoCreate(blockpos);
297 blockpos_last = blockpos;
299 block_checked_in_modified = false;
303 catch(InvalidPositionException &e)
311 // Calculate relative position in block
312 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
314 // Get node straight from the block
315 //MapNode n = block->getNode(relpos);
317 u8 oldlight = j->second;
319 // Loop through 6 neighbors
320 for(u16 i=0; i<6; i++)
322 // Get the position of the neighbor node
323 v3s16 n2pos = pos + dirs[i];
325 // Get the block where the node is located
326 v3s16 blockpos, relpos;
327 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
329 // Only fetch a new block if the block position has changed
331 if(block == NULL || blockpos != blockpos_last){
332 block = getBlockNoCreate(blockpos);
333 blockpos_last = blockpos;
335 block_checked_in_modified = false;
339 catch(InvalidPositionException &e) {
343 // Get node straight from the block
344 bool is_valid_position;
345 MapNode n2 = block->getNode(relpos, &is_valid_position);
346 if (!is_valid_position)
349 bool changed = false;
351 //TODO: Optimize output by optimizing light_sources?
354 If the neighbor is dimmer than what was specified
355 as oldlight (the light of the previous node)
357 if(n2.getLight(bank, nodemgr) < oldlight)
360 And the neighbor is transparent and it has some light
362 if(nodemgr->get(n2).light_propagates
363 && n2.getLight(bank, nodemgr) != 0)
366 Set light to 0 and add to queue
369 u8 current_light = n2.getLight(bank, nodemgr);
370 n2.setLight(bank, 0, nodemgr);
371 block->setNode(relpos, n2);
373 unlighted_nodes[n2pos] = current_light;
377 Remove from light_sources if it is there
378 NOTE: This doesn't happen nearly at all
380 /*if(light_sources.find(n2pos))
382 infostream<<"Removed from light_sources"<<std::endl;
383 light_sources.remove(n2pos);
388 if(light_sources.find(n2pos) != NULL)
389 light_sources.remove(n2pos);*/
392 light_sources.insert(n2pos);
395 // Add to modified_blocks
396 if(changed == true && block_checked_in_modified == false)
398 // If the block is not found in modified_blocks, add.
399 if(modified_blocks.find(blockpos) == modified_blocks.end())
401 modified_blocks[blockpos] = block;
403 block_checked_in_modified = true;
408 /*infostream<<"unspreadLight(): Changed block "
409 <<blockchangecount<<" times"
410 <<" for "<<from_nodes.size()<<" nodes"
413 if(!unlighted_nodes.empty())
414 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
418 Lights neighbors of from_nodes, collects all them and then
421 void Map::spreadLight(enum LightBank bank,
422 std::set<v3s16> & from_nodes,
423 std::map<v3s16, MapBlock*> & modified_blocks)
425 INodeDefManager *nodemgr = m_gamedef->ndef();
427 const v3s16 dirs[6] = {
428 v3s16(0,0,1), // back
430 v3s16(1,0,0), // right
431 v3s16(0,0,-1), // front
432 v3s16(0,-1,0), // bottom
433 v3s16(-1,0,0), // left
436 if(from_nodes.empty())
439 u32 blockchangecount = 0;
441 std::set<v3s16> lighted_nodes;
444 Initialize block cache
447 MapBlock *block = NULL;
448 // Cache this a bit, too
449 bool block_checked_in_modified = false;
451 for(std::set<v3s16>::iterator j = from_nodes.begin();
452 j != from_nodes.end(); ++j)
455 v3s16 blockpos, relpos;
457 getNodeBlockPosWithOffset(pos, blockpos, relpos);
459 // Only fetch a new block if the block position has changed
461 if(block == NULL || blockpos != blockpos_last){
462 block = getBlockNoCreate(blockpos);
463 blockpos_last = blockpos;
465 block_checked_in_modified = false;
469 catch(InvalidPositionException &e) {
476 // Get node straight from the block
477 bool is_valid_position;
478 MapNode n = block->getNode(relpos, &is_valid_position);
480 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
481 u8 newlight = diminish_light(oldlight);
483 // Loop through 6 neighbors
484 for(u16 i=0; i<6; i++){
485 // Get the position of the neighbor node
486 v3s16 n2pos = pos + dirs[i];
488 // Get the block where the node is located
489 v3s16 blockpos, relpos;
490 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
492 // Only fetch a new block if the block position has changed
494 if(block == NULL || blockpos != blockpos_last){
495 block = getBlockNoCreate(blockpos);
496 blockpos_last = blockpos;
498 block_checked_in_modified = false;
502 catch(InvalidPositionException &e) {
506 // Get node straight from the block
507 MapNode n2 = block->getNode(relpos, &is_valid_position);
508 if (!is_valid_position)
511 bool changed = false;
513 If the neighbor is brighter than the current node,
514 add to list (it will light up this node on its turn)
516 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
518 lighted_nodes.insert(n2pos);
522 If the neighbor is dimmer than how much light this node
523 would spread on it, add to list
525 if(n2.getLight(bank, nodemgr) < newlight)
527 if(nodemgr->get(n2).light_propagates)
529 n2.setLight(bank, newlight, nodemgr);
530 block->setNode(relpos, n2);
531 lighted_nodes.insert(n2pos);
536 // Add to modified_blocks
537 if(changed == true && block_checked_in_modified == false)
539 // If the block is not found in modified_blocks, add.
540 if(modified_blocks.find(blockpos) == modified_blocks.end())
542 modified_blocks[blockpos] = block;
544 block_checked_in_modified = true;
549 /*infostream<<"spreadLight(): Changed block "
550 <<blockchangecount<<" times"
551 <<" for "<<from_nodes.size()<<" nodes"
554 if(!lighted_nodes.empty())
555 spreadLight(bank, lighted_nodes, modified_blocks);
558 void Map::updateLighting(enum LightBank bank,
559 std::map<v3s16, MapBlock*> & a_blocks,
560 std::map<v3s16, MapBlock*> & modified_blocks)
562 INodeDefManager *nodemgr = m_gamedef->ndef();
564 /*m_dout<<"Map::updateLighting(): "
565 <<a_blocks.size()<<" blocks."<<std::endl;*/
567 //TimeTaker timer("updateLighting");
571 //u32 count_was = modified_blocks.size();
573 //std::map<v3s16, MapBlock*> blocks_to_update;
575 std::set<v3s16> light_sources;
577 std::map<v3s16, u8> unlight_from;
579 int num_bottom_invalid = 0;
582 //TimeTaker t("first stuff");
584 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
585 i != a_blocks.end(); ++i)
587 MapBlock *block = i->second;
591 // Don't bother with dummy blocks.
595 v3s16 pos = block->getPos();
596 v3s16 posnodes = block->getPosRelative();
597 modified_blocks[pos] = block;
598 //blocks_to_update[pos] = block;
601 Clear all light from block
603 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
604 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
605 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
608 bool is_valid_position;
609 MapNode n = block->getNode(p, &is_valid_position);
610 if (!is_valid_position) {
611 /* This would happen when dealing with a
614 infostream<<"updateLighting(): InvalidPositionException"
618 u8 oldlight = n.getLight(bank, nodemgr);
619 n.setLight(bank, 0, nodemgr);
620 block->setNode(p, n);
622 // If node sources light, add to list
623 u8 source = nodemgr->get(n).light_source;
625 light_sources.insert(p + posnodes);
627 // Collect borders for unlighting
628 if((x==0 || x == MAP_BLOCKSIZE-1
629 || y==0 || y == MAP_BLOCKSIZE-1
630 || z==0 || z == MAP_BLOCKSIZE-1)
633 v3s16 p_map = p + posnodes;
634 unlight_from[p_map] = oldlight;
640 if(bank == LIGHTBANK_DAY)
642 bool bottom_valid = block->propagateSunlight(light_sources);
645 num_bottom_invalid++;
647 // If bottom is valid, we're done.
651 else if(bank == LIGHTBANK_NIGHT)
653 // For night lighting, sunlight is not propagated
658 assert("Invalid lighting bank" == NULL);
661 /*infostream<<"Bottom for sunlight-propagated block ("
662 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
665 // Bottom sunlight is not valid; get the block and loop to it
669 block = getBlockNoCreate(pos);
671 catch(InvalidPositionException &e)
673 FATAL_ERROR("Invalid position");
682 Enable this to disable proper lighting for speeding up map
683 generation for testing or whatever
686 //if(g_settings->get(""))
688 core::map<v3s16, MapBlock*>::Iterator i;
689 i = blocks_to_update.getIterator();
690 for(; i.atEnd() == false; i++)
692 MapBlock *block = i.getNode()->getValue();
693 v3s16 p = block->getPos();
694 block->setLightingExpired(false);
702 //TimeTaker timer("unspreadLight");
703 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
708 u32 diff = modified_blocks.size() - count_was;
709 count_was = modified_blocks.size();
710 infostream<<"unspreadLight modified "<<diff<<std::endl;
714 //TimeTaker timer("spreadLight");
715 spreadLight(bank, light_sources, modified_blocks);
720 u32 diff = modified_blocks.size() - count_was;
721 count_was = modified_blocks.size();
722 infostream<<"spreadLight modified "<<diff<<std::endl;
728 //MapVoxelManipulator vmanip(this);
730 // Make a manual voxel manipulator and load all the blocks
731 // that touch the requested blocks
732 ManualMapVoxelManipulator vmanip(this);
735 //TimeTaker timer("initialEmerge");
737 core::map<v3s16, MapBlock*>::Iterator i;
738 i = blocks_to_update.getIterator();
739 for(; i.atEnd() == false; i++)
741 MapBlock *block = i.getNode()->getValue();
742 v3s16 p = block->getPos();
744 // Add all surrounding blocks
745 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
748 Add all surrounding blocks that have up-to-date lighting
749 NOTE: This doesn't quite do the job (not everything
750 appropriate is lighted)
752 /*for(s16 z=-1; z<=1; z++)
753 for(s16 y=-1; y<=1; y++)
754 for(s16 x=-1; x<=1; x++)
756 v3s16 p2 = p + v3s16(x,y,z);
757 MapBlock *block = getBlockNoCreateNoEx(p2);
762 if(block->getLightingExpired())
764 vmanip.initialEmerge(p2, p2);
767 // Lighting of block will be updated completely
768 block->setLightingExpired(false);
773 //TimeTaker timer("unSpreadLight");
774 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
777 //TimeTaker timer("spreadLight");
778 vmanip.spreadLight(bank, light_sources, nodemgr);
781 //TimeTaker timer("blitBack");
782 vmanip.blitBack(modified_blocks);
784 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
789 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
792 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
793 std::map<v3s16, MapBlock*> & modified_blocks)
795 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
796 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
799 Update information about whether day and night light differ
801 for(std::map<v3s16, MapBlock*>::iterator
802 i = modified_blocks.begin();
803 i != modified_blocks.end(); ++i)
805 MapBlock *block = i->second;
806 block->expireDayNightDiff();
810 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
811 std::map<v3s16, MapBlock*> &modified_blocks,
812 bool remove_metadata)
814 INodeDefManager *ndef = m_gamedef->ndef();
816 // Collect old node for rollback
817 RollbackNode rollback_oldnode(this, p, m_gamedef);
819 // This is needed for updating the lighting
820 MapNode oldnode = getNodeNoEx(p);
822 // Remove node metadata
823 if (remove_metadata) {
824 removeNodeMetadata(p);
827 // Set the node on the map
828 // Ignore light (because calling voxalgo::update_lighting_nodes)
829 n.setLight(LIGHTBANK_DAY, 0, ndef);
830 n.setLight(LIGHTBANK_NIGHT, 0, ndef);
834 std::vector<std::pair<v3s16, MapNode> > oldnodes;
835 oldnodes.push_back(std::pair<v3s16, MapNode>(p, oldnode));
836 voxalgo::update_lighting_nodes(this, ndef, oldnodes, modified_blocks);
838 for(std::map<v3s16, MapBlock*>::iterator
839 i = modified_blocks.begin();
840 i != modified_blocks.end(); ++i)
842 i->second->expireDayNightDiff();
845 // Report for rollback
846 if(m_gamedef->rollback())
848 RollbackNode rollback_newnode(this, p, m_gamedef);
849 RollbackAction action;
850 action.setSetNode(p, rollback_oldnode, rollback_newnode);
851 m_gamedef->rollback()->reportAction(action);
855 Add neighboring liquid nodes and this node to transform queue.
856 (it's vital for the node itself to get updated last, if it was removed.)
859 v3s16(0,0,1), // back
861 v3s16(1,0,0), // right
862 v3s16(0,0,-1), // front
863 v3s16(0,-1,0), // bottom
864 v3s16(-1,0,0), // left
865 v3s16(0,0,0), // self
867 for(u16 i=0; i<7; i++)
869 v3s16 p2 = p + dirs[i];
871 bool is_valid_position;
872 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
874 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
876 m_transforming_liquid.push_back(p2);
881 void Map::removeNodeAndUpdate(v3s16 p,
882 std::map<v3s16, MapBlock*> &modified_blocks)
884 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
887 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
890 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
894 bool succeeded = true;
896 std::map<v3s16, MapBlock*> modified_blocks;
897 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
899 // Copy modified_blocks to event
900 for(std::map<v3s16, MapBlock*>::iterator
901 i = modified_blocks.begin();
902 i != modified_blocks.end(); ++i)
904 event.modified_blocks.insert(i->first);
907 catch(InvalidPositionException &e){
911 dispatchEvent(&event);
916 bool Map::removeNodeWithEvent(v3s16 p)
919 event.type = MEET_REMOVENODE;
922 bool succeeded = true;
924 std::map<v3s16, MapBlock*> modified_blocks;
925 removeNodeAndUpdate(p, modified_blocks);
927 // Copy modified_blocks to event
928 for(std::map<v3s16, MapBlock*>::iterator
929 i = modified_blocks.begin();
930 i != modified_blocks.end(); ++i)
932 event.modified_blocks.insert(i->first);
935 catch(InvalidPositionException &e){
939 dispatchEvent(&event);
944 bool Map::getDayNightDiff(v3s16 blockpos)
947 v3s16 p = blockpos + v3s16(0,0,0);
948 MapBlock *b = getBlockNoCreate(p);
949 if(b->getDayNightDiff())
952 catch(InvalidPositionException &e){}
955 v3s16 p = blockpos + v3s16(-1,0,0);
956 MapBlock *b = getBlockNoCreate(p);
957 if(b->getDayNightDiff())
960 catch(InvalidPositionException &e){}
962 v3s16 p = blockpos + v3s16(0,-1,0);
963 MapBlock *b = getBlockNoCreate(p);
964 if(b->getDayNightDiff())
967 catch(InvalidPositionException &e){}
969 v3s16 p = blockpos + v3s16(0,0,-1);
970 MapBlock *b = getBlockNoCreate(p);
971 if(b->getDayNightDiff())
974 catch(InvalidPositionException &e){}
977 v3s16 p = blockpos + v3s16(1,0,0);
978 MapBlock *b = getBlockNoCreate(p);
979 if(b->getDayNightDiff())
982 catch(InvalidPositionException &e){}
984 v3s16 p = blockpos + v3s16(0,1,0);
985 MapBlock *b = getBlockNoCreate(p);
986 if(b->getDayNightDiff())
989 catch(InvalidPositionException &e){}
991 v3s16 p = blockpos + v3s16(0,0,1);
992 MapBlock *b = getBlockNoCreate(p);
993 if(b->getDayNightDiff())
996 catch(InvalidPositionException &e){}
1001 struct TimeOrderedMapBlock {
1005 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
1010 bool operator<(const TimeOrderedMapBlock &b) const
1012 return block->getUsageTimer() < b.block->getUsageTimer();
1017 Updates usage timers
1019 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
1020 std::vector<v3s16> *unloaded_blocks)
1022 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1024 // Profile modified reasons
1025 Profiler modprofiler;
1027 std::vector<v2s16> sector_deletion_queue;
1028 u32 deleted_blocks_count = 0;
1029 u32 saved_blocks_count = 0;
1030 u32 block_count_all = 0;
1034 // If there is no practical limit, we spare creation of mapblock_queue
1035 if (max_loaded_blocks == U32_MAX) {
1036 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1037 si != m_sectors.end(); ++si) {
1038 MapSector *sector = si->second;
1040 bool all_blocks_deleted = true;
1042 MapBlockVect blocks;
1043 sector->getBlocks(blocks);
1045 for (MapBlockVect::iterator i = blocks.begin();
1046 i != blocks.end(); ++i) {
1047 MapBlock *block = (*i);
1049 block->incrementUsageTimer(dtime);
1051 if (block->refGet() == 0
1052 && block->getUsageTimer() > unload_timeout) {
1053 v3s16 p = block->getPos();
1056 if (block->getModified() != MOD_STATE_CLEAN
1057 && save_before_unloading) {
1058 modprofiler.add(block->getModifiedReasonString(), 1);
1059 if (!saveBlock(block))
1061 saved_blocks_count++;
1064 // Delete from memory
1065 sector->deleteBlock(block);
1067 if (unloaded_blocks)
1068 unloaded_blocks->push_back(p);
1070 deleted_blocks_count++;
1072 all_blocks_deleted = false;
1077 if (all_blocks_deleted) {
1078 sector_deletion_queue.push_back(si->first);
1082 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
1083 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1084 si != m_sectors.end(); ++si) {
1085 MapSector *sector = si->second;
1087 MapBlockVect blocks;
1088 sector->getBlocks(blocks);
1090 for(MapBlockVect::iterator i = blocks.begin();
1091 i != blocks.end(); ++i) {
1092 MapBlock *block = (*i);
1094 block->incrementUsageTimer(dtime);
1095 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
1098 block_count_all = mapblock_queue.size();
1099 // Delete old blocks, and blocks over the limit from the memory
1100 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
1101 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
1102 TimeOrderedMapBlock b = mapblock_queue.top();
1103 mapblock_queue.pop();
1105 MapBlock *block = b.block;
1107 if (block->refGet() != 0)
1110 v3s16 p = block->getPos();
1113 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1114 modprofiler.add(block->getModifiedReasonString(), 1);
1115 if (!saveBlock(block))
1117 saved_blocks_count++;
1120 // Delete from memory
1121 b.sect->deleteBlock(block);
1123 if (unloaded_blocks)
1124 unloaded_blocks->push_back(p);
1126 deleted_blocks_count++;
1129 // Delete empty sectors
1130 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1131 si != m_sectors.end(); ++si) {
1132 if (si->second->empty()) {
1133 sector_deletion_queue.push_back(si->first);
1139 // Finally delete the empty sectors
1140 deleteSectors(sector_deletion_queue);
1142 if(deleted_blocks_count != 0)
1144 PrintInfo(infostream); // ServerMap/ClientMap:
1145 infostream<<"Unloaded "<<deleted_blocks_count
1146 <<" blocks from memory";
1147 if(save_before_unloading)
1148 infostream<<", of which "<<saved_blocks_count<<" were written";
1149 infostream<<", "<<block_count_all<<" blocks in memory";
1150 infostream<<"."<<std::endl;
1151 if(saved_blocks_count != 0){
1152 PrintInfo(infostream); // ServerMap/ClientMap:
1153 infostream<<"Blocks modified by: "<<std::endl;
1154 modprofiler.print(infostream);
1159 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1161 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
1164 void Map::deleteSectors(std::vector<v2s16> §orList)
1166 for(std::vector<v2s16>::iterator j = sectorList.begin();
1167 j != sectorList.end(); ++j) {
1168 MapSector *sector = m_sectors[*j];
1169 // If sector is in sector cache, remove it from there
1170 if(m_sector_cache == sector)
1171 m_sector_cache = NULL;
1172 // Remove from map and delete
1173 m_sectors.erase(*j);
1178 void Map::PrintInfo(std::ostream &out)
1183 #define WATER_DROP_BOOST 4
1187 NEIGHBOR_SAME_LEVEL,
1190 struct NodeNeighbor {
1194 bool l; //can liquid
1200 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1207 void Map::transforming_liquid_add(v3s16 p) {
1208 m_transforming_liquid.push_back(p);
1211 s32 Map::transforming_liquid_size() {
1212 return m_transforming_liquid.size();
1215 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
1218 INodeDefManager *nodemgr = m_gamedef->ndef();
1220 DSTACK(FUNCTION_NAME);
1221 //TimeTaker timer("transformLiquids()");
1224 u32 initial_size = m_transforming_liquid.size();
1226 /*if(initial_size != 0)
1227 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1229 // list of nodes that due to viscosity have not reached their max level height
1230 std::deque<v3s16> must_reflow;
1232 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
1234 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1235 u32 loop_max = liquid_loop_max;
1239 /* If liquid_loop_max is not keeping up with the queue size increase
1240 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1242 if (m_transforming_liquid.size() > loop_max * 2) {
1244 float server_step = g_settings->getFloat("dedicated_server_step");
1245 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1246 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1248 m_transforming_liquid_loop_count_multiplier = 1.0;
1251 loop_max *= m_transforming_liquid_loop_count_multiplier;
1254 while (m_transforming_liquid.size() != 0)
1256 // This should be done here so that it is done when continue is used
1257 if (loopcount >= initial_size || loopcount >= loop_max)
1262 Get a queued transforming liquid node
1264 v3s16 p0 = m_transforming_liquid.front();
1265 m_transforming_liquid.pop_front();
1267 MapNode n0 = getNodeNoEx(p0);
1270 Collect information about current node
1272 s8 liquid_level = -1;
1273 // The liquid node which will be placed there if
1274 // the liquid flows into this node.
1275 content_t liquid_kind = CONTENT_IGNORE;
1276 // The node which will be placed there if liquid
1277 // can't flow into this node.
1278 content_t floodable_node = CONTENT_AIR;
1279 const ContentFeatures &cf = nodemgr->get(n0);
1280 LiquidType liquid_type = cf.liquid_type;
1281 switch (liquid_type) {
1283 liquid_level = LIQUID_LEVEL_SOURCE;
1284 liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing);
1286 case LIQUID_FLOWING:
1287 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1288 liquid_kind = n0.getContent();
1291 // if this node is 'floodable', it *could* be transformed
1292 // into a liquid, otherwise, continue with the next node.
1295 floodable_node = n0.getContent();
1296 liquid_kind = CONTENT_AIR;
1301 Collect information about the environment
1303 const v3s16 *dirs = g_6dirs;
1304 NodeNeighbor sources[6]; // surrounding sources
1305 int num_sources = 0;
1306 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1308 NodeNeighbor airs[6]; // surrounding air
1310 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1311 int num_neutrals = 0;
1312 bool flowing_down = false;
1313 bool ignored_sources = false;
1314 for (u16 i = 0; i < 6; i++) {
1315 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1318 nt = NEIGHBOR_UPPER;
1321 nt = NEIGHBOR_LOWER;
1324 v3s16 npos = p0 + dirs[i];
1325 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1326 const ContentFeatures &cfnb = nodemgr->get(nb.n);
1327 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1329 if (cfnb.floodable) {
1330 airs[num_airs++] = nb;
1331 // if the current node is a water source the neighbor
1332 // should be enqueded for transformation regardless of whether the
1333 // current node changes or not.
1334 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1335 m_transforming_liquid.push_back(npos);
1336 // if the current node happens to be a flowing node, it will start to flow down here.
1337 if (nb.t == NEIGHBOR_LOWER)
1338 flowing_down = true;
1340 neutrals[num_neutrals++] = nb;
1341 if (nb.n.getContent() == CONTENT_IGNORE) {
1342 // If node below is ignore prevent water from
1343 // spreading outwards and otherwise prevent from
1344 // flowing away as ignore node might be the source
1345 if (nb.t == NEIGHBOR_LOWER)
1346 flowing_down = true;
1348 ignored_sources = true;
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 = nodemgr->getId(cfnb.liquid_alternative_flowing);
1356 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1357 neutrals[num_neutrals++] = nb;
1359 // Do not count bottom source, it will screw things up
1361 sources[num_sources++] = nb;
1364 case LIQUID_FLOWING:
1365 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1366 if (liquid_kind == CONTENT_AIR)
1367 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1368 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1369 neutrals[num_neutrals++] = nb;
1371 flows[num_flows++] = nb;
1372 if (nb.t == NEIGHBOR_LOWER)
1373 flowing_down = true;
1380 decide on the type (and possibly level) of the current node
1382 content_t new_node_content;
1383 s8 new_node_level = -1;
1384 s8 max_node_level = -1;
1386 u8 range = nodemgr->get(liquid_kind).liquid_range;
1387 if (range > LIQUID_LEVEL_MAX + 1)
1388 range = LIQUID_LEVEL_MAX + 1;
1390 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1391 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1392 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1393 // it's perfectly safe to use liquid_kind here to determine the new node content.
1394 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1395 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1396 // liquid_kind is set properly, see above
1397 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1398 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1399 new_node_content = liquid_kind;
1401 new_node_content = floodable_node;
1402 } else if (ignored_sources && liquid_level >= 0) {
1403 // Maybe there are neighbouring sources that aren't loaded yet
1404 // so prevent flowing away.
1405 new_node_level = liquid_level;
1406 new_node_content = liquid_kind;
1408 // no surrounding sources, so get the maximum level that can flow into this node
1409 for (u16 i = 0; i < num_flows; i++) {
1410 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1411 switch (flows[i].t) {
1412 case NEIGHBOR_UPPER:
1413 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1414 max_node_level = LIQUID_LEVEL_MAX;
1415 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1416 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1417 } else if (nb_liquid_level > max_node_level) {
1418 max_node_level = nb_liquid_level;
1421 case NEIGHBOR_LOWER:
1423 case NEIGHBOR_SAME_LEVEL:
1424 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1425 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
1426 max_node_level = nb_liquid_level - 1;
1431 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1432 if (viscosity > 1 && max_node_level != liquid_level) {
1433 // amount to gain, limited by viscosity
1434 // must be at least 1 in absolute value
1435 s8 level_inc = max_node_level - liquid_level;
1436 if (level_inc < -viscosity || level_inc > viscosity)
1437 new_node_level = liquid_level + level_inc/viscosity;
1438 else if (level_inc < 0)
1439 new_node_level = liquid_level - 1;
1440 else if (level_inc > 0)
1441 new_node_level = liquid_level + 1;
1442 if (new_node_level != max_node_level)
1443 must_reflow.push_back(p0);
1445 new_node_level = max_node_level;
1448 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1449 new_node_content = liquid_kind;
1451 new_node_content = floodable_node;
1456 check if anything has changed. if not, just continue with the next node.
1458 if (new_node_content == n0.getContent() &&
1459 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1460 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1461 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1467 update the current node
1470 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1471 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1472 // set level to last 3 bits, flowing down bit to 4th bit
1473 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1475 // set the liquid level and flow bit to 0
1476 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1478 n0.setContent(new_node_content);
1480 // Ignore light (because calling voxalgo::update_lighting_nodes)
1481 n0.setLight(LIGHTBANK_DAY, 0, nodemgr);
1482 n0.setLight(LIGHTBANK_NIGHT, 0, nodemgr);
1484 // Find out whether there is a suspect for this action
1485 std::string suspect;
1486 if (m_gamedef->rollback())
1487 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1489 if (m_gamedef->rollback() && !suspect.empty()) {
1491 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1492 // Get old node for rollback
1493 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1497 RollbackNode rollback_newnode(this, p0, m_gamedef);
1498 RollbackAction action;
1499 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1500 m_gamedef->rollback()->reportAction(action);
1506 v3s16 blockpos = getNodeBlockPos(p0);
1507 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1508 if (block != NULL) {
1509 modified_blocks[blockpos] = block;
1510 changed_nodes.push_back(std::pair<v3s16, MapNode>(p0, n00));
1514 enqueue neighbors for update if neccessary
1516 switch (nodemgr->get(n0.getContent()).liquid_type) {
1518 case LIQUID_FLOWING:
1519 // make sure source flows into all neighboring nodes
1520 for (u16 i = 0; i < num_flows; i++)
1521 if (flows[i].t != NEIGHBOR_UPPER)
1522 m_transforming_liquid.push_back(flows[i].p);
1523 for (u16 i = 0; i < num_airs; i++)
1524 if (airs[i].t != NEIGHBOR_UPPER)
1525 m_transforming_liquid.push_back(airs[i].p);
1528 // this flow has turned to air; neighboring flows might need to do the same
1529 for (u16 i = 0; i < num_flows; i++)
1530 m_transforming_liquid.push_back(flows[i].p);
1534 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1536 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1537 m_transforming_liquid.push_back(*iter);
1539 voxalgo::update_lighting_nodes(this, nodemgr, changed_nodes, modified_blocks);
1542 /* ----------------------------------------------------------------------
1543 * Manage the queue so that it does not grow indefinately
1545 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1547 if (time_until_purge == 0)
1548 return; // Feature disabled
1550 time_until_purge *= 1000; // seconds -> milliseconds
1552 u32 curr_time = getTime(PRECISION_MILLI);
1553 u32 prev_unprocessed = m_unprocessed_count;
1554 m_unprocessed_count = m_transforming_liquid.size();
1556 // if unprocessed block count is decreasing or stable
1557 if (m_unprocessed_count <= prev_unprocessed) {
1558 m_queue_size_timer_started = false;
1560 if (!m_queue_size_timer_started)
1561 m_inc_trending_up_start_time = curr_time;
1562 m_queue_size_timer_started = true;
1565 // Account for curr_time overflowing
1566 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1567 m_queue_size_timer_started = false;
1569 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1570 * and the number of unprocessed blocks is still > liquid_loop_max then we
1571 * cannot keep up; dump the oldest blocks from the queue so that the queue
1572 * has liquid_loop_max items in it
1574 if (m_queue_size_timer_started
1575 && curr_time - m_inc_trending_up_start_time > time_until_purge
1576 && m_unprocessed_count > liquid_loop_max) {
1578 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1580 infostream << "transformLiquids(): DUMPING " << dump_qty
1581 << " blocks from the queue" << std::endl;
1584 m_transforming_liquid.pop_front();
1586 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1587 m_unprocessed_count = m_transforming_liquid.size();
1591 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1593 std::vector<v3s16> positions_with_meta;
1595 sortBoxVerticies(p1, p2);
1596 v3s16 bpmin = getNodeBlockPos(p1);
1597 v3s16 bpmax = getNodeBlockPos(p2);
1599 VoxelArea area(p1, p2);
1601 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1602 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1603 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1604 v3s16 blockpos(x, y, z);
1606 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1608 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1609 << PP(blockpos) << std::endl;
1610 block = emergeBlock(blockpos, false);
1613 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1618 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1619 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1620 for (size_t i = 0; i != keys.size(); i++) {
1621 v3s16 p(keys[i] + p_base);
1622 if (!area.contains(p))
1625 positions_with_meta.push_back(p);
1629 return positions_with_meta;
1632 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1634 v3s16 blockpos = getNodeBlockPos(p);
1635 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1636 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1638 infostream<<"Map::getNodeMetadata(): Need to emerge "
1639 <<PP(blockpos)<<std::endl;
1640 block = emergeBlock(blockpos, false);
1643 warningstream<<"Map::getNodeMetadata(): Block not found"
1647 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1651 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1653 v3s16 blockpos = getNodeBlockPos(p);
1654 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1655 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1657 infostream<<"Map::setNodeMetadata(): Need to emerge "
1658 <<PP(blockpos)<<std::endl;
1659 block = emergeBlock(blockpos, false);
1662 warningstream<<"Map::setNodeMetadata(): Block not found"
1666 block->m_node_metadata.set(p_rel, meta);
1670 void Map::removeNodeMetadata(v3s16 p)
1672 v3s16 blockpos = getNodeBlockPos(p);
1673 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1674 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1677 warningstream<<"Map::removeNodeMetadata(): Block not found"
1681 block->m_node_metadata.remove(p_rel);
1684 NodeTimer Map::getNodeTimer(v3s16 p)
1686 v3s16 blockpos = getNodeBlockPos(p);
1687 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1688 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1690 infostream<<"Map::getNodeTimer(): Need to emerge "
1691 <<PP(blockpos)<<std::endl;
1692 block = emergeBlock(blockpos, false);
1695 warningstream<<"Map::getNodeTimer(): Block not found"
1699 NodeTimer t = block->m_node_timers.get(p_rel);
1700 NodeTimer nt(t.timeout, t.elapsed, p);
1704 void Map::setNodeTimer(const NodeTimer &t)
1706 v3s16 p = t.position;
1707 v3s16 blockpos = getNodeBlockPos(p);
1708 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1709 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1711 infostream<<"Map::setNodeTimer(): Need to emerge "
1712 <<PP(blockpos)<<std::endl;
1713 block = emergeBlock(blockpos, false);
1716 warningstream<<"Map::setNodeTimer(): Block not found"
1720 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1721 block->m_node_timers.set(nt);
1724 void Map::removeNodeTimer(v3s16 p)
1726 v3s16 blockpos = getNodeBlockPos(p);
1727 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1728 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1731 warningstream<<"Map::removeNodeTimer(): Block not found"
1735 block->m_node_timers.remove(p_rel);
1741 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1742 Map(dout_server, gamedef),
1743 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1745 m_map_metadata_changed(true)
1747 verbosestream<<FUNCTION_NAME<<std::endl;
1749 // Tell the EmergeManager about our MapSettingsManager
1750 emerge->map_settings_mgr = &settings_mgr;
1753 Try to load map; if not found, create a new one.
1756 // Determine which database backend to use
1757 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1759 bool succeeded = conf.readConfigFile(conf_path.c_str());
1760 if (!succeeded || !conf.exists("backend")) {
1761 // fall back to sqlite3
1762 conf.set("backend", "sqlite3");
1764 std::string backend = conf.get("backend");
1765 dbase = createDatabase(backend, savedir, conf);
1767 if (!conf.updateConfigFile(conf_path.c_str()))
1768 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1770 m_savedir = savedir;
1771 m_map_saving_enabled = false;
1775 // If directory exists, check contents and load if possible
1776 if(fs::PathExists(m_savedir))
1778 // If directory is empty, it is safe to save into it.
1779 if(fs::GetDirListing(m_savedir).size() == 0)
1781 infostream<<"ServerMap: Empty save directory is valid."
1783 m_map_saving_enabled = true;
1788 if (settings_mgr.loadMapMeta()) {
1789 infostream << "ServerMap: Metadata loaded from "
1790 << savedir << std::endl;
1792 infostream << "ServerMap: Metadata could not be loaded "
1793 "from " << savedir << ", assuming valid save "
1794 "directory." << std::endl;
1797 m_map_saving_enabled = true;
1798 // Map loaded, not creating new one
1802 // If directory doesn't exist, it is safe to save to it
1804 m_map_saving_enabled = true;
1807 catch(std::exception &e)
1809 warningstream<<"ServerMap: Failed to load map from "<<savedir
1810 <<", exception: "<<e.what()<<std::endl;
1811 infostream<<"Please remove the map or fix it."<<std::endl;
1812 warningstream<<"Map saving will be disabled."<<std::endl;
1815 infostream<<"Initializing new map."<<std::endl;
1817 // Create zero sector
1818 emergeSector(v2s16(0,0));
1820 // Initially write whole map
1821 save(MOD_STATE_CLEAN);
1824 ServerMap::~ServerMap()
1826 verbosestream<<FUNCTION_NAME<<std::endl;
1830 if(m_map_saving_enabled)
1832 // Save only changed parts
1833 save(MOD_STATE_WRITE_AT_UNLOAD);
1834 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
1838 infostream<<"ServerMap: Map not saved"<<std::endl;
1841 catch(std::exception &e)
1843 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1844 <<", exception: "<<e.what()<<std::endl;
1848 Close database if it was opened
1856 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1857 for(; i.atEnd() == false; i++)
1859 MapChunk *chunk = i.getNode()->getValue();
1865 MapgenParams *ServerMap::getMapgenParams()
1867 // getMapgenParams() should only ever be called after Server is initialized
1868 assert(settings_mgr.mapgen_params != NULL);
1869 return settings_mgr.mapgen_params;
1872 u64 ServerMap::getSeed()
1874 return getMapgenParams()->seed;
1877 s16 ServerMap::getWaterLevel()
1879 return getMapgenParams()->water_level;
1882 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1884 s16 csize = getMapgenParams()->chunksize;
1885 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1886 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1888 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1889 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1891 v3s16 extra_borders(1, 1, 1);
1892 v3s16 full_bpmin = bpmin - extra_borders;
1893 v3s16 full_bpmax = bpmax + extra_borders;
1895 // Do nothing if not inside limits (+-1 because of neighbors)
1896 if (blockpos_over_limit(full_bpmin) ||
1897 blockpos_over_limit(full_bpmax))
1900 data->seed = getSeed();
1901 data->blockpos_min = bpmin;
1902 data->blockpos_max = bpmax;
1903 data->blockpos_requested = blockpos;
1904 data->nodedef = m_gamedef->ndef();
1907 Create the whole area of this and the neighboring blocks
1909 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1910 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1911 v2s16 sectorpos(x, z);
1912 // Sector metadata is loaded from disk if not already loaded.
1913 ServerMapSector *sector = createSector(sectorpos);
1914 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1916 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1919 MapBlock *block = emergeBlock(p, false);
1920 if (block == NULL) {
1921 block = createBlock(p);
1923 // Block gets sunlight if this is true.
1924 // Refer to the map generator heuristics.
1925 bool ug = m_emerge->isBlockUnderground(p);
1926 block->setIsUnderground(ug);
1932 Now we have a big empty area.
1934 Make a ManualMapVoxelManipulator that contains this and the
1938 data->vmanip = new MMVManip(this);
1939 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1941 // Note: we may need this again at some point.
1943 // Ensure none of the blocks to be generated were marked as
1944 // containing CONTENT_IGNORE
1945 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1946 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1947 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1948 core::map<v3s16, u8>::Node *n;
1949 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1952 u8 flags = n->getValue();
1953 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1960 // Data is ready now.
1964 void ServerMap::finishBlockMake(BlockMakeData *data,
1965 std::map<v3s16, MapBlock*> *changed_blocks)
1967 v3s16 bpmin = data->blockpos_min;
1968 v3s16 bpmax = data->blockpos_max;
1970 v3s16 extra_borders(1, 1, 1);
1971 v3s16 full_bpmin = bpmin - extra_borders;
1972 v3s16 full_bpmax = bpmax + extra_borders;
1974 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1975 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1978 Set lighting to non-expired state in all of them.
1979 This is cheating, but it is not fast enough if all of them
1980 would actually be updated.
1982 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1983 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
1984 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1985 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
1989 block->setLightingExpired(false);
1993 Blit generated stuff to map
1994 NOTE: blitBackAll adds nearly everything to changed_blocks
1996 data->vmanip->blitBackAll(changed_blocks);
1998 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1999 << changed_blocks->size());
2002 Copy transforming liquid information
2004 while (data->transforming_liquid.size()) {
2005 m_transforming_liquid.push_back(data->transforming_liquid.front());
2006 data->transforming_liquid.pop_front();
2009 for (std::map<v3s16, MapBlock *>::iterator
2010 it = changed_blocks->begin();
2011 it != changed_blocks->end(); ++it) {
2012 MapBlock *block = it->second;
2016 Update day/night difference cache of the MapBlocks
2018 block->expireDayNightDiff();
2020 Set block as modified
2022 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2023 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2027 Set central blocks as generated
2029 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2030 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2031 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2032 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2036 block->setGenerated(true);
2040 Save changed parts of map
2041 NOTE: Will be saved later.
2043 //save(MOD_STATE_WRITE_AT_UNLOAD);
2046 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2048 DSTACKF("%s: p2d=(%d,%d)",
2053 Check if it exists already in memory
2055 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2060 Try to load it from disk (with blocks)
2062 //if(loadSectorFull(p2d) == true)
2065 Try to load metadata from disk
2068 if(loadSectorMeta(p2d) == true)
2070 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2073 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2074 throw InvalidPositionException("");
2080 Do not create over-limit
2082 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2083 g_settings->getU16("map_generation_limit"));
2084 if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2085 || p2d.X > map_gen_limit / MAP_BLOCKSIZE
2086 || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2087 || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
2088 throw InvalidPositionException("createSector(): pos. over limit");
2091 Generate blank sector
2094 sector = new ServerMapSector(this, p2d, m_gamedef);
2096 // Sector position on map in nodes
2097 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2102 m_sectors[p2d] = sector;
2109 This is a quick-hand function for calling makeBlock().
2111 MapBlock * ServerMap::generateBlock(
2113 std::map<v3s16, MapBlock*> &modified_blocks
2116 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2118 /*infostream<<"generateBlock(): "
2119 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2122 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2124 TimeTaker timer("generateBlock");
2126 //MapBlock *block = original_dummy;
2128 v2s16 p2d(p.X, p.Z);
2129 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2132 Do not generate over-limit
2134 if(blockpos_over_limit(p))
2136 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2137 throw InvalidPositionException("generateBlock(): pos. over limit");
2141 Create block make data
2144 initBlockMake(&data, p);
2150 TimeTaker t("mapgen::make_block()");
2151 mapgen->makeChunk(&data);
2152 //mapgen::make_block(&data);
2154 if(enable_mapgen_debug_info == false)
2155 t.stop(true); // Hide output
2159 Blit data back on map, update lighting, add mobs and whatever this does
2161 finishBlockMake(&data, modified_blocks);
2166 MapBlock *block = getBlockNoCreateNoEx(p);
2174 bool erroneus_content = false;
2175 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2176 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2177 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2180 MapNode n = block->getNode(p);
2181 if(n.getContent() == CONTENT_IGNORE)
2183 infostream<<"CONTENT_IGNORE at "
2184 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2186 erroneus_content = true;
2190 if(erroneus_content)
2199 Generate a completely empty block
2203 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2204 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2206 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2209 n.setContent(CONTENT_AIR);
2210 block->setNode(v3s16(x0,y0,z0), n);
2216 if(enable_mapgen_debug_info == false)
2217 timer.stop(true); // Hide output
2223 MapBlock * ServerMap::createBlock(v3s16 p)
2225 DSTACKF("%s: p=(%d,%d,%d)",
2226 FUNCTION_NAME, p.X, p.Y, p.Z);
2229 Do not create over-limit
2231 if (blockpos_over_limit(p))
2232 throw InvalidPositionException("createBlock(): pos. over limit");
2234 v2s16 p2d(p.X, p.Z);
2237 This will create or load a sector if not found in memory.
2238 If block exists on disk, it will be loaded.
2240 NOTE: On old save formats, this will be slow, as it generates
2241 lighting on blocks for them.
2243 ServerMapSector *sector;
2245 sector = (ServerMapSector*)createSector(p2d);
2246 assert(sector->getId() == MAPSECTOR_SERVER);
2248 catch(InvalidPositionException &e)
2250 infostream<<"createBlock: createSector() failed"<<std::endl;
2254 NOTE: This should not be done, or at least the exception
2255 should not be passed on as std::exception, because it
2256 won't be catched at all.
2258 /*catch(std::exception &e)
2260 infostream<<"createBlock: createSector() failed: "
2261 <<e.what()<<std::endl;
2266 Try to get a block from the sector
2269 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2272 if(block->isDummy())
2277 block = sector->createBlankBlock(block_y);
2282 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2284 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2286 p.X, p.Y, p.Z, create_blank);
2289 MapBlock *block = getBlockNoCreateNoEx(p);
2290 if(block && block->isDummy() == false)
2295 MapBlock *block = loadBlock(p);
2301 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2302 MapBlock *block = sector->createBlankBlock(p.Y);
2310 std::map<v3s16, MapBlock*> modified_blocks;
2311 MapBlock *block = generateBlock(p, modified_blocks);
2315 event.type = MEET_OTHER;
2318 // Copy modified_blocks to event
2319 for(std::map<v3s16, MapBlock*>::iterator
2320 i = modified_blocks.begin();
2321 i != modified_blocks.end(); ++i)
2323 event.modified_blocks.insert(i->first);
2327 dispatchEvent(&event);
2337 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2339 MapBlock *block = getBlockNoCreateNoEx(p3d);
2341 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2346 void ServerMap::prepareBlock(MapBlock *block) {
2349 // N.B. This requires no synchronization, since data will not be modified unless
2350 // the VoxelManipulator being updated belongs to the same thread.
2351 void ServerMap::updateVManip(v3s16 pos)
2353 Mapgen *mg = m_emerge->getCurrentMapgen();
2357 MMVManip *vm = mg->vm;
2361 if (!vm->m_area.contains(pos))
2364 s32 idx = vm->m_area.index(pos);
2365 vm->m_data[idx] = getNodeNoEx(pos);
2366 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2368 vm->m_is_dirty = true;
2371 s16 ServerMap::findGroundLevel(v2s16 p2d)
2375 Uh, just do something random...
2377 // Find existing map from top to down
2380 v3s16 p(p2d.X, max, p2d.Y);
2381 for(; p.Y>min; p.Y--)
2383 MapNode n = getNodeNoEx(p);
2384 if(n.getContent() != CONTENT_IGNORE)
2389 // If this node is not air, go to plan b
2390 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2392 // Search existing walkable and return it
2393 for(; p.Y>min; p.Y--)
2395 MapNode n = getNodeNoEx(p);
2396 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2405 Determine from map generator noise functions
2408 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2411 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2412 //return (s16)level;
2415 bool ServerMap::loadFromFolders() {
2416 if (!dbase->initialized() &&
2417 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2422 void ServerMap::createDirs(std::string path)
2424 if(fs::CreateAllDirs(path) == false)
2426 m_dout<<"ServerMap: Failed to create directory "
2427 <<"\""<<path<<"\""<<std::endl;
2428 throw BaseException("ServerMap failed to create directory");
2432 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2438 snprintf(cc, 9, "%.4x%.4x",
2439 (unsigned int) pos.X & 0xffff,
2440 (unsigned int) pos.Y & 0xffff);
2442 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2444 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2445 (unsigned int) pos.X & 0xfff,
2446 (unsigned int) pos.Y & 0xfff);
2448 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2455 v2s16 ServerMap::getSectorPos(std::string dirname)
2457 unsigned int x = 0, y = 0;
2459 std::string component;
2460 fs::RemoveLastPathComponent(dirname, &component, 1);
2461 if(component.size() == 8)
2464 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2466 else if(component.size() == 3)
2469 fs::RemoveLastPathComponent(dirname, &component, 2);
2470 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2471 // Sign-extend the 12 bit values up to 16 bits...
2472 if(x & 0x800) x |= 0xF000;
2473 if(y & 0x800) y |= 0xF000;
2480 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2481 v2s16 pos((s16)x, (s16)y);
2485 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2487 v2s16 p2d = getSectorPos(sectordir);
2489 if(blockfile.size() != 4){
2490 throw InvalidFilenameException("Invalid block filename");
2493 int r = sscanf(blockfile.c_str(), "%4x", &y);
2495 throw InvalidFilenameException("Invalid block filename");
2496 return v3s16(p2d.X, y, p2d.Y);
2499 std::string ServerMap::getBlockFilename(v3s16 p)
2502 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2506 void ServerMap::save(ModifiedState save_level)
2508 DSTACK(FUNCTION_NAME);
2509 if(m_map_saving_enabled == false) {
2510 warningstream<<"Not saving map, saving disabled."<<std::endl;
2514 if(save_level == MOD_STATE_CLEAN)
2515 infostream<<"ServerMap: Saving whole map, this can take time."
2518 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2519 if (settings_mgr.saveMapMeta())
2520 m_map_metadata_changed = false;
2523 // Profile modified reasons
2524 Profiler modprofiler;
2526 u32 sector_meta_count = 0;
2527 u32 block_count = 0;
2528 u32 block_count_all = 0; // Number of blocks in memory
2530 // Don't do anything with sqlite unless something is really saved
2531 bool save_started = false;
2533 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2534 i != m_sectors.end(); ++i) {
2535 ServerMapSector *sector = (ServerMapSector*)i->second;
2536 assert(sector->getId() == MAPSECTOR_SERVER);
2538 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2539 saveSectorMeta(sector);
2540 sector_meta_count++;
2543 MapBlockVect blocks;
2544 sector->getBlocks(blocks);
2546 for(MapBlockVect::iterator j = blocks.begin();
2547 j != blocks.end(); ++j) {
2548 MapBlock *block = *j;
2552 if(block->getModified() >= (u32)save_level) {
2556 save_started = true;
2559 modprofiler.add(block->getModifiedReasonString(), 1);
2564 /*infostream<<"ServerMap: Written block ("
2565 <<block->getPos().X<<","
2566 <<block->getPos().Y<<","
2567 <<block->getPos().Z<<")"
2577 Only print if something happened or saved whole map
2579 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2580 || block_count != 0) {
2581 infostream<<"ServerMap: Written: "
2582 <<sector_meta_count<<" sector metadata files, "
2583 <<block_count<<" block files"
2584 <<", "<<block_count_all<<" blocks in memory."
2586 PrintInfo(infostream); // ServerMap/ClientMap:
2587 infostream<<"Blocks modified by: "<<std::endl;
2588 modprofiler.print(infostream);
2592 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2594 if (loadFromFolders()) {
2595 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2596 << "all blocks that are stored in flat files." << std::endl;
2598 dbase->listAllLoadableBlocks(dst);
2601 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2603 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2604 si != m_sectors.end(); ++si)
2606 MapSector *sector = si->second;
2608 MapBlockVect blocks;
2609 sector->getBlocks(blocks);
2611 for(MapBlockVect::iterator i = blocks.begin();
2612 i != blocks.end(); ++i) {
2613 v3s16 p = (*i)->getPos();
2619 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2621 DSTACK(FUNCTION_NAME);
2622 // Format used for writing
2623 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2625 v2s16 pos = sector->getPos();
2626 std::string dir = getSectorDir(pos);
2629 std::string fullpath = dir + DIR_DELIM + "meta";
2630 std::ostringstream ss(std::ios_base::binary);
2632 sector->serialize(ss, version);
2634 if(!fs::safeWriteToFile(fullpath, ss.str()))
2635 throw FileNotGoodException("Cannot write sector metafile");
2637 sector->differs_from_disk = false;
2640 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2642 DSTACK(FUNCTION_NAME);
2644 v2s16 p2d = getSectorPos(sectordir);
2646 ServerMapSector *sector = NULL;
2648 std::string fullpath = sectordir + DIR_DELIM + "meta";
2649 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2650 if(is.good() == false)
2652 // If the directory exists anyway, it probably is in some old
2653 // format. Just go ahead and create the sector.
2654 if(fs::PathExists(sectordir))
2656 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
2657 <<fullpath<<" doesn't exist but directory does."
2658 <<" Continuing with a sector with no metadata."
2660 sector = new ServerMapSector(this, p2d, m_gamedef);
2661 m_sectors[p2d] = sector;
2665 throw FileNotGoodException("Cannot open sector metafile");
2670 sector = ServerMapSector::deSerialize
2671 (is, this, p2d, m_sectors, m_gamedef);
2673 saveSectorMeta(sector);
2676 sector->differs_from_disk = false;
2681 bool ServerMap::loadSectorMeta(v2s16 p2d)
2683 DSTACK(FUNCTION_NAME);
2685 // The directory layout we're going to load from.
2686 // 1 - original sectors/xxxxzzzz/
2687 // 2 - new sectors2/xxx/zzz/
2688 // If we load from anything but the latest structure, we will
2689 // immediately save to the new one, and remove the old.
2691 std::string sectordir1 = getSectorDir(p2d, 1);
2692 std::string sectordir;
2693 if(fs::PathExists(sectordir1))
2695 sectordir = sectordir1;
2700 sectordir = getSectorDir(p2d, 2);
2704 loadSectorMeta(sectordir, loadlayout != 2);
2706 catch(InvalidFilenameException &e)
2710 catch(FileNotGoodException &e)
2714 catch(std::exception &e)
2723 bool ServerMap::loadSectorFull(v2s16 p2d)
2725 DSTACK(FUNCTION_NAME);
2727 MapSector *sector = NULL;
2729 // The directory layout we're going to load from.
2730 // 1 - original sectors/xxxxzzzz/
2731 // 2 - new sectors2/xxx/zzz/
2732 // If we load from anything but the latest structure, we will
2733 // immediately save to the new one, and remove the old.
2735 std::string sectordir1 = getSectorDir(p2d, 1);
2736 std::string sectordir;
2737 if(fs::PathExists(sectordir1))
2739 sectordir = sectordir1;
2744 sectordir = getSectorDir(p2d, 2);
2748 sector = loadSectorMeta(sectordir, loadlayout != 2);
2750 catch(InvalidFilenameException &e)
2754 catch(FileNotGoodException &e)
2758 catch(std::exception &e)
2766 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2768 std::vector<fs::DirListNode>::iterator i2;
2769 for(i2=list2.begin(); i2!=list2.end(); i2++)
2775 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2777 catch(InvalidFilenameException &e)
2779 // This catches unknown crap in directory
2785 infostream<<"Sector converted to new layout - deleting "<<
2786 sectordir1<<std::endl;
2787 fs::RecursiveDelete(sectordir1);
2794 Database *ServerMap::createDatabase(
2795 const std::string &name,
2796 const std::string &savedir,
2799 if (name == "sqlite3")
2800 return new Database_SQLite3(savedir);
2801 if (name == "dummy")
2802 return new Database_Dummy();
2804 else if (name == "leveldb")
2805 return new Database_LevelDB(savedir);
2808 else if (name == "redis")
2809 return new Database_Redis(conf);
2812 else if (name == "postgresql")
2813 return new Database_PostgreSQL(conf);
2816 throw BaseException(std::string("Database backend ") + name + " not supported.");
2819 void ServerMap::beginSave()
2824 void ServerMap::endSave()
2829 bool ServerMap::saveBlock(MapBlock *block)
2831 return saveBlock(block, dbase);
2834 bool ServerMap::saveBlock(MapBlock *block, Database *db)
2836 v3s16 p3d = block->getPos();
2838 // Dummy blocks are not written
2839 if (block->isDummy()) {
2840 warningstream << "saveBlock: Not writing dummy block "
2841 << PP(p3d) << std::endl;
2845 // Format used for writing
2846 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2849 [0] u8 serialization version
2852 std::ostringstream o(std::ios_base::binary);
2853 o.write((char*) &version, 1);
2854 block->serialize(o, version, true);
2856 std::string data = o.str();
2857 bool ret = db->saveBlock(p3d, data);
2859 // We just wrote it to the disk so clear modified flag
2860 block->resetModified();
2865 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
2866 MapSector *sector, bool save_after_load)
2868 DSTACK(FUNCTION_NAME);
2870 std::string fullpath = sectordir + DIR_DELIM + blockfile;
2873 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2874 if(is.good() == false)
2875 throw FileNotGoodException("Cannot open block file");
2877 v3s16 p3d = getBlockPos(sectordir, blockfile);
2878 v2s16 p2d(p3d.X, p3d.Z);
2880 assert(sector->getPos() == p2d);
2882 u8 version = SER_FMT_VER_INVALID;
2883 is.read((char*)&version, 1);
2886 throw SerializationError("ServerMap::loadBlock(): Failed"
2887 " to read MapBlock version");
2889 /*u32 block_size = MapBlock::serializedLength(version);
2890 SharedBuffer<u8> data(block_size);
2891 is.read((char*)*data, block_size);*/
2893 // This will always return a sector because we're the server
2894 //MapSector *sector = emergeSector(p2d);
2896 MapBlock *block = NULL;
2897 bool created_new = false;
2898 block = sector->getBlockNoCreateNoEx(p3d.Y);
2901 block = sector->createBlankBlockNoInsert(p3d.Y);
2906 block->deSerialize(is, version, true);
2908 // If it's a new block, insert it to the map
2910 sector->insertBlock(block);
2911 ReflowScan scanner(this, m_emerge->ndef);
2912 scanner.scan(block, &m_transforming_liquid);
2916 Save blocks loaded in old format in new format
2919 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
2923 // Should be in database now, so delete the old file
2924 fs::RecursiveDelete(fullpath);
2927 // We just loaded it from the disk, so it's up-to-date.
2928 block->resetModified();
2931 catch(SerializationError &e)
2933 warningstream<<"Invalid block data on disk "
2934 <<"fullpath="<<fullpath
2935 <<" (SerializationError). "
2936 <<"what()="<<e.what()
2938 // Ignoring. A new one will be generated.
2941 // TODO: Backup file; name is in fullpath.
2945 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
2947 DSTACK(FUNCTION_NAME);
2950 std::istringstream is(*blob, std::ios_base::binary);
2952 u8 version = SER_FMT_VER_INVALID;
2953 is.read((char*)&version, 1);
2956 throw SerializationError("ServerMap::loadBlock(): Failed"
2957 " to read MapBlock version");
2959 /*u32 block_size = MapBlock::serializedLength(version);
2960 SharedBuffer<u8> data(block_size);
2961 is.read((char*)*data, block_size);*/
2963 // This will always return a sector because we're the server
2964 //MapSector *sector = emergeSector(p2d);
2966 MapBlock *block = NULL;
2967 bool created_new = false;
2968 block = sector->getBlockNoCreateNoEx(p3d.Y);
2971 block = sector->createBlankBlockNoInsert(p3d.Y);
2976 block->deSerialize(is, version, true);
2978 // If it's a new block, insert it to the map
2980 sector->insertBlock(block);
2981 ReflowScan scanner(this, m_emerge->ndef);
2982 scanner.scan(block, &m_transforming_liquid);
2986 Save blocks loaded in old format in new format
2989 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
2990 // Only save if asked to; no need to update version
2994 // We just loaded it from, so it's up-to-date.
2995 block->resetModified();
2998 catch(SerializationError &e)
3000 errorstream<<"Invalid block data in database"
3001 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3002 <<" (SerializationError): "<<e.what()<<std::endl;
3004 // TODO: Block should be marked as invalid in memory so that it is
3005 // not touched but the game can run
3007 if(g_settings->getBool("ignore_world_load_errors")){
3008 errorstream<<"Ignoring block load error. Duck and cover! "
3009 <<"(ignore_world_load_errors)"<<std::endl;
3011 throw SerializationError("Invalid block data in database");
3016 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3018 DSTACK(FUNCTION_NAME);
3020 v2s16 p2d(blockpos.X, blockpos.Z);
3023 dbase->loadBlock(blockpos, &ret);
3025 loadBlock(&ret, blockpos, createSector(p2d), false);
3026 return getBlockNoCreateNoEx(blockpos);
3028 // Not found in database, try the files
3030 // The directory layout we're going to load from.
3031 // 1 - original sectors/xxxxzzzz/
3032 // 2 - new sectors2/xxx/zzz/
3033 // If we load from anything but the latest structure, we will
3034 // immediately save to the new one, and remove the old.
3036 std::string sectordir1 = getSectorDir(p2d, 1);
3037 std::string sectordir;
3038 if(fs::PathExists(sectordir1))
3040 sectordir = sectordir1;
3045 sectordir = getSectorDir(p2d, 2);
3049 Make sure sector is loaded
3052 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3056 sector = loadSectorMeta(sectordir, loadlayout != 2);
3058 catch(InvalidFilenameException &e)
3062 catch(FileNotGoodException &e)
3066 catch(std::exception &e)
3073 Make sure file exists
3076 std::string blockfilename = getBlockFilename(blockpos);
3077 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3081 Load block and save it to the database
3083 loadBlock(sectordir, blockfilename, sector, true);
3084 return getBlockNoCreateNoEx(blockpos);
3087 bool ServerMap::deleteBlock(v3s16 blockpos)
3089 if (!dbase->deleteBlock(blockpos))
3092 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3094 v2s16 p2d(blockpos.X, blockpos.Z);
3095 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3098 sector->deleteBlock(block);
3104 void ServerMap::PrintInfo(std::ostream &out)
3109 MMVManip::MMVManip(Map *map):
3112 m_create_area(false),
3117 MMVManip::~MMVManip()
3121 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3122 bool load_if_inexistent)
3124 TimeTaker timer1("initialEmerge", &emerge_time);
3126 // Units of these are MapBlocks
3127 v3s16 p_min = blockpos_min;
3128 v3s16 p_max = blockpos_max;
3130 VoxelArea block_area_nodes
3131 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3133 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3136 infostream<<"initialEmerge: area: ";
3137 block_area_nodes.print(infostream);
3138 infostream<<" ("<<size_MB<<"MB)";
3139 infostream<<std::endl;
3142 addArea(block_area_nodes);
3144 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3145 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3146 for(s32 x=p_min.X; x<=p_max.X; x++)
3151 std::map<v3s16, u8>::iterator n;
3152 n = m_loaded_blocks.find(p);
3153 if(n != m_loaded_blocks.end())
3156 bool block_data_inexistent = false;
3159 TimeTaker timer1("emerge load", &emerge_load_time);
3161 block = m_map->getBlockNoCreate(p);
3162 if(block->isDummy())
3163 block_data_inexistent = true;
3165 block->copyTo(*this);
3167 catch(InvalidPositionException &e)
3169 block_data_inexistent = true;
3172 if(block_data_inexistent)
3175 if (load_if_inexistent) {
3176 ServerMap *svrmap = (ServerMap *)m_map;
3177 block = svrmap->emergeBlock(p, false);
3179 block = svrmap->createBlock(p);
3180 block->copyTo(*this);
3182 flags |= VMANIP_BLOCK_DATA_INEXIST;
3185 Mark area inexistent
3187 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3188 // Fill with VOXELFLAG_NO_DATA
3189 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3190 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3192 s32 i = m_area.index(a.MinEdge.X,y,z);
3193 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3197 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3199 // Mark that block was loaded as blank
3200 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3203 m_loaded_blocks[p] = flags;
3209 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3210 bool overwrite_generated)
3212 if(m_area.getExtent() == v3s16(0,0,0))
3216 Copy data of all blocks
3218 for(std::map<v3s16, u8>::iterator
3219 i = m_loaded_blocks.begin();
3220 i != m_loaded_blocks.end(); ++i)
3223 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3224 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3225 if ((existed == false) || (block == NULL) ||
3226 (overwrite_generated == false && block->isGenerated() == true))
3229 block->copyFrom(*this);
3232 (*modified_blocks)[p] = block;