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 auto 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;
161 MapNode node = block->getNodeNoCheck(relpos);
162 if (is_valid_position != NULL)
163 *is_valid_position = true;
167 static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n)
169 // Never allow placing CONTENT_IGNORE, it causes problems
170 if(n.getContent() == CONTENT_IGNORE){
171 const NodeDefManager *nodedef = block->getParent()->getNodeDefManager();
172 v3s16 blockpos = block->getPos();
173 v3s16 p = blockpos * MAP_BLOCKSIZE + relpos;
174 errorstream<<"Not allowing to place CONTENT_IGNORE"
175 <<" while trying to replace \""
176 <<nodedef->get(block->getNodeNoCheck(relpos)).name
177 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
180 block->setNodeNoCheck(relpos, n);
183 // throws InvalidPositionException if not found
184 void Map::setNode(v3s16 p, MapNode n)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreate(blockpos);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 set_node_in_block(block, relpos, n);
192 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
193 std::map<v3s16, MapBlock*> &modified_blocks,
194 bool remove_metadata)
196 // Collect old node for rollback
197 RollbackNode rollback_oldnode(this, p, m_gamedef);
199 v3s16 blockpos = getNodeBlockPos(p);
200 MapBlock *block = getBlockNoCreate(blockpos);
201 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
203 // This is needed for updating the lighting
204 MapNode oldnode = block->getNodeNoCheck(relpos);
206 // Remove node metadata
207 if (remove_metadata) {
208 removeNodeMetadata(p);
211 // Set the node on the map
212 ContentLightingFlags f = m_nodedef->getLightingFlags(n);
213 ContentLightingFlags oldf = m_nodedef->getLightingFlags(oldnode);
215 // No light update needed, just copy over the old light.
216 n.setLight(LIGHTBANK_DAY, oldnode.getLightRaw(LIGHTBANK_DAY, oldf), f);
217 n.setLight(LIGHTBANK_NIGHT, oldnode.getLightRaw(LIGHTBANK_NIGHT, oldf), f);
218 set_node_in_block(block, relpos, n);
220 modified_blocks[blockpos] = block;
222 // Ignore light (because calling voxalgo::update_lighting_nodes)
223 n.setLight(LIGHTBANK_DAY, 0, f);
224 n.setLight(LIGHTBANK_NIGHT, 0, f);
225 set_node_in_block(block, relpos, n);
228 std::vector<std::pair<v3s16, MapNode> > oldnodes;
229 oldnodes.emplace_back(p, oldnode);
230 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
232 for (auto &modified_block : modified_blocks) {
233 modified_block.second->expireDayNightDiff();
237 // Report for rollback
238 if(m_gamedef->rollback())
240 RollbackNode rollback_newnode(this, p, m_gamedef);
241 RollbackAction action;
242 action.setSetNode(p, rollback_oldnode, rollback_newnode);
243 m_gamedef->rollback()->reportAction(action);
247 void Map::removeNodeAndUpdate(v3s16 p,
248 std::map<v3s16, MapBlock*> &modified_blocks)
250 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
253 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
256 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
260 bool succeeded = true;
262 std::map<v3s16, MapBlock*> modified_blocks;
263 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
265 event.setModifiedBlocks(modified_blocks);
267 catch(InvalidPositionException &e){
271 dispatchEvent(event);
276 bool Map::removeNodeWithEvent(v3s16 p)
279 event.type = MEET_REMOVENODE;
282 bool succeeded = true;
284 std::map<v3s16, MapBlock*> modified_blocks;
285 removeNodeAndUpdate(p, modified_blocks);
287 event.setModifiedBlocks(modified_blocks);
289 catch(InvalidPositionException &e){
293 dispatchEvent(event);
298 struct TimeOrderedMapBlock {
302 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
307 bool operator<(const TimeOrderedMapBlock &b) const
309 return block->getUsageTimer() < b.block->getUsageTimer();
316 void Map::timerUpdate(float dtime, float unload_timeout, s32 max_loaded_blocks,
317 std::vector<v3s16> *unloaded_blocks)
319 bool save_before_unloading = maySaveBlocks();
321 // Profile modified reasons
322 Profiler modprofiler;
324 std::vector<v2s16> sector_deletion_queue;
325 u32 deleted_blocks_count = 0;
326 u32 saved_blocks_count = 0;
327 u32 block_count_all = 0;
328 u32 locked_blocks = 0;
330 const auto start_time = porting::getTimeUs();
333 // If there is no practical limit, we spare creation of mapblock_queue
334 if (max_loaded_blocks < 0) {
335 for (auto §or_it : m_sectors) {
336 MapSector *sector = sector_it.second;
338 bool all_blocks_deleted = true;
341 sector->getBlocks(blocks);
343 for (MapBlock *block : blocks) {
344 block->incrementUsageTimer(dtime);
346 if (block->refGet() == 0
347 && block->getUsageTimer() > unload_timeout) {
348 v3s16 p = block->getPos();
351 if (block->getModified() != MOD_STATE_CLEAN
352 && save_before_unloading) {
353 modprofiler.add(block->getModifiedReasonString(), 1);
354 if (!saveBlock(block))
356 saved_blocks_count++;
359 // Delete from memory
360 sector->deleteBlock(block);
363 unloaded_blocks->push_back(p);
365 deleted_blocks_count++;
367 all_blocks_deleted = false;
372 // Delete sector if we emptied it
373 if (all_blocks_deleted) {
374 sector_deletion_queue.push_back(sector_it.first);
378 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
379 for (auto §or_it : m_sectors) {
380 MapSector *sector = sector_it.second;
383 sector->getBlocks(blocks);
385 for (MapBlock *block : blocks) {
386 block->incrementUsageTimer(dtime);
387 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
390 block_count_all = mapblock_queue.size();
392 // Delete old blocks, and blocks over the limit from the memory
393 while (!mapblock_queue.empty() && ((s32)mapblock_queue.size() > max_loaded_blocks
394 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
395 TimeOrderedMapBlock b = mapblock_queue.top();
396 mapblock_queue.pop();
398 MapBlock *block = b.block;
400 if (block->refGet() != 0) {
405 v3s16 p = block->getPos();
408 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
409 modprofiler.add(block->getModifiedReasonString(), 1);
410 if (!saveBlock(block))
412 saved_blocks_count++;
415 // Delete from memory
416 b.sect->deleteBlock(block);
419 unloaded_blocks->push_back(p);
421 deleted_blocks_count++;
425 // Delete empty sectors
426 for (auto §or_it : m_sectors) {
427 if (sector_it.second->empty()) {
428 sector_deletion_queue.push_back(sector_it.first);
434 const auto end_time = porting::getTimeUs();
436 reportMetrics(end_time - start_time, saved_blocks_count, block_count_all);
438 // Finally delete the empty sectors
439 deleteSectors(sector_deletion_queue);
441 if(deleted_blocks_count != 0)
443 PrintInfo(infostream); // ServerMap/ClientMap:
444 infostream<<"Unloaded "<<deleted_blocks_count
445 <<" blocks from memory";
446 if(save_before_unloading)
447 infostream<<", of which "<<saved_blocks_count<<" were written";
448 infostream<<", "<<block_count_all<<" blocks in memory, " << locked_blocks << " locked";
449 infostream<<"."<<std::endl;
450 if(saved_blocks_count != 0){
451 PrintInfo(infostream); // ServerMap/ClientMap:
452 infostream<<"Blocks modified by: "<<std::endl;
453 modprofiler.print(infostream);
458 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
460 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
463 void Map::deleteSectors(std::vector<v2s16> §orList)
465 for (v2s16 j : sectorList) {
466 MapSector *sector = m_sectors[j];
467 // If sector is in sector cache, remove it from there
468 if(m_sector_cache == sector)
469 m_sector_cache = NULL;
470 // Remove from map and delete
476 void Map::PrintInfo(std::ostream &out)
481 #define WATER_DROP_BOOST 4
483 const static v3s16 liquid_6dirs[6] = {
484 // order: upper before same level before lower
493 enum NeighborType : u8 {
499 struct NodeNeighbor {
505 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
508 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
515 void ServerMap::transforming_liquid_add(v3s16 p) {
516 m_transforming_liquid.push_back(p);
519 void ServerMap::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
520 ServerEnvironment *env)
523 u32 initial_size = m_transforming_liquid.size();
525 /*if(initial_size != 0)
526 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
528 // list of nodes that due to viscosity have not reached their max level height
529 std::vector<v3s16> must_reflow;
531 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
533 std::vector<v3s16> check_for_falling;
535 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
536 u32 loop_max = liquid_loop_max;
538 while (m_transforming_liquid.size() != 0)
540 // This should be done here so that it is done when continue is used
541 if (loopcount >= initial_size || loopcount >= loop_max)
546 Get a queued transforming liquid node
548 v3s16 p0 = m_transforming_liquid.front();
549 m_transforming_liquid.pop_front();
551 MapNode n0 = getNode(p0);
554 Collect information about current node
556 s8 liquid_level = -1;
557 // The liquid node which will be placed there if
558 // the liquid flows into this node.
559 content_t liquid_kind = CONTENT_IGNORE;
560 // The node which will be placed there if liquid
561 // can't flow into this node.
562 content_t floodable_node = CONTENT_AIR;
563 const ContentFeatures &cf = m_nodedef->get(n0);
564 LiquidType liquid_type = cf.liquid_type;
565 switch (liquid_type) {
567 liquid_level = LIQUID_LEVEL_SOURCE;
568 liquid_kind = cf.liquid_alternative_flowing_id;
571 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
572 liquid_kind = n0.getContent();
575 // if this node is 'floodable', it *could* be transformed
576 // into a liquid, otherwise, continue with the next node.
579 floodable_node = n0.getContent();
580 liquid_kind = CONTENT_AIR;
585 Collect information about the environment
587 NodeNeighbor sources[6]; // surrounding sources
589 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
591 NodeNeighbor airs[6]; // surrounding air
593 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
594 int num_neutrals = 0;
595 bool flowing_down = false;
596 bool ignored_sources = false;
597 bool floating_node_above = false;
598 for (u16 i = 0; i < 6; i++) {
599 NeighborType nt = NEIGHBOR_SAME_LEVEL;
610 v3s16 npos = p0 + liquid_6dirs[i];
611 NodeNeighbor nb(getNode(npos), nt, npos);
612 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
613 if (nt == NEIGHBOR_UPPER && cfnb.floats)
614 floating_node_above = true;
615 switch (cfnb.liquid_type) {
617 if (cfnb.floodable) {
618 airs[num_airs++] = nb;
619 // if the current node is a water source the neighbor
620 // should be enqueded for transformation regardless of whether the
621 // current node changes or not.
622 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
623 m_transforming_liquid.push_back(npos);
624 // if the current node happens to be a flowing node, it will start to flow down here.
625 if (nb.t == NEIGHBOR_LOWER)
628 neutrals[num_neutrals++] = nb;
629 if (nb.n.getContent() == CONTENT_IGNORE) {
630 // If node below is ignore prevent water from
631 // spreading outwards and otherwise prevent from
632 // flowing away as ignore node might be the source
633 if (nb.t == NEIGHBOR_LOWER)
636 ignored_sources = true;
641 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
642 if (liquid_kind == CONTENT_AIR)
643 liquid_kind = cfnb.liquid_alternative_flowing_id;
644 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
645 neutrals[num_neutrals++] = nb;
647 // Do not count bottom source, it will screw things up
648 if(nt != NEIGHBOR_LOWER)
649 sources[num_sources++] = nb;
653 if (nb.t != NEIGHBOR_SAME_LEVEL ||
654 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
655 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
656 // but exclude falling liquids on the same level, they cannot flow here anyway
657 if (liquid_kind == CONTENT_AIR)
658 liquid_kind = cfnb.liquid_alternative_flowing_id;
660 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
661 neutrals[num_neutrals++] = nb;
663 flows[num_flows++] = nb;
664 if (nb.t == NEIGHBOR_LOWER)
672 decide on the type (and possibly level) of the current node
674 content_t new_node_content;
675 s8 new_node_level = -1;
676 s8 max_node_level = -1;
678 u8 range = m_nodedef->get(liquid_kind).liquid_range;
679 if (range > LIQUID_LEVEL_MAX + 1)
680 range = LIQUID_LEVEL_MAX + 1;
682 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
683 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
684 // or the flowing alternative of the first of the surrounding sources (if it's air), so
685 // it's perfectly safe to use liquid_kind here to determine the new node content.
686 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
687 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
688 // liquid_kind is set properly, see above
689 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
690 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
691 new_node_content = liquid_kind;
693 new_node_content = floodable_node;
694 } else if (ignored_sources && liquid_level >= 0) {
695 // Maybe there are neighboring sources that aren't loaded yet
696 // so prevent flowing away.
697 new_node_level = liquid_level;
698 new_node_content = liquid_kind;
700 // no surrounding sources, so get the maximum level that can flow into this node
701 for (u16 i = 0; i < num_flows; i++) {
702 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
703 switch (flows[i].t) {
705 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
706 max_node_level = LIQUID_LEVEL_MAX;
707 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
708 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
709 } else if (nb_liquid_level > max_node_level) {
710 max_node_level = nb_liquid_level;
715 case NEIGHBOR_SAME_LEVEL:
716 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
717 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
718 max_node_level = nb_liquid_level - 1;
723 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
724 if (viscosity > 1 && max_node_level != liquid_level) {
725 // amount to gain, limited by viscosity
726 // must be at least 1 in absolute value
727 s8 level_inc = max_node_level - liquid_level;
728 if (level_inc < -viscosity || level_inc > viscosity)
729 new_node_level = liquid_level + level_inc/viscosity;
730 else if (level_inc < 0)
731 new_node_level = liquid_level - 1;
732 else if (level_inc > 0)
733 new_node_level = liquid_level + 1;
734 if (new_node_level != max_node_level)
735 must_reflow.push_back(p0);
737 new_node_level = max_node_level;
740 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
741 new_node_content = liquid_kind;
743 new_node_content = floodable_node;
748 check if anything has changed. if not, just continue with the next node.
750 if (new_node_content == n0.getContent() &&
751 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
752 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
753 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
758 check if there is a floating node above that needs to be updated.
760 if (floating_node_above && new_node_content == CONTENT_AIR)
761 check_for_falling.push_back(p0);
764 update the current node
767 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
768 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
769 // set level to last 3 bits, flowing down bit to 4th bit
770 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
772 // set the liquid level and flow bits to 0
773 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
777 n0.setContent(new_node_content);
779 // on_flood() the node
780 if (floodable_node != CONTENT_AIR) {
781 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
785 // Ignore light (because calling voxalgo::update_lighting_nodes)
786 ContentLightingFlags f0 = m_nodedef->getLightingFlags(n0);
787 n0.setLight(LIGHTBANK_DAY, 0, f0);
788 n0.setLight(LIGHTBANK_NIGHT, 0, f0);
790 // Find out whether there is a suspect for this action
792 if (m_gamedef->rollback())
793 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
795 if (m_gamedef->rollback() && !suspect.empty()) {
797 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
798 // Get old node for rollback
799 RollbackNode rollback_oldnode(this, p0, m_gamedef);
803 RollbackNode rollback_newnode(this, p0, m_gamedef);
804 RollbackAction action;
805 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
806 m_gamedef->rollback()->reportAction(action);
812 v3s16 blockpos = getNodeBlockPos(p0);
813 MapBlock *block = getBlockNoCreateNoEx(blockpos);
815 modified_blocks[blockpos] = block;
816 changed_nodes.emplace_back(p0, n00);
820 enqueue neighbors for update if necessary
822 switch (m_nodedef->get(n0.getContent()).liquid_type) {
825 // make sure source flows into all neighboring nodes
826 for (u16 i = 0; i < num_flows; i++)
827 if (flows[i].t != NEIGHBOR_UPPER)
828 m_transforming_liquid.push_back(flows[i].p);
829 for (u16 i = 0; i < num_airs; i++)
830 if (airs[i].t != NEIGHBOR_UPPER)
831 m_transforming_liquid.push_back(airs[i].p);
834 // this flow has turned to air; neighboring flows might need to do the same
835 for (u16 i = 0; i < num_flows; i++)
836 m_transforming_liquid.push_back(flows[i].p);
840 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
842 for (const auto &iter : must_reflow)
843 m_transforming_liquid.push_back(iter);
845 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
847 for (const v3s16 &p : check_for_falling) {
848 env->getScriptIface()->check_for_falling(p);
851 env->getScriptIface()->on_liquid_transformed(changed_nodes);
853 /* ----------------------------------------------------------------------
854 * Manage the queue so that it does not grow indefinitely
856 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
858 if (time_until_purge == 0)
859 return; // Feature disabled
861 time_until_purge *= 1000; // seconds -> milliseconds
863 u64 curr_time = porting::getTimeMs();
864 u32 prev_unprocessed = m_unprocessed_count;
865 m_unprocessed_count = m_transforming_liquid.size();
867 // if unprocessed block count is decreasing or stable
868 if (m_unprocessed_count <= prev_unprocessed) {
869 m_queue_size_timer_started = false;
871 if (!m_queue_size_timer_started)
872 m_inc_trending_up_start_time = curr_time;
873 m_queue_size_timer_started = true;
876 // Account for curr_time overflowing
877 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
878 m_queue_size_timer_started = false;
880 /* If the queue has been growing for more than liquid_queue_purge_time seconds
881 * and the number of unprocessed blocks is still > liquid_loop_max then we
882 * cannot keep up; dump the oldest blocks from the queue so that the queue
883 * has liquid_loop_max items in it
885 if (m_queue_size_timer_started
886 && curr_time - m_inc_trending_up_start_time > time_until_purge
887 && m_unprocessed_count > liquid_loop_max) {
889 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
891 infostream << "transformLiquids(): DUMPING " << dump_qty
892 << " blocks from the queue" << std::endl;
895 m_transforming_liquid.pop_front();
897 m_queue_size_timer_started = false; // optimistically assume we can keep up now
898 m_unprocessed_count = m_transforming_liquid.size();
902 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
904 std::vector<v3s16> positions_with_meta;
906 sortBoxVerticies(p1, p2);
907 v3s16 bpmin = getNodeBlockPos(p1);
908 v3s16 bpmax = getNodeBlockPos(p2);
910 VoxelArea area(p1, p2);
912 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
913 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
914 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
915 v3s16 blockpos(x, y, z);
917 MapBlock *block = getBlockNoCreateNoEx(blockpos);
919 verbosestream << "Map::getNodeMetadata(): Need to emerge "
920 << PP(blockpos) << std::endl;
921 block = emergeBlock(blockpos, false);
924 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
929 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
930 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
931 for (size_t i = 0; i != keys.size(); i++) {
932 v3s16 p(keys[i] + p_base);
933 if (!area.contains(p))
936 positions_with_meta.push_back(p);
940 return positions_with_meta;
943 NodeMetadata *Map::getNodeMetadata(v3s16 p)
945 v3s16 blockpos = getNodeBlockPos(p);
946 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
947 MapBlock *block = getBlockNoCreateNoEx(blockpos);
949 infostream<<"Map::getNodeMetadata(): Need to emerge "
950 <<PP(blockpos)<<std::endl;
951 block = emergeBlock(blockpos, false);
954 warningstream<<"Map::getNodeMetadata(): Block not found"
958 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
962 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
964 v3s16 blockpos = getNodeBlockPos(p);
965 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
966 MapBlock *block = getBlockNoCreateNoEx(blockpos);
968 infostream<<"Map::setNodeMetadata(): Need to emerge "
969 <<PP(blockpos)<<std::endl;
970 block = emergeBlock(blockpos, false);
973 warningstream<<"Map::setNodeMetadata(): Block not found"
977 block->m_node_metadata.set(p_rel, meta);
981 void Map::removeNodeMetadata(v3s16 p)
983 v3s16 blockpos = getNodeBlockPos(p);
984 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
985 MapBlock *block = getBlockNoCreateNoEx(blockpos);
988 warningstream<<"Map::removeNodeMetadata(): Block not found"
992 block->m_node_metadata.remove(p_rel);
995 NodeTimer Map::getNodeTimer(v3s16 p)
997 v3s16 blockpos = getNodeBlockPos(p);
998 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
999 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1001 infostream<<"Map::getNodeTimer(): Need to emerge "
1002 <<PP(blockpos)<<std::endl;
1003 block = emergeBlock(blockpos, false);
1006 warningstream<<"Map::getNodeTimer(): Block not found"
1010 NodeTimer t = block->getNodeTimer(p_rel);
1011 NodeTimer nt(t.timeout, t.elapsed, p);
1015 void Map::setNodeTimer(const NodeTimer &t)
1017 v3s16 p = t.position;
1018 v3s16 blockpos = getNodeBlockPos(p);
1019 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1020 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1022 infostream<<"Map::setNodeTimer(): Need to emerge "
1023 <<PP(blockpos)<<std::endl;
1024 block = emergeBlock(blockpos, false);
1027 warningstream<<"Map::setNodeTimer(): Block not found"
1031 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1032 block->setNodeTimer(nt);
1035 void Map::removeNodeTimer(v3s16 p)
1037 v3s16 blockpos = getNodeBlockPos(p);
1038 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1039 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1042 warningstream<<"Map::removeNodeTimer(): Block not found"
1046 block->removeNodeTimer(p_rel);
1049 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1050 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1053 This functions determines the node inside the target block that is
1054 closest to the camera position. This increases the occlusion culling
1055 accuracy in straight and diagonal corridors.
1056 The returned position will be occlusion checked first in addition to the
1057 others (8 corners + center).
1058 No position is returned if
1059 - the closest node is a corner, corners are checked anyway.
1060 - the camera is inside the target block, it will never be occluded.
1062 #define CLOSEST_EDGE(pos, bounds, axis) \
1063 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1064 (bounds).MaxEdge.axis
1066 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1067 (pos_camera.X <= block_bounds.MaxEdge.X);
1068 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1069 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1070 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1071 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1073 if (x_inside && y_inside && z_inside)
1074 return false; // Camera inside target mapblock
1077 if (x_inside && y_inside) {
1078 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1079 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1081 } else if (y_inside && z_inside) {
1082 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1083 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1085 } else if (x_inside && z_inside) {
1086 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1087 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1093 check = v3s16(pos_camera.X, 0, 0);
1094 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1095 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1097 } else if (y_inside) {
1098 check = v3s16(0, pos_camera.Y, 0);
1099 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1100 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1102 } else if (z_inside) {
1103 check = v3s16(0, 0, pos_camera.Z);
1104 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1105 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1109 // Closest node would be a corner, none returned
1113 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1114 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1116 v3f direction = intToFloat(pos_target - pos_camera, BS);
1117 float distance = direction.getLength();
1119 // Normalize direction vector
1120 if (distance > 0.0f)
1121 direction /= distance;
1123 v3f pos_origin_f = intToFloat(pos_camera, BS);
1125 bool is_valid_position;
1127 for (; offset < distance + end_offset; offset += step) {
1128 v3f pos_node_f = pos_origin_f + direction * offset;
1129 v3s16 pos_node = floatToInt(pos_node_f, BS);
1131 MapNode node = getNode(pos_node, &is_valid_position);
1133 if (is_valid_position &&
1134 !m_nodedef->getLightingFlags(node).light_propagates) {
1135 // Cannot see through light-blocking nodes --> occluded
1137 if (count >= needed_count)
1145 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1147 // Check occlusion for center and all 8 corners of the mapblock
1148 // Overshoot a little for less flickering
1149 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1150 static const v3s16 dir9[9] = {
1152 v3s16( 1, 1, 1) * bs2,
1153 v3s16( 1, 1, -1) * bs2,
1154 v3s16( 1, -1, 1) * bs2,
1155 v3s16( 1, -1, -1) * bs2,
1156 v3s16(-1, 1, 1) * bs2,
1157 v3s16(-1, 1, -1) * bs2,
1158 v3s16(-1, -1, 1) * bs2,
1159 v3s16(-1, -1, -1) * bs2,
1162 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1164 // Starting step size, value between 1m and sqrt(3)m
1165 float step = BS * 1.2f;
1166 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1167 float stepfac = 1.05f;
1169 float start_offset = BS * 1.0f;
1171 // The occlusion search of 'isOccluded()' must stop short of the target
1172 // point by distance 'end_offset' to not enter the target mapblock.
1173 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1174 // diagonal of a mapblock, because we must consider all view angles.
1175 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1176 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1178 // to reduce the likelihood of falsely occluded blocks
1179 // require at least two solid blocks
1180 // this is a HACK, we should think of a more precise algorithm
1181 u32 needed_count = 2;
1183 // Additional occlusion check, see comments in that function
1185 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1186 // node is always on a side facing the camera, end_offset can be lower
1187 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1188 -1.0f, needed_count))
1192 for (const v3s16 &dir : dir9) {
1193 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1194 start_offset, end_offset, needed_count))
1203 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1204 EmergeManager *emerge, MetricsBackend *mb):
1206 settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
1209 verbosestream<<FUNCTION_NAME<<std::endl;
1211 // Tell the EmergeManager about our MapSettingsManager
1212 emerge->map_settings_mgr = &settings_mgr;
1215 Try to load map; if not found, create a new one.
1218 // Determine which database backend to use
1219 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1221 bool succeeded = conf.readConfigFile(conf_path.c_str());
1222 if (!succeeded || !conf.exists("backend")) {
1223 // fall back to sqlite3
1224 conf.set("backend", "sqlite3");
1226 std::string backend = conf.get("backend");
1227 dbase = createDatabase(backend, savedir, conf);
1228 if (conf.exists("readonly_backend")) {
1229 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1230 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1232 if (!conf.updateConfigFile(conf_path.c_str()))
1233 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1235 m_savedir = savedir;
1236 m_map_saving_enabled = false;
1238 m_save_time_counter = mb->addCounter(
1239 "minetest_map_save_time", "Time spent saving blocks (in microseconds)");
1240 m_save_count_counter = mb->addCounter(
1241 "minetest_map_saved_blocks", "Number of blocks saved");
1242 m_loaded_blocks_gauge = mb->addGauge(
1243 "minetest_map_loaded_blocks", "Number of loaded blocks");
1245 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1248 // If directory exists, check contents and load if possible
1249 if (fs::PathExists(m_savedir)) {
1250 // If directory is empty, it is safe to save into it.
1251 if (fs::GetDirListing(m_savedir).empty()) {
1252 infostream<<"ServerMap: Empty save directory is valid."
1254 m_map_saving_enabled = true;
1259 if (settings_mgr.loadMapMeta()) {
1260 infostream << "ServerMap: Metadata loaded from "
1261 << savedir << std::endl;
1263 infostream << "ServerMap: Metadata could not be loaded "
1264 "from " << savedir << ", assuming valid save "
1265 "directory." << std::endl;
1268 m_map_saving_enabled = true;
1269 // Map loaded, not creating new one
1273 // If directory doesn't exist, it is safe to save to it
1275 m_map_saving_enabled = true;
1278 catch(std::exception &e)
1280 warningstream<<"ServerMap: Failed to load map from "<<savedir
1281 <<", exception: "<<e.what()<<std::endl;
1282 infostream<<"Please remove the map or fix it."<<std::endl;
1283 warningstream<<"Map saving will be disabled."<<std::endl;
1287 ServerMap::~ServerMap()
1289 verbosestream<<FUNCTION_NAME<<std::endl;
1293 if (m_map_saving_enabled) {
1294 // Save only changed parts
1295 save(MOD_STATE_WRITE_AT_UNLOAD);
1296 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1298 infostream << "ServerMap: Map not saved" << std::endl;
1301 catch(std::exception &e)
1303 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1304 <<", exception: "<<e.what()<<std::endl;
1308 Close database if it was opened
1314 MapgenParams *ServerMap::getMapgenParams()
1316 // getMapgenParams() should only ever be called after Server is initialized
1317 assert(settings_mgr.mapgen_params != NULL);
1318 return settings_mgr.mapgen_params;
1321 u64 ServerMap::getSeed()
1323 return getMapgenParams()->seed;
1326 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1328 const s16 mapgen_limit_bp = rangelim(
1329 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1331 return p.X < -mapgen_limit_bp ||
1332 p.X > mapgen_limit_bp ||
1333 p.Y < -mapgen_limit_bp ||
1334 p.Y > mapgen_limit_bp ||
1335 p.Z < -mapgen_limit_bp ||
1336 p.Z > mapgen_limit_bp;
1339 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1341 s16 csize = getMapgenParams()->chunksize;
1342 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1343 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1345 if (!m_chunks_in_progress.insert(bpmin).second)
1348 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1349 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1351 v3s16 extra_borders(1, 1, 1);
1352 v3s16 full_bpmin = bpmin - extra_borders;
1353 v3s16 full_bpmax = bpmax + extra_borders;
1355 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1356 if (blockpos_over_mapgen_limit(full_bpmin) ||
1357 blockpos_over_mapgen_limit(full_bpmax))
1360 data->seed = getSeed();
1361 data->blockpos_min = bpmin;
1362 data->blockpos_max = bpmax;
1363 data->nodedef = m_nodedef;
1366 Create the whole area of this and the neighboring blocks
1368 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1369 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1370 v2s16 sectorpos(x, z);
1371 // Sector metadata is loaded from disk if not already loaded.
1372 MapSector *sector = createSector(sectorpos);
1373 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1375 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1378 MapBlock *block = emergeBlock(p, false);
1379 if (block == NULL) {
1380 block = createBlock(p);
1382 // Block gets sunlight if this is true.
1383 // Refer to the map generator heuristics.
1384 bool ug = m_emerge->isBlockUnderground(p);
1385 block->setIsUnderground(ug);
1391 Now we have a big empty area.
1393 Make a ManualMapVoxelManipulator that contains this and the
1397 data->vmanip = new MMVManip(this);
1398 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1400 // Data is ready now.
1404 void ServerMap::finishBlockMake(BlockMakeData *data,
1405 std::map<v3s16, MapBlock*> *changed_blocks)
1407 v3s16 bpmin = data->blockpos_min;
1408 v3s16 bpmax = data->blockpos_max;
1410 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1411 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1414 Blit generated stuff to map
1415 NOTE: blitBackAll adds nearly everything to changed_blocks
1417 data->vmanip->blitBackAll(changed_blocks);
1419 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1420 << changed_blocks->size());
1423 Copy transforming liquid information
1425 while (data->transforming_liquid.size()) {
1426 m_transforming_liquid.push_back(data->transforming_liquid.front());
1427 data->transforming_liquid.pop_front();
1430 for (auto &changed_block : *changed_blocks) {
1431 MapBlock *block = changed_block.second;
1435 Update day/night difference cache of the MapBlocks
1437 block->expireDayNightDiff();
1439 Set block as modified
1441 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1442 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1446 Set central blocks as generated
1448 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1449 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1450 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1451 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1455 block->setGenerated(true);
1459 Save changed parts of map
1460 NOTE: Will be saved later.
1462 //save(MOD_STATE_WRITE_AT_UNLOAD);
1463 m_chunks_in_progress.erase(bpmin);
1466 MapSector *ServerMap::createSector(v2s16 p2d)
1469 Check if it exists already in memory
1471 MapSector *sector = getSectorNoGenerate(p2d);
1476 Do not create over max mapgen limit
1478 if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y)))
1479 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1482 Generate blank sector
1485 sector = new MapSector(this, p2d, m_gamedef);
1490 m_sectors[p2d] = sector;
1495 MapBlock * ServerMap::createBlock(v3s16 p)
1498 Do not create over max mapgen limit
1500 if (blockpos_over_max_limit(p))
1501 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1503 v2s16 p2d(p.X, p.Z);
1506 This will create or load a sector if not found in memory.
1507 If block exists on disk, it will be loaded.
1509 NOTE: On old save formats, this will be slow, as it generates
1510 lighting on blocks for them.
1514 sector = createSector(p2d);
1515 } catch (InvalidPositionException &e) {
1516 infostream<<"createBlock: createSector() failed"<<std::endl;
1521 Try to get a block from the sector
1524 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1529 block = sector->createBlankBlock(block_y);
1534 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1537 MapBlock *block = getBlockNoCreateNoEx(p);
1543 MapBlock *block = loadBlock(p);
1549 MapSector *sector = createSector(v2s16(p.X, p.Z));
1550 MapBlock *block = sector->createBlankBlock(p.Y);
1558 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1560 MapBlock *block = getBlockNoCreateNoEx(p3d);
1562 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1567 bool ServerMap::isBlockInQueue(v3s16 pos)
1569 return m_emerge && m_emerge->isBlockInQueue(pos);
1572 void ServerMap::addNodeAndUpdate(v3s16 p, MapNode n,
1573 std::map<v3s16, MapBlock*> &modified_blocks,
1574 bool remove_metadata)
1576 Map::addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1579 Add neighboring liquid nodes and this node to transform queue.
1580 (it's vital for the node itself to get updated last, if it was removed.)
1583 for (const v3s16 &dir : g_7dirs) {
1586 bool is_valid_position;
1587 MapNode n2 = getNode(p2, &is_valid_position);
1588 if(is_valid_position &&
1589 (m_nodedef->get(n2).isLiquid() ||
1590 n2.getContent() == CONTENT_AIR))
1591 m_transforming_liquid.push_back(p2);
1595 // N.B. This requires no synchronization, since data will not be modified unless
1596 // the VoxelManipulator being updated belongs to the same thread.
1597 void ServerMap::updateVManip(v3s16 pos)
1599 Mapgen *mg = m_emerge->getCurrentMapgen();
1603 MMVManip *vm = mg->vm;
1607 if (!vm->m_area.contains(pos))
1610 s32 idx = vm->m_area.index(pos);
1611 vm->m_data[idx] = getNode(pos);
1612 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1614 vm->m_is_dirty = true;
1617 void ServerMap::reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks)
1619 m_loaded_blocks_gauge->set(all_blocks);
1620 m_save_time_counter->increment(save_time_us);
1621 m_save_count_counter->increment(saved_blocks);
1624 void ServerMap::save(ModifiedState save_level)
1626 if (!m_map_saving_enabled) {
1627 warningstream<<"Not saving map, saving disabled."<<std::endl;
1631 const auto start_time = porting::getTimeUs();
1633 if(save_level == MOD_STATE_CLEAN)
1634 infostream<<"ServerMap: Saving whole map, this can take time."
1637 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1638 if (settings_mgr.saveMapMeta())
1639 m_map_metadata_changed = false;
1642 // Profile modified reasons
1643 Profiler modprofiler;
1645 u32 block_count = 0;
1646 u32 block_count_all = 0; // Number of blocks in memory
1648 // Don't do anything with sqlite unless something is really saved
1649 bool save_started = false;
1651 for (auto §or_it : m_sectors) {
1652 MapSector *sector = sector_it.second;
1654 MapBlockVect blocks;
1655 sector->getBlocks(blocks);
1657 for (MapBlock *block : blocks) {
1660 if(block->getModified() >= (u32)save_level) {
1664 save_started = true;
1667 modprofiler.add(block->getModifiedReasonString(), 1);
1679 Only print if something happened or saved whole map
1681 if(save_level == MOD_STATE_CLEAN
1682 || block_count != 0) {
1683 infostream << "ServerMap: Written: "
1684 << block_count << " blocks"
1685 << ", " << block_count_all << " blocks in memory."
1687 PrintInfo(infostream); // ServerMap/ClientMap:
1688 infostream<<"Blocks modified by: "<<std::endl;
1689 modprofiler.print(infostream);
1692 const auto end_time = porting::getTimeUs();
1693 reportMetrics(end_time - start_time, block_count, block_count_all);
1696 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1698 dbase->listAllLoadableBlocks(dst);
1700 dbase_ro->listAllLoadableBlocks(dst);
1703 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1705 for (auto §or_it : m_sectors) {
1706 MapSector *sector = sector_it.second;
1708 MapBlockVect blocks;
1709 sector->getBlocks(blocks);
1711 for (MapBlock *block : blocks) {
1712 v3s16 p = block->getPos();
1718 MapDatabase *ServerMap::createDatabase(
1719 const std::string &name,
1720 const std::string &savedir,
1723 if (name == "sqlite3")
1724 return new MapDatabaseSQLite3(savedir);
1725 if (name == "dummy")
1726 return new Database_Dummy();
1728 if (name == "leveldb")
1729 return new Database_LevelDB(savedir);
1732 if (name == "redis")
1733 return new Database_Redis(conf);
1736 if (name == "postgresql") {
1737 std::string connect_string;
1738 conf.getNoEx("pgsql_connection", connect_string);
1739 return new MapDatabasePostgreSQL(connect_string);
1743 throw BaseException(std::string("Database backend ") + name + " not supported.");
1746 void ServerMap::beginSave()
1751 void ServerMap::endSave()
1756 bool ServerMap::saveBlock(MapBlock *block)
1758 return saveBlock(block, dbase, m_map_compression_level);
1761 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1763 v3s16 p3d = block->getPos();
1765 // Format used for writing
1766 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1769 [0] u8 serialization version
1772 std::ostringstream o(std::ios_base::binary);
1773 o.write((char*) &version, 1);
1774 block->serialize(o, version, true, compression_level);
1776 bool ret = db->saveBlock(p3d, o.str());
1778 // We just wrote it to the disk so clear modified flag
1779 block->resetModified();
1784 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1787 std::istringstream is(*blob, std::ios_base::binary);
1789 u8 version = SER_FMT_VER_INVALID;
1790 is.read((char*)&version, 1);
1793 throw SerializationError("ServerMap::loadBlock(): Failed"
1794 " to read MapBlock version");
1796 MapBlock *block = NULL;
1797 bool created_new = false;
1798 block = sector->getBlockNoCreateNoEx(p3d.Y);
1801 block = sector->createBlankBlockNoInsert(p3d.Y);
1806 block->deSerialize(is, version, true);
1808 // If it's a new block, insert it to the map
1810 sector->insertBlock(block);
1811 ReflowScan scanner(this, m_emerge->ndef);
1812 scanner.scan(block, &m_transforming_liquid);
1816 Save blocks loaded in old format in new format
1819 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1820 // Only save if asked to; no need to update version
1824 // We just loaded it from, so it's up-to-date.
1825 block->resetModified();
1827 catch(SerializationError &e)
1829 errorstream<<"Invalid block data in database"
1830 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1831 <<" (SerializationError): "<<e.what()<<std::endl;
1833 // TODO: Block should be marked as invalid in memory so that it is
1834 // not touched but the game can run
1836 if(g_settings->getBool("ignore_world_load_errors")){
1837 errorstream<<"Ignoring block load error. Duck and cover! "
1838 <<"(ignore_world_load_errors)"<<std::endl;
1840 throw SerializationError("Invalid block data in database");
1845 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1847 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1849 v2s16 p2d(blockpos.X, blockpos.Z);
1852 dbase->loadBlock(blockpos, &ret);
1854 loadBlock(&ret, blockpos, createSector(p2d), false);
1855 } else if (dbase_ro) {
1856 dbase_ro->loadBlock(blockpos, &ret);
1858 loadBlock(&ret, blockpos, createSector(p2d), false);
1864 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1865 if (created_new && (block != NULL)) {
1866 std::map<v3s16, MapBlock*> modified_blocks;
1867 // Fix lighting if necessary
1868 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1869 if (!modified_blocks.empty()) {
1870 //Modified lighting, send event
1872 event.type = MEET_OTHER;
1873 event.setModifiedBlocks(modified_blocks);
1874 dispatchEvent(event);
1880 bool ServerMap::deleteBlock(v3s16 blockpos)
1882 if (!dbase->deleteBlock(blockpos))
1885 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1887 v2s16 p2d(blockpos.X, blockpos.Z);
1888 MapSector *sector = getSectorNoGenerate(p2d);
1891 sector->deleteBlock(block);
1897 void ServerMap::PrintInfo(std::ostream &out)
1902 bool ServerMap::repairBlockLight(v3s16 blockpos,
1903 std::map<v3s16, MapBlock *> *modified_blocks)
1905 MapBlock *block = emergeBlock(blockpos, false);
1906 if (!block || !block->isGenerated())
1908 voxalgo::repair_block_light(this, block, modified_blocks);
1912 MMVManip::MMVManip(Map *map):
1919 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1920 bool load_if_inexistent)
1922 TimeTaker timer1("initialEmerge", &emerge_time);
1926 // Units of these are MapBlocks
1927 v3s16 p_min = blockpos_min;
1928 v3s16 p_max = blockpos_max;
1930 VoxelArea block_area_nodes
1931 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1933 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1936 infostream<<"initialEmerge: area: ";
1937 block_area_nodes.print(infostream);
1938 infostream<<" ("<<size_MB<<"MB)";
1939 infostream<<std::endl;
1942 addArea(block_area_nodes);
1944 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1945 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1946 for(s32 x=p_min.X; x<=p_max.X; x++)
1951 std::map<v3s16, u8>::iterator n;
1952 n = m_loaded_blocks.find(p);
1953 if(n != m_loaded_blocks.end())
1956 bool block_data_inexistent = false;
1958 TimeTaker timer2("emerge load", &emerge_load_time);
1960 block = m_map->getBlockNoCreateNoEx(p);
1962 block_data_inexistent = true;
1964 block->copyTo(*this);
1967 if(block_data_inexistent)
1970 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1971 ServerMap *svrmap = (ServerMap *)m_map;
1972 block = svrmap->emergeBlock(p, false);
1974 block = svrmap->createBlock(p);
1975 block->copyTo(*this);
1977 flags |= VMANIP_BLOCK_DATA_INEXIST;
1980 Mark area inexistent
1982 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1983 // Fill with VOXELFLAG_NO_DATA
1984 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1985 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1987 s32 i = m_area.index(a.MinEdge.X,y,z);
1988 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1992 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1994 // Mark that block was loaded as blank
1995 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
1998 m_loaded_blocks[p] = flags;
2004 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2005 bool overwrite_generated)
2007 if(m_area.getExtent() == v3s16(0,0,0))
2012 Copy data of all blocks
2014 for (auto &loaded_block : m_loaded_blocks) {
2015 v3s16 p = loaded_block.first;
2016 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2017 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2018 if (!existed || (block == NULL) ||
2019 (!overwrite_generated && block->isGenerated()))
2022 block->copyFrom(*this);
2023 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2026 (*modified_blocks)[p] = block;
2030 MMVManip *MMVManip::clone() const
2032 MMVManip *ret = new MMVManip();
2034 const s32 size = m_area.getVolume();
2035 ret->m_area = m_area;
2037 ret->m_data = new MapNode[size];
2038 memcpy(ret->m_data, m_data, size * sizeof(MapNode));
2041 ret->m_flags = new u8[size];
2042 memcpy(ret->m_flags, m_flags, size * sizeof(u8));
2045 ret->m_is_dirty = m_is_dirty;
2046 // Even if the copy is disconnected from a map object keep the information
2047 // needed to write it back to one
2048 ret->m_loaded_blocks = m_loaded_blocks;
2053 void MMVManip::reparent(Map *map)
2055 assert(map && !m_map);