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 = getBlockNoCreateNoEx(blockpos);
178 return (block != NULL);
181 // Returns a CONTENT_IGNORE node if not found
182 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
184 v3s16 blockpos = getNodeBlockPos(p);
185 MapBlock *block = getBlockNoCreateNoEx(blockpos);
187 if (is_valid_position != NULL)
188 *is_valid_position = false;
189 return MapNode(CONTENT_IGNORE);
192 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
194 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
195 if (is_valid_position != NULL)
196 *is_valid_position = is_valid_p;
202 // throws InvalidPositionException if not found
203 // TODO: Now this is deprecated, getNodeNoEx should be renamed
204 MapNode Map::getNode(v3s16 p)
206 v3s16 blockpos = getNodeBlockPos(p);
207 MapBlock *block = getBlockNoCreateNoEx(blockpos);
209 throw InvalidPositionException();
210 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
211 bool is_valid_position;
212 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
213 if (!is_valid_position)
214 throw InvalidPositionException();
219 // throws InvalidPositionException if not found
220 void Map::setNode(v3s16 p, MapNode & n)
222 v3s16 blockpos = getNodeBlockPos(p);
223 MapBlock *block = getBlockNoCreate(blockpos);
224 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
225 // Never allow placing CONTENT_IGNORE, it fucks up stuff
226 if(n.getContent() == CONTENT_IGNORE){
228 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
229 <<" while trying to replace \""
230 <<m_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
827 // Ignore light (because calling voxalgo::update_lighting_nodes)
828 n.setLight(LIGHTBANK_DAY, 0, ndef);
829 n.setLight(LIGHTBANK_NIGHT, 0, ndef);
833 std::vector<std::pair<v3s16, MapNode> > oldnodes;
834 oldnodes.push_back(std::pair<v3s16, MapNode>(p, oldnode));
835 voxalgo::update_lighting_nodes(this, ndef, oldnodes, modified_blocks);
837 for(std::map<v3s16, MapBlock*>::iterator
838 i = modified_blocks.begin();
839 i != modified_blocks.end(); ++i)
841 i->second->expireDayNightDiff();
844 // Report for rollback
845 if(m_gamedef->rollback())
847 RollbackNode rollback_newnode(this, p, m_gamedef);
848 RollbackAction action;
849 action.setSetNode(p, rollback_oldnode, rollback_newnode);
850 m_gamedef->rollback()->reportAction(action);
854 Add neighboring liquid nodes and this node to transform queue.
855 (it's vital for the node itself to get updated last, if it was removed.)
858 v3s16(0,0,1), // back
860 v3s16(1,0,0), // right
861 v3s16(0,0,-1), // front
862 v3s16(0,-1,0), // bottom
863 v3s16(-1,0,0), // left
864 v3s16(0,0,0), // self
866 for(u16 i=0; i<7; i++)
868 v3s16 p2 = p + dirs[i];
870 bool is_valid_position;
871 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
873 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
875 m_transforming_liquid.push_back(p2);
880 void Map::removeNodeAndUpdate(v3s16 p,
881 std::map<v3s16, MapBlock*> &modified_blocks)
883 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
886 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
889 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
893 bool succeeded = true;
895 std::map<v3s16, MapBlock*> modified_blocks;
896 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
898 // Copy modified_blocks to event
899 for(std::map<v3s16, MapBlock*>::iterator
900 i = modified_blocks.begin();
901 i != modified_blocks.end(); ++i)
903 event.modified_blocks.insert(i->first);
906 catch(InvalidPositionException &e){
910 dispatchEvent(&event);
915 bool Map::removeNodeWithEvent(v3s16 p)
918 event.type = MEET_REMOVENODE;
921 bool succeeded = true;
923 std::map<v3s16, MapBlock*> modified_blocks;
924 removeNodeAndUpdate(p, modified_blocks);
926 // Copy modified_blocks to event
927 for(std::map<v3s16, MapBlock*>::iterator
928 i = modified_blocks.begin();
929 i != modified_blocks.end(); ++i)
931 event.modified_blocks.insert(i->first);
934 catch(InvalidPositionException &e){
938 dispatchEvent(&event);
943 bool Map::getDayNightDiff(v3s16 blockpos)
946 v3s16 p = blockpos + v3s16(0,0,0);
947 MapBlock *b = getBlockNoCreate(p);
948 if(b->getDayNightDiff())
951 catch(InvalidPositionException &e){}
954 v3s16 p = blockpos + v3s16(-1,0,0);
955 MapBlock *b = getBlockNoCreate(p);
956 if(b->getDayNightDiff())
959 catch(InvalidPositionException &e){}
961 v3s16 p = blockpos + v3s16(0,-1,0);
962 MapBlock *b = getBlockNoCreate(p);
963 if(b->getDayNightDiff())
966 catch(InvalidPositionException &e){}
968 v3s16 p = blockpos + v3s16(0,0,-1);
969 MapBlock *b = getBlockNoCreate(p);
970 if(b->getDayNightDiff())
973 catch(InvalidPositionException &e){}
976 v3s16 p = blockpos + v3s16(1,0,0);
977 MapBlock *b = getBlockNoCreate(p);
978 if(b->getDayNightDiff())
981 catch(InvalidPositionException &e){}
983 v3s16 p = blockpos + v3s16(0,1,0);
984 MapBlock *b = getBlockNoCreate(p);
985 if(b->getDayNightDiff())
988 catch(InvalidPositionException &e){}
990 v3s16 p = blockpos + v3s16(0,0,1);
991 MapBlock *b = getBlockNoCreate(p);
992 if(b->getDayNightDiff())
995 catch(InvalidPositionException &e){}
1000 struct TimeOrderedMapBlock {
1004 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
1009 bool operator<(const TimeOrderedMapBlock &b) const
1011 return block->getUsageTimer() < b.block->getUsageTimer();
1016 Updates usage timers
1018 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
1019 std::vector<v3s16> *unloaded_blocks)
1021 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1023 // Profile modified reasons
1024 Profiler modprofiler;
1026 std::vector<v2s16> sector_deletion_queue;
1027 u32 deleted_blocks_count = 0;
1028 u32 saved_blocks_count = 0;
1029 u32 block_count_all = 0;
1033 // If there is no practical limit, we spare creation of mapblock_queue
1034 if (max_loaded_blocks == U32_MAX) {
1035 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1036 si != m_sectors.end(); ++si) {
1037 MapSector *sector = si->second;
1039 bool all_blocks_deleted = true;
1041 MapBlockVect blocks;
1042 sector->getBlocks(blocks);
1044 for (MapBlockVect::iterator i = blocks.begin();
1045 i != blocks.end(); ++i) {
1046 MapBlock *block = (*i);
1048 block->incrementUsageTimer(dtime);
1050 if (block->refGet() == 0
1051 && block->getUsageTimer() > unload_timeout) {
1052 v3s16 p = block->getPos();
1055 if (block->getModified() != MOD_STATE_CLEAN
1056 && save_before_unloading) {
1057 modprofiler.add(block->getModifiedReasonString(), 1);
1058 if (!saveBlock(block))
1060 saved_blocks_count++;
1063 // Delete from memory
1064 sector->deleteBlock(block);
1066 if (unloaded_blocks)
1067 unloaded_blocks->push_back(p);
1069 deleted_blocks_count++;
1071 all_blocks_deleted = false;
1076 if (all_blocks_deleted) {
1077 sector_deletion_queue.push_back(si->first);
1081 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
1082 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1083 si != m_sectors.end(); ++si) {
1084 MapSector *sector = si->second;
1086 MapBlockVect blocks;
1087 sector->getBlocks(blocks);
1089 for(MapBlockVect::iterator i = blocks.begin();
1090 i != blocks.end(); ++i) {
1091 MapBlock *block = (*i);
1093 block->incrementUsageTimer(dtime);
1094 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
1097 block_count_all = mapblock_queue.size();
1098 // Delete old blocks, and blocks over the limit from the memory
1099 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
1100 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
1101 TimeOrderedMapBlock b = mapblock_queue.top();
1102 mapblock_queue.pop();
1104 MapBlock *block = b.block;
1106 if (block->refGet() != 0)
1109 v3s16 p = block->getPos();
1112 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1113 modprofiler.add(block->getModifiedReasonString(), 1);
1114 if (!saveBlock(block))
1116 saved_blocks_count++;
1119 // Delete from memory
1120 b.sect->deleteBlock(block);
1122 if (unloaded_blocks)
1123 unloaded_blocks->push_back(p);
1125 deleted_blocks_count++;
1128 // Delete empty sectors
1129 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1130 si != m_sectors.end(); ++si) {
1131 if (si->second->empty()) {
1132 sector_deletion_queue.push_back(si->first);
1138 // Finally delete the empty sectors
1139 deleteSectors(sector_deletion_queue);
1141 if(deleted_blocks_count != 0)
1143 PrintInfo(infostream); // ServerMap/ClientMap:
1144 infostream<<"Unloaded "<<deleted_blocks_count
1145 <<" blocks from memory";
1146 if(save_before_unloading)
1147 infostream<<", of which "<<saved_blocks_count<<" were written";
1148 infostream<<", "<<block_count_all<<" blocks in memory";
1149 infostream<<"."<<std::endl;
1150 if(saved_blocks_count != 0){
1151 PrintInfo(infostream); // ServerMap/ClientMap:
1152 infostream<<"Blocks modified by: "<<std::endl;
1153 modprofiler.print(infostream);
1158 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1160 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
1163 void Map::deleteSectors(std::vector<v2s16> §orList)
1165 for(std::vector<v2s16>::iterator j = sectorList.begin();
1166 j != sectorList.end(); ++j) {
1167 MapSector *sector = m_sectors[*j];
1168 // If sector is in sector cache, remove it from there
1169 if(m_sector_cache == sector)
1170 m_sector_cache = NULL;
1171 // Remove from map and delete
1172 m_sectors.erase(*j);
1177 void Map::PrintInfo(std::ostream &out)
1182 #define WATER_DROP_BOOST 4
1186 NEIGHBOR_SAME_LEVEL,
1189 struct NodeNeighbor {
1193 bool l; //can liquid
1199 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1206 void Map::transforming_liquid_add(v3s16 p) {
1207 m_transforming_liquid.push_back(p);
1210 s32 Map::transforming_liquid_size() {
1211 return m_transforming_liquid.size();
1214 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
1217 INodeDefManager *nodemgr = m_gamedef->ndef();
1219 DSTACK(FUNCTION_NAME);
1220 //TimeTaker timer("transformLiquids()");
1223 u32 initial_size = m_transforming_liquid.size();
1225 /*if(initial_size != 0)
1226 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1228 // list of nodes that due to viscosity have not reached their max level height
1229 std::deque<v3s16> must_reflow;
1231 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
1233 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1234 u32 loop_max = liquid_loop_max;
1238 /* If liquid_loop_max is not keeping up with the queue size increase
1239 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1241 if (m_transforming_liquid.size() > loop_max * 2) {
1243 float server_step = g_settings->getFloat("dedicated_server_step");
1244 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1245 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1247 m_transforming_liquid_loop_count_multiplier = 1.0;
1250 loop_max *= m_transforming_liquid_loop_count_multiplier;
1253 while (m_transforming_liquid.size() != 0)
1255 // This should be done here so that it is done when continue is used
1256 if (loopcount >= initial_size || loopcount >= loop_max)
1261 Get a queued transforming liquid node
1263 v3s16 p0 = m_transforming_liquid.front();
1264 m_transforming_liquid.pop_front();
1266 MapNode n0 = getNodeNoEx(p0);
1269 Collect information about current node
1271 s8 liquid_level = -1;
1272 // The liquid node which will be placed there if
1273 // the liquid flows into this node.
1274 content_t liquid_kind = CONTENT_IGNORE;
1275 // The node which will be placed there if liquid
1276 // can't flow into this node.
1277 content_t floodable_node = CONTENT_AIR;
1278 const ContentFeatures &cf = nodemgr->get(n0);
1279 LiquidType liquid_type = cf.liquid_type;
1280 switch (liquid_type) {
1282 liquid_level = LIQUID_LEVEL_SOURCE;
1283 liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing);
1285 case LIQUID_FLOWING:
1286 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1287 liquid_kind = n0.getContent();
1290 // if this node is 'floodable', it *could* be transformed
1291 // into a liquid, otherwise, continue with the next node.
1294 floodable_node = n0.getContent();
1295 liquid_kind = CONTENT_AIR;
1300 Collect information about the environment
1302 const v3s16 *dirs = g_6dirs;
1303 NodeNeighbor sources[6]; // surrounding sources
1304 int num_sources = 0;
1305 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1307 NodeNeighbor airs[6]; // surrounding air
1309 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1310 int num_neutrals = 0;
1311 bool flowing_down = false;
1312 bool ignored_sources = false;
1313 for (u16 i = 0; i < 6; i++) {
1314 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1317 nt = NEIGHBOR_UPPER;
1320 nt = NEIGHBOR_LOWER;
1323 v3s16 npos = p0 + dirs[i];
1324 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1325 const ContentFeatures &cfnb = nodemgr->get(nb.n);
1326 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1328 if (cfnb.floodable) {
1329 airs[num_airs++] = nb;
1330 // if the current node is a water source the neighbor
1331 // should be enqueded for transformation regardless of whether the
1332 // current node changes or not.
1333 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1334 m_transforming_liquid.push_back(npos);
1335 // if the current node happens to be a flowing node, it will start to flow down here.
1336 if (nb.t == NEIGHBOR_LOWER)
1337 flowing_down = true;
1339 neutrals[num_neutrals++] = nb;
1340 if (nb.n.getContent() == CONTENT_IGNORE) {
1341 // If node below is ignore prevent water from
1342 // spreading outwards and otherwise prevent from
1343 // flowing away as ignore node might be the source
1344 if (nb.t == NEIGHBOR_LOWER)
1345 flowing_down = true;
1347 ignored_sources = true;
1352 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1353 if (liquid_kind == CONTENT_AIR)
1354 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1355 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1356 neutrals[num_neutrals++] = nb;
1358 // Do not count bottom source, it will screw things up
1360 sources[num_sources++] = nb;
1363 case LIQUID_FLOWING:
1364 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1365 if (liquid_kind == CONTENT_AIR)
1366 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1367 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1368 neutrals[num_neutrals++] = nb;
1370 flows[num_flows++] = nb;
1371 if (nb.t == NEIGHBOR_LOWER)
1372 flowing_down = true;
1379 decide on the type (and possibly level) of the current node
1381 content_t new_node_content;
1382 s8 new_node_level = -1;
1383 s8 max_node_level = -1;
1385 u8 range = nodemgr->get(liquid_kind).liquid_range;
1386 if (range > LIQUID_LEVEL_MAX + 1)
1387 range = LIQUID_LEVEL_MAX + 1;
1389 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1390 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1391 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1392 // it's perfectly safe to use liquid_kind here to determine the new node content.
1393 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1394 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1395 // liquid_kind is set properly, see above
1396 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1397 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1398 new_node_content = liquid_kind;
1400 new_node_content = floodable_node;
1401 } else if (ignored_sources && liquid_level >= 0) {
1402 // Maybe there are neighbouring sources that aren't loaded yet
1403 // so prevent flowing away.
1404 new_node_level = liquid_level;
1405 new_node_content = liquid_kind;
1407 // no surrounding sources, so get the maximum level that can flow into this node
1408 for (u16 i = 0; i < num_flows; i++) {
1409 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1410 switch (flows[i].t) {
1411 case NEIGHBOR_UPPER:
1412 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1413 max_node_level = LIQUID_LEVEL_MAX;
1414 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1415 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1416 } else if (nb_liquid_level > max_node_level) {
1417 max_node_level = nb_liquid_level;
1420 case NEIGHBOR_LOWER:
1422 case NEIGHBOR_SAME_LEVEL:
1423 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1424 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
1425 max_node_level = nb_liquid_level - 1;
1430 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1431 if (viscosity > 1 && max_node_level != liquid_level) {
1432 // amount to gain, limited by viscosity
1433 // must be at least 1 in absolute value
1434 s8 level_inc = max_node_level - liquid_level;
1435 if (level_inc < -viscosity || level_inc > viscosity)
1436 new_node_level = liquid_level + level_inc/viscosity;
1437 else if (level_inc < 0)
1438 new_node_level = liquid_level - 1;
1439 else if (level_inc > 0)
1440 new_node_level = liquid_level + 1;
1441 if (new_node_level != max_node_level)
1442 must_reflow.push_back(p0);
1444 new_node_level = max_node_level;
1447 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1448 new_node_content = liquid_kind;
1450 new_node_content = floodable_node;
1455 check if anything has changed. if not, just continue with the next node.
1457 if (new_node_content == n0.getContent() &&
1458 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1459 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1460 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1466 update the current node
1469 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1470 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1471 // set level to last 3 bits, flowing down bit to 4th bit
1472 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1474 // set the liquid level and flow bit to 0
1475 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1477 n0.setContent(new_node_content);
1479 // Ignore light (because calling voxalgo::update_lighting_nodes)
1480 n0.setLight(LIGHTBANK_DAY, 0, nodemgr);
1481 n0.setLight(LIGHTBANK_NIGHT, 0, nodemgr);
1483 // Find out whether there is a suspect for this action
1484 std::string suspect;
1485 if (m_gamedef->rollback())
1486 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1488 if (m_gamedef->rollback() && !suspect.empty()) {
1490 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1491 // Get old node for rollback
1492 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1496 RollbackNode rollback_newnode(this, p0, m_gamedef);
1497 RollbackAction action;
1498 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1499 m_gamedef->rollback()->reportAction(action);
1505 v3s16 blockpos = getNodeBlockPos(p0);
1506 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1507 if (block != NULL) {
1508 modified_blocks[blockpos] = block;
1509 changed_nodes.push_back(std::pair<v3s16, MapNode>(p0, n00));
1513 enqueue neighbors for update if neccessary
1515 switch (nodemgr->get(n0.getContent()).liquid_type) {
1517 case LIQUID_FLOWING:
1518 // make sure source flows into all neighboring nodes
1519 for (u16 i = 0; i < num_flows; i++)
1520 if (flows[i].t != NEIGHBOR_UPPER)
1521 m_transforming_liquid.push_back(flows[i].p);
1522 for (u16 i = 0; i < num_airs; i++)
1523 if (airs[i].t != NEIGHBOR_UPPER)
1524 m_transforming_liquid.push_back(airs[i].p);
1527 // this flow has turned to air; neighboring flows might need to do the same
1528 for (u16 i = 0; i < num_flows; i++)
1529 m_transforming_liquid.push_back(flows[i].p);
1533 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1535 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1536 m_transforming_liquid.push_back(*iter);
1538 voxalgo::update_lighting_nodes(this, nodemgr, changed_nodes, modified_blocks);
1541 /* ----------------------------------------------------------------------
1542 * Manage the queue so that it does not grow indefinately
1544 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1546 if (time_until_purge == 0)
1547 return; // Feature disabled
1549 time_until_purge *= 1000; // seconds -> milliseconds
1551 u32 curr_time = getTime(PRECISION_MILLI);
1552 u32 prev_unprocessed = m_unprocessed_count;
1553 m_unprocessed_count = m_transforming_liquid.size();
1555 // if unprocessed block count is decreasing or stable
1556 if (m_unprocessed_count <= prev_unprocessed) {
1557 m_queue_size_timer_started = false;
1559 if (!m_queue_size_timer_started)
1560 m_inc_trending_up_start_time = curr_time;
1561 m_queue_size_timer_started = true;
1564 // Account for curr_time overflowing
1565 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1566 m_queue_size_timer_started = false;
1568 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1569 * and the number of unprocessed blocks is still > liquid_loop_max then we
1570 * cannot keep up; dump the oldest blocks from the queue so that the queue
1571 * has liquid_loop_max items in it
1573 if (m_queue_size_timer_started
1574 && curr_time - m_inc_trending_up_start_time > time_until_purge
1575 && m_unprocessed_count > liquid_loop_max) {
1577 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1579 infostream << "transformLiquids(): DUMPING " << dump_qty
1580 << " blocks from the queue" << std::endl;
1583 m_transforming_liquid.pop_front();
1585 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1586 m_unprocessed_count = m_transforming_liquid.size();
1590 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1592 std::vector<v3s16> positions_with_meta;
1594 sortBoxVerticies(p1, p2);
1595 v3s16 bpmin = getNodeBlockPos(p1);
1596 v3s16 bpmax = getNodeBlockPos(p2);
1598 VoxelArea area(p1, p2);
1600 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1601 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1602 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1603 v3s16 blockpos(x, y, z);
1605 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1607 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1608 << PP(blockpos) << std::endl;
1609 block = emergeBlock(blockpos, false);
1612 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1617 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1618 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1619 for (size_t i = 0; i != keys.size(); i++) {
1620 v3s16 p(keys[i] + p_base);
1621 if (!area.contains(p))
1624 positions_with_meta.push_back(p);
1628 return positions_with_meta;
1631 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1633 v3s16 blockpos = getNodeBlockPos(p);
1634 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1635 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1637 infostream<<"Map::getNodeMetadata(): Need to emerge "
1638 <<PP(blockpos)<<std::endl;
1639 block = emergeBlock(blockpos, false);
1642 warningstream<<"Map::getNodeMetadata(): Block not found"
1646 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1650 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1652 v3s16 blockpos = getNodeBlockPos(p);
1653 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1654 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1656 infostream<<"Map::setNodeMetadata(): Need to emerge "
1657 <<PP(blockpos)<<std::endl;
1658 block = emergeBlock(blockpos, false);
1661 warningstream<<"Map::setNodeMetadata(): Block not found"
1665 block->m_node_metadata.set(p_rel, meta);
1669 void Map::removeNodeMetadata(v3s16 p)
1671 v3s16 blockpos = getNodeBlockPos(p);
1672 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1673 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1676 warningstream<<"Map::removeNodeMetadata(): Block not found"
1680 block->m_node_metadata.remove(p_rel);
1683 NodeTimer Map::getNodeTimer(v3s16 p)
1685 v3s16 blockpos = getNodeBlockPos(p);
1686 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1687 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1689 infostream<<"Map::getNodeTimer(): Need to emerge "
1690 <<PP(blockpos)<<std::endl;
1691 block = emergeBlock(blockpos, false);
1694 warningstream<<"Map::getNodeTimer(): Block not found"
1698 NodeTimer t = block->m_node_timers.get(p_rel);
1699 NodeTimer nt(t.timeout, t.elapsed, p);
1703 void Map::setNodeTimer(const NodeTimer &t)
1705 v3s16 p = t.position;
1706 v3s16 blockpos = getNodeBlockPos(p);
1707 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1708 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1710 infostream<<"Map::setNodeTimer(): Need to emerge "
1711 <<PP(blockpos)<<std::endl;
1712 block = emergeBlock(blockpos, false);
1715 warningstream<<"Map::setNodeTimer(): Block not found"
1719 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1720 block->m_node_timers.set(nt);
1723 void Map::removeNodeTimer(v3s16 p)
1725 v3s16 blockpos = getNodeBlockPos(p);
1726 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1727 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1730 warningstream<<"Map::removeNodeTimer(): Block not found"
1734 block->m_node_timers.remove(p_rel);
1740 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1741 Map(dout_server, gamedef),
1742 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1744 m_map_metadata_changed(true)
1746 verbosestream<<FUNCTION_NAME<<std::endl;
1748 // Tell the EmergeManager about our MapSettingsManager
1749 emerge->map_settings_mgr = &settings_mgr;
1752 Try to load map; if not found, create a new one.
1755 // Determine which database backend to use
1756 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1758 bool succeeded = conf.readConfigFile(conf_path.c_str());
1759 if (!succeeded || !conf.exists("backend")) {
1760 // fall back to sqlite3
1761 conf.set("backend", "sqlite3");
1763 std::string backend = conf.get("backend");
1764 dbase = createDatabase(backend, savedir, conf);
1766 if (!conf.updateConfigFile(conf_path.c_str()))
1767 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1769 m_savedir = savedir;
1770 m_map_saving_enabled = false;
1774 // If directory exists, check contents and load if possible
1775 if(fs::PathExists(m_savedir))
1777 // If directory is empty, it is safe to save into it.
1778 if(fs::GetDirListing(m_savedir).size() == 0)
1780 infostream<<"ServerMap: Empty save directory is valid."
1782 m_map_saving_enabled = true;
1787 if (settings_mgr.loadMapMeta()) {
1788 infostream << "ServerMap: Metadata loaded from "
1789 << savedir << std::endl;
1791 infostream << "ServerMap: Metadata could not be loaded "
1792 "from " << savedir << ", assuming valid save "
1793 "directory." << std::endl;
1796 m_map_saving_enabled = true;
1797 // Map loaded, not creating new one
1801 // If directory doesn't exist, it is safe to save to it
1803 m_map_saving_enabled = true;
1806 catch(std::exception &e)
1808 warningstream<<"ServerMap: Failed to load map from "<<savedir
1809 <<", exception: "<<e.what()<<std::endl;
1810 infostream<<"Please remove the map or fix it."<<std::endl;
1811 warningstream<<"Map saving will be disabled."<<std::endl;
1814 infostream<<"Initializing new map."<<std::endl;
1816 // Create zero sector
1817 emergeSector(v2s16(0,0));
1819 // Initially write whole map
1820 save(MOD_STATE_CLEAN);
1823 ServerMap::~ServerMap()
1825 verbosestream<<FUNCTION_NAME<<std::endl;
1829 if(m_map_saving_enabled)
1831 // Save only changed parts
1832 save(MOD_STATE_WRITE_AT_UNLOAD);
1833 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
1837 infostream<<"ServerMap: Map not saved"<<std::endl;
1840 catch(std::exception &e)
1842 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1843 <<", exception: "<<e.what()<<std::endl;
1847 Close database if it was opened
1855 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1856 for(; i.atEnd() == false; i++)
1858 MapChunk *chunk = i.getNode()->getValue();
1864 MapgenParams *ServerMap::getMapgenParams()
1866 // getMapgenParams() should only ever be called after Server is initialized
1867 assert(settings_mgr.mapgen_params != NULL);
1868 return settings_mgr.mapgen_params;
1871 u64 ServerMap::getSeed()
1873 return getMapgenParams()->seed;
1876 s16 ServerMap::getWaterLevel()
1878 return getMapgenParams()->water_level;
1881 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1883 s16 csize = getMapgenParams()->chunksize;
1884 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1885 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1887 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1888 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1890 v3s16 extra_borders(1, 1, 1);
1891 v3s16 full_bpmin = bpmin - extra_borders;
1892 v3s16 full_bpmax = bpmax + extra_borders;
1894 // Do nothing if not inside limits (+-1 because of neighbors)
1895 if (blockpos_over_limit(full_bpmin) ||
1896 blockpos_over_limit(full_bpmax))
1899 data->seed = getSeed();
1900 data->blockpos_min = bpmin;
1901 data->blockpos_max = bpmax;
1902 data->blockpos_requested = blockpos;
1903 data->nodedef = m_gamedef->ndef();
1906 Create the whole area of this and the neighboring blocks
1908 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1909 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1910 v2s16 sectorpos(x, z);
1911 // Sector metadata is loaded from disk if not already loaded.
1912 ServerMapSector *sector = createSector(sectorpos);
1913 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1915 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1918 MapBlock *block = emergeBlock(p, false);
1919 if (block == NULL) {
1920 block = createBlock(p);
1922 // Block gets sunlight if this is true.
1923 // Refer to the map generator heuristics.
1924 bool ug = m_emerge->isBlockUnderground(p);
1925 block->setIsUnderground(ug);
1931 Now we have a big empty area.
1933 Make a ManualMapVoxelManipulator that contains this and the
1937 data->vmanip = new MMVManip(this);
1938 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1940 // Note: we may need this again at some point.
1942 // Ensure none of the blocks to be generated were marked as
1943 // containing CONTENT_IGNORE
1944 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1945 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1946 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1947 core::map<v3s16, u8>::Node *n;
1948 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1951 u8 flags = n->getValue();
1952 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1959 // Data is ready now.
1963 void ServerMap::finishBlockMake(BlockMakeData *data,
1964 std::map<v3s16, MapBlock*> *changed_blocks)
1966 v3s16 bpmin = data->blockpos_min;
1967 v3s16 bpmax = data->blockpos_max;
1969 v3s16 extra_borders(1, 1, 1);
1970 v3s16 full_bpmin = bpmin - extra_borders;
1971 v3s16 full_bpmax = bpmax + extra_borders;
1973 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1974 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1977 Set lighting to non-expired state in all of them.
1978 This is cheating, but it is not fast enough if all of them
1979 would actually be updated.
1981 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1982 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
1983 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1984 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
1988 block->setLightingExpired(false);
1992 Blit generated stuff to map
1993 NOTE: blitBackAll adds nearly everything to changed_blocks
1995 data->vmanip->blitBackAll(changed_blocks);
1997 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1998 << changed_blocks->size());
2001 Copy transforming liquid information
2003 while (data->transforming_liquid.size()) {
2004 m_transforming_liquid.push_back(data->transforming_liquid.front());
2005 data->transforming_liquid.pop_front();
2008 for (std::map<v3s16, MapBlock *>::iterator
2009 it = changed_blocks->begin();
2010 it != changed_blocks->end(); ++it) {
2011 MapBlock *block = it->second;
2015 Update day/night difference cache of the MapBlocks
2017 block->expireDayNightDiff();
2019 Set block as modified
2021 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2022 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2026 Set central blocks as generated
2028 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2029 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2030 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2031 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2035 block->setGenerated(true);
2039 Save changed parts of map
2040 NOTE: Will be saved later.
2042 //save(MOD_STATE_WRITE_AT_UNLOAD);
2045 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2047 DSTACKF("%s: p2d=(%d,%d)",
2052 Check if it exists already in memory
2054 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2059 Try to load it from disk (with blocks)
2061 //if(loadSectorFull(p2d) == true)
2064 Try to load metadata from disk
2067 if(loadSectorMeta(p2d) == true)
2069 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2072 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2073 throw InvalidPositionException("");
2079 Do not create over-limit
2081 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2082 g_settings->getU16("map_generation_limit"));
2083 if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2084 || p2d.X > map_gen_limit / MAP_BLOCKSIZE
2085 || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2086 || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
2087 throw InvalidPositionException("createSector(): pos. over limit");
2090 Generate blank sector
2093 sector = new ServerMapSector(this, p2d, m_gamedef);
2095 // Sector position on map in nodes
2096 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2101 m_sectors[p2d] = sector;
2108 This is a quick-hand function for calling makeBlock().
2110 MapBlock * ServerMap::generateBlock(
2112 std::map<v3s16, MapBlock*> &modified_blocks
2115 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2117 /*infostream<<"generateBlock(): "
2118 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2121 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2123 TimeTaker timer("generateBlock");
2125 //MapBlock *block = original_dummy;
2127 v2s16 p2d(p.X, p.Z);
2128 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2131 Do not generate over-limit
2133 if(blockpos_over_limit(p))
2135 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2136 throw InvalidPositionException("generateBlock(): pos. over limit");
2140 Create block make data
2143 initBlockMake(&data, p);
2149 TimeTaker t("mapgen::make_block()");
2150 mapgen->makeChunk(&data);
2151 //mapgen::make_block(&data);
2153 if(enable_mapgen_debug_info == false)
2154 t.stop(true); // Hide output
2158 Blit data back on map, update lighting, add mobs and whatever this does
2160 finishBlockMake(&data, modified_blocks);
2165 MapBlock *block = getBlockNoCreateNoEx(p);
2173 bool erroneus_content = false;
2174 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2175 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2176 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2179 MapNode n = block->getNode(p);
2180 if(n.getContent() == CONTENT_IGNORE)
2182 infostream<<"CONTENT_IGNORE at "
2183 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2185 erroneus_content = true;
2189 if(erroneus_content)
2198 Generate a completely empty block
2202 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2203 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2205 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2208 n.setContent(CONTENT_AIR);
2209 block->setNode(v3s16(x0,y0,z0), n);
2215 if(enable_mapgen_debug_info == false)
2216 timer.stop(true); // Hide output
2222 MapBlock * ServerMap::createBlock(v3s16 p)
2224 DSTACKF("%s: p=(%d,%d,%d)",
2225 FUNCTION_NAME, p.X, p.Y, p.Z);
2228 Do not create over-limit
2230 if (blockpos_over_limit(p))
2231 throw InvalidPositionException("createBlock(): pos. over limit");
2233 v2s16 p2d(p.X, p.Z);
2236 This will create or load a sector if not found in memory.
2237 If block exists on disk, it will be loaded.
2239 NOTE: On old save formats, this will be slow, as it generates
2240 lighting on blocks for them.
2242 ServerMapSector *sector;
2244 sector = (ServerMapSector*)createSector(p2d);
2245 assert(sector->getId() == MAPSECTOR_SERVER);
2247 catch(InvalidPositionException &e)
2249 infostream<<"createBlock: createSector() failed"<<std::endl;
2253 NOTE: This should not be done, or at least the exception
2254 should not be passed on as std::exception, because it
2255 won't be catched at all.
2257 /*catch(std::exception &e)
2259 infostream<<"createBlock: createSector() failed: "
2260 <<e.what()<<std::endl;
2265 Try to get a block from the sector
2268 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2271 if(block->isDummy())
2276 block = sector->createBlankBlock(block_y);
2281 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2283 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2285 p.X, p.Y, p.Z, create_blank);
2288 MapBlock *block = getBlockNoCreateNoEx(p);
2289 if(block && block->isDummy() == false)
2294 MapBlock *block = loadBlock(p);
2300 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2301 MapBlock *block = sector->createBlankBlock(p.Y);
2309 std::map<v3s16, MapBlock*> modified_blocks;
2310 MapBlock *block = generateBlock(p, modified_blocks);
2314 event.type = MEET_OTHER;
2317 // Copy modified_blocks to event
2318 for(std::map<v3s16, MapBlock*>::iterator
2319 i = modified_blocks.begin();
2320 i != modified_blocks.end(); ++i)
2322 event.modified_blocks.insert(i->first);
2326 dispatchEvent(&event);
2336 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2338 MapBlock *block = getBlockNoCreateNoEx(p3d);
2340 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2345 void ServerMap::prepareBlock(MapBlock *block) {
2348 // N.B. This requires no synchronization, since data will not be modified unless
2349 // the VoxelManipulator being updated belongs to the same thread.
2350 void ServerMap::updateVManip(v3s16 pos)
2352 Mapgen *mg = m_emerge->getCurrentMapgen();
2356 MMVManip *vm = mg->vm;
2360 if (!vm->m_area.contains(pos))
2363 s32 idx = vm->m_area.index(pos);
2364 vm->m_data[idx] = getNodeNoEx(pos);
2365 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2367 vm->m_is_dirty = true;
2370 s16 ServerMap::findGroundLevel(v2s16 p2d)
2374 Uh, just do something random...
2376 // Find existing map from top to down
2379 v3s16 p(p2d.X, max, p2d.Y);
2380 for(; p.Y>min; p.Y--)
2382 MapNode n = getNodeNoEx(p);
2383 if(n.getContent() != CONTENT_IGNORE)
2388 // If this node is not air, go to plan b
2389 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2391 // Search existing walkable and return it
2392 for(; p.Y>min; p.Y--)
2394 MapNode n = getNodeNoEx(p);
2395 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2404 Determine from map generator noise functions
2407 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2410 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2411 //return (s16)level;
2414 bool ServerMap::loadFromFolders() {
2415 if (!dbase->initialized() &&
2416 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2421 void ServerMap::createDirs(std::string path)
2423 if(fs::CreateAllDirs(path) == false)
2425 m_dout<<"ServerMap: Failed to create directory "
2426 <<"\""<<path<<"\""<<std::endl;
2427 throw BaseException("ServerMap failed to create directory");
2431 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2437 snprintf(cc, 9, "%.4x%.4x",
2438 (unsigned int) pos.X & 0xffff,
2439 (unsigned int) pos.Y & 0xffff);
2441 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2443 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2444 (unsigned int) pos.X & 0xfff,
2445 (unsigned int) pos.Y & 0xfff);
2447 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2454 v2s16 ServerMap::getSectorPos(std::string dirname)
2456 unsigned int x = 0, y = 0;
2458 std::string component;
2459 fs::RemoveLastPathComponent(dirname, &component, 1);
2460 if(component.size() == 8)
2463 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2465 else if(component.size() == 3)
2468 fs::RemoveLastPathComponent(dirname, &component, 2);
2469 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2470 // Sign-extend the 12 bit values up to 16 bits...
2471 if(x & 0x800) x |= 0xF000;
2472 if(y & 0x800) y |= 0xF000;
2479 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2480 v2s16 pos((s16)x, (s16)y);
2484 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2486 v2s16 p2d = getSectorPos(sectordir);
2488 if(blockfile.size() != 4){
2489 throw InvalidFilenameException("Invalid block filename");
2492 int r = sscanf(blockfile.c_str(), "%4x", &y);
2494 throw InvalidFilenameException("Invalid block filename");
2495 return v3s16(p2d.X, y, p2d.Y);
2498 std::string ServerMap::getBlockFilename(v3s16 p)
2501 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2505 void ServerMap::save(ModifiedState save_level)
2507 DSTACK(FUNCTION_NAME);
2508 if(m_map_saving_enabled == false) {
2509 warningstream<<"Not saving map, saving disabled."<<std::endl;
2513 if(save_level == MOD_STATE_CLEAN)
2514 infostream<<"ServerMap: Saving whole map, this can take time."
2517 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2518 if (settings_mgr.saveMapMeta())
2519 m_map_metadata_changed = false;
2522 // Profile modified reasons
2523 Profiler modprofiler;
2525 u32 sector_meta_count = 0;
2526 u32 block_count = 0;
2527 u32 block_count_all = 0; // Number of blocks in memory
2529 // Don't do anything with sqlite unless something is really saved
2530 bool save_started = false;
2532 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2533 i != m_sectors.end(); ++i) {
2534 ServerMapSector *sector = (ServerMapSector*)i->second;
2535 assert(sector->getId() == MAPSECTOR_SERVER);
2537 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2538 saveSectorMeta(sector);
2539 sector_meta_count++;
2542 MapBlockVect blocks;
2543 sector->getBlocks(blocks);
2545 for(MapBlockVect::iterator j = blocks.begin();
2546 j != blocks.end(); ++j) {
2547 MapBlock *block = *j;
2551 if(block->getModified() >= (u32)save_level) {
2555 save_started = true;
2558 modprofiler.add(block->getModifiedReasonString(), 1);
2563 /*infostream<<"ServerMap: Written block ("
2564 <<block->getPos().X<<","
2565 <<block->getPos().Y<<","
2566 <<block->getPos().Z<<")"
2576 Only print if something happened or saved whole map
2578 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2579 || block_count != 0) {
2580 infostream<<"ServerMap: Written: "
2581 <<sector_meta_count<<" sector metadata files, "
2582 <<block_count<<" block files"
2583 <<", "<<block_count_all<<" blocks in memory."
2585 PrintInfo(infostream); // ServerMap/ClientMap:
2586 infostream<<"Blocks modified by: "<<std::endl;
2587 modprofiler.print(infostream);
2591 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2593 if (loadFromFolders()) {
2594 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2595 << "all blocks that are stored in flat files." << std::endl;
2597 dbase->listAllLoadableBlocks(dst);
2600 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2602 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2603 si != m_sectors.end(); ++si)
2605 MapSector *sector = si->second;
2607 MapBlockVect blocks;
2608 sector->getBlocks(blocks);
2610 for(MapBlockVect::iterator i = blocks.begin();
2611 i != blocks.end(); ++i) {
2612 v3s16 p = (*i)->getPos();
2618 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2620 DSTACK(FUNCTION_NAME);
2621 // Format used for writing
2622 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2624 v2s16 pos = sector->getPos();
2625 std::string dir = getSectorDir(pos);
2628 std::string fullpath = dir + DIR_DELIM + "meta";
2629 std::ostringstream ss(std::ios_base::binary);
2631 sector->serialize(ss, version);
2633 if(!fs::safeWriteToFile(fullpath, ss.str()))
2634 throw FileNotGoodException("Cannot write sector metafile");
2636 sector->differs_from_disk = false;
2639 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2641 DSTACK(FUNCTION_NAME);
2643 v2s16 p2d = getSectorPos(sectordir);
2645 ServerMapSector *sector = NULL;
2647 std::string fullpath = sectordir + DIR_DELIM + "meta";
2648 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2649 if(is.good() == false)
2651 // If the directory exists anyway, it probably is in some old
2652 // format. Just go ahead and create the sector.
2653 if(fs::PathExists(sectordir))
2655 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
2656 <<fullpath<<" doesn't exist but directory does."
2657 <<" Continuing with a sector with no metadata."
2659 sector = new ServerMapSector(this, p2d, m_gamedef);
2660 m_sectors[p2d] = sector;
2664 throw FileNotGoodException("Cannot open sector metafile");
2669 sector = ServerMapSector::deSerialize
2670 (is, this, p2d, m_sectors, m_gamedef);
2672 saveSectorMeta(sector);
2675 sector->differs_from_disk = false;
2680 bool ServerMap::loadSectorMeta(v2s16 p2d)
2682 DSTACK(FUNCTION_NAME);
2684 // The directory layout we're going to load from.
2685 // 1 - original sectors/xxxxzzzz/
2686 // 2 - new sectors2/xxx/zzz/
2687 // If we load from anything but the latest structure, we will
2688 // immediately save to the new one, and remove the old.
2690 std::string sectordir1 = getSectorDir(p2d, 1);
2691 std::string sectordir;
2692 if(fs::PathExists(sectordir1))
2694 sectordir = sectordir1;
2699 sectordir = getSectorDir(p2d, 2);
2703 loadSectorMeta(sectordir, loadlayout != 2);
2705 catch(InvalidFilenameException &e)
2709 catch(FileNotGoodException &e)
2713 catch(std::exception &e)
2722 bool ServerMap::loadSectorFull(v2s16 p2d)
2724 DSTACK(FUNCTION_NAME);
2726 MapSector *sector = NULL;
2728 // The directory layout we're going to load from.
2729 // 1 - original sectors/xxxxzzzz/
2730 // 2 - new sectors2/xxx/zzz/
2731 // If we load from anything but the latest structure, we will
2732 // immediately save to the new one, and remove the old.
2734 std::string sectordir1 = getSectorDir(p2d, 1);
2735 std::string sectordir;
2736 if(fs::PathExists(sectordir1))
2738 sectordir = sectordir1;
2743 sectordir = getSectorDir(p2d, 2);
2747 sector = loadSectorMeta(sectordir, loadlayout != 2);
2749 catch(InvalidFilenameException &e)
2753 catch(FileNotGoodException &e)
2757 catch(std::exception &e)
2765 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2767 std::vector<fs::DirListNode>::iterator i2;
2768 for(i2=list2.begin(); i2!=list2.end(); i2++)
2774 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2776 catch(InvalidFilenameException &e)
2778 // This catches unknown crap in directory
2784 infostream<<"Sector converted to new layout - deleting "<<
2785 sectordir1<<std::endl;
2786 fs::RecursiveDelete(sectordir1);
2793 Database *ServerMap::createDatabase(
2794 const std::string &name,
2795 const std::string &savedir,
2798 if (name == "sqlite3")
2799 return new Database_SQLite3(savedir);
2800 if (name == "dummy")
2801 return new Database_Dummy();
2803 else if (name == "leveldb")
2804 return new Database_LevelDB(savedir);
2807 else if (name == "redis")
2808 return new Database_Redis(conf);
2811 else if (name == "postgresql")
2812 return new Database_PostgreSQL(conf);
2815 throw BaseException(std::string("Database backend ") + name + " not supported.");
2818 void ServerMap::beginSave()
2823 void ServerMap::endSave()
2828 bool ServerMap::saveBlock(MapBlock *block)
2830 return saveBlock(block, dbase);
2833 bool ServerMap::saveBlock(MapBlock *block, Database *db)
2835 v3s16 p3d = block->getPos();
2837 // Dummy blocks are not written
2838 if (block->isDummy()) {
2839 warningstream << "saveBlock: Not writing dummy block "
2840 << PP(p3d) << std::endl;
2844 // Format used for writing
2845 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2848 [0] u8 serialization version
2851 std::ostringstream o(std::ios_base::binary);
2852 o.write((char*) &version, 1);
2853 block->serialize(o, version, true);
2855 std::string data = o.str();
2856 bool ret = db->saveBlock(p3d, data);
2858 // We just wrote it to the disk so clear modified flag
2859 block->resetModified();
2864 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
2865 MapSector *sector, bool save_after_load)
2867 DSTACK(FUNCTION_NAME);
2869 std::string fullpath = sectordir + DIR_DELIM + blockfile;
2872 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2873 if(is.good() == false)
2874 throw FileNotGoodException("Cannot open block file");
2876 v3s16 p3d = getBlockPos(sectordir, blockfile);
2877 v2s16 p2d(p3d.X, p3d.Z);
2879 assert(sector->getPos() == p2d);
2881 u8 version = SER_FMT_VER_INVALID;
2882 is.read((char*)&version, 1);
2885 throw SerializationError("ServerMap::loadBlock(): Failed"
2886 " to read MapBlock version");
2888 /*u32 block_size = MapBlock::serializedLength(version);
2889 SharedBuffer<u8> data(block_size);
2890 is.read((char*)*data, block_size);*/
2892 // This will always return a sector because we're the server
2893 //MapSector *sector = emergeSector(p2d);
2895 MapBlock *block = NULL;
2896 bool created_new = false;
2897 block = sector->getBlockNoCreateNoEx(p3d.Y);
2900 block = sector->createBlankBlockNoInsert(p3d.Y);
2905 block->deSerialize(is, version, true);
2907 // If it's a new block, insert it to the map
2909 sector->insertBlock(block);
2912 Save blocks loaded in old format in new format
2915 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
2919 // Should be in database now, so delete the old file
2920 fs::RecursiveDelete(fullpath);
2923 // We just loaded it from the disk, so it's up-to-date.
2924 block->resetModified();
2927 catch(SerializationError &e)
2929 warningstream<<"Invalid block data on disk "
2930 <<"fullpath="<<fullpath
2931 <<" (SerializationError). "
2932 <<"what()="<<e.what()
2934 // Ignoring. A new one will be generated.
2937 // TODO: Backup file; name is in fullpath.
2941 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
2943 DSTACK(FUNCTION_NAME);
2946 std::istringstream is(*blob, std::ios_base::binary);
2948 u8 version = SER_FMT_VER_INVALID;
2949 is.read((char*)&version, 1);
2952 throw SerializationError("ServerMap::loadBlock(): Failed"
2953 " to read MapBlock version");
2955 /*u32 block_size = MapBlock::serializedLength(version);
2956 SharedBuffer<u8> data(block_size);
2957 is.read((char*)*data, block_size);*/
2959 // This will always return a sector because we're the server
2960 //MapSector *sector = emergeSector(p2d);
2962 MapBlock *block = NULL;
2963 bool created_new = false;
2964 block = sector->getBlockNoCreateNoEx(p3d.Y);
2967 block = sector->createBlankBlockNoInsert(p3d.Y);
2972 block->deSerialize(is, version, true);
2974 // If it's a new block, insert it to the map
2976 sector->insertBlock(block);
2979 Save blocks loaded in old format in new format
2982 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
2983 // Only save if asked to; no need to update version
2987 // We just loaded it from, so it's up-to-date.
2988 block->resetModified();
2991 catch(SerializationError &e)
2993 errorstream<<"Invalid block data in database"
2994 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2995 <<" (SerializationError): "<<e.what()<<std::endl;
2997 // TODO: Block should be marked as invalid in memory so that it is
2998 // not touched but the game can run
3000 if(g_settings->getBool("ignore_world_load_errors")){
3001 errorstream<<"Ignoring block load error. Duck and cover! "
3002 <<"(ignore_world_load_errors)"<<std::endl;
3004 throw SerializationError("Invalid block data in database");
3009 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3011 DSTACK(FUNCTION_NAME);
3013 v2s16 p2d(blockpos.X, blockpos.Z);
3016 dbase->loadBlock(blockpos, &ret);
3018 loadBlock(&ret, blockpos, createSector(p2d), false);
3019 return getBlockNoCreateNoEx(blockpos);
3021 // Not found in database, try the files
3023 // The directory layout we're going to load from.
3024 // 1 - original sectors/xxxxzzzz/
3025 // 2 - new sectors2/xxx/zzz/
3026 // If we load from anything but the latest structure, we will
3027 // immediately save to the new one, and remove the old.
3029 std::string sectordir1 = getSectorDir(p2d, 1);
3030 std::string sectordir;
3031 if(fs::PathExists(sectordir1))
3033 sectordir = sectordir1;
3038 sectordir = getSectorDir(p2d, 2);
3042 Make sure sector is loaded
3045 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3049 sector = loadSectorMeta(sectordir, loadlayout != 2);
3051 catch(InvalidFilenameException &e)
3055 catch(FileNotGoodException &e)
3059 catch(std::exception &e)
3066 Make sure file exists
3069 std::string blockfilename = getBlockFilename(blockpos);
3070 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3074 Load block and save it to the database
3076 loadBlock(sectordir, blockfilename, sector, true);
3077 return getBlockNoCreateNoEx(blockpos);
3080 bool ServerMap::deleteBlock(v3s16 blockpos)
3082 if (!dbase->deleteBlock(blockpos))
3085 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3087 v2s16 p2d(blockpos.X, blockpos.Z);
3088 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3091 sector->deleteBlock(block);
3097 void ServerMap::PrintInfo(std::ostream &out)
3102 MMVManip::MMVManip(Map *map):
3105 m_create_area(false),
3110 MMVManip::~MMVManip()
3114 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3115 bool load_if_inexistent)
3117 TimeTaker timer1("initialEmerge", &emerge_time);
3119 // Units of these are MapBlocks
3120 v3s16 p_min = blockpos_min;
3121 v3s16 p_max = blockpos_max;
3123 VoxelArea block_area_nodes
3124 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3126 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3129 infostream<<"initialEmerge: area: ";
3130 block_area_nodes.print(infostream);
3131 infostream<<" ("<<size_MB<<"MB)";
3132 infostream<<std::endl;
3135 addArea(block_area_nodes);
3137 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3138 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3139 for(s32 x=p_min.X; x<=p_max.X; x++)
3144 std::map<v3s16, u8>::iterator n;
3145 n = m_loaded_blocks.find(p);
3146 if(n != m_loaded_blocks.end())
3149 bool block_data_inexistent = false;
3152 TimeTaker timer1("emerge load", &emerge_load_time);
3154 block = m_map->getBlockNoCreate(p);
3155 if(block->isDummy())
3156 block_data_inexistent = true;
3158 block->copyTo(*this);
3160 catch(InvalidPositionException &e)
3162 block_data_inexistent = true;
3165 if(block_data_inexistent)
3168 if (load_if_inexistent) {
3169 ServerMap *svrmap = (ServerMap *)m_map;
3170 block = svrmap->emergeBlock(p, false);
3172 block = svrmap->createBlock(p);
3173 block->copyTo(*this);
3175 flags |= VMANIP_BLOCK_DATA_INEXIST;
3178 Mark area inexistent
3180 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3181 // Fill with VOXELFLAG_NO_DATA
3182 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3183 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3185 s32 i = m_area.index(a.MinEdge.X,y,z);
3186 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3190 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3192 // Mark that block was loaded as blank
3193 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3196 m_loaded_blocks[p] = flags;
3202 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3203 bool overwrite_generated)
3205 if(m_area.getExtent() == v3s16(0,0,0))
3209 Copy data of all blocks
3211 for(std::map<v3s16, u8>::iterator
3212 i = m_loaded_blocks.begin();
3213 i != m_loaded_blocks.end(); ++i)
3216 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3217 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3218 if ((existed == false) || (block == NULL) ||
3219 (overwrite_generated == false && block->isGenerated() == true))
3222 block->copyFrom(*this);
3225 (*modified_blocks)[p] = block;