3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
25 #include "voxelalgorithms.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/mathconstants.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
44 #include "database-dummy.h"
45 #include "database-sqlite3.h"
49 #include "database-leveldb.h"
52 #include "database-redis.h"
55 #include "database-postgresql.h"
58 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
65 Map::Map(std::ostream &dout, IGameDef *gamedef):
69 m_transforming_liquid_loop_count_multiplier(1.0f),
70 m_unprocessed_count(0),
71 m_inc_trending_up_start_time(0),
72 m_queue_size_timer_started(false)
81 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
82 i != m_sectors.end(); ++i)
88 void Map::addEventReceiver(MapEventReceiver *event_receiver)
90 m_event_receivers.insert(event_receiver);
93 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
95 m_event_receivers.erase(event_receiver);
98 void Map::dispatchEvent(MapEditEvent *event)
100 for(std::set<MapEventReceiver*>::iterator
101 i = m_event_receivers.begin();
102 i != m_event_receivers.end(); ++i)
104 (*i)->onMapEditEvent(event);
108 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
110 if(m_sector_cache != NULL && p == m_sector_cache_p){
111 MapSector * sector = m_sector_cache;
115 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
117 if(n == m_sectors.end())
120 MapSector *sector = n->second;
122 // Cache the last result
123 m_sector_cache_p = p;
124 m_sector_cache = sector;
129 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
131 return getSectorNoGenerateNoExNoLock(p);
134 MapSector * Map::getSectorNoGenerate(v2s16 p)
136 MapSector *sector = getSectorNoGenerateNoEx(p);
138 throw InvalidPositionException();
143 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
145 v2s16 p2d(p3d.X, p3d.Z);
146 MapSector * sector = getSectorNoGenerateNoEx(p2d);
149 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
153 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
155 MapBlock *block = getBlockNoCreateNoEx(p3d);
157 throw InvalidPositionException();
161 bool Map::isNodeUnderground(v3s16 p)
163 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock * block = getBlockNoCreate(blockpos);
166 return block->getIsUnderground();
168 catch(InvalidPositionException &e)
174 bool Map::isValidPosition(v3s16 p)
176 v3s16 blockpos = getNodeBlockPos(p);
177 MapBlock *block = getBlockNoCreate(blockpos);
178 return (block != NULL);
181 // Returns a CONTENT_IGNORE node if not found
182 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
184 v3s16 blockpos = getNodeBlockPos(p);
185 MapBlock *block = getBlockNoCreateNoEx(blockpos);
187 if (is_valid_position != NULL)
188 *is_valid_position = false;
189 return MapNode(CONTENT_IGNORE);
192 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
194 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
195 if (is_valid_position != NULL)
196 *is_valid_position = is_valid_p;
202 // throws InvalidPositionException if not found
203 // TODO: Now this is deprecated, getNodeNoEx should be renamed
204 MapNode Map::getNode(v3s16 p)
206 v3s16 blockpos = getNodeBlockPos(p);
207 MapBlock *block = getBlockNoCreateNoEx(blockpos);
209 throw InvalidPositionException();
210 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
211 bool is_valid_position;
212 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
213 if (!is_valid_position)
214 throw InvalidPositionException();
219 // throws InvalidPositionException if not found
220 void Map::setNode(v3s16 p, MapNode & n)
222 v3s16 blockpos = getNodeBlockPos(p);
223 MapBlock *block = getBlockNoCreate(blockpos);
224 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
225 // Never allow placing CONTENT_IGNORE, it fucks up stuff
226 if(n.getContent() == CONTENT_IGNORE){
228 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
229 <<" while trying to replace \""
230 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
231 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
232 debug_stacks_print_to(infostream);
235 block->setNodeNoCheck(relpos, n);
239 Goes recursively through the neighbours of the node.
241 Alters only transparent nodes.
243 If the lighting of the neighbour is lower than the lighting of
244 the node was (before changing it to 0 at the step before), the
245 lighting of the neighbour is set to 0 and then the same stuff
246 repeats for the neighbour.
248 The ending nodes of the routine are stored in light_sources.
249 This is useful when a light is removed. In such case, this
250 routine can be called for the light node and then again for
251 light_sources to re-light the area without the removed light.
253 values of from_nodes are lighting values.
255 void Map::unspreadLight(enum LightBank bank,
256 std::map<v3s16, u8> & from_nodes,
257 std::set<v3s16> & light_sources,
258 std::map<v3s16, MapBlock*> & modified_blocks)
260 INodeDefManager *nodemgr = m_gamedef->ndef();
263 v3s16(0,0,1), // back
265 v3s16(1,0,0), // right
266 v3s16(0,0,-1), // front
267 v3s16(0,-1,0), // bottom
268 v3s16(-1,0,0), // left
271 if(from_nodes.empty())
274 u32 blockchangecount = 0;
276 std::map<v3s16, u8> unlighted_nodes;
279 Initialize block cache
282 MapBlock *block = NULL;
283 // Cache this a bit, too
284 bool block_checked_in_modified = false;
286 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
287 j != from_nodes.end(); ++j)
289 v3s16 pos = j->first;
290 v3s16 blockpos = getNodeBlockPos(pos);
292 // Only fetch a new block if the block position has changed
294 if(block == NULL || blockpos != blockpos_last){
295 block = getBlockNoCreate(blockpos);
296 blockpos_last = blockpos;
298 block_checked_in_modified = false;
302 catch(InvalidPositionException &e)
310 // Calculate relative position in block
311 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
313 // Get node straight from the block
314 //MapNode n = block->getNode(relpos);
316 u8 oldlight = j->second;
318 // Loop through 6 neighbors
319 for(u16 i=0; i<6; i++)
321 // Get the position of the neighbor node
322 v3s16 n2pos = pos + dirs[i];
324 // Get the block where the node is located
325 v3s16 blockpos, relpos;
326 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
328 // Only fetch a new block if the block position has changed
330 if(block == NULL || blockpos != blockpos_last){
331 block = getBlockNoCreate(blockpos);
332 blockpos_last = blockpos;
334 block_checked_in_modified = false;
338 catch(InvalidPositionException &e) {
342 // Get node straight from the block
343 bool is_valid_position;
344 MapNode n2 = block->getNode(relpos, &is_valid_position);
345 if (!is_valid_position)
348 bool changed = false;
350 //TODO: Optimize output by optimizing light_sources?
353 If the neighbor is dimmer than what was specified
354 as oldlight (the light of the previous node)
356 if(n2.getLight(bank, nodemgr) < oldlight)
359 And the neighbor is transparent and it has some light
361 if(nodemgr->get(n2).light_propagates
362 && n2.getLight(bank, nodemgr) != 0)
365 Set light to 0 and add to queue
368 u8 current_light = n2.getLight(bank, nodemgr);
369 n2.setLight(bank, 0, nodemgr);
370 block->setNode(relpos, n2);
372 unlighted_nodes[n2pos] = current_light;
376 Remove from light_sources if it is there
377 NOTE: This doesn't happen nearly at all
379 /*if(light_sources.find(n2pos))
381 infostream<<"Removed from light_sources"<<std::endl;
382 light_sources.remove(n2pos);
387 if(light_sources.find(n2pos) != NULL)
388 light_sources.remove(n2pos);*/
391 light_sources.insert(n2pos);
394 // Add to modified_blocks
395 if(changed == true && block_checked_in_modified == false)
397 // If the block is not found in modified_blocks, add.
398 if(modified_blocks.find(blockpos) == modified_blocks.end())
400 modified_blocks[blockpos] = block;
402 block_checked_in_modified = true;
407 /*infostream<<"unspreadLight(): Changed block "
408 <<blockchangecount<<" times"
409 <<" for "<<from_nodes.size()<<" nodes"
412 if(!unlighted_nodes.empty())
413 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
417 Lights neighbors of from_nodes, collects all them and then
420 void Map::spreadLight(enum LightBank bank,
421 std::set<v3s16> & from_nodes,
422 std::map<v3s16, MapBlock*> & modified_blocks)
424 INodeDefManager *nodemgr = m_gamedef->ndef();
426 const v3s16 dirs[6] = {
427 v3s16(0,0,1), // back
429 v3s16(1,0,0), // right
430 v3s16(0,0,-1), // front
431 v3s16(0,-1,0), // bottom
432 v3s16(-1,0,0), // left
435 if(from_nodes.empty())
438 u32 blockchangecount = 0;
440 std::set<v3s16> lighted_nodes;
443 Initialize block cache
446 MapBlock *block = NULL;
447 // Cache this a bit, too
448 bool block_checked_in_modified = false;
450 for(std::set<v3s16>::iterator j = from_nodes.begin();
451 j != from_nodes.end(); ++j)
454 v3s16 blockpos, relpos;
456 getNodeBlockPosWithOffset(pos, blockpos, relpos);
458 // Only fetch a new block if the block position has changed
460 if(block == NULL || blockpos != blockpos_last){
461 block = getBlockNoCreate(blockpos);
462 blockpos_last = blockpos;
464 block_checked_in_modified = false;
468 catch(InvalidPositionException &e) {
475 // Get node straight from the block
476 bool is_valid_position;
477 MapNode n = block->getNode(relpos, &is_valid_position);
479 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
480 u8 newlight = diminish_light(oldlight);
482 // Loop through 6 neighbors
483 for(u16 i=0; i<6; i++){
484 // Get the position of the neighbor node
485 v3s16 n2pos = pos + dirs[i];
487 // Get the block where the node is located
488 v3s16 blockpos, relpos;
489 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
491 // Only fetch a new block if the block position has changed
493 if(block == NULL || blockpos != blockpos_last){
494 block = getBlockNoCreate(blockpos);
495 blockpos_last = blockpos;
497 block_checked_in_modified = false;
501 catch(InvalidPositionException &e) {
505 // Get node straight from the block
506 MapNode n2 = block->getNode(relpos, &is_valid_position);
507 if (!is_valid_position)
510 bool changed = false;
512 If the neighbor is brighter than the current node,
513 add to list (it will light up this node on its turn)
515 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
517 lighted_nodes.insert(n2pos);
521 If the neighbor is dimmer than how much light this node
522 would spread on it, add to list
524 if(n2.getLight(bank, nodemgr) < newlight)
526 if(nodemgr->get(n2).light_propagates)
528 n2.setLight(bank, newlight, nodemgr);
529 block->setNode(relpos, n2);
530 lighted_nodes.insert(n2pos);
535 // Add to modified_blocks
536 if(changed == true && block_checked_in_modified == false)
538 // If the block is not found in modified_blocks, add.
539 if(modified_blocks.find(blockpos) == modified_blocks.end())
541 modified_blocks[blockpos] = block;
543 block_checked_in_modified = true;
548 /*infostream<<"spreadLight(): Changed block "
549 <<blockchangecount<<" times"
550 <<" for "<<from_nodes.size()<<" nodes"
553 if(!lighted_nodes.empty())
554 spreadLight(bank, lighted_nodes, modified_blocks);
557 void Map::updateLighting(enum LightBank bank,
558 std::map<v3s16, MapBlock*> & a_blocks,
559 std::map<v3s16, MapBlock*> & modified_blocks)
561 INodeDefManager *nodemgr = m_gamedef->ndef();
563 /*m_dout<<"Map::updateLighting(): "
564 <<a_blocks.size()<<" blocks."<<std::endl;*/
566 //TimeTaker timer("updateLighting");
570 //u32 count_was = modified_blocks.size();
572 //std::map<v3s16, MapBlock*> blocks_to_update;
574 std::set<v3s16> light_sources;
576 std::map<v3s16, u8> unlight_from;
578 int num_bottom_invalid = 0;
581 //TimeTaker t("first stuff");
583 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
584 i != a_blocks.end(); ++i)
586 MapBlock *block = i->second;
590 // Don't bother with dummy blocks.
594 v3s16 pos = block->getPos();
595 v3s16 posnodes = block->getPosRelative();
596 modified_blocks[pos] = block;
597 //blocks_to_update[pos] = block;
600 Clear all light from block
602 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
603 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
604 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
607 bool is_valid_position;
608 MapNode n = block->getNode(p, &is_valid_position);
609 if (!is_valid_position) {
610 /* This would happen when dealing with a
613 infostream<<"updateLighting(): InvalidPositionException"
617 u8 oldlight = n.getLight(bank, nodemgr);
618 n.setLight(bank, 0, nodemgr);
619 block->setNode(p, n);
621 // If node sources light, add to list
622 u8 source = nodemgr->get(n).light_source;
624 light_sources.insert(p + posnodes);
626 // Collect borders for unlighting
627 if((x==0 || x == MAP_BLOCKSIZE-1
628 || y==0 || y == MAP_BLOCKSIZE-1
629 || z==0 || z == MAP_BLOCKSIZE-1)
632 v3s16 p_map = p + posnodes;
633 unlight_from[p_map] = oldlight;
639 if(bank == LIGHTBANK_DAY)
641 bool bottom_valid = block->propagateSunlight(light_sources);
644 num_bottom_invalid++;
646 // If bottom is valid, we're done.
650 else if(bank == LIGHTBANK_NIGHT)
652 // For night lighting, sunlight is not propagated
657 assert("Invalid lighting bank" == NULL);
660 /*infostream<<"Bottom for sunlight-propagated block ("
661 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
664 // Bottom sunlight is not valid; get the block and loop to it
668 block = getBlockNoCreate(pos);
670 catch(InvalidPositionException &e)
672 FATAL_ERROR("Invalid position");
681 Enable this to disable proper lighting for speeding up map
682 generation for testing or whatever
685 //if(g_settings->get(""))
687 core::map<v3s16, MapBlock*>::Iterator i;
688 i = blocks_to_update.getIterator();
689 for(; i.atEnd() == false; i++)
691 MapBlock *block = i.getNode()->getValue();
692 v3s16 p = block->getPos();
693 block->setLightingExpired(false);
701 //TimeTaker timer("unspreadLight");
702 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
707 u32 diff = modified_blocks.size() - count_was;
708 count_was = modified_blocks.size();
709 infostream<<"unspreadLight modified "<<diff<<std::endl;
713 //TimeTaker timer("spreadLight");
714 spreadLight(bank, light_sources, modified_blocks);
719 u32 diff = modified_blocks.size() - count_was;
720 count_was = modified_blocks.size();
721 infostream<<"spreadLight modified "<<diff<<std::endl;
727 //MapVoxelManipulator vmanip(this);
729 // Make a manual voxel manipulator and load all the blocks
730 // that touch the requested blocks
731 ManualMapVoxelManipulator vmanip(this);
734 //TimeTaker timer("initialEmerge");
736 core::map<v3s16, MapBlock*>::Iterator i;
737 i = blocks_to_update.getIterator();
738 for(; i.atEnd() == false; i++)
740 MapBlock *block = i.getNode()->getValue();
741 v3s16 p = block->getPos();
743 // Add all surrounding blocks
744 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
747 Add all surrounding blocks that have up-to-date lighting
748 NOTE: This doesn't quite do the job (not everything
749 appropriate is lighted)
751 /*for(s16 z=-1; z<=1; z++)
752 for(s16 y=-1; y<=1; y++)
753 for(s16 x=-1; x<=1; x++)
755 v3s16 p2 = p + v3s16(x,y,z);
756 MapBlock *block = getBlockNoCreateNoEx(p2);
761 if(block->getLightingExpired())
763 vmanip.initialEmerge(p2, p2);
766 // Lighting of block will be updated completely
767 block->setLightingExpired(false);
772 //TimeTaker timer("unSpreadLight");
773 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
776 //TimeTaker timer("spreadLight");
777 vmanip.spreadLight(bank, light_sources, nodemgr);
780 //TimeTaker timer("blitBack");
781 vmanip.blitBack(modified_blocks);
783 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
788 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
791 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
792 std::map<v3s16, MapBlock*> & modified_blocks)
794 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
795 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
798 Update information about whether day and night light differ
800 for(std::map<v3s16, MapBlock*>::iterator
801 i = modified_blocks.begin();
802 i != modified_blocks.end(); ++i)
804 MapBlock *block = i->second;
805 block->expireDayNightDiff();
809 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
810 std::map<v3s16, MapBlock*> &modified_blocks,
811 bool remove_metadata)
813 INodeDefManager *ndef = m_gamedef->ndef();
815 // Collect old node for rollback
816 RollbackNode rollback_oldnode(this, p, m_gamedef);
818 // This is needed for updating the lighting
819 MapNode oldnode = getNodeNoEx(p);
821 // Remove node metadata
822 if (remove_metadata) {
823 removeNodeMetadata(p);
826 // Set the node on the map
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 for (u16 i = 0; i < 6; i++) {
1313 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1316 nt = NEIGHBOR_UPPER;
1319 nt = NEIGHBOR_LOWER;
1322 v3s16 npos = p0 + dirs[i];
1323 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1324 const ContentFeatures &cfnb = nodemgr->get(nb.n);
1325 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1327 if (cfnb.floodable) {
1328 airs[num_airs++] = nb;
1329 // if the current node is a water source the neighbor
1330 // should be enqueded for transformation regardless of whether the
1331 // current node changes or not.
1332 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1333 m_transforming_liquid.push_back(npos);
1334 // if the current node happens to be a flowing node, it will start to flow down here.
1335 if (nb.t == NEIGHBOR_LOWER)
1336 flowing_down = true;
1338 neutrals[num_neutrals++] = nb;
1339 // If neutral below is ignore prevent water spreading outwards
1340 if (nb.t == NEIGHBOR_LOWER &&
1341 nb.n.getContent() == CONTENT_IGNORE)
1342 flowing_down = true;
1346 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1347 if (liquid_kind == CONTENT_AIR)
1348 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1349 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1350 neutrals[num_neutrals++] = nb;
1352 // Do not count bottom source, it will screw things up
1354 sources[num_sources++] = nb;
1357 case LIQUID_FLOWING:
1358 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1359 if (liquid_kind == CONTENT_AIR)
1360 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1361 if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1362 neutrals[num_neutrals++] = nb;
1364 flows[num_flows++] = nb;
1365 if (nb.t == NEIGHBOR_LOWER)
1366 flowing_down = true;
1373 decide on the type (and possibly level) of the current node
1375 content_t new_node_content;
1376 s8 new_node_level = -1;
1377 s8 max_node_level = -1;
1379 u8 range = nodemgr->get(liquid_kind).liquid_range;
1380 if (range > LIQUID_LEVEL_MAX + 1)
1381 range = LIQUID_LEVEL_MAX + 1;
1383 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1384 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1385 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1386 // it's perfectly safe to use liquid_kind here to determine the new node content.
1387 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1388 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1389 // liquid_kind is set properly, see above
1390 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1391 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1392 new_node_content = liquid_kind;
1394 new_node_content = floodable_node;
1396 // no surrounding sources, so get the maximum level that can flow into this node
1397 for (u16 i = 0; i < num_flows; i++) {
1398 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1399 switch (flows[i].t) {
1400 case NEIGHBOR_UPPER:
1401 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1402 max_node_level = LIQUID_LEVEL_MAX;
1403 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1404 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1405 } else if (nb_liquid_level > max_node_level) {
1406 max_node_level = nb_liquid_level;
1409 case NEIGHBOR_LOWER:
1411 case NEIGHBOR_SAME_LEVEL:
1412 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1413 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
1414 max_node_level = nb_liquid_level - 1;
1419 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1420 if (viscosity > 1 && max_node_level != liquid_level) {
1421 // amount to gain, limited by viscosity
1422 // must be at least 1 in absolute value
1423 s8 level_inc = max_node_level - liquid_level;
1424 if (level_inc < -viscosity || level_inc > viscosity)
1425 new_node_level = liquid_level + level_inc/viscosity;
1426 else if (level_inc < 0)
1427 new_node_level = liquid_level - 1;
1428 else if (level_inc > 0)
1429 new_node_level = liquid_level + 1;
1430 if (new_node_level != max_node_level)
1431 must_reflow.push_back(p0);
1433 new_node_level = max_node_level;
1436 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1437 new_node_content = liquid_kind;
1439 new_node_content = floodable_node;
1444 check if anything has changed. if not, just continue with the next node.
1446 if (new_node_content == n0.getContent() &&
1447 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1448 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1449 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1455 update the current node
1458 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1459 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1460 // set level to last 3 bits, flowing down bit to 4th bit
1461 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1463 // set the liquid level and flow bit to 0
1464 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1466 n0.setContent(new_node_content);
1468 // Ignore light (because calling voxalgo::update_lighting_nodes)
1469 n0.setLight(LIGHTBANK_DAY, 0, nodemgr);
1470 n0.setLight(LIGHTBANK_NIGHT, 0, nodemgr);
1472 // Find out whether there is a suspect for this action
1473 std::string suspect;
1474 if (m_gamedef->rollback())
1475 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1477 if (m_gamedef->rollback() && !suspect.empty()) {
1479 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1480 // Get old node for rollback
1481 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1485 RollbackNode rollback_newnode(this, p0, m_gamedef);
1486 RollbackAction action;
1487 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1488 m_gamedef->rollback()->reportAction(action);
1494 v3s16 blockpos = getNodeBlockPos(p0);
1495 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1496 if (block != NULL) {
1497 modified_blocks[blockpos] = block;
1498 changed_nodes.push_back(std::pair<v3s16, MapNode>(p0, n00));
1502 enqueue neighbors for update if neccessary
1504 switch (nodemgr->get(n0.getContent()).liquid_type) {
1506 case LIQUID_FLOWING:
1507 // make sure source flows into all neighboring nodes
1508 for (u16 i = 0; i < num_flows; i++)
1509 if (flows[i].t != NEIGHBOR_UPPER)
1510 m_transforming_liquid.push_back(flows[i].p);
1511 for (u16 i = 0; i < num_airs; i++)
1512 if (airs[i].t != NEIGHBOR_UPPER)
1513 m_transforming_liquid.push_back(airs[i].p);
1516 // this flow has turned to air; neighboring flows might need to do the same
1517 for (u16 i = 0; i < num_flows; i++)
1518 m_transforming_liquid.push_back(flows[i].p);
1522 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1524 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1525 m_transforming_liquid.push_back(*iter);
1527 voxalgo::update_lighting_nodes(this, nodemgr, changed_nodes, modified_blocks);
1530 /* ----------------------------------------------------------------------
1531 * Manage the queue so that it does not grow indefinately
1533 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1535 if (time_until_purge == 0)
1536 return; // Feature disabled
1538 time_until_purge *= 1000; // seconds -> milliseconds
1540 u32 curr_time = getTime(PRECISION_MILLI);
1541 u32 prev_unprocessed = m_unprocessed_count;
1542 m_unprocessed_count = m_transforming_liquid.size();
1544 // if unprocessed block count is decreasing or stable
1545 if (m_unprocessed_count <= prev_unprocessed) {
1546 m_queue_size_timer_started = false;
1548 if (!m_queue_size_timer_started)
1549 m_inc_trending_up_start_time = curr_time;
1550 m_queue_size_timer_started = true;
1553 // Account for curr_time overflowing
1554 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1555 m_queue_size_timer_started = false;
1557 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1558 * and the number of unprocessed blocks is still > liquid_loop_max then we
1559 * cannot keep up; dump the oldest blocks from the queue so that the queue
1560 * has liquid_loop_max items in it
1562 if (m_queue_size_timer_started
1563 && curr_time - m_inc_trending_up_start_time > time_until_purge
1564 && m_unprocessed_count > liquid_loop_max) {
1566 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1568 infostream << "transformLiquids(): DUMPING " << dump_qty
1569 << " blocks from the queue" << std::endl;
1572 m_transforming_liquid.pop_front();
1574 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1575 m_unprocessed_count = m_transforming_liquid.size();
1579 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1581 std::vector<v3s16> positions_with_meta;
1583 sortBoxVerticies(p1, p2);
1584 v3s16 bpmin = getNodeBlockPos(p1);
1585 v3s16 bpmax = getNodeBlockPos(p2);
1587 VoxelArea area(p1, p2);
1589 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1590 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1591 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1592 v3s16 blockpos(x, y, z);
1594 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1596 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1597 << PP(blockpos) << std::endl;
1598 block = emergeBlock(blockpos, false);
1601 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1606 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1607 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1608 for (size_t i = 0; i != keys.size(); i++) {
1609 v3s16 p(keys[i] + p_base);
1610 if (!area.contains(p))
1613 positions_with_meta.push_back(p);
1617 return positions_with_meta;
1620 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1622 v3s16 blockpos = getNodeBlockPos(p);
1623 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1624 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1626 infostream<<"Map::getNodeMetadata(): Need to emerge "
1627 <<PP(blockpos)<<std::endl;
1628 block = emergeBlock(blockpos, false);
1631 warningstream<<"Map::getNodeMetadata(): Block not found"
1635 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1639 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1641 v3s16 blockpos = getNodeBlockPos(p);
1642 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1643 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1645 infostream<<"Map::setNodeMetadata(): Need to emerge "
1646 <<PP(blockpos)<<std::endl;
1647 block = emergeBlock(blockpos, false);
1650 warningstream<<"Map::setNodeMetadata(): Block not found"
1654 block->m_node_metadata.set(p_rel, meta);
1658 void Map::removeNodeMetadata(v3s16 p)
1660 v3s16 blockpos = getNodeBlockPos(p);
1661 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1662 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1665 warningstream<<"Map::removeNodeMetadata(): Block not found"
1669 block->m_node_metadata.remove(p_rel);
1672 NodeTimer Map::getNodeTimer(v3s16 p)
1674 v3s16 blockpos = getNodeBlockPos(p);
1675 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1676 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1678 infostream<<"Map::getNodeTimer(): Need to emerge "
1679 <<PP(blockpos)<<std::endl;
1680 block = emergeBlock(blockpos, false);
1683 warningstream<<"Map::getNodeTimer(): Block not found"
1687 NodeTimer t = block->m_node_timers.get(p_rel);
1688 NodeTimer nt(t.timeout, t.elapsed, p);
1692 void Map::setNodeTimer(const NodeTimer &t)
1694 v3s16 p = t.position;
1695 v3s16 blockpos = getNodeBlockPos(p);
1696 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1697 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1699 infostream<<"Map::setNodeTimer(): Need to emerge "
1700 <<PP(blockpos)<<std::endl;
1701 block = emergeBlock(blockpos, false);
1704 warningstream<<"Map::setNodeTimer(): Block not found"
1708 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1709 block->m_node_timers.set(nt);
1712 void Map::removeNodeTimer(v3s16 p)
1714 v3s16 blockpos = getNodeBlockPos(p);
1715 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1716 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1719 warningstream<<"Map::removeNodeTimer(): Block not found"
1723 block->m_node_timers.remove(p_rel);
1729 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1730 Map(dout_server, gamedef),
1731 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1733 m_map_metadata_changed(true)
1735 verbosestream<<FUNCTION_NAME<<std::endl;
1737 // Tell the EmergeManager about our MapSettingsManager
1738 emerge->map_settings_mgr = &settings_mgr;
1741 Try to load map; if not found, create a new one.
1744 // Determine which database backend to use
1745 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1747 bool succeeded = conf.readConfigFile(conf_path.c_str());
1748 if (!succeeded || !conf.exists("backend")) {
1749 // fall back to sqlite3
1750 conf.set("backend", "sqlite3");
1752 std::string backend = conf.get("backend");
1753 dbase = createDatabase(backend, savedir, conf);
1755 if (!conf.updateConfigFile(conf_path.c_str()))
1756 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1758 m_savedir = savedir;
1759 m_map_saving_enabled = false;
1763 // If directory exists, check contents and load if possible
1764 if(fs::PathExists(m_savedir))
1766 // If directory is empty, it is safe to save into it.
1767 if(fs::GetDirListing(m_savedir).size() == 0)
1769 infostream<<"ServerMap: Empty save directory is valid."
1771 m_map_saving_enabled = true;
1776 if (settings_mgr.loadMapMeta()) {
1777 infostream << "ServerMap: Metadata loaded from "
1778 << savedir << std::endl;
1780 infostream << "ServerMap: Metadata could not be loaded "
1781 "from " << savedir << ", assuming valid save "
1782 "directory." << std::endl;
1785 m_map_saving_enabled = true;
1786 // Map loaded, not creating new one
1790 // If directory doesn't exist, it is safe to save to it
1792 m_map_saving_enabled = true;
1795 catch(std::exception &e)
1797 warningstream<<"ServerMap: Failed to load map from "<<savedir
1798 <<", exception: "<<e.what()<<std::endl;
1799 infostream<<"Please remove the map or fix it."<<std::endl;
1800 warningstream<<"Map saving will be disabled."<<std::endl;
1803 infostream<<"Initializing new map."<<std::endl;
1805 // Create zero sector
1806 emergeSector(v2s16(0,0));
1808 // Initially write whole map
1809 save(MOD_STATE_CLEAN);
1812 ServerMap::~ServerMap()
1814 verbosestream<<FUNCTION_NAME<<std::endl;
1818 if(m_map_saving_enabled)
1820 // Save only changed parts
1821 save(MOD_STATE_WRITE_AT_UNLOAD);
1822 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
1826 infostream<<"ServerMap: Map not saved"<<std::endl;
1829 catch(std::exception &e)
1831 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1832 <<", exception: "<<e.what()<<std::endl;
1836 Close database if it was opened
1844 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1845 for(; i.atEnd() == false; i++)
1847 MapChunk *chunk = i.getNode()->getValue();
1853 MapgenParams *ServerMap::getMapgenParams()
1855 // getMapgenParams() should only ever be called after Server is initialized
1856 assert(settings_mgr.mapgen_params != NULL);
1857 return settings_mgr.mapgen_params;
1860 u64 ServerMap::getSeed()
1862 return getMapgenParams()->seed;
1865 s16 ServerMap::getWaterLevel()
1867 return getMapgenParams()->water_level;
1870 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1872 s16 csize = getMapgenParams()->chunksize;
1873 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1874 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1876 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1877 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1879 v3s16 extra_borders(1, 1, 1);
1880 v3s16 full_bpmin = bpmin - extra_borders;
1881 v3s16 full_bpmax = bpmax + extra_borders;
1883 // Do nothing if not inside limits (+-1 because of neighbors)
1884 if (blockpos_over_limit(full_bpmin) ||
1885 blockpos_over_limit(full_bpmax))
1888 data->seed = getSeed();
1889 data->blockpos_min = bpmin;
1890 data->blockpos_max = bpmax;
1891 data->blockpos_requested = blockpos;
1892 data->nodedef = m_gamedef->ndef();
1895 Create the whole area of this and the neighboring blocks
1897 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1898 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1899 v2s16 sectorpos(x, z);
1900 // Sector metadata is loaded from disk if not already loaded.
1901 ServerMapSector *sector = createSector(sectorpos);
1902 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1904 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1907 MapBlock *block = emergeBlock(p, false);
1908 if (block == NULL) {
1909 block = createBlock(p);
1911 // Block gets sunlight if this is true.
1912 // Refer to the map generator heuristics.
1913 bool ug = m_emerge->isBlockUnderground(p);
1914 block->setIsUnderground(ug);
1920 Now we have a big empty area.
1922 Make a ManualMapVoxelManipulator that contains this and the
1926 data->vmanip = new MMVManip(this);
1927 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1929 // Note: we may need this again at some point.
1931 // Ensure none of the blocks to be generated were marked as
1932 // containing CONTENT_IGNORE
1933 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1934 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1935 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1936 core::map<v3s16, u8>::Node *n;
1937 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1940 u8 flags = n->getValue();
1941 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1948 // Data is ready now.
1952 void ServerMap::finishBlockMake(BlockMakeData *data,
1953 std::map<v3s16, MapBlock*> *changed_blocks)
1955 v3s16 bpmin = data->blockpos_min;
1956 v3s16 bpmax = data->blockpos_max;
1958 v3s16 extra_borders(1, 1, 1);
1959 v3s16 full_bpmin = bpmin - extra_borders;
1960 v3s16 full_bpmax = bpmax + extra_borders;
1962 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1963 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1966 Set lighting to non-expired state in all of them.
1967 This is cheating, but it is not fast enough if all of them
1968 would actually be updated.
1970 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1971 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
1972 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1973 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
1977 block->setLightingExpired(false);
1981 Blit generated stuff to map
1982 NOTE: blitBackAll adds nearly everything to changed_blocks
1984 data->vmanip->blitBackAll(changed_blocks);
1986 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1987 << changed_blocks->size());
1990 Copy transforming liquid information
1992 while (data->transforming_liquid.size()) {
1993 m_transforming_liquid.push_back(data->transforming_liquid.front());
1994 data->transforming_liquid.pop_front();
1997 for (std::map<v3s16, MapBlock *>::iterator
1998 it = changed_blocks->begin();
1999 it != changed_blocks->end(); ++it) {
2000 MapBlock *block = it->second;
2004 Update day/night difference cache of the MapBlocks
2006 block->expireDayNightDiff();
2008 Set block as modified
2010 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2011 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2015 Set central blocks as generated
2017 for (s16 x = bpmin.X; x <= bpmax.X; x++)
2018 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2019 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2020 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2024 block->setGenerated(true);
2028 Save changed parts of map
2029 NOTE: Will be saved later.
2031 //save(MOD_STATE_WRITE_AT_UNLOAD);
2034 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2036 DSTACKF("%s: p2d=(%d,%d)",
2041 Check if it exists already in memory
2043 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2048 Try to load it from disk (with blocks)
2050 //if(loadSectorFull(p2d) == true)
2053 Try to load metadata from disk
2056 if(loadSectorMeta(p2d) == true)
2058 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2061 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2062 throw InvalidPositionException("");
2068 Do not create over-limit
2070 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2071 g_settings->getU16("map_generation_limit"));
2072 if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2073 || p2d.X > map_gen_limit / MAP_BLOCKSIZE
2074 || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2075 || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
2076 throw InvalidPositionException("createSector(): pos. over limit");
2079 Generate blank sector
2082 sector = new ServerMapSector(this, p2d, m_gamedef);
2084 // Sector position on map in nodes
2085 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2090 m_sectors[p2d] = sector;
2097 This is a quick-hand function for calling makeBlock().
2099 MapBlock * ServerMap::generateBlock(
2101 std::map<v3s16, MapBlock*> &modified_blocks
2104 DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2106 /*infostream<<"generateBlock(): "
2107 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2110 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2112 TimeTaker timer("generateBlock");
2114 //MapBlock *block = original_dummy;
2116 v2s16 p2d(p.X, p.Z);
2117 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2120 Do not generate over-limit
2122 if(blockpos_over_limit(p))
2124 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2125 throw InvalidPositionException("generateBlock(): pos. over limit");
2129 Create block make data
2132 initBlockMake(&data, p);
2138 TimeTaker t("mapgen::make_block()");
2139 mapgen->makeChunk(&data);
2140 //mapgen::make_block(&data);
2142 if(enable_mapgen_debug_info == false)
2143 t.stop(true); // Hide output
2147 Blit data back on map, update lighting, add mobs and whatever this does
2149 finishBlockMake(&data, modified_blocks);
2154 MapBlock *block = getBlockNoCreateNoEx(p);
2162 bool erroneus_content = false;
2163 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2164 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2165 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2168 MapNode n = block->getNode(p);
2169 if(n.getContent() == CONTENT_IGNORE)
2171 infostream<<"CONTENT_IGNORE at "
2172 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2174 erroneus_content = true;
2178 if(erroneus_content)
2187 Generate a completely empty block
2191 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2192 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2194 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2197 n.setContent(CONTENT_AIR);
2198 block->setNode(v3s16(x0,y0,z0), n);
2204 if(enable_mapgen_debug_info == false)
2205 timer.stop(true); // Hide output
2211 MapBlock * ServerMap::createBlock(v3s16 p)
2213 DSTACKF("%s: p=(%d,%d,%d)",
2214 FUNCTION_NAME, p.X, p.Y, p.Z);
2217 Do not create over-limit
2219 if (blockpos_over_limit(p))
2220 throw InvalidPositionException("createBlock(): pos. over limit");
2222 v2s16 p2d(p.X, p.Z);
2225 This will create or load a sector if not found in memory.
2226 If block exists on disk, it will be loaded.
2228 NOTE: On old save formats, this will be slow, as it generates
2229 lighting on blocks for them.
2231 ServerMapSector *sector;
2233 sector = (ServerMapSector*)createSector(p2d);
2234 assert(sector->getId() == MAPSECTOR_SERVER);
2236 catch(InvalidPositionException &e)
2238 infostream<<"createBlock: createSector() failed"<<std::endl;
2242 NOTE: This should not be done, or at least the exception
2243 should not be passed on as std::exception, because it
2244 won't be catched at all.
2246 /*catch(std::exception &e)
2248 infostream<<"createBlock: createSector() failed: "
2249 <<e.what()<<std::endl;
2254 Try to get a block from the sector
2257 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2260 if(block->isDummy())
2265 block = sector->createBlankBlock(block_y);
2270 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2272 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2274 p.X, p.Y, p.Z, create_blank);
2277 MapBlock *block = getBlockNoCreateNoEx(p);
2278 if(block && block->isDummy() == false)
2283 MapBlock *block = loadBlock(p);
2289 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2290 MapBlock *block = sector->createBlankBlock(p.Y);
2298 std::map<v3s16, MapBlock*> modified_blocks;
2299 MapBlock *block = generateBlock(p, modified_blocks);
2303 event.type = MEET_OTHER;
2306 // Copy modified_blocks to event
2307 for(std::map<v3s16, MapBlock*>::iterator
2308 i = modified_blocks.begin();
2309 i != modified_blocks.end(); ++i)
2311 event.modified_blocks.insert(i->first);
2315 dispatchEvent(&event);
2325 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2327 MapBlock *block = getBlockNoCreateNoEx(p3d);
2329 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2334 void ServerMap::prepareBlock(MapBlock *block) {
2337 // N.B. This requires no synchronization, since data will not be modified unless
2338 // the VoxelManipulator being updated belongs to the same thread.
2339 void ServerMap::updateVManip(v3s16 pos)
2341 Mapgen *mg = m_emerge->getCurrentMapgen();
2345 MMVManip *vm = mg->vm;
2349 if (!vm->m_area.contains(pos))
2352 s32 idx = vm->m_area.index(pos);
2353 vm->m_data[idx] = getNodeNoEx(pos);
2354 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2356 vm->m_is_dirty = true;
2359 s16 ServerMap::findGroundLevel(v2s16 p2d)
2363 Uh, just do something random...
2365 // Find existing map from top to down
2368 v3s16 p(p2d.X, max, p2d.Y);
2369 for(; p.Y>min; p.Y--)
2371 MapNode n = getNodeNoEx(p);
2372 if(n.getContent() != CONTENT_IGNORE)
2377 // If this node is not air, go to plan b
2378 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2380 // Search existing walkable and return it
2381 for(; p.Y>min; p.Y--)
2383 MapNode n = getNodeNoEx(p);
2384 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2393 Determine from map generator noise functions
2396 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2399 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2400 //return (s16)level;
2403 bool ServerMap::loadFromFolders() {
2404 if (!dbase->initialized() &&
2405 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2410 void ServerMap::createDirs(std::string path)
2412 if(fs::CreateAllDirs(path) == false)
2414 m_dout<<"ServerMap: Failed to create directory "
2415 <<"\""<<path<<"\""<<std::endl;
2416 throw BaseException("ServerMap failed to create directory");
2420 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2426 snprintf(cc, 9, "%.4x%.4x",
2427 (unsigned int) pos.X & 0xffff,
2428 (unsigned int) pos.Y & 0xffff);
2430 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2432 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2433 (unsigned int) pos.X & 0xfff,
2434 (unsigned int) pos.Y & 0xfff);
2436 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2443 v2s16 ServerMap::getSectorPos(std::string dirname)
2445 unsigned int x = 0, y = 0;
2447 std::string component;
2448 fs::RemoveLastPathComponent(dirname, &component, 1);
2449 if(component.size() == 8)
2452 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2454 else if(component.size() == 3)
2457 fs::RemoveLastPathComponent(dirname, &component, 2);
2458 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2459 // Sign-extend the 12 bit values up to 16 bits...
2460 if(x & 0x800) x |= 0xF000;
2461 if(y & 0x800) y |= 0xF000;
2468 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2469 v2s16 pos((s16)x, (s16)y);
2473 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2475 v2s16 p2d = getSectorPos(sectordir);
2477 if(blockfile.size() != 4){
2478 throw InvalidFilenameException("Invalid block filename");
2481 int r = sscanf(blockfile.c_str(), "%4x", &y);
2483 throw InvalidFilenameException("Invalid block filename");
2484 return v3s16(p2d.X, y, p2d.Y);
2487 std::string ServerMap::getBlockFilename(v3s16 p)
2490 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2494 void ServerMap::save(ModifiedState save_level)
2496 DSTACK(FUNCTION_NAME);
2497 if(m_map_saving_enabled == false) {
2498 warningstream<<"Not saving map, saving disabled."<<std::endl;
2502 if(save_level == MOD_STATE_CLEAN)
2503 infostream<<"ServerMap: Saving whole map, this can take time."
2506 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2507 if (settings_mgr.saveMapMeta())
2508 m_map_metadata_changed = false;
2511 // Profile modified reasons
2512 Profiler modprofiler;
2514 u32 sector_meta_count = 0;
2515 u32 block_count = 0;
2516 u32 block_count_all = 0; // Number of blocks in memory
2518 // Don't do anything with sqlite unless something is really saved
2519 bool save_started = false;
2521 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2522 i != m_sectors.end(); ++i) {
2523 ServerMapSector *sector = (ServerMapSector*)i->second;
2524 assert(sector->getId() == MAPSECTOR_SERVER);
2526 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2527 saveSectorMeta(sector);
2528 sector_meta_count++;
2531 MapBlockVect blocks;
2532 sector->getBlocks(blocks);
2534 for(MapBlockVect::iterator j = blocks.begin();
2535 j != blocks.end(); ++j) {
2536 MapBlock *block = *j;
2540 if(block->getModified() >= (u32)save_level) {
2544 save_started = true;
2547 modprofiler.add(block->getModifiedReasonString(), 1);
2552 /*infostream<<"ServerMap: Written block ("
2553 <<block->getPos().X<<","
2554 <<block->getPos().Y<<","
2555 <<block->getPos().Z<<")"
2565 Only print if something happened or saved whole map
2567 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2568 || block_count != 0) {
2569 infostream<<"ServerMap: Written: "
2570 <<sector_meta_count<<" sector metadata files, "
2571 <<block_count<<" block files"
2572 <<", "<<block_count_all<<" blocks in memory."
2574 PrintInfo(infostream); // ServerMap/ClientMap:
2575 infostream<<"Blocks modified by: "<<std::endl;
2576 modprofiler.print(infostream);
2580 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2582 if (loadFromFolders()) {
2583 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2584 << "all blocks that are stored in flat files." << std::endl;
2586 dbase->listAllLoadableBlocks(dst);
2589 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2591 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2592 si != m_sectors.end(); ++si)
2594 MapSector *sector = si->second;
2596 MapBlockVect blocks;
2597 sector->getBlocks(blocks);
2599 for(MapBlockVect::iterator i = blocks.begin();
2600 i != blocks.end(); ++i) {
2601 v3s16 p = (*i)->getPos();
2607 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2609 DSTACK(FUNCTION_NAME);
2610 // Format used for writing
2611 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2613 v2s16 pos = sector->getPos();
2614 std::string dir = getSectorDir(pos);
2617 std::string fullpath = dir + DIR_DELIM + "meta";
2618 std::ostringstream ss(std::ios_base::binary);
2620 sector->serialize(ss, version);
2622 if(!fs::safeWriteToFile(fullpath, ss.str()))
2623 throw FileNotGoodException("Cannot write sector metafile");
2625 sector->differs_from_disk = false;
2628 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2630 DSTACK(FUNCTION_NAME);
2632 v2s16 p2d = getSectorPos(sectordir);
2634 ServerMapSector *sector = NULL;
2636 std::string fullpath = sectordir + DIR_DELIM + "meta";
2637 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2638 if(is.good() == false)
2640 // If the directory exists anyway, it probably is in some old
2641 // format. Just go ahead and create the sector.
2642 if(fs::PathExists(sectordir))
2644 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
2645 <<fullpath<<" doesn't exist but directory does."
2646 <<" Continuing with a sector with no metadata."
2648 sector = new ServerMapSector(this, p2d, m_gamedef);
2649 m_sectors[p2d] = sector;
2653 throw FileNotGoodException("Cannot open sector metafile");
2658 sector = ServerMapSector::deSerialize
2659 (is, this, p2d, m_sectors, m_gamedef);
2661 saveSectorMeta(sector);
2664 sector->differs_from_disk = false;
2669 bool ServerMap::loadSectorMeta(v2s16 p2d)
2671 DSTACK(FUNCTION_NAME);
2673 // The directory layout we're going to load from.
2674 // 1 - original sectors/xxxxzzzz/
2675 // 2 - new sectors2/xxx/zzz/
2676 // If we load from anything but the latest structure, we will
2677 // immediately save to the new one, and remove the old.
2679 std::string sectordir1 = getSectorDir(p2d, 1);
2680 std::string sectordir;
2681 if(fs::PathExists(sectordir1))
2683 sectordir = sectordir1;
2688 sectordir = getSectorDir(p2d, 2);
2692 loadSectorMeta(sectordir, loadlayout != 2);
2694 catch(InvalidFilenameException &e)
2698 catch(FileNotGoodException &e)
2702 catch(std::exception &e)
2711 bool ServerMap::loadSectorFull(v2s16 p2d)
2713 DSTACK(FUNCTION_NAME);
2715 MapSector *sector = NULL;
2717 // The directory layout we're going to load from.
2718 // 1 - original sectors/xxxxzzzz/
2719 // 2 - new sectors2/xxx/zzz/
2720 // If we load from anything but the latest structure, we will
2721 // immediately save to the new one, and remove the old.
2723 std::string sectordir1 = getSectorDir(p2d, 1);
2724 std::string sectordir;
2725 if(fs::PathExists(sectordir1))
2727 sectordir = sectordir1;
2732 sectordir = getSectorDir(p2d, 2);
2736 sector = loadSectorMeta(sectordir, loadlayout != 2);
2738 catch(InvalidFilenameException &e)
2742 catch(FileNotGoodException &e)
2746 catch(std::exception &e)
2754 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2756 std::vector<fs::DirListNode>::iterator i2;
2757 for(i2=list2.begin(); i2!=list2.end(); i2++)
2763 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2765 catch(InvalidFilenameException &e)
2767 // This catches unknown crap in directory
2773 infostream<<"Sector converted to new layout - deleting "<<
2774 sectordir1<<std::endl;
2775 fs::RecursiveDelete(sectordir1);
2782 Database *ServerMap::createDatabase(
2783 const std::string &name,
2784 const std::string &savedir,
2787 if (name == "sqlite3")
2788 return new Database_SQLite3(savedir);
2789 if (name == "dummy")
2790 return new Database_Dummy();
2792 else if (name == "leveldb")
2793 return new Database_LevelDB(savedir);
2796 else if (name == "redis")
2797 return new Database_Redis(conf);
2800 else if (name == "postgresql")
2801 return new Database_PostgreSQL(conf);
2804 throw BaseException(std::string("Database backend ") + name + " not supported.");
2807 void ServerMap::beginSave()
2812 void ServerMap::endSave()
2817 bool ServerMap::saveBlock(MapBlock *block)
2819 return saveBlock(block, dbase);
2822 bool ServerMap::saveBlock(MapBlock *block, Database *db)
2824 v3s16 p3d = block->getPos();
2826 // Dummy blocks are not written
2827 if (block->isDummy()) {
2828 warningstream << "saveBlock: Not writing dummy block "
2829 << PP(p3d) << std::endl;
2833 // Format used for writing
2834 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2837 [0] u8 serialization version
2840 std::ostringstream o(std::ios_base::binary);
2841 o.write((char*) &version, 1);
2842 block->serialize(o, version, true);
2844 std::string data = o.str();
2845 bool ret = db->saveBlock(p3d, data);
2847 // We just wrote it to the disk so clear modified flag
2848 block->resetModified();
2853 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
2854 MapSector *sector, bool save_after_load)
2856 DSTACK(FUNCTION_NAME);
2858 std::string fullpath = sectordir + DIR_DELIM + blockfile;
2861 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2862 if(is.good() == false)
2863 throw FileNotGoodException("Cannot open block file");
2865 v3s16 p3d = getBlockPos(sectordir, blockfile);
2866 v2s16 p2d(p3d.X, p3d.Z);
2868 assert(sector->getPos() == p2d);
2870 u8 version = SER_FMT_VER_INVALID;
2871 is.read((char*)&version, 1);
2874 throw SerializationError("ServerMap::loadBlock(): Failed"
2875 " to read MapBlock version");
2877 /*u32 block_size = MapBlock::serializedLength(version);
2878 SharedBuffer<u8> data(block_size);
2879 is.read((char*)*data, block_size);*/
2881 // This will always return a sector because we're the server
2882 //MapSector *sector = emergeSector(p2d);
2884 MapBlock *block = NULL;
2885 bool created_new = false;
2886 block = sector->getBlockNoCreateNoEx(p3d.Y);
2889 block = sector->createBlankBlockNoInsert(p3d.Y);
2894 block->deSerialize(is, version, true);
2896 // If it's a new block, insert it to the map
2898 sector->insertBlock(block);
2901 Save blocks loaded in old format in new format
2904 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
2908 // Should be in database now, so delete the old file
2909 fs::RecursiveDelete(fullpath);
2912 // We just loaded it from the disk, so it's up-to-date.
2913 block->resetModified();
2916 catch(SerializationError &e)
2918 warningstream<<"Invalid block data on disk "
2919 <<"fullpath="<<fullpath
2920 <<" (SerializationError). "
2921 <<"what()="<<e.what()
2923 // Ignoring. A new one will be generated.
2926 // TODO: Backup file; name is in fullpath.
2930 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
2932 DSTACK(FUNCTION_NAME);
2935 std::istringstream is(*blob, std::ios_base::binary);
2937 u8 version = SER_FMT_VER_INVALID;
2938 is.read((char*)&version, 1);
2941 throw SerializationError("ServerMap::loadBlock(): Failed"
2942 " to read MapBlock version");
2944 /*u32 block_size = MapBlock::serializedLength(version);
2945 SharedBuffer<u8> data(block_size);
2946 is.read((char*)*data, block_size);*/
2948 // This will always return a sector because we're the server
2949 //MapSector *sector = emergeSector(p2d);
2951 MapBlock *block = NULL;
2952 bool created_new = false;
2953 block = sector->getBlockNoCreateNoEx(p3d.Y);
2956 block = sector->createBlankBlockNoInsert(p3d.Y);
2961 block->deSerialize(is, version, true);
2963 // If it's a new block, insert it to the map
2965 sector->insertBlock(block);
2968 Save blocks loaded in old format in new format
2971 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
2972 // Only save if asked to; no need to update version
2976 // We just loaded it from, so it's up-to-date.
2977 block->resetModified();
2980 catch(SerializationError &e)
2982 errorstream<<"Invalid block data in database"
2983 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2984 <<" (SerializationError): "<<e.what()<<std::endl;
2986 // TODO: Block should be marked as invalid in memory so that it is
2987 // not touched but the game can run
2989 if(g_settings->getBool("ignore_world_load_errors")){
2990 errorstream<<"Ignoring block load error. Duck and cover! "
2991 <<"(ignore_world_load_errors)"<<std::endl;
2993 throw SerializationError("Invalid block data in database");
2998 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3000 DSTACK(FUNCTION_NAME);
3002 v2s16 p2d(blockpos.X, blockpos.Z);
3005 dbase->loadBlock(blockpos, &ret);
3007 loadBlock(&ret, blockpos, createSector(p2d), false);
3008 return getBlockNoCreateNoEx(blockpos);
3010 // Not found in database, try the files
3012 // The directory layout we're going to load from.
3013 // 1 - original sectors/xxxxzzzz/
3014 // 2 - new sectors2/xxx/zzz/
3015 // If we load from anything but the latest structure, we will
3016 // immediately save to the new one, and remove the old.
3018 std::string sectordir1 = getSectorDir(p2d, 1);
3019 std::string sectordir;
3020 if(fs::PathExists(sectordir1))
3022 sectordir = sectordir1;
3027 sectordir = getSectorDir(p2d, 2);
3031 Make sure sector is loaded
3034 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3038 sector = loadSectorMeta(sectordir, loadlayout != 2);
3040 catch(InvalidFilenameException &e)
3044 catch(FileNotGoodException &e)
3048 catch(std::exception &e)
3055 Make sure file exists
3058 std::string blockfilename = getBlockFilename(blockpos);
3059 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3063 Load block and save it to the database
3065 loadBlock(sectordir, blockfilename, sector, true);
3066 return getBlockNoCreateNoEx(blockpos);
3069 bool ServerMap::deleteBlock(v3s16 blockpos)
3071 if (!dbase->deleteBlock(blockpos))
3074 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3076 v2s16 p2d(blockpos.X, blockpos.Z);
3077 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3080 sector->deleteBlock(block);
3086 void ServerMap::PrintInfo(std::ostream &out)
3091 MMVManip::MMVManip(Map *map):
3094 m_create_area(false),
3099 MMVManip::~MMVManip()
3103 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3104 bool load_if_inexistent)
3106 TimeTaker timer1("initialEmerge", &emerge_time);
3108 // Units of these are MapBlocks
3109 v3s16 p_min = blockpos_min;
3110 v3s16 p_max = blockpos_max;
3112 VoxelArea block_area_nodes
3113 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3115 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3118 infostream<<"initialEmerge: area: ";
3119 block_area_nodes.print(infostream);
3120 infostream<<" ("<<size_MB<<"MB)";
3121 infostream<<std::endl;
3124 addArea(block_area_nodes);
3126 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3127 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3128 for(s32 x=p_min.X; x<=p_max.X; x++)
3133 std::map<v3s16, u8>::iterator n;
3134 n = m_loaded_blocks.find(p);
3135 if(n != m_loaded_blocks.end())
3138 bool block_data_inexistent = false;
3141 TimeTaker timer1("emerge load", &emerge_load_time);
3143 block = m_map->getBlockNoCreate(p);
3144 if(block->isDummy())
3145 block_data_inexistent = true;
3147 block->copyTo(*this);
3149 catch(InvalidPositionException &e)
3151 block_data_inexistent = true;
3154 if(block_data_inexistent)
3157 if (load_if_inexistent) {
3158 ServerMap *svrmap = (ServerMap *)m_map;
3159 block = svrmap->emergeBlock(p, false);
3161 block = svrmap->createBlock(p);
3162 block->copyTo(*this);
3164 flags |= VMANIP_BLOCK_DATA_INEXIST;
3167 Mark area inexistent
3169 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3170 // Fill with VOXELFLAG_NO_DATA
3171 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3172 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3174 s32 i = m_area.index(a.MinEdge.X,y,z);
3175 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3179 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3181 // Mark that block was loaded as blank
3182 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3185 m_loaded_blocks[p] = flags;
3191 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3192 bool overwrite_generated)
3194 if(m_area.getExtent() == v3s16(0,0,0))
3198 Copy data of all blocks
3200 for(std::map<v3s16, u8>::iterator
3201 i = m_loaded_blocks.begin();
3202 i != m_loaded_blocks.end(); ++i)
3205 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3206 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3207 if ((existed == false) || (block == NULL) ||
3208 (overwrite_generated == false && block->isGenerated() == true))
3211 block->copyFrom(*this);
3214 (*modified_blocks)[p] = block;