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 void Map::listAllLoadedBlocks(std::vector<v3s16> &dst)
144 for (auto §or_it : m_sectors) {
145 MapSector *sector = sector_it.second;
148 sector->getBlocks(blocks);
150 for (MapBlock *block : blocks) {
151 v3s16 p = block->getPos();
157 bool Map::isValidPosition(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
160 MapBlock *block = getBlockNoCreateNoEx(blockpos);
161 return (block != NULL);
164 // Returns a CONTENT_IGNORE node if not found
165 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
167 v3s16 blockpos = getNodeBlockPos(p);
168 MapBlock *block = getBlockNoCreateNoEx(blockpos);
170 if (is_valid_position != NULL)
171 *is_valid_position = false;
172 return {CONTENT_IGNORE};
175 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
177 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
178 if (is_valid_position != NULL)
179 *is_valid_position = is_valid_p;
183 static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n)
185 // Never allow placing CONTENT_IGNORE, it causes problems
186 if(n.getContent() == CONTENT_IGNORE){
187 const NodeDefManager *nodedef = block->getParent()->getNodeDefManager();
188 v3s16 blockpos = block->getPos();
189 v3s16 p = blockpos * MAP_BLOCKSIZE + relpos;
191 errorstream<<"Not allowing to place CONTENT_IGNORE"
192 <<" while trying to replace \""
193 <<nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
194 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
197 block->setNodeNoCheck(relpos, n);
200 // throws InvalidPositionException if not found
201 void Map::setNode(v3s16 p, MapNode & n)
203 v3s16 blockpos = getNodeBlockPos(p);
204 MapBlock *block = getBlockNoCreate(blockpos);
205 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 set_node_in_block(block, relpos, n);
209 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
210 std::map<v3s16, MapBlock*> &modified_blocks,
211 bool remove_metadata)
213 // Collect old node for rollback
214 RollbackNode rollback_oldnode(this, p, m_gamedef);
216 v3s16 blockpos = getNodeBlockPos(p);
217 MapBlock *block = getBlockNoCreate(blockpos);
218 if (block->isDummy())
219 throw InvalidPositionException();
220 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
222 // This is needed for updating the lighting
223 MapNode oldnode = block->getNodeUnsafe(relpos);
225 // Remove node metadata
226 if (remove_metadata) {
227 removeNodeMetadata(p);
230 // Set the node on the map
231 const ContentFeatures &cf = m_nodedef->get(n);
232 const ContentFeatures &oldcf = m_nodedef->get(oldnode);
233 if (cf.lightingEquivalent(oldcf)) {
234 // No light update needed, just copy over the old light.
235 n.setLight(LIGHTBANK_DAY, oldnode.getLightRaw(LIGHTBANK_DAY, oldcf), cf);
236 n.setLight(LIGHTBANK_NIGHT, oldnode.getLightRaw(LIGHTBANK_NIGHT, oldcf), cf);
237 set_node_in_block(block, relpos, n);
239 modified_blocks[blockpos] = block;
241 // Ignore light (because calling voxalgo::update_lighting_nodes)
242 n.setLight(LIGHTBANK_DAY, 0, cf);
243 n.setLight(LIGHTBANK_NIGHT, 0, cf);
244 set_node_in_block(block, relpos, n);
247 std::vector<std::pair<v3s16, MapNode> > oldnodes;
248 oldnodes.emplace_back(p, oldnode);
249 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
251 for (auto &modified_block : modified_blocks) {
252 modified_block.second->expireDayNightDiff();
256 // Report for rollback
257 if(m_gamedef->rollback())
259 RollbackNode rollback_newnode(this, p, m_gamedef);
260 RollbackAction action;
261 action.setSetNode(p, rollback_oldnode, rollback_newnode);
262 m_gamedef->rollback()->reportAction(action);
266 void Map::removeNodeAndUpdate(v3s16 p,
267 std::map<v3s16, MapBlock*> &modified_blocks)
269 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
272 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
275 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
279 bool succeeded = true;
281 std::map<v3s16, MapBlock*> modified_blocks;
282 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
284 // Copy modified_blocks to event
285 for (auto &modified_block : modified_blocks) {
286 event.modified_blocks.insert(modified_block.first);
289 catch(InvalidPositionException &e){
293 dispatchEvent(event);
298 bool Map::removeNodeWithEvent(v3s16 p)
301 event.type = MEET_REMOVENODE;
304 bool succeeded = true;
306 std::map<v3s16, MapBlock*> modified_blocks;
307 removeNodeAndUpdate(p, modified_blocks);
309 // Copy modified_blocks to event
310 for (auto &modified_block : modified_blocks) {
311 event.modified_blocks.insert(modified_block.first);
314 catch(InvalidPositionException &e){
318 dispatchEvent(event);
323 struct TimeOrderedMapBlock {
327 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
332 bool operator<(const TimeOrderedMapBlock &b) const
334 return block->getUsageTimer() < b.block->getUsageTimer();
341 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
342 std::vector<v3s16> *unloaded_blocks)
344 bool save_before_unloading = maySaveBlocks();
346 // Profile modified reasons
347 Profiler modprofiler;
349 std::vector<v2s16> sector_deletion_queue;
350 u32 deleted_blocks_count = 0;
351 u32 saved_blocks_count = 0;
352 u32 block_count_all = 0;
354 const auto start_time = porting::getTimeUs();
357 // If there is no practical limit, we spare creation of mapblock_queue
358 if (max_loaded_blocks == U32_MAX) {
359 for (auto §or_it : m_sectors) {
360 MapSector *sector = sector_it.second;
362 bool all_blocks_deleted = true;
365 sector->getBlocks(blocks);
367 for (MapBlock *block : blocks) {
368 block->incrementUsageTimer(dtime);
370 if (block->refGet() == 0
371 && block->getUsageTimer() > unload_timeout) {
372 v3s16 p = block->getPos();
375 if (block->getModified() != MOD_STATE_CLEAN
376 && save_before_unloading) {
377 modprofiler.add(block->getModifiedReasonString(), 1);
378 if (!saveBlock(block))
380 saved_blocks_count++;
383 // Delete from memory
384 sector->deleteBlock(block);
387 unloaded_blocks->push_back(p);
389 deleted_blocks_count++;
391 all_blocks_deleted = false;
396 // Delete sector if we emptied it
397 if (all_blocks_deleted) {
398 sector_deletion_queue.push_back(sector_it.first);
402 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
403 for (auto §or_it : m_sectors) {
404 MapSector *sector = sector_it.second;
407 sector->getBlocks(blocks);
409 for (MapBlock *block : blocks) {
410 block->incrementUsageTimer(dtime);
411 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
414 block_count_all = mapblock_queue.size();
416 // Delete old blocks, and blocks over the limit from the memory
417 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
418 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
419 TimeOrderedMapBlock b = mapblock_queue.top();
420 mapblock_queue.pop();
422 MapBlock *block = b.block;
424 if (block->refGet() != 0)
427 v3s16 p = block->getPos();
430 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
431 modprofiler.add(block->getModifiedReasonString(), 1);
432 if (!saveBlock(block))
434 saved_blocks_count++;
437 // Delete from memory
438 b.sect->deleteBlock(block);
441 unloaded_blocks->push_back(p);
443 deleted_blocks_count++;
447 // Delete empty sectors
448 for (auto §or_it : m_sectors) {
449 if (sector_it.second->empty()) {
450 sector_deletion_queue.push_back(sector_it.first);
456 const auto end_time = porting::getTimeUs();
458 reportMetrics(end_time - start_time, saved_blocks_count, block_count_all);
460 // Finally delete the empty sectors
461 deleteSectors(sector_deletion_queue);
463 if(deleted_blocks_count != 0)
465 PrintInfo(infostream); // ServerMap/ClientMap:
466 infostream<<"Unloaded "<<deleted_blocks_count
467 <<" blocks from memory";
468 if(save_before_unloading)
469 infostream<<", of which "<<saved_blocks_count<<" were written";
470 infostream<<", "<<block_count_all<<" blocks in memory";
471 infostream<<"."<<std::endl;
472 if(saved_blocks_count != 0){
473 PrintInfo(infostream); // ServerMap/ClientMap:
474 infostream<<"Blocks modified by: "<<std::endl;
475 modprofiler.print(infostream);
480 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
482 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
485 void Map::deleteSectors(std::vector<v2s16> §orList)
487 for (v2s16 j : sectorList) {
488 MapSector *sector = m_sectors[j];
489 // If sector is in sector cache, remove it from there
490 if(m_sector_cache == sector)
491 m_sector_cache = NULL;
492 // Remove from map and delete
498 void Map::PrintInfo(std::ostream &out)
503 #define WATER_DROP_BOOST 4
505 const static v3s16 liquid_6dirs[6] = {
506 // order: upper before same level before lower
515 enum NeighborType : u8 {
521 struct NodeNeighbor {
527 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
530 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
537 void ServerMap::transforming_liquid_add(v3s16 p) {
538 m_transforming_liquid.push_back(p);
541 void ServerMap::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
542 ServerEnvironment *env)
545 u32 initial_size = m_transforming_liquid.size();
547 /*if(initial_size != 0)
548 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
550 // list of nodes that due to viscosity have not reached their max level height
551 std::deque<v3s16> must_reflow;
553 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
555 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
556 u32 loop_max = liquid_loop_max;
558 while (m_transforming_liquid.size() != 0)
560 // This should be done here so that it is done when continue is used
561 if (loopcount >= initial_size || loopcount >= loop_max)
566 Get a queued transforming liquid node
568 v3s16 p0 = m_transforming_liquid.front();
569 m_transforming_liquid.pop_front();
571 MapNode n0 = getNode(p0);
574 Collect information about current node
576 s8 liquid_level = -1;
577 // The liquid node which will be placed there if
578 // the liquid flows into this node.
579 content_t liquid_kind = CONTENT_IGNORE;
580 // The node which will be placed there if liquid
581 // can't flow into this node.
582 content_t floodable_node = CONTENT_AIR;
583 const ContentFeatures &cf = m_nodedef->get(n0);
584 LiquidType liquid_type = cf.liquid_type;
585 switch (liquid_type) {
587 liquid_level = LIQUID_LEVEL_SOURCE;
588 liquid_kind = cf.liquid_alternative_flowing_id;
591 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
592 liquid_kind = n0.getContent();
595 // if this node is 'floodable', it *could* be transformed
596 // into a liquid, otherwise, continue with the next node.
599 floodable_node = n0.getContent();
600 liquid_kind = CONTENT_AIR;
605 Collect information about the environment
607 NodeNeighbor sources[6]; // surrounding sources
609 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
611 NodeNeighbor airs[6]; // surrounding air
613 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
614 int num_neutrals = 0;
615 bool flowing_down = false;
616 bool ignored_sources = false;
617 for (u16 i = 0; i < 6; i++) {
618 NeighborType nt = NEIGHBOR_SAME_LEVEL;
629 v3s16 npos = p0 + liquid_6dirs[i];
630 NodeNeighbor nb(getNode(npos), nt, npos);
631 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
632 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
634 if (cfnb.floodable) {
635 airs[num_airs++] = nb;
636 // if the current node is a water source the neighbor
637 // should be enqueded for transformation regardless of whether the
638 // current node changes or not.
639 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
640 m_transforming_liquid.push_back(npos);
641 // if the current node happens to be a flowing node, it will start to flow down here.
642 if (nb.t == NEIGHBOR_LOWER)
645 neutrals[num_neutrals++] = nb;
646 if (nb.n.getContent() == CONTENT_IGNORE) {
647 // If node below is ignore prevent water from
648 // spreading outwards and otherwise prevent from
649 // flowing away as ignore node might be the source
650 if (nb.t == NEIGHBOR_LOWER)
653 ignored_sources = true;
658 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
659 if (liquid_kind == CONTENT_AIR)
660 liquid_kind = cfnb.liquid_alternative_flowing_id;
661 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
662 neutrals[num_neutrals++] = nb;
664 // Do not count bottom source, it will screw things up
665 if(nt != NEIGHBOR_LOWER)
666 sources[num_sources++] = nb;
670 if (nb.t != NEIGHBOR_SAME_LEVEL ||
671 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
672 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
673 // but exclude falling liquids on the same level, they cannot flow here anyway
674 if (liquid_kind == CONTENT_AIR)
675 liquid_kind = cfnb.liquid_alternative_flowing_id;
677 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
678 neutrals[num_neutrals++] = nb;
680 flows[num_flows++] = nb;
681 if (nb.t == NEIGHBOR_LOWER)
689 decide on the type (and possibly level) of the current node
691 content_t new_node_content;
692 s8 new_node_level = -1;
693 s8 max_node_level = -1;
695 u8 range = m_nodedef->get(liquid_kind).liquid_range;
696 if (range > LIQUID_LEVEL_MAX + 1)
697 range = LIQUID_LEVEL_MAX + 1;
699 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
700 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
701 // or the flowing alternative of the first of the surrounding sources (if it's air), so
702 // it's perfectly safe to use liquid_kind here to determine the new node content.
703 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
704 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
705 // liquid_kind is set properly, see above
706 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
707 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
708 new_node_content = liquid_kind;
710 new_node_content = floodable_node;
711 } else if (ignored_sources && liquid_level >= 0) {
712 // Maybe there are neighbouring sources that aren't loaded yet
713 // so prevent flowing away.
714 new_node_level = liquid_level;
715 new_node_content = liquid_kind;
717 // no surrounding sources, so get the maximum level that can flow into this node
718 for (u16 i = 0; i < num_flows; i++) {
719 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
720 switch (flows[i].t) {
722 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
723 max_node_level = LIQUID_LEVEL_MAX;
724 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
725 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
726 } else if (nb_liquid_level > max_node_level) {
727 max_node_level = nb_liquid_level;
732 case NEIGHBOR_SAME_LEVEL:
733 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
734 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
735 max_node_level = nb_liquid_level - 1;
740 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
741 if (viscosity > 1 && max_node_level != liquid_level) {
742 // amount to gain, limited by viscosity
743 // must be at least 1 in absolute value
744 s8 level_inc = max_node_level - liquid_level;
745 if (level_inc < -viscosity || level_inc > viscosity)
746 new_node_level = liquid_level + level_inc/viscosity;
747 else if (level_inc < 0)
748 new_node_level = liquid_level - 1;
749 else if (level_inc > 0)
750 new_node_level = liquid_level + 1;
751 if (new_node_level != max_node_level)
752 must_reflow.push_back(p0);
754 new_node_level = max_node_level;
757 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
758 new_node_content = liquid_kind;
760 new_node_content = floodable_node;
765 check if anything has changed. if not, just continue with the next node.
767 if (new_node_content == n0.getContent() &&
768 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
769 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
770 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
776 update the current node
779 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
780 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
781 // set level to last 3 bits, flowing down bit to 4th bit
782 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
784 // set the liquid level and flow bits to 0
785 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
789 n0.setContent(new_node_content);
791 // on_flood() the node
792 if (floodable_node != CONTENT_AIR) {
793 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
797 // Ignore light (because calling voxalgo::update_lighting_nodes)
798 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
799 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
801 // Find out whether there is a suspect for this action
803 if (m_gamedef->rollback())
804 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
806 if (m_gamedef->rollback() && !suspect.empty()) {
808 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
809 // Get old node for rollback
810 RollbackNode rollback_oldnode(this, p0, m_gamedef);
814 RollbackNode rollback_newnode(this, p0, m_gamedef);
815 RollbackAction action;
816 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
817 m_gamedef->rollback()->reportAction(action);
823 v3s16 blockpos = getNodeBlockPos(p0);
824 MapBlock *block = getBlockNoCreateNoEx(blockpos);
826 modified_blocks[blockpos] = block;
827 changed_nodes.emplace_back(p0, n00);
831 enqueue neighbors for update if neccessary
833 switch (m_nodedef->get(n0.getContent()).liquid_type) {
836 // make sure source flows into all neighboring nodes
837 for (u16 i = 0; i < num_flows; i++)
838 if (flows[i].t != NEIGHBOR_UPPER)
839 m_transforming_liquid.push_back(flows[i].p);
840 for (u16 i = 0; i < num_airs; i++)
841 if (airs[i].t != NEIGHBOR_UPPER)
842 m_transforming_liquid.push_back(airs[i].p);
845 // this flow has turned to air; neighboring flows might need to do the same
846 for (u16 i = 0; i < num_flows; i++)
847 m_transforming_liquid.push_back(flows[i].p);
851 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
853 for (auto &iter : must_reflow)
854 m_transforming_liquid.push_back(iter);
856 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
857 env->getScriptIface()->on_liquid_transformed(changed_nodes);
859 /* ----------------------------------------------------------------------
860 * Manage the queue so that it does not grow indefinately
862 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
864 if (time_until_purge == 0)
865 return; // Feature disabled
867 time_until_purge *= 1000; // seconds -> milliseconds
869 u64 curr_time = porting::getTimeMs();
870 u32 prev_unprocessed = m_unprocessed_count;
871 m_unprocessed_count = m_transforming_liquid.size();
873 // if unprocessed block count is decreasing or stable
874 if (m_unprocessed_count <= prev_unprocessed) {
875 m_queue_size_timer_started = false;
877 if (!m_queue_size_timer_started)
878 m_inc_trending_up_start_time = curr_time;
879 m_queue_size_timer_started = true;
882 // Account for curr_time overflowing
883 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
884 m_queue_size_timer_started = false;
886 /* If the queue has been growing for more than liquid_queue_purge_time seconds
887 * and the number of unprocessed blocks is still > liquid_loop_max then we
888 * cannot keep up; dump the oldest blocks from the queue so that the queue
889 * has liquid_loop_max items in it
891 if (m_queue_size_timer_started
892 && curr_time - m_inc_trending_up_start_time > time_until_purge
893 && m_unprocessed_count > liquid_loop_max) {
895 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
897 infostream << "transformLiquids(): DUMPING " << dump_qty
898 << " blocks from the queue" << std::endl;
901 m_transforming_liquid.pop_front();
903 m_queue_size_timer_started = false; // optimistically assume we can keep up now
904 m_unprocessed_count = m_transforming_liquid.size();
908 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
910 std::vector<v3s16> positions_with_meta;
912 sortBoxVerticies(p1, p2);
913 v3s16 bpmin = getNodeBlockPos(p1);
914 v3s16 bpmax = getNodeBlockPos(p2);
916 VoxelArea area(p1, p2);
918 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
919 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
920 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
921 v3s16 blockpos(x, y, z);
923 MapBlock *block = getBlockNoCreateNoEx(blockpos);
925 verbosestream << "Map::getNodeMetadata(): Need to emerge "
926 << PP(blockpos) << std::endl;
927 block = emergeBlock(blockpos, false);
930 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
935 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
936 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
937 for (size_t i = 0; i != keys.size(); i++) {
938 v3s16 p(keys[i] + p_base);
939 if (!area.contains(p))
942 positions_with_meta.push_back(p);
946 return positions_with_meta;
949 NodeMetadata *Map::getNodeMetadata(v3s16 p)
951 v3s16 blockpos = getNodeBlockPos(p);
952 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
953 MapBlock *block = getBlockNoCreateNoEx(blockpos);
955 infostream<<"Map::getNodeMetadata(): Need to emerge "
956 <<PP(blockpos)<<std::endl;
957 block = emergeBlock(blockpos, false);
960 warningstream<<"Map::getNodeMetadata(): Block not found"
964 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
968 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
970 v3s16 blockpos = getNodeBlockPos(p);
971 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
972 MapBlock *block = getBlockNoCreateNoEx(blockpos);
974 infostream<<"Map::setNodeMetadata(): Need to emerge "
975 <<PP(blockpos)<<std::endl;
976 block = emergeBlock(blockpos, false);
979 warningstream<<"Map::setNodeMetadata(): Block not found"
983 block->m_node_metadata.set(p_rel, meta);
987 void Map::removeNodeMetadata(v3s16 p)
989 v3s16 blockpos = getNodeBlockPos(p);
990 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
991 MapBlock *block = getBlockNoCreateNoEx(blockpos);
994 warningstream<<"Map::removeNodeMetadata(): Block not found"
998 block->m_node_metadata.remove(p_rel);
1001 NodeTimer Map::getNodeTimer(v3s16 p)
1003 v3s16 blockpos = getNodeBlockPos(p);
1004 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1005 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1007 infostream<<"Map::getNodeTimer(): Need to emerge "
1008 <<PP(blockpos)<<std::endl;
1009 block = emergeBlock(blockpos, false);
1012 warningstream<<"Map::getNodeTimer(): Block not found"
1016 NodeTimer t = block->m_node_timers.get(p_rel);
1017 NodeTimer nt(t.timeout, t.elapsed, p);
1021 void Map::setNodeTimer(const NodeTimer &t)
1023 v3s16 p = t.position;
1024 v3s16 blockpos = getNodeBlockPos(p);
1025 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1026 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1028 infostream<<"Map::setNodeTimer(): Need to emerge "
1029 <<PP(blockpos)<<std::endl;
1030 block = emergeBlock(blockpos, false);
1033 warningstream<<"Map::setNodeTimer(): Block not found"
1037 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1038 block->m_node_timers.set(nt);
1041 void Map::removeNodeTimer(v3s16 p)
1043 v3s16 blockpos = getNodeBlockPos(p);
1044 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1045 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1048 warningstream<<"Map::removeNodeTimer(): Block not found"
1052 block->m_node_timers.remove(p_rel);
1055 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1056 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1059 This functions determines the node inside the target block that is
1060 closest to the camera position. This increases the occlusion culling
1061 accuracy in straight and diagonal corridors.
1062 The returned position will be occlusion checked first in addition to the
1063 others (8 corners + center).
1064 No position is returned if
1065 - the closest node is a corner, corners are checked anyway.
1066 - the camera is inside the target block, it will never be occluded.
1068 #define CLOSEST_EDGE(pos, bounds, axis) \
1069 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1070 (bounds).MaxEdge.axis
1072 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1073 (pos_camera.X <= block_bounds.MaxEdge.X);
1074 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1075 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1076 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1077 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1079 if (x_inside && y_inside && z_inside)
1080 return false; // Camera inside target mapblock
1083 if (x_inside && y_inside) {
1084 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1085 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1087 } else if (y_inside && z_inside) {
1088 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1089 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1091 } else if (x_inside && z_inside) {
1092 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1093 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1099 check = v3s16(pos_camera.X, 0, 0);
1100 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1101 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1103 } else if (y_inside) {
1104 check = v3s16(0, pos_camera.Y, 0);
1105 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1106 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1108 } else if (z_inside) {
1109 check = v3s16(0, 0, pos_camera.Z);
1110 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1111 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1115 // Closest node would be a corner, none returned
1119 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1120 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1122 v3f direction = intToFloat(pos_target - pos_camera, BS);
1123 float distance = direction.getLength();
1125 // Normalize direction vector
1126 if (distance > 0.0f)
1127 direction /= distance;
1129 v3f pos_origin_f = intToFloat(pos_camera, BS);
1131 bool is_valid_position;
1133 for (; offset < distance + end_offset; offset += step) {
1134 v3f pos_node_f = pos_origin_f + direction * offset;
1135 v3s16 pos_node = floatToInt(pos_node_f, BS);
1137 MapNode node = getNode(pos_node, &is_valid_position);
1139 if (is_valid_position &&
1140 !m_nodedef->get(node).light_propagates) {
1141 // Cannot see through light-blocking nodes --> occluded
1143 if (count >= needed_count)
1151 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1153 // Check occlusion for center and all 8 corners of the mapblock
1154 // Overshoot a little for less flickering
1155 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1156 static const v3s16 dir9[9] = {
1158 v3s16( 1, 1, 1) * bs2,
1159 v3s16( 1, 1, -1) * bs2,
1160 v3s16( 1, -1, 1) * bs2,
1161 v3s16( 1, -1, -1) * bs2,
1162 v3s16(-1, 1, 1) * bs2,
1163 v3s16(-1, 1, -1) * bs2,
1164 v3s16(-1, -1, 1) * bs2,
1165 v3s16(-1, -1, -1) * bs2,
1168 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1170 // Starting step size, value between 1m and sqrt(3)m
1171 float step = BS * 1.2f;
1172 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1173 float stepfac = 1.05f;
1175 float start_offset = BS * 1.0f;
1177 // The occlusion search of 'isOccluded()' must stop short of the target
1178 // point by distance 'end_offset' to not enter the target mapblock.
1179 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1180 // diagonal of a mapblock, because we must consider all view angles.
1181 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1182 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1184 // to reduce the likelihood of falsely occluded blocks
1185 // require at least two solid blocks
1186 // this is a HACK, we should think of a more precise algorithm
1187 u32 needed_count = 2;
1189 // Additional occlusion check, see comments in that function
1191 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1192 // node is always on a side facing the camera, end_offset can be lower
1193 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1194 -1.0f, needed_count))
1198 for (const v3s16 &dir : dir9) {
1199 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1200 start_offset, end_offset, needed_count))
1209 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1210 EmergeManager *emerge, MetricsBackend *mb):
1212 settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
1215 verbosestream<<FUNCTION_NAME<<std::endl;
1217 // Tell the EmergeManager about our MapSettingsManager
1218 emerge->map_settings_mgr = &settings_mgr;
1221 Try to load map; if not found, create a new one.
1224 // Determine which database backend to use
1225 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1227 bool succeeded = conf.readConfigFile(conf_path.c_str());
1228 if (!succeeded || !conf.exists("backend")) {
1229 // fall back to sqlite3
1230 conf.set("backend", "sqlite3");
1232 std::string backend = conf.get("backend");
1233 dbase = createDatabase(backend, savedir, conf);
1234 if (conf.exists("readonly_backend")) {
1235 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1236 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1238 if (!conf.updateConfigFile(conf_path.c_str()))
1239 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1241 m_savedir = savedir;
1242 m_map_saving_enabled = false;
1244 m_save_time_counter = mb->addCounter(
1245 "minetest_map_save_time", "Time spent saving blocks (in microseconds)");
1246 m_save_count_counter = mb->addCounter(
1247 "minetest_map_saved_blocks", "Number of blocks saved");
1248 m_loaded_blocks_gauge = mb->addGauge(
1249 "minetest_map_loaded_blocks", "Number of loaded blocks");
1251 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1254 // If directory exists, check contents and load if possible
1255 if (fs::PathExists(m_savedir)) {
1256 // If directory is empty, it is safe to save into it.
1257 if (fs::GetDirListing(m_savedir).empty()) {
1258 infostream<<"ServerMap: Empty save directory is valid."
1260 m_map_saving_enabled = true;
1265 if (settings_mgr.loadMapMeta()) {
1266 infostream << "ServerMap: Metadata loaded from "
1267 << savedir << std::endl;
1269 infostream << "ServerMap: Metadata could not be loaded "
1270 "from " << savedir << ", assuming valid save "
1271 "directory." << std::endl;
1274 m_map_saving_enabled = true;
1275 // Map loaded, not creating new one
1279 // If directory doesn't exist, it is safe to save to it
1281 m_map_saving_enabled = true;
1284 catch(std::exception &e)
1286 warningstream<<"ServerMap: Failed to load map from "<<savedir
1287 <<", exception: "<<e.what()<<std::endl;
1288 infostream<<"Please remove the map or fix it."<<std::endl;
1289 warningstream<<"Map saving will be disabled."<<std::endl;
1293 ServerMap::~ServerMap()
1295 verbosestream<<FUNCTION_NAME<<std::endl;
1299 if (m_map_saving_enabled) {
1300 // Save only changed parts
1301 save(MOD_STATE_WRITE_AT_UNLOAD);
1302 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1304 infostream << "ServerMap: Map not saved" << std::endl;
1307 catch(std::exception &e)
1309 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1310 <<", exception: "<<e.what()<<std::endl;
1314 Close database if it was opened
1320 MapgenParams *ServerMap::getMapgenParams()
1322 // getMapgenParams() should only ever be called after Server is initialized
1323 assert(settings_mgr.mapgen_params != NULL);
1324 return settings_mgr.mapgen_params;
1327 u64 ServerMap::getSeed()
1329 return getMapgenParams()->seed;
1332 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1334 const s16 mapgen_limit_bp = rangelim(
1335 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1337 return p.X < -mapgen_limit_bp ||
1338 p.X > mapgen_limit_bp ||
1339 p.Y < -mapgen_limit_bp ||
1340 p.Y > mapgen_limit_bp ||
1341 p.Z < -mapgen_limit_bp ||
1342 p.Z > mapgen_limit_bp;
1345 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1347 s16 csize = getMapgenParams()->chunksize;
1348 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1349 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1351 if (!m_chunks_in_progress.insert(bpmin).second)
1354 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1355 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1357 v3s16 extra_borders(1, 1, 1);
1358 v3s16 full_bpmin = bpmin - extra_borders;
1359 v3s16 full_bpmax = bpmax + extra_borders;
1361 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1362 if (blockpos_over_mapgen_limit(full_bpmin) ||
1363 blockpos_over_mapgen_limit(full_bpmax))
1366 data->seed = getSeed();
1367 data->blockpos_min = bpmin;
1368 data->blockpos_max = bpmax;
1369 data->nodedef = m_nodedef;
1372 Create the whole area of this and the neighboring blocks
1374 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1375 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1376 v2s16 sectorpos(x, z);
1377 // Sector metadata is loaded from disk if not already loaded.
1378 MapSector *sector = createSector(sectorpos);
1379 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1381 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1384 MapBlock *block = emergeBlock(p, false);
1385 if (block == NULL) {
1386 block = createBlock(p);
1388 // Block gets sunlight if this is true.
1389 // Refer to the map generator heuristics.
1390 bool ug = m_emerge->isBlockUnderground(p);
1391 block->setIsUnderground(ug);
1397 Now we have a big empty area.
1399 Make a ManualMapVoxelManipulator that contains this and the
1403 data->vmanip = new MMVManip(this);
1404 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1406 // Data is ready now.
1410 void ServerMap::finishBlockMake(BlockMakeData *data,
1411 std::map<v3s16, MapBlock*> *changed_blocks)
1413 v3s16 bpmin = data->blockpos_min;
1414 v3s16 bpmax = data->blockpos_max;
1416 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1417 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1420 Blit generated stuff to map
1421 NOTE: blitBackAll adds nearly everything to changed_blocks
1423 data->vmanip->blitBackAll(changed_blocks);
1425 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1426 << changed_blocks->size());
1429 Copy transforming liquid information
1431 while (data->transforming_liquid.size()) {
1432 m_transforming_liquid.push_back(data->transforming_liquid.front());
1433 data->transforming_liquid.pop_front();
1436 for (auto &changed_block : *changed_blocks) {
1437 MapBlock *block = changed_block.second;
1441 Update day/night difference cache of the MapBlocks
1443 block->expireDayNightDiff();
1445 Set block as modified
1447 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1448 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1452 Set central blocks as generated
1454 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1455 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1456 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1457 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1461 block->setGenerated(true);
1465 Save changed parts of map
1466 NOTE: Will be saved later.
1468 //save(MOD_STATE_WRITE_AT_UNLOAD);
1469 m_chunks_in_progress.erase(bpmin);
1472 MapSector *ServerMap::createSector(v2s16 p2d)
1475 Check if it exists already in memory
1477 MapSector *sector = getSectorNoGenerate(p2d);
1482 Do not create over max mapgen limit
1484 if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y)))
1485 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1488 Generate blank sector
1491 sector = new MapSector(this, p2d, m_gamedef);
1496 m_sectors[p2d] = sector;
1501 MapBlock * ServerMap::createBlock(v3s16 p)
1504 Do not create over max mapgen limit
1506 if (blockpos_over_max_limit(p))
1507 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1509 v2s16 p2d(p.X, p.Z);
1512 This will create or load a sector if not found in memory.
1513 If block exists on disk, it will be loaded.
1515 NOTE: On old save formats, this will be slow, as it generates
1516 lighting on blocks for them.
1520 sector = createSector(p2d);
1521 } catch (InvalidPositionException &e) {
1522 infostream<<"createBlock: createSector() failed"<<std::endl;
1527 Try to get a block from the sector
1530 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1532 if(block->isDummy())
1537 block = sector->createBlankBlock(block_y);
1542 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1545 MapBlock *block = getBlockNoCreateNoEx(p);
1546 if (block && !block->isDummy())
1551 MapBlock *block = loadBlock(p);
1557 MapSector *sector = createSector(v2s16(p.X, p.Z));
1558 MapBlock *block = sector->createBlankBlock(p.Y);
1566 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1568 MapBlock *block = getBlockNoCreateNoEx(p3d);
1570 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1575 bool ServerMap::isBlockInQueue(v3s16 pos)
1577 return m_emerge && m_emerge->isBlockInQueue(pos);
1580 void ServerMap::addNodeAndUpdate(v3s16 p, MapNode n,
1581 std::map<v3s16, MapBlock*> &modified_blocks,
1582 bool remove_metadata)
1584 Map::addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1587 Add neighboring liquid nodes and this node to transform queue.
1588 (it's vital for the node itself to get updated last, if it was removed.)
1591 for (const v3s16 &dir : g_7dirs) {
1594 bool is_valid_position;
1595 MapNode n2 = getNode(p2, &is_valid_position);
1596 if(is_valid_position &&
1597 (m_nodedef->get(n2).isLiquid() ||
1598 n2.getContent() == CONTENT_AIR))
1599 m_transforming_liquid.push_back(p2);
1603 // N.B. This requires no synchronization, since data will not be modified unless
1604 // the VoxelManipulator being updated belongs to the same thread.
1605 void ServerMap::updateVManip(v3s16 pos)
1607 Mapgen *mg = m_emerge->getCurrentMapgen();
1611 MMVManip *vm = mg->vm;
1615 if (!vm->m_area.contains(pos))
1618 s32 idx = vm->m_area.index(pos);
1619 vm->m_data[idx] = getNode(pos);
1620 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1622 vm->m_is_dirty = true;
1625 void ServerMap::reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks)
1627 m_loaded_blocks_gauge->set(all_blocks);
1628 m_save_time_counter->increment(save_time_us);
1629 m_save_count_counter->increment(saved_blocks);
1632 void ServerMap::save(ModifiedState save_level)
1634 if (!m_map_saving_enabled) {
1635 warningstream<<"Not saving map, saving disabled."<<std::endl;
1639 const auto start_time = porting::getTimeUs();
1641 if(save_level == MOD_STATE_CLEAN)
1642 infostream<<"ServerMap: Saving whole map, this can take time."
1645 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1646 if (settings_mgr.saveMapMeta())
1647 m_map_metadata_changed = false;
1650 // Profile modified reasons
1651 Profiler modprofiler;
1653 u32 block_count = 0;
1654 u32 block_count_all = 0; // Number of blocks in memory
1656 // Don't do anything with sqlite unless something is really saved
1657 bool save_started = false;
1659 for (auto §or_it : m_sectors) {
1660 MapSector *sector = sector_it.second;
1662 MapBlockVect blocks;
1663 sector->getBlocks(blocks);
1665 for (MapBlock *block : blocks) {
1668 if(block->getModified() >= (u32)save_level) {
1672 save_started = true;
1675 modprofiler.add(block->getModifiedReasonString(), 1);
1687 Only print if something happened or saved whole map
1689 if(save_level == MOD_STATE_CLEAN
1690 || block_count != 0) {
1691 infostream << "ServerMap: Written: "
1692 << block_count << " blocks"
1693 << ", " << block_count_all << " blocks in memory."
1695 PrintInfo(infostream); // ServerMap/ClientMap:
1696 infostream<<"Blocks modified by: "<<std::endl;
1697 modprofiler.print(infostream);
1700 const auto end_time = porting::getTimeUs();
1701 reportMetrics(end_time - start_time, block_count, block_count_all);
1704 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1706 dbase->listAllLoadableBlocks(dst);
1708 dbase_ro->listAllLoadableBlocks(dst);
1711 MapDatabase *ServerMap::createDatabase(
1712 const std::string &name,
1713 const std::string &savedir,
1716 if (name == "sqlite3")
1717 return new MapDatabaseSQLite3(savedir);
1718 if (name == "dummy")
1719 return new Database_Dummy();
1721 if (name == "leveldb")
1722 return new Database_LevelDB(savedir);
1725 if (name == "redis")
1726 return new Database_Redis(conf);
1729 if (name == "postgresql") {
1730 std::string connect_string;
1731 conf.getNoEx("pgsql_connection", connect_string);
1732 return new MapDatabasePostgreSQL(connect_string);
1736 throw BaseException(std::string("Database backend ") + name + " not supported.");
1739 void ServerMap::beginSave()
1744 void ServerMap::endSave()
1749 bool ServerMap::saveBlock(MapBlock *block)
1751 return saveBlock(block, dbase, m_map_compression_level);
1754 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1756 v3s16 p3d = block->getPos();
1758 // Dummy blocks are not written
1759 if (block->isDummy()) {
1760 warningstream << "saveBlock: Not writing dummy block "
1761 << PP(p3d) << std::endl;
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 std::map<v3s16, MapBlock *>::iterator it;
1874 for (it = modified_blocks.begin();
1875 it != modified_blocks.end(); ++it)
1876 event.modified_blocks.insert(it->first);
1877 dispatchEvent(event);
1883 bool ServerMap::deleteBlock(v3s16 blockpos)
1885 if (!dbase->deleteBlock(blockpos))
1888 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1890 v2s16 p2d(blockpos.X, blockpos.Z);
1891 MapSector *sector = getSectorNoGenerate(p2d);
1894 sector->deleteBlock(block);
1900 void ServerMap::PrintInfo(std::ostream &out)
1905 bool ServerMap::repairBlockLight(v3s16 blockpos,
1906 std::map<v3s16, MapBlock *> *modified_blocks)
1908 MapBlock *block = emergeBlock(blockpos, false);
1909 if (!block || !block->isGenerated())
1911 voxalgo::repair_block_light(this, block, modified_blocks);
1915 MMVManip::MMVManip(Map *map):
1922 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1923 bool load_if_inexistent)
1925 TimeTaker timer1("initialEmerge", &emerge_time);
1929 // Units of these are MapBlocks
1930 v3s16 p_min = blockpos_min;
1931 v3s16 p_max = blockpos_max;
1933 VoxelArea block_area_nodes
1934 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1936 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1939 infostream<<"initialEmerge: area: ";
1940 block_area_nodes.print(infostream);
1941 infostream<<" ("<<size_MB<<"MB)";
1942 infostream<<std::endl;
1945 addArea(block_area_nodes);
1947 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1948 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1949 for(s32 x=p_min.X; x<=p_max.X; x++)
1954 std::map<v3s16, u8>::iterator n;
1955 n = m_loaded_blocks.find(p);
1956 if(n != m_loaded_blocks.end())
1959 bool block_data_inexistent = false;
1961 TimeTaker timer2("emerge load", &emerge_load_time);
1963 block = m_map->getBlockNoCreateNoEx(p);
1964 if (!block || block->isDummy())
1965 block_data_inexistent = true;
1967 block->copyTo(*this);
1970 if(block_data_inexistent)
1973 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1974 ServerMap *svrmap = (ServerMap *)m_map;
1975 block = svrmap->emergeBlock(p, false);
1977 block = svrmap->createBlock(p);
1978 block->copyTo(*this);
1980 flags |= VMANIP_BLOCK_DATA_INEXIST;
1983 Mark area inexistent
1985 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1986 // Fill with VOXELFLAG_NO_DATA
1987 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1988 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1990 s32 i = m_area.index(a.MinEdge.X,y,z);
1991 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1995 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1997 // Mark that block was loaded as blank
1998 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2001 m_loaded_blocks[p] = flags;
2007 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2008 bool overwrite_generated)
2010 if(m_area.getExtent() == v3s16(0,0,0))
2015 Copy data of all blocks
2017 for (auto &loaded_block : m_loaded_blocks) {
2018 v3s16 p = loaded_block.first;
2019 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2020 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2021 if (!existed || (block == NULL) ||
2022 (!overwrite_generated && block->isGenerated()))
2025 block->copyFrom(*this);
2026 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2029 (*modified_blocks)[p] = block;
2033 MMVManip *MMVManip::clone() const
2035 MMVManip *ret = new MMVManip();
2037 const s32 size = m_area.getVolume();
2038 ret->m_area = m_area;
2040 ret->m_data = new MapNode[size];
2041 memcpy(ret->m_data, m_data, size * sizeof(MapNode));
2044 ret->m_flags = new u8[size];
2045 memcpy(ret->m_flags, m_flags, size * sizeof(u8));
2048 ret->m_is_dirty = m_is_dirty;
2049 // Even if the copy is disconnected from a map object keep the information
2050 // needed to write it back to one
2051 ret->m_loaded_blocks = m_loaded_blocks;
2056 void MMVManip::reparent(Map *map)
2058 assert(map && !m_map);