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/basic_macros.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
38 #include "reflowscan.h"
40 #include "mapgen/mapgen_v6.h"
41 #include "mapgen/mg_biome.h"
44 #include "database/database.h"
45 #include "database/database-dummy.h"
46 #include "database/database-sqlite3.h"
47 #include "script/scripting_server.h"
51 #include "database/database-leveldb.h"
54 #include "database/database-redis.h"
57 #include "database/database-postgresql.h"
65 Map::Map(IGameDef *gamedef):
67 m_nodedef(gamedef->ndef())
76 for (auto §or : m_sectors) {
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 m_event_receivers.erase(event_receiver);
91 void Map::dispatchEvent(const MapEditEvent &event)
93 for (MapEventReceiver *event_receiver : m_event_receivers) {
94 event_receiver->onMapEditEvent(event);
98 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
100 if(m_sector_cache != NULL && p == m_sector_cache_p){
101 MapSector * sector = m_sector_cache;
105 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
107 if (n == m_sectors.end())
110 MapSector *sector = n->second;
112 // Cache the last result
113 m_sector_cache_p = p;
114 m_sector_cache = sector;
119 MapSector * Map::getSectorNoGenerate(v2s16 p)
121 return getSectorNoGenerateNoLock(p);
124 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
136 MapBlock *block = getBlockNoCreateNoEx(p3d);
138 throw InvalidPositionException();
142 bool Map::isValidPosition(v3s16 p)
144 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock *block = getBlockNoCreateNoEx(blockpos);
146 return (block != NULL);
149 // Returns a CONTENT_IGNORE node if not found
150 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
152 v3s16 blockpos = getNodeBlockPos(p);
153 MapBlock *block = getBlockNoCreateNoEx(blockpos);
155 if (is_valid_position != NULL)
156 *is_valid_position = false;
157 return {CONTENT_IGNORE};
160 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
162 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
163 if (is_valid_position != NULL)
164 *is_valid_position = is_valid_p;
168 static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n)
170 // Never allow placing CONTENT_IGNORE, it causes problems
171 if(n.getContent() == CONTENT_IGNORE){
172 const NodeDefManager *nodedef = block->getParent()->getNodeDefManager();
173 v3s16 blockpos = block->getPos();
174 v3s16 p = blockpos * MAP_BLOCKSIZE + relpos;
176 errorstream<<"Not allowing to place CONTENT_IGNORE"
177 <<" while trying to replace \""
178 <<nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
179 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
182 block->setNodeNoCheck(relpos, n);
185 // throws InvalidPositionException if not found
186 void Map::setNode(v3s16 p, MapNode & n)
188 v3s16 blockpos = getNodeBlockPos(p);
189 MapBlock *block = getBlockNoCreate(blockpos);
190 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
191 set_node_in_block(block, relpos, n);
194 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
195 std::map<v3s16, MapBlock*> &modified_blocks,
196 bool remove_metadata)
198 // Collect old node for rollback
199 RollbackNode rollback_oldnode(this, p, m_gamedef);
201 v3s16 blockpos = getNodeBlockPos(p);
202 MapBlock *block = getBlockNoCreate(blockpos);
203 if (block->isDummy())
204 throw InvalidPositionException();
205 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
207 // This is needed for updating the lighting
208 MapNode oldnode = block->getNodeUnsafe(relpos);
210 // Remove node metadata
211 if (remove_metadata) {
212 removeNodeMetadata(p);
215 // Set the node on the map
216 const ContentFeatures &cf = m_nodedef->get(n);
217 const ContentFeatures &oldcf = m_nodedef->get(oldnode);
218 if (cf.lightingEquivalent(oldcf)) {
219 // No light update needed, just copy over the old light.
220 n.setLight(LIGHTBANK_DAY, oldnode.getLightRaw(LIGHTBANK_DAY, oldcf), cf);
221 n.setLight(LIGHTBANK_NIGHT, oldnode.getLightRaw(LIGHTBANK_NIGHT, oldcf), cf);
222 set_node_in_block(block, relpos, n);
224 modified_blocks[blockpos] = block;
226 // Ignore light (because calling voxalgo::update_lighting_nodes)
227 n.setLight(LIGHTBANK_DAY, 0, cf);
228 n.setLight(LIGHTBANK_NIGHT, 0, cf);
229 set_node_in_block(block, relpos, n);
232 std::vector<std::pair<v3s16, MapNode> > oldnodes;
233 oldnodes.emplace_back(p, oldnode);
234 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
236 for (auto &modified_block : modified_blocks) {
237 modified_block.second->expireDayNightDiff();
241 // Report for rollback
242 if(m_gamedef->rollback())
244 RollbackNode rollback_newnode(this, p, m_gamedef);
245 RollbackAction action;
246 action.setSetNode(p, rollback_oldnode, rollback_newnode);
247 m_gamedef->rollback()->reportAction(action);
251 void Map::removeNodeAndUpdate(v3s16 p,
252 std::map<v3s16, MapBlock*> &modified_blocks)
254 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
257 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
260 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
264 bool succeeded = true;
266 std::map<v3s16, MapBlock*> modified_blocks;
267 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
269 // Copy modified_blocks to event
270 for (auto &modified_block : modified_blocks) {
271 event.modified_blocks.insert(modified_block.first);
274 catch(InvalidPositionException &e){
278 dispatchEvent(event);
283 bool Map::removeNodeWithEvent(v3s16 p)
286 event.type = MEET_REMOVENODE;
289 bool succeeded = true;
291 std::map<v3s16, MapBlock*> modified_blocks;
292 removeNodeAndUpdate(p, modified_blocks);
294 // Copy modified_blocks to event
295 for (auto &modified_block : modified_blocks) {
296 event.modified_blocks.insert(modified_block.first);
299 catch(InvalidPositionException &e){
303 dispatchEvent(event);
308 struct TimeOrderedMapBlock {
312 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
317 bool operator<(const TimeOrderedMapBlock &b) const
319 return block->getUsageTimer() < b.block->getUsageTimer();
326 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
327 std::vector<v3s16> *unloaded_blocks)
329 bool save_before_unloading = maySaveBlocks();
331 // Profile modified reasons
332 Profiler modprofiler;
334 std::vector<v2s16> sector_deletion_queue;
335 u32 deleted_blocks_count = 0;
336 u32 saved_blocks_count = 0;
337 u32 block_count_all = 0;
341 // If there is no practical limit, we spare creation of mapblock_queue
342 if (max_loaded_blocks == U32_MAX) {
343 for (auto §or_it : m_sectors) {
344 MapSector *sector = sector_it.second;
346 bool all_blocks_deleted = true;
349 sector->getBlocks(blocks);
351 for (MapBlock *block : blocks) {
352 block->incrementUsageTimer(dtime);
354 if (block->refGet() == 0
355 && block->getUsageTimer() > unload_timeout) {
356 v3s16 p = block->getPos();
359 if (block->getModified() != MOD_STATE_CLEAN
360 && save_before_unloading) {
361 modprofiler.add(block->getModifiedReasonString(), 1);
362 if (!saveBlock(block))
364 saved_blocks_count++;
367 // Delete from memory
368 sector->deleteBlock(block);
371 unloaded_blocks->push_back(p);
373 deleted_blocks_count++;
375 all_blocks_deleted = false;
380 if (all_blocks_deleted) {
381 sector_deletion_queue.push_back(sector_it.first);
385 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
386 for (auto §or_it : m_sectors) {
387 MapSector *sector = sector_it.second;
390 sector->getBlocks(blocks);
392 for (MapBlock *block : blocks) {
393 block->incrementUsageTimer(dtime);
394 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
397 block_count_all = mapblock_queue.size();
398 // Delete old blocks, and blocks over the limit from the memory
399 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
400 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
401 TimeOrderedMapBlock b = mapblock_queue.top();
402 mapblock_queue.pop();
404 MapBlock *block = b.block;
406 if (block->refGet() != 0)
409 v3s16 p = block->getPos();
412 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
413 modprofiler.add(block->getModifiedReasonString(), 1);
414 if (!saveBlock(block))
416 saved_blocks_count++;
419 // Delete from memory
420 b.sect->deleteBlock(block);
423 unloaded_blocks->push_back(p);
425 deleted_blocks_count++;
428 // Delete empty sectors
429 for (auto §or_it : m_sectors) {
430 if (sector_it.second->empty()) {
431 sector_deletion_queue.push_back(sector_it.first);
437 // Finally delete the empty sectors
438 deleteSectors(sector_deletion_queue);
440 if(deleted_blocks_count != 0)
442 PrintInfo(infostream); // ServerMap/ClientMap:
443 infostream<<"Unloaded "<<deleted_blocks_count
444 <<" blocks from memory";
445 if(save_before_unloading)
446 infostream<<", of which "<<saved_blocks_count<<" were written";
447 infostream<<", "<<block_count_all<<" blocks in memory";
448 infostream<<"."<<std::endl;
449 if(saved_blocks_count != 0){
450 PrintInfo(infostream); // ServerMap/ClientMap:
451 infostream<<"Blocks modified by: "<<std::endl;
452 modprofiler.print(infostream);
457 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
459 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
462 void Map::deleteSectors(std::vector<v2s16> §orList)
464 for (v2s16 j : sectorList) {
465 MapSector *sector = m_sectors[j];
466 // If sector is in sector cache, remove it from there
467 if(m_sector_cache == sector)
468 m_sector_cache = NULL;
469 // Remove from map and delete
475 void Map::PrintInfo(std::ostream &out)
480 #define WATER_DROP_BOOST 4
482 const static v3s16 liquid_6dirs[6] = {
483 // order: upper before same level before lower
492 enum NeighborType : u8 {
498 struct NodeNeighbor {
504 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
507 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
514 void ServerMap::transforming_liquid_add(v3s16 p) {
515 m_transforming_liquid.push_back(p);
518 void ServerMap::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
519 ServerEnvironment *env)
522 u32 initial_size = m_transforming_liquid.size();
524 /*if(initial_size != 0)
525 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
527 // list of nodes that due to viscosity have not reached their max level height
528 std::deque<v3s16> must_reflow;
530 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
532 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
533 u32 loop_max = liquid_loop_max;
535 while (m_transforming_liquid.size() != 0)
537 // This should be done here so that it is done when continue is used
538 if (loopcount >= initial_size || loopcount >= loop_max)
543 Get a queued transforming liquid node
545 v3s16 p0 = m_transforming_liquid.front();
546 m_transforming_liquid.pop_front();
548 MapNode n0 = getNode(p0);
551 Collect information about current node
553 s8 liquid_level = -1;
554 // The liquid node which will be placed there if
555 // the liquid flows into this node.
556 content_t liquid_kind = CONTENT_IGNORE;
557 // The node which will be placed there if liquid
558 // can't flow into this node.
559 content_t floodable_node = CONTENT_AIR;
560 const ContentFeatures &cf = m_nodedef->get(n0);
561 LiquidType liquid_type = cf.liquid_type;
562 switch (liquid_type) {
564 liquid_level = LIQUID_LEVEL_SOURCE;
565 liquid_kind = cf.liquid_alternative_flowing_id;
568 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
569 liquid_kind = n0.getContent();
572 // if this node is 'floodable', it *could* be transformed
573 // into a liquid, otherwise, continue with the next node.
576 floodable_node = n0.getContent();
577 liquid_kind = CONTENT_AIR;
582 Collect information about the environment
584 NodeNeighbor sources[6]; // surrounding sources
586 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
588 NodeNeighbor airs[6]; // surrounding air
590 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
591 int num_neutrals = 0;
592 bool flowing_down = false;
593 bool ignored_sources = false;
594 for (u16 i = 0; i < 6; i++) {
595 NeighborType nt = NEIGHBOR_SAME_LEVEL;
606 v3s16 npos = p0 + liquid_6dirs[i];
607 NodeNeighbor nb(getNode(npos), nt, npos);
608 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
609 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
611 if (cfnb.floodable) {
612 airs[num_airs++] = nb;
613 // if the current node is a water source the neighbor
614 // should be enqueded for transformation regardless of whether the
615 // current node changes or not.
616 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
617 m_transforming_liquid.push_back(npos);
618 // if the current node happens to be a flowing node, it will start to flow down here.
619 if (nb.t == NEIGHBOR_LOWER)
622 neutrals[num_neutrals++] = nb;
623 if (nb.n.getContent() == CONTENT_IGNORE) {
624 // If node below is ignore prevent water from
625 // spreading outwards and otherwise prevent from
626 // flowing away as ignore node might be the source
627 if (nb.t == NEIGHBOR_LOWER)
630 ignored_sources = true;
635 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
636 if (liquid_kind == CONTENT_AIR)
637 liquid_kind = cfnb.liquid_alternative_flowing_id;
638 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
639 neutrals[num_neutrals++] = nb;
641 // Do not count bottom source, it will screw things up
642 if(nt != NEIGHBOR_LOWER)
643 sources[num_sources++] = nb;
647 if (nb.t != NEIGHBOR_SAME_LEVEL ||
648 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
649 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
650 // but exclude falling liquids on the same level, they cannot flow here anyway
651 if (liquid_kind == CONTENT_AIR)
652 liquid_kind = cfnb.liquid_alternative_flowing_id;
654 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
655 neutrals[num_neutrals++] = nb;
657 flows[num_flows++] = nb;
658 if (nb.t == NEIGHBOR_LOWER)
666 decide on the type (and possibly level) of the current node
668 content_t new_node_content;
669 s8 new_node_level = -1;
670 s8 max_node_level = -1;
672 u8 range = m_nodedef->get(liquid_kind).liquid_range;
673 if (range > LIQUID_LEVEL_MAX + 1)
674 range = LIQUID_LEVEL_MAX + 1;
676 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
677 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
678 // or the flowing alternative of the first of the surrounding sources (if it's air), so
679 // it's perfectly safe to use liquid_kind here to determine the new node content.
680 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
681 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
682 // liquid_kind is set properly, see above
683 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
684 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
685 new_node_content = liquid_kind;
687 new_node_content = floodable_node;
688 } else if (ignored_sources && liquid_level >= 0) {
689 // Maybe there are neighbouring sources that aren't loaded yet
690 // so prevent flowing away.
691 new_node_level = liquid_level;
692 new_node_content = liquid_kind;
694 // no surrounding sources, so get the maximum level that can flow into this node
695 for (u16 i = 0; i < num_flows; i++) {
696 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
697 switch (flows[i].t) {
699 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
700 max_node_level = LIQUID_LEVEL_MAX;
701 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
702 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
703 } else if (nb_liquid_level > max_node_level) {
704 max_node_level = nb_liquid_level;
709 case NEIGHBOR_SAME_LEVEL:
710 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
711 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
712 max_node_level = nb_liquid_level - 1;
717 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
718 if (viscosity > 1 && max_node_level != liquid_level) {
719 // amount to gain, limited by viscosity
720 // must be at least 1 in absolute value
721 s8 level_inc = max_node_level - liquid_level;
722 if (level_inc < -viscosity || level_inc > viscosity)
723 new_node_level = liquid_level + level_inc/viscosity;
724 else if (level_inc < 0)
725 new_node_level = liquid_level - 1;
726 else if (level_inc > 0)
727 new_node_level = liquid_level + 1;
728 if (new_node_level != max_node_level)
729 must_reflow.push_back(p0);
731 new_node_level = max_node_level;
734 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
735 new_node_content = liquid_kind;
737 new_node_content = floodable_node;
742 check if anything has changed. if not, just continue with the next node.
744 if (new_node_content == n0.getContent() &&
745 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
746 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
747 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
753 update the current node
756 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
757 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
758 // set level to last 3 bits, flowing down bit to 4th bit
759 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
761 // set the liquid level and flow bits to 0
762 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
766 n0.setContent(new_node_content);
768 // on_flood() the node
769 if (floodable_node != CONTENT_AIR) {
770 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
774 // Ignore light (because calling voxalgo::update_lighting_nodes)
775 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
776 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
778 // Find out whether there is a suspect for this action
780 if (m_gamedef->rollback())
781 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
783 if (m_gamedef->rollback() && !suspect.empty()) {
785 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
786 // Get old node for rollback
787 RollbackNode rollback_oldnode(this, p0, m_gamedef);
791 RollbackNode rollback_newnode(this, p0, m_gamedef);
792 RollbackAction action;
793 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
794 m_gamedef->rollback()->reportAction(action);
800 v3s16 blockpos = getNodeBlockPos(p0);
801 MapBlock *block = getBlockNoCreateNoEx(blockpos);
803 modified_blocks[blockpos] = block;
804 changed_nodes.emplace_back(p0, n00);
808 enqueue neighbors for update if neccessary
810 switch (m_nodedef->get(n0.getContent()).liquid_type) {
813 // make sure source flows into all neighboring nodes
814 for (u16 i = 0; i < num_flows; i++)
815 if (flows[i].t != NEIGHBOR_UPPER)
816 m_transforming_liquid.push_back(flows[i].p);
817 for (u16 i = 0; i < num_airs; i++)
818 if (airs[i].t != NEIGHBOR_UPPER)
819 m_transforming_liquid.push_back(airs[i].p);
822 // this flow has turned to air; neighboring flows might need to do the same
823 for (u16 i = 0; i < num_flows; i++)
824 m_transforming_liquid.push_back(flows[i].p);
828 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
830 for (auto &iter : must_reflow)
831 m_transforming_liquid.push_back(iter);
833 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
834 env->getScriptIface()->on_liquid_transformed(changed_nodes);
836 /* ----------------------------------------------------------------------
837 * Manage the queue so that it does not grow indefinately
839 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
841 if (time_until_purge == 0)
842 return; // Feature disabled
844 time_until_purge *= 1000; // seconds -> milliseconds
846 u64 curr_time = porting::getTimeMs();
847 u32 prev_unprocessed = m_unprocessed_count;
848 m_unprocessed_count = m_transforming_liquid.size();
850 // if unprocessed block count is decreasing or stable
851 if (m_unprocessed_count <= prev_unprocessed) {
852 m_queue_size_timer_started = false;
854 if (!m_queue_size_timer_started)
855 m_inc_trending_up_start_time = curr_time;
856 m_queue_size_timer_started = true;
859 // Account for curr_time overflowing
860 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
861 m_queue_size_timer_started = false;
863 /* If the queue has been growing for more than liquid_queue_purge_time seconds
864 * and the number of unprocessed blocks is still > liquid_loop_max then we
865 * cannot keep up; dump the oldest blocks from the queue so that the queue
866 * has liquid_loop_max items in it
868 if (m_queue_size_timer_started
869 && curr_time - m_inc_trending_up_start_time > time_until_purge
870 && m_unprocessed_count > liquid_loop_max) {
872 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
874 infostream << "transformLiquids(): DUMPING " << dump_qty
875 << " blocks from the queue" << std::endl;
878 m_transforming_liquid.pop_front();
880 m_queue_size_timer_started = false; // optimistically assume we can keep up now
881 m_unprocessed_count = m_transforming_liquid.size();
885 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
887 std::vector<v3s16> positions_with_meta;
889 sortBoxVerticies(p1, p2);
890 v3s16 bpmin = getNodeBlockPos(p1);
891 v3s16 bpmax = getNodeBlockPos(p2);
893 VoxelArea area(p1, p2);
895 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
896 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
897 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
898 v3s16 blockpos(x, y, z);
900 MapBlock *block = getBlockNoCreateNoEx(blockpos);
902 verbosestream << "Map::getNodeMetadata(): Need to emerge "
903 << PP(blockpos) << std::endl;
904 block = emergeBlock(blockpos, false);
907 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
912 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
913 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
914 for (size_t i = 0; i != keys.size(); i++) {
915 v3s16 p(keys[i] + p_base);
916 if (!area.contains(p))
919 positions_with_meta.push_back(p);
923 return positions_with_meta;
926 NodeMetadata *Map::getNodeMetadata(v3s16 p)
928 v3s16 blockpos = getNodeBlockPos(p);
929 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
930 MapBlock *block = getBlockNoCreateNoEx(blockpos);
932 infostream<<"Map::getNodeMetadata(): Need to emerge "
933 <<PP(blockpos)<<std::endl;
934 block = emergeBlock(blockpos, false);
937 warningstream<<"Map::getNodeMetadata(): Block not found"
941 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
945 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
947 v3s16 blockpos = getNodeBlockPos(p);
948 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
949 MapBlock *block = getBlockNoCreateNoEx(blockpos);
951 infostream<<"Map::setNodeMetadata(): Need to emerge "
952 <<PP(blockpos)<<std::endl;
953 block = emergeBlock(blockpos, false);
956 warningstream<<"Map::setNodeMetadata(): Block not found"
960 block->m_node_metadata.set(p_rel, meta);
964 void Map::removeNodeMetadata(v3s16 p)
966 v3s16 blockpos = getNodeBlockPos(p);
967 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
968 MapBlock *block = getBlockNoCreateNoEx(blockpos);
971 warningstream<<"Map::removeNodeMetadata(): Block not found"
975 block->m_node_metadata.remove(p_rel);
978 NodeTimer Map::getNodeTimer(v3s16 p)
980 v3s16 blockpos = getNodeBlockPos(p);
981 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
982 MapBlock *block = getBlockNoCreateNoEx(blockpos);
984 infostream<<"Map::getNodeTimer(): Need to emerge "
985 <<PP(blockpos)<<std::endl;
986 block = emergeBlock(blockpos, false);
989 warningstream<<"Map::getNodeTimer(): Block not found"
993 NodeTimer t = block->m_node_timers.get(p_rel);
994 NodeTimer nt(t.timeout, t.elapsed, p);
998 void Map::setNodeTimer(const NodeTimer &t)
1000 v3s16 p = t.position;
1001 v3s16 blockpos = getNodeBlockPos(p);
1002 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1003 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1005 infostream<<"Map::setNodeTimer(): Need to emerge "
1006 <<PP(blockpos)<<std::endl;
1007 block = emergeBlock(blockpos, false);
1010 warningstream<<"Map::setNodeTimer(): Block not found"
1014 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1015 block->m_node_timers.set(nt);
1018 void Map::removeNodeTimer(v3s16 p)
1020 v3s16 blockpos = getNodeBlockPos(p);
1021 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1022 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1025 warningstream<<"Map::removeNodeTimer(): Block not found"
1029 block->m_node_timers.remove(p_rel);
1032 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1033 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1036 This functions determines the node inside the target block that is
1037 closest to the camera position. This increases the occlusion culling
1038 accuracy in straight and diagonal corridors.
1039 The returned position will be occlusion checked first in addition to the
1040 others (8 corners + center).
1041 No position is returned if
1042 - the closest node is a corner, corners are checked anyway.
1043 - the camera is inside the target block, it will never be occluded.
1045 #define CLOSEST_EDGE(pos, bounds, axis) \
1046 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1047 (bounds).MaxEdge.axis
1049 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1050 (pos_camera.X <= block_bounds.MaxEdge.X);
1051 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1052 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1053 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1054 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1056 if (x_inside && y_inside && z_inside)
1057 return false; // Camera inside target mapblock
1060 if (x_inside && y_inside) {
1061 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1062 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1064 } else if (y_inside && z_inside) {
1065 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1066 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1068 } else if (x_inside && z_inside) {
1069 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1070 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1076 check = v3s16(pos_camera.X, 0, 0);
1077 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1078 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1080 } else if (y_inside) {
1081 check = v3s16(0, pos_camera.Y, 0);
1082 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1083 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1085 } else if (z_inside) {
1086 check = v3s16(0, 0, pos_camera.Z);
1087 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1088 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1092 // Closest node would be a corner, none returned
1096 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1097 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1099 v3f direction = intToFloat(pos_target - pos_camera, BS);
1100 float distance = direction.getLength();
1102 // Normalize direction vector
1103 if (distance > 0.0f)
1104 direction /= distance;
1106 v3f pos_origin_f = intToFloat(pos_camera, BS);
1108 bool is_valid_position;
1110 for (; offset < distance + end_offset; offset += step) {
1111 v3f pos_node_f = pos_origin_f + direction * offset;
1112 v3s16 pos_node = floatToInt(pos_node_f, BS);
1114 MapNode node = getNode(pos_node, &is_valid_position);
1116 if (is_valid_position &&
1117 !m_nodedef->get(node).light_propagates) {
1118 // Cannot see through light-blocking nodes --> occluded
1120 if (count >= needed_count)
1128 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1130 // Check occlusion for center and all 8 corners of the mapblock
1131 // Overshoot a little for less flickering
1132 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1133 static const v3s16 dir9[9] = {
1135 v3s16( 1, 1, 1) * bs2,
1136 v3s16( 1, 1, -1) * bs2,
1137 v3s16( 1, -1, 1) * bs2,
1138 v3s16( 1, -1, -1) * bs2,
1139 v3s16(-1, 1, 1) * bs2,
1140 v3s16(-1, 1, -1) * bs2,
1141 v3s16(-1, -1, 1) * bs2,
1142 v3s16(-1, -1, -1) * bs2,
1145 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1147 // Starting step size, value between 1m and sqrt(3)m
1148 float step = BS * 1.2f;
1149 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1150 float stepfac = 1.05f;
1152 float start_offset = BS * 1.0f;
1154 // The occlusion search of 'isOccluded()' must stop short of the target
1155 // point by distance 'end_offset' to not enter the target mapblock.
1156 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1157 // diagonal of a mapblock, because we must consider all view angles.
1158 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1159 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1161 // to reduce the likelihood of falsely occluded blocks
1162 // require at least two solid blocks
1163 // this is a HACK, we should think of a more precise algorithm
1164 u32 needed_count = 2;
1166 // Additional occlusion check, see comments in that function
1168 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1169 // node is always on a side facing the camera, end_offset can be lower
1170 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1171 -1.0f, needed_count))
1175 for (const v3s16 &dir : dir9) {
1176 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1177 start_offset, end_offset, needed_count))
1186 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1187 EmergeManager *emerge, MetricsBackend *mb):
1189 settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
1192 verbosestream<<FUNCTION_NAME<<std::endl;
1194 // Tell the EmergeManager about our MapSettingsManager
1195 emerge->map_settings_mgr = &settings_mgr;
1198 Try to load map; if not found, create a new one.
1201 // Determine which database backend to use
1202 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1204 bool succeeded = conf.readConfigFile(conf_path.c_str());
1205 if (!succeeded || !conf.exists("backend")) {
1206 // fall back to sqlite3
1207 conf.set("backend", "sqlite3");
1209 std::string backend = conf.get("backend");
1210 dbase = createDatabase(backend, savedir, conf);
1211 if (conf.exists("readonly_backend")) {
1212 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1213 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1215 if (!conf.updateConfigFile(conf_path.c_str()))
1216 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1218 m_savedir = savedir;
1219 m_map_saving_enabled = false;
1221 m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1223 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1226 // If directory exists, check contents and load if possible
1227 if (fs::PathExists(m_savedir)) {
1228 // If directory is empty, it is safe to save into it.
1229 if (fs::GetDirListing(m_savedir).empty()) {
1230 infostream<<"ServerMap: Empty save directory is valid."
1232 m_map_saving_enabled = true;
1237 if (settings_mgr.loadMapMeta()) {
1238 infostream << "ServerMap: Metadata loaded from "
1239 << savedir << std::endl;
1241 infostream << "ServerMap: Metadata could not be loaded "
1242 "from " << savedir << ", assuming valid save "
1243 "directory." << std::endl;
1246 m_map_saving_enabled = true;
1247 // Map loaded, not creating new one
1251 // If directory doesn't exist, it is safe to save to it
1253 m_map_saving_enabled = true;
1256 catch(std::exception &e)
1258 warningstream<<"ServerMap: Failed to load map from "<<savedir
1259 <<", exception: "<<e.what()<<std::endl;
1260 infostream<<"Please remove the map or fix it."<<std::endl;
1261 warningstream<<"Map saving will be disabled."<<std::endl;
1265 ServerMap::~ServerMap()
1267 verbosestream<<FUNCTION_NAME<<std::endl;
1271 if (m_map_saving_enabled) {
1272 // Save only changed parts
1273 save(MOD_STATE_WRITE_AT_UNLOAD);
1274 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1276 infostream << "ServerMap: Map not saved" << std::endl;
1279 catch(std::exception &e)
1281 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1282 <<", exception: "<<e.what()<<std::endl;
1286 Close database if it was opened
1292 MapgenParams *ServerMap::getMapgenParams()
1294 // getMapgenParams() should only ever be called after Server is initialized
1295 assert(settings_mgr.mapgen_params != NULL);
1296 return settings_mgr.mapgen_params;
1299 u64 ServerMap::getSeed()
1301 return getMapgenParams()->seed;
1304 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1306 const s16 mapgen_limit_bp = rangelim(
1307 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1309 return p.X < -mapgen_limit_bp ||
1310 p.X > mapgen_limit_bp ||
1311 p.Y < -mapgen_limit_bp ||
1312 p.Y > mapgen_limit_bp ||
1313 p.Z < -mapgen_limit_bp ||
1314 p.Z > mapgen_limit_bp;
1317 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1319 s16 csize = getMapgenParams()->chunksize;
1320 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1321 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1323 if (!m_chunks_in_progress.insert(bpmin).second)
1326 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1327 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1329 v3s16 extra_borders(1, 1, 1);
1330 v3s16 full_bpmin = bpmin - extra_borders;
1331 v3s16 full_bpmax = bpmax + extra_borders;
1333 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1334 if (blockpos_over_mapgen_limit(full_bpmin) ||
1335 blockpos_over_mapgen_limit(full_bpmax))
1338 data->seed = getSeed();
1339 data->blockpos_min = bpmin;
1340 data->blockpos_max = bpmax;
1341 data->nodedef = m_nodedef;
1344 Create the whole area of this and the neighboring blocks
1346 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1347 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1348 v2s16 sectorpos(x, z);
1349 // Sector metadata is loaded from disk if not already loaded.
1350 MapSector *sector = createSector(sectorpos);
1351 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1353 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1356 MapBlock *block = emergeBlock(p, false);
1357 if (block == NULL) {
1358 block = createBlock(p);
1360 // Block gets sunlight if this is true.
1361 // Refer to the map generator heuristics.
1362 bool ug = m_emerge->isBlockUnderground(p);
1363 block->setIsUnderground(ug);
1369 Now we have a big empty area.
1371 Make a ManualMapVoxelManipulator that contains this and the
1375 data->vmanip = new MMVManip(this);
1376 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1378 // Data is ready now.
1382 void ServerMap::finishBlockMake(BlockMakeData *data,
1383 std::map<v3s16, MapBlock*> *changed_blocks)
1385 v3s16 bpmin = data->blockpos_min;
1386 v3s16 bpmax = data->blockpos_max;
1388 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1389 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1392 Blit generated stuff to map
1393 NOTE: blitBackAll adds nearly everything to changed_blocks
1395 data->vmanip->blitBackAll(changed_blocks);
1397 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1398 << changed_blocks->size());
1401 Copy transforming liquid information
1403 while (data->transforming_liquid.size()) {
1404 m_transforming_liquid.push_back(data->transforming_liquid.front());
1405 data->transforming_liquid.pop_front();
1408 for (auto &changed_block : *changed_blocks) {
1409 MapBlock *block = changed_block.second;
1413 Update day/night difference cache of the MapBlocks
1415 block->expireDayNightDiff();
1417 Set block as modified
1419 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1420 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1424 Set central blocks as generated
1426 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1427 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1428 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1429 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1433 block->setGenerated(true);
1437 Save changed parts of map
1438 NOTE: Will be saved later.
1440 //save(MOD_STATE_WRITE_AT_UNLOAD);
1441 m_chunks_in_progress.erase(bpmin);
1444 MapSector *ServerMap::createSector(v2s16 p2d)
1447 Check if it exists already in memory
1449 MapSector *sector = getSectorNoGenerate(p2d);
1454 Do not create over max mapgen limit
1456 if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y)))
1457 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1460 Generate blank sector
1463 sector = new MapSector(this, p2d, m_gamedef);
1468 m_sectors[p2d] = sector;
1473 MapBlock * ServerMap::createBlock(v3s16 p)
1476 Do not create over max mapgen limit
1478 if (blockpos_over_max_limit(p))
1479 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1481 v2s16 p2d(p.X, p.Z);
1484 This will create or load a sector if not found in memory.
1485 If block exists on disk, it will be loaded.
1487 NOTE: On old save formats, this will be slow, as it generates
1488 lighting on blocks for them.
1492 sector = createSector(p2d);
1493 } catch (InvalidPositionException &e) {
1494 infostream<<"createBlock: createSector() failed"<<std::endl;
1499 Try to get a block from the sector
1502 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1504 if(block->isDummy())
1509 block = sector->createBlankBlock(block_y);
1514 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1517 MapBlock *block = getBlockNoCreateNoEx(p);
1518 if (block && !block->isDummy())
1523 MapBlock *block = loadBlock(p);
1529 MapSector *sector = createSector(v2s16(p.X, p.Z));
1530 MapBlock *block = sector->createBlankBlock(p.Y);
1538 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1540 MapBlock *block = getBlockNoCreateNoEx(p3d);
1542 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1547 bool ServerMap::isBlockInQueue(v3s16 pos)
1549 return m_emerge && m_emerge->isBlockInQueue(pos);
1552 void ServerMap::addNodeAndUpdate(v3s16 p, MapNode n,
1553 std::map<v3s16, MapBlock*> &modified_blocks,
1554 bool remove_metadata)
1556 Map::addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1559 Add neighboring liquid nodes and this node to transform queue.
1560 (it's vital for the node itself to get updated last, if it was removed.)
1563 for (const v3s16 &dir : g_7dirs) {
1566 bool is_valid_position;
1567 MapNode n2 = getNode(p2, &is_valid_position);
1568 if(is_valid_position &&
1569 (m_nodedef->get(n2).isLiquid() ||
1570 n2.getContent() == CONTENT_AIR))
1571 m_transforming_liquid.push_back(p2);
1575 // N.B. This requires no synchronization, since data will not be modified unless
1576 // the VoxelManipulator being updated belongs to the same thread.
1577 void ServerMap::updateVManip(v3s16 pos)
1579 Mapgen *mg = m_emerge->getCurrentMapgen();
1583 MMVManip *vm = mg->vm;
1587 if (!vm->m_area.contains(pos))
1590 s32 idx = vm->m_area.index(pos);
1591 vm->m_data[idx] = getNode(pos);
1592 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1594 vm->m_is_dirty = true;
1597 void ServerMap::save(ModifiedState save_level)
1599 if (!m_map_saving_enabled) {
1600 warningstream<<"Not saving map, saving disabled."<<std::endl;
1604 u64 start_time = porting::getTimeNs();
1606 if(save_level == MOD_STATE_CLEAN)
1607 infostream<<"ServerMap: Saving whole map, this can take time."
1610 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1611 if (settings_mgr.saveMapMeta())
1612 m_map_metadata_changed = false;
1615 // Profile modified reasons
1616 Profiler modprofiler;
1618 u32 block_count = 0;
1619 u32 block_count_all = 0; // Number of blocks in memory
1621 // Don't do anything with sqlite unless something is really saved
1622 bool save_started = false;
1624 for (auto §or_it : m_sectors) {
1625 MapSector *sector = sector_it.second;
1627 MapBlockVect blocks;
1628 sector->getBlocks(blocks);
1630 for (MapBlock *block : blocks) {
1633 if(block->getModified() >= (u32)save_level) {
1637 save_started = true;
1640 modprofiler.add(block->getModifiedReasonString(), 1);
1652 Only print if something happened or saved whole map
1654 if(save_level == MOD_STATE_CLEAN
1655 || block_count != 0) {
1656 infostream << "ServerMap: Written: "
1657 << block_count << " blocks"
1658 << ", " << block_count_all << " blocks in memory."
1660 PrintInfo(infostream); // ServerMap/ClientMap:
1661 infostream<<"Blocks modified by: "<<std::endl;
1662 modprofiler.print(infostream);
1665 auto end_time = porting::getTimeNs();
1666 m_save_time_counter->increment(end_time - start_time);
1669 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1671 dbase->listAllLoadableBlocks(dst);
1673 dbase_ro->listAllLoadableBlocks(dst);
1676 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1678 for (auto §or_it : m_sectors) {
1679 MapSector *sector = sector_it.second;
1681 MapBlockVect blocks;
1682 sector->getBlocks(blocks);
1684 for (MapBlock *block : blocks) {
1685 v3s16 p = block->getPos();
1691 MapDatabase *ServerMap::createDatabase(
1692 const std::string &name,
1693 const std::string &savedir,
1696 if (name == "sqlite3")
1697 return new MapDatabaseSQLite3(savedir);
1698 if (name == "dummy")
1699 return new Database_Dummy();
1701 if (name == "leveldb")
1702 return new Database_LevelDB(savedir);
1705 if (name == "redis")
1706 return new Database_Redis(conf);
1709 if (name == "postgresql") {
1710 std::string connect_string;
1711 conf.getNoEx("pgsql_connection", connect_string);
1712 return new MapDatabasePostgreSQL(connect_string);
1716 throw BaseException(std::string("Database backend ") + name + " not supported.");
1719 void ServerMap::beginSave()
1724 void ServerMap::endSave()
1729 bool ServerMap::saveBlock(MapBlock *block)
1731 return saveBlock(block, dbase, m_map_compression_level);
1734 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1736 v3s16 p3d = block->getPos();
1738 // Dummy blocks are not written
1739 if (block->isDummy()) {
1740 warningstream << "saveBlock: Not writing dummy block "
1741 << PP(p3d) << std::endl;
1745 // Format used for writing
1746 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1749 [0] u8 serialization version
1752 std::ostringstream o(std::ios_base::binary);
1753 o.write((char*) &version, 1);
1754 block->serialize(o, version, true, compression_level);
1756 bool ret = db->saveBlock(p3d, o.str());
1758 // We just wrote it to the disk so clear modified flag
1759 block->resetModified();
1764 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1767 std::istringstream is(*blob, std::ios_base::binary);
1769 u8 version = SER_FMT_VER_INVALID;
1770 is.read((char*)&version, 1);
1773 throw SerializationError("ServerMap::loadBlock(): Failed"
1774 " to read MapBlock version");
1776 MapBlock *block = NULL;
1777 bool created_new = false;
1778 block = sector->getBlockNoCreateNoEx(p3d.Y);
1781 block = sector->createBlankBlockNoInsert(p3d.Y);
1786 block->deSerialize(is, version, true);
1788 // If it's a new block, insert it to the map
1790 sector->insertBlock(block);
1791 ReflowScan scanner(this, m_emerge->ndef);
1792 scanner.scan(block, &m_transforming_liquid);
1796 Save blocks loaded in old format in new format
1799 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1800 // Only save if asked to; no need to update version
1804 // We just loaded it from, so it's up-to-date.
1805 block->resetModified();
1807 catch(SerializationError &e)
1809 errorstream<<"Invalid block data in database"
1810 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1811 <<" (SerializationError): "<<e.what()<<std::endl;
1813 // TODO: Block should be marked as invalid in memory so that it is
1814 // not touched but the game can run
1816 if(g_settings->getBool("ignore_world_load_errors")){
1817 errorstream<<"Ignoring block load error. Duck and cover! "
1818 <<"(ignore_world_load_errors)"<<std::endl;
1820 throw SerializationError("Invalid block data in database");
1825 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1827 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1829 v2s16 p2d(blockpos.X, blockpos.Z);
1832 dbase->loadBlock(blockpos, &ret);
1834 loadBlock(&ret, blockpos, createSector(p2d), false);
1835 } else if (dbase_ro) {
1836 dbase_ro->loadBlock(blockpos, &ret);
1838 loadBlock(&ret, blockpos, createSector(p2d), false);
1844 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1845 if (created_new && (block != NULL)) {
1846 std::map<v3s16, MapBlock*> modified_blocks;
1847 // Fix lighting if necessary
1848 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1849 if (!modified_blocks.empty()) {
1850 //Modified lighting, send event
1852 event.type = MEET_OTHER;
1853 std::map<v3s16, MapBlock *>::iterator it;
1854 for (it = modified_blocks.begin();
1855 it != modified_blocks.end(); ++it)
1856 event.modified_blocks.insert(it->first);
1857 dispatchEvent(event);
1863 bool ServerMap::deleteBlock(v3s16 blockpos)
1865 if (!dbase->deleteBlock(blockpos))
1868 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1870 v2s16 p2d(blockpos.X, blockpos.Z);
1871 MapSector *sector = getSectorNoGenerate(p2d);
1874 sector->deleteBlock(block);
1880 void ServerMap::PrintInfo(std::ostream &out)
1885 bool ServerMap::repairBlockLight(v3s16 blockpos,
1886 std::map<v3s16, MapBlock *> *modified_blocks)
1888 MapBlock *block = emergeBlock(blockpos, false);
1889 if (!block || !block->isGenerated())
1891 voxalgo::repair_block_light(this, block, modified_blocks);
1895 MMVManip::MMVManip(Map *map):
1902 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1903 bool load_if_inexistent)
1905 TimeTaker timer1("initialEmerge", &emerge_time);
1909 // Units of these are MapBlocks
1910 v3s16 p_min = blockpos_min;
1911 v3s16 p_max = blockpos_max;
1913 VoxelArea block_area_nodes
1914 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1916 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1919 infostream<<"initialEmerge: area: ";
1920 block_area_nodes.print(infostream);
1921 infostream<<" ("<<size_MB<<"MB)";
1922 infostream<<std::endl;
1925 addArea(block_area_nodes);
1927 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1928 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1929 for(s32 x=p_min.X; x<=p_max.X; x++)
1934 std::map<v3s16, u8>::iterator n;
1935 n = m_loaded_blocks.find(p);
1936 if(n != m_loaded_blocks.end())
1939 bool block_data_inexistent = false;
1941 TimeTaker timer2("emerge load", &emerge_load_time);
1943 block = m_map->getBlockNoCreateNoEx(p);
1944 if (!block || block->isDummy())
1945 block_data_inexistent = true;
1947 block->copyTo(*this);
1950 if(block_data_inexistent)
1953 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1954 ServerMap *svrmap = (ServerMap *)m_map;
1955 block = svrmap->emergeBlock(p, false);
1957 block = svrmap->createBlock(p);
1958 block->copyTo(*this);
1960 flags |= VMANIP_BLOCK_DATA_INEXIST;
1963 Mark area inexistent
1965 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1966 // Fill with VOXELFLAG_NO_DATA
1967 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1968 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1970 s32 i = m_area.index(a.MinEdge.X,y,z);
1971 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1975 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1977 // Mark that block was loaded as blank
1978 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
1981 m_loaded_blocks[p] = flags;
1987 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
1988 bool overwrite_generated)
1990 if(m_area.getExtent() == v3s16(0,0,0))
1995 Copy data of all blocks
1997 for (auto &loaded_block : m_loaded_blocks) {
1998 v3s16 p = loaded_block.first;
1999 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2000 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2001 if (!existed || (block == NULL) ||
2002 (!overwrite_generated && block->isGenerated()))
2005 block->copyFrom(*this);
2006 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2009 (*modified_blocks)[p] = block;
2013 MMVManip *MMVManip::clone() const
2015 MMVManip *ret = new MMVManip();
2017 const s32 size = m_area.getVolume();
2018 ret->m_area = m_area;
2020 ret->m_data = new MapNode[size];
2021 memcpy(ret->m_data, m_data, size * sizeof(MapNode));
2024 ret->m_flags = new u8[size];
2025 memcpy(ret->m_flags, m_flags, size * sizeof(u8));
2028 ret->m_is_dirty = m_is_dirty;
2029 // Even if the copy is disconnected from a map object keep the information
2030 // needed to write it back to one
2031 ret->m_loaded_blocks = m_loaded_blocks;
2036 void MMVManip::reparent(Map *map)
2038 assert(map && !m_map);