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;
329 const auto start_time = porting::getTimeUs();
332 // If there is no practical limit, we spare creation of mapblock_queue
333 if (max_loaded_blocks < 0) {
334 for (auto §or_it : m_sectors) {
335 MapSector *sector = sector_it.second;
337 bool all_blocks_deleted = true;
340 sector->getBlocks(blocks);
342 for (MapBlock *block : blocks) {
343 block->incrementUsageTimer(dtime);
345 if (block->refGet() == 0
346 && block->getUsageTimer() > unload_timeout) {
347 v3s16 p = block->getPos();
350 if (block->getModified() != MOD_STATE_CLEAN
351 && save_before_unloading) {
352 modprofiler.add(block->getModifiedReasonString(), 1);
353 if (!saveBlock(block))
355 saved_blocks_count++;
358 // Delete from memory
359 sector->deleteBlock(block);
362 unloaded_blocks->push_back(p);
364 deleted_blocks_count++;
366 all_blocks_deleted = false;
371 // Delete sector if we emptied it
372 if (all_blocks_deleted) {
373 sector_deletion_queue.push_back(sector_it.first);
377 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
378 for (auto §or_it : m_sectors) {
379 MapSector *sector = sector_it.second;
382 sector->getBlocks(blocks);
384 for (MapBlock *block : blocks) {
385 block->incrementUsageTimer(dtime);
386 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
389 block_count_all = mapblock_queue.size();
391 // Delete old blocks, and blocks over the limit from the memory
392 while (!mapblock_queue.empty() && ((s32)mapblock_queue.size() > max_loaded_blocks
393 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
394 TimeOrderedMapBlock b = mapblock_queue.top();
395 mapblock_queue.pop();
397 MapBlock *block = b.block;
399 if (block->refGet() != 0)
402 v3s16 p = block->getPos();
405 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
406 modprofiler.add(block->getModifiedReasonString(), 1);
407 if (!saveBlock(block))
409 saved_blocks_count++;
412 // Delete from memory
413 b.sect->deleteBlock(block);
416 unloaded_blocks->push_back(p);
418 deleted_blocks_count++;
422 // Delete empty sectors
423 for (auto §or_it : m_sectors) {
424 if (sector_it.second->empty()) {
425 sector_deletion_queue.push_back(sector_it.first);
431 const auto end_time = porting::getTimeUs();
433 reportMetrics(end_time - start_time, saved_blocks_count, block_count_all);
435 // Finally delete the empty sectors
436 deleteSectors(sector_deletion_queue);
438 if(deleted_blocks_count != 0)
440 PrintInfo(infostream); // ServerMap/ClientMap:
441 infostream<<"Unloaded "<<deleted_blocks_count
442 <<" blocks from memory";
443 if(save_before_unloading)
444 infostream<<", of which "<<saved_blocks_count<<" were written";
445 infostream<<", "<<block_count_all<<" blocks in memory";
446 infostream<<"."<<std::endl;
447 if(saved_blocks_count != 0){
448 PrintInfo(infostream); // ServerMap/ClientMap:
449 infostream<<"Blocks modified by: "<<std::endl;
450 modprofiler.print(infostream);
455 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
457 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
460 void Map::deleteSectors(std::vector<v2s16> §orList)
462 for (v2s16 j : sectorList) {
463 MapSector *sector = m_sectors[j];
464 // If sector is in sector cache, remove it from there
465 if(m_sector_cache == sector)
466 m_sector_cache = NULL;
467 // Remove from map and delete
473 void Map::PrintInfo(std::ostream &out)
478 #define WATER_DROP_BOOST 4
480 const static v3s16 liquid_6dirs[6] = {
481 // order: upper before same level before lower
490 enum NeighborType : u8 {
496 struct NodeNeighbor {
502 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
505 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
512 void ServerMap::transforming_liquid_add(v3s16 p) {
513 m_transforming_liquid.push_back(p);
516 void ServerMap::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
517 ServerEnvironment *env)
520 u32 initial_size = m_transforming_liquid.size();
522 /*if(initial_size != 0)
523 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
525 // list of nodes that due to viscosity have not reached their max level height
526 std::vector<v3s16> must_reflow;
528 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
530 std::vector<v3s16> check_for_falling;
532 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
533 u32 loop_max = liquid_loop_max;
535 while (m_transforming_liquid.size() != 0)
537 // This should be done here so that it is done when continue is used
538 if (loopcount >= initial_size || loopcount >= loop_max)
543 Get a queued transforming liquid node
545 v3s16 p0 = m_transforming_liquid.front();
546 m_transforming_liquid.pop_front();
548 MapNode n0 = getNode(p0);
551 Collect information about current node
553 s8 liquid_level = -1;
554 // The liquid node which will be placed there if
555 // the liquid flows into this node.
556 content_t liquid_kind = CONTENT_IGNORE;
557 // The node which will be placed there if liquid
558 // can't flow into this node.
559 content_t floodable_node = CONTENT_AIR;
560 const ContentFeatures &cf = m_nodedef->get(n0);
561 LiquidType liquid_type = cf.liquid_type;
562 switch (liquid_type) {
564 liquid_level = LIQUID_LEVEL_SOURCE;
565 liquid_kind = cf.liquid_alternative_flowing_id;
568 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
569 liquid_kind = n0.getContent();
572 // if this node is 'floodable', it *could* be transformed
573 // into a liquid, otherwise, continue with the next node.
576 floodable_node = n0.getContent();
577 liquid_kind = CONTENT_AIR;
582 Collect information about the environment
584 NodeNeighbor sources[6]; // surrounding sources
586 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
588 NodeNeighbor airs[6]; // surrounding air
590 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
591 int num_neutrals = 0;
592 bool flowing_down = false;
593 bool ignored_sources = false;
594 bool floating_node_above = false;
595 for (u16 i = 0; i < 6; i++) {
596 NeighborType nt = NEIGHBOR_SAME_LEVEL;
607 v3s16 npos = p0 + liquid_6dirs[i];
608 NodeNeighbor nb(getNode(npos), nt, npos);
609 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
610 if (nt == NEIGHBOR_UPPER && cfnb.floats)
611 floating_node_above = true;
612 switch (cfnb.liquid_type) {
614 if (cfnb.floodable) {
615 airs[num_airs++] = nb;
616 // if the current node is a water source the neighbor
617 // should be enqueded for transformation regardless of whether the
618 // current node changes or not.
619 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
620 m_transforming_liquid.push_back(npos);
621 // if the current node happens to be a flowing node, it will start to flow down here.
622 if (nb.t == NEIGHBOR_LOWER)
625 neutrals[num_neutrals++] = nb;
626 if (nb.n.getContent() == CONTENT_IGNORE) {
627 // If node below is ignore prevent water from
628 // spreading outwards and otherwise prevent from
629 // flowing away as ignore node might be the source
630 if (nb.t == NEIGHBOR_LOWER)
633 ignored_sources = true;
638 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
639 if (liquid_kind == CONTENT_AIR)
640 liquid_kind = cfnb.liquid_alternative_flowing_id;
641 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
642 neutrals[num_neutrals++] = nb;
644 // Do not count bottom source, it will screw things up
645 if(nt != NEIGHBOR_LOWER)
646 sources[num_sources++] = nb;
650 if (nb.t != NEIGHBOR_SAME_LEVEL ||
651 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
652 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
653 // but exclude falling liquids on the same level, they cannot flow here anyway
654 if (liquid_kind == CONTENT_AIR)
655 liquid_kind = cfnb.liquid_alternative_flowing_id;
657 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
658 neutrals[num_neutrals++] = nb;
660 flows[num_flows++] = nb;
661 if (nb.t == NEIGHBOR_LOWER)
669 decide on the type (and possibly level) of the current node
671 content_t new_node_content;
672 s8 new_node_level = -1;
673 s8 max_node_level = -1;
675 u8 range = m_nodedef->get(liquid_kind).liquid_range;
676 if (range > LIQUID_LEVEL_MAX + 1)
677 range = LIQUID_LEVEL_MAX + 1;
679 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
680 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
681 // or the flowing alternative of the first of the surrounding sources (if it's air), so
682 // it's perfectly safe to use liquid_kind here to determine the new node content.
683 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
684 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
685 // liquid_kind is set properly, see above
686 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
687 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
688 new_node_content = liquid_kind;
690 new_node_content = floodable_node;
691 } else if (ignored_sources && liquid_level >= 0) {
692 // Maybe there are neighboring sources that aren't loaded yet
693 // so prevent flowing away.
694 new_node_level = liquid_level;
695 new_node_content = liquid_kind;
697 // no surrounding sources, so get the maximum level that can flow into this node
698 for (u16 i = 0; i < num_flows; i++) {
699 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
700 switch (flows[i].t) {
702 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
703 max_node_level = LIQUID_LEVEL_MAX;
704 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
705 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
706 } else if (nb_liquid_level > max_node_level) {
707 max_node_level = nb_liquid_level;
712 case NEIGHBOR_SAME_LEVEL:
713 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
714 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
715 max_node_level = nb_liquid_level - 1;
720 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
721 if (viscosity > 1 && max_node_level != liquid_level) {
722 // amount to gain, limited by viscosity
723 // must be at least 1 in absolute value
724 s8 level_inc = max_node_level - liquid_level;
725 if (level_inc < -viscosity || level_inc > viscosity)
726 new_node_level = liquid_level + level_inc/viscosity;
727 else if (level_inc < 0)
728 new_node_level = liquid_level - 1;
729 else if (level_inc > 0)
730 new_node_level = liquid_level + 1;
731 if (new_node_level != max_node_level)
732 must_reflow.push_back(p0);
734 new_node_level = max_node_level;
737 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
738 new_node_content = liquid_kind;
740 new_node_content = floodable_node;
745 check if anything has changed. if not, just continue with the next node.
747 if (new_node_content == n0.getContent() &&
748 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
749 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
750 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
755 check if there is a floating node above that needs to be updated.
757 if (floating_node_above && new_node_content == CONTENT_AIR)
758 check_for_falling.push_back(p0);
761 update the current node
764 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
765 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
766 // set level to last 3 bits, flowing down bit to 4th bit
767 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
769 // set the liquid level and flow bits to 0
770 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
774 n0.setContent(new_node_content);
776 // on_flood() the node
777 if (floodable_node != CONTENT_AIR) {
778 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
782 // Ignore light (because calling voxalgo::update_lighting_nodes)
783 ContentLightingFlags f0 = m_nodedef->getLightingFlags(n0);
784 n0.setLight(LIGHTBANK_DAY, 0, f0);
785 n0.setLight(LIGHTBANK_NIGHT, 0, f0);
787 // Find out whether there is a suspect for this action
789 if (m_gamedef->rollback())
790 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
792 if (m_gamedef->rollback() && !suspect.empty()) {
794 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
795 // Get old node for rollback
796 RollbackNode rollback_oldnode(this, p0, m_gamedef);
800 RollbackNode rollback_newnode(this, p0, m_gamedef);
801 RollbackAction action;
802 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
803 m_gamedef->rollback()->reportAction(action);
809 v3s16 blockpos = getNodeBlockPos(p0);
810 MapBlock *block = getBlockNoCreateNoEx(blockpos);
812 modified_blocks[blockpos] = block;
813 changed_nodes.emplace_back(p0, n00);
817 enqueue neighbors for update if necessary
819 switch (m_nodedef->get(n0.getContent()).liquid_type) {
822 // make sure source flows into all neighboring nodes
823 for (u16 i = 0; i < num_flows; i++)
824 if (flows[i].t != NEIGHBOR_UPPER)
825 m_transforming_liquid.push_back(flows[i].p);
826 for (u16 i = 0; i < num_airs; i++)
827 if (airs[i].t != NEIGHBOR_UPPER)
828 m_transforming_liquid.push_back(airs[i].p);
831 // this flow has turned to air; neighboring flows might need to do the same
832 for (u16 i = 0; i < num_flows; i++)
833 m_transforming_liquid.push_back(flows[i].p);
837 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
839 for (const auto &iter : must_reflow)
840 m_transforming_liquid.push_back(iter);
842 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
844 for (const v3s16 &p : check_for_falling) {
845 env->getScriptIface()->check_for_falling(p);
848 env->getScriptIface()->on_liquid_transformed(changed_nodes);
850 /* ----------------------------------------------------------------------
851 * Manage the queue so that it does not grow indefinitely
853 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
855 if (time_until_purge == 0)
856 return; // Feature disabled
858 time_until_purge *= 1000; // seconds -> milliseconds
860 u64 curr_time = porting::getTimeMs();
861 u32 prev_unprocessed = m_unprocessed_count;
862 m_unprocessed_count = m_transforming_liquid.size();
864 // if unprocessed block count is decreasing or stable
865 if (m_unprocessed_count <= prev_unprocessed) {
866 m_queue_size_timer_started = false;
868 if (!m_queue_size_timer_started)
869 m_inc_trending_up_start_time = curr_time;
870 m_queue_size_timer_started = true;
873 // Account for curr_time overflowing
874 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
875 m_queue_size_timer_started = false;
877 /* If the queue has been growing for more than liquid_queue_purge_time seconds
878 * and the number of unprocessed blocks is still > liquid_loop_max then we
879 * cannot keep up; dump the oldest blocks from the queue so that the queue
880 * has liquid_loop_max items in it
882 if (m_queue_size_timer_started
883 && curr_time - m_inc_trending_up_start_time > time_until_purge
884 && m_unprocessed_count > liquid_loop_max) {
886 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
888 infostream << "transformLiquids(): DUMPING " << dump_qty
889 << " blocks from the queue" << std::endl;
892 m_transforming_liquid.pop_front();
894 m_queue_size_timer_started = false; // optimistically assume we can keep up now
895 m_unprocessed_count = m_transforming_liquid.size();
899 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
901 std::vector<v3s16> positions_with_meta;
903 sortBoxVerticies(p1, p2);
904 v3s16 bpmin = getNodeBlockPos(p1);
905 v3s16 bpmax = getNodeBlockPos(p2);
907 VoxelArea area(p1, p2);
909 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
910 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
911 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
912 v3s16 blockpos(x, y, z);
914 MapBlock *block = getBlockNoCreateNoEx(blockpos);
916 verbosestream << "Map::getNodeMetadata(): Need to emerge "
917 << PP(blockpos) << std::endl;
918 block = emergeBlock(blockpos, false);
921 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
926 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
927 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
928 for (size_t i = 0; i != keys.size(); i++) {
929 v3s16 p(keys[i] + p_base);
930 if (!area.contains(p))
933 positions_with_meta.push_back(p);
937 return positions_with_meta;
940 NodeMetadata *Map::getNodeMetadata(v3s16 p)
942 v3s16 blockpos = getNodeBlockPos(p);
943 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
944 MapBlock *block = getBlockNoCreateNoEx(blockpos);
946 infostream<<"Map::getNodeMetadata(): Need to emerge "
947 <<PP(blockpos)<<std::endl;
948 block = emergeBlock(blockpos, false);
951 warningstream<<"Map::getNodeMetadata(): Block not found"
955 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
959 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
961 v3s16 blockpos = getNodeBlockPos(p);
962 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
963 MapBlock *block = getBlockNoCreateNoEx(blockpos);
965 infostream<<"Map::setNodeMetadata(): Need to emerge "
966 <<PP(blockpos)<<std::endl;
967 block = emergeBlock(blockpos, false);
970 warningstream<<"Map::setNodeMetadata(): Block not found"
974 block->m_node_metadata.set(p_rel, meta);
978 void Map::removeNodeMetadata(v3s16 p)
980 v3s16 blockpos = getNodeBlockPos(p);
981 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
982 MapBlock *block = getBlockNoCreateNoEx(blockpos);
985 warningstream<<"Map::removeNodeMetadata(): Block not found"
989 block->m_node_metadata.remove(p_rel);
992 NodeTimer Map::getNodeTimer(v3s16 p)
994 v3s16 blockpos = getNodeBlockPos(p);
995 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
996 MapBlock *block = getBlockNoCreateNoEx(blockpos);
998 infostream<<"Map::getNodeTimer(): Need to emerge "
999 <<PP(blockpos)<<std::endl;
1000 block = emergeBlock(blockpos, false);
1003 warningstream<<"Map::getNodeTimer(): Block not found"
1007 NodeTimer t = block->getNodeTimer(p_rel);
1008 NodeTimer nt(t.timeout, t.elapsed, p);
1012 void Map::setNodeTimer(const NodeTimer &t)
1014 v3s16 p = t.position;
1015 v3s16 blockpos = getNodeBlockPos(p);
1016 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1017 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1019 infostream<<"Map::setNodeTimer(): Need to emerge "
1020 <<PP(blockpos)<<std::endl;
1021 block = emergeBlock(blockpos, false);
1024 warningstream<<"Map::setNodeTimer(): Block not found"
1028 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1029 block->setNodeTimer(nt);
1032 void Map::removeNodeTimer(v3s16 p)
1034 v3s16 blockpos = getNodeBlockPos(p);
1035 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1036 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1039 warningstream<<"Map::removeNodeTimer(): Block not found"
1043 block->removeNodeTimer(p_rel);
1046 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1047 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1050 This functions determines the node inside the target block that is
1051 closest to the camera position. This increases the occlusion culling
1052 accuracy in straight and diagonal corridors.
1053 The returned position will be occlusion checked first in addition to the
1054 others (8 corners + center).
1055 No position is returned if
1056 - the closest node is a corner, corners are checked anyway.
1057 - the camera is inside the target block, it will never be occluded.
1059 #define CLOSEST_EDGE(pos, bounds, axis) \
1060 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1061 (bounds).MaxEdge.axis
1063 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1064 (pos_camera.X <= block_bounds.MaxEdge.X);
1065 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1066 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1067 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1068 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1070 if (x_inside && y_inside && z_inside)
1071 return false; // Camera inside target mapblock
1074 if (x_inside && y_inside) {
1075 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1076 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1078 } else if (y_inside && z_inside) {
1079 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1080 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1082 } else if (x_inside && z_inside) {
1083 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1084 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1090 check = v3s16(pos_camera.X, 0, 0);
1091 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1092 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1094 } else if (y_inside) {
1095 check = v3s16(0, pos_camera.Y, 0);
1096 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1097 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1099 } else if (z_inside) {
1100 check = v3s16(0, 0, pos_camera.Z);
1101 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1102 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1106 // Closest node would be a corner, none returned
1110 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1111 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1113 v3f direction = intToFloat(pos_target - pos_camera, BS);
1114 float distance = direction.getLength();
1116 // Normalize direction vector
1117 if (distance > 0.0f)
1118 direction /= distance;
1120 v3f pos_origin_f = intToFloat(pos_camera, BS);
1122 bool is_valid_position;
1124 for (; offset < distance + end_offset; offset += step) {
1125 v3f pos_node_f = pos_origin_f + direction * offset;
1126 v3s16 pos_node = floatToInt(pos_node_f, BS);
1128 MapNode node = getNode(pos_node, &is_valid_position);
1130 if (is_valid_position &&
1131 !m_nodedef->getLightingFlags(node).light_propagates) {
1132 // Cannot see through light-blocking nodes --> occluded
1134 if (count >= needed_count)
1142 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1144 // Check occlusion for center and all 8 corners of the mapblock
1145 // Overshoot a little for less flickering
1146 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1147 static const v3s16 dir9[9] = {
1149 v3s16( 1, 1, 1) * bs2,
1150 v3s16( 1, 1, -1) * bs2,
1151 v3s16( 1, -1, 1) * bs2,
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,
1159 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1161 // Starting step size, value between 1m and sqrt(3)m
1162 float step = BS * 1.2f;
1163 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1164 float stepfac = 1.05f;
1166 float start_offset = BS * 1.0f;
1168 // The occlusion search of 'isOccluded()' must stop short of the target
1169 // point by distance 'end_offset' to not enter the target mapblock.
1170 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1171 // diagonal of a mapblock, because we must consider all view angles.
1172 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1173 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1175 // to reduce the likelihood of falsely occluded blocks
1176 // require at least two solid blocks
1177 // this is a HACK, we should think of a more precise algorithm
1178 u32 needed_count = 2;
1180 // Additional occlusion check, see comments in that function
1182 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1183 // node is always on a side facing the camera, end_offset can be lower
1184 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1185 -1.0f, needed_count))
1189 for (const v3s16 &dir : dir9) {
1190 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1191 start_offset, end_offset, needed_count))
1200 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1201 EmergeManager *emerge, MetricsBackend *mb):
1203 settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
1206 verbosestream<<FUNCTION_NAME<<std::endl;
1208 // Tell the EmergeManager about our MapSettingsManager
1209 emerge->map_settings_mgr = &settings_mgr;
1212 Try to load map; if not found, create a new one.
1215 // Determine which database backend to use
1216 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1218 bool succeeded = conf.readConfigFile(conf_path.c_str());
1219 if (!succeeded || !conf.exists("backend")) {
1220 // fall back to sqlite3
1221 conf.set("backend", "sqlite3");
1223 std::string backend = conf.get("backend");
1224 dbase = createDatabase(backend, savedir, conf);
1225 if (conf.exists("readonly_backend")) {
1226 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1227 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1229 if (!conf.updateConfigFile(conf_path.c_str()))
1230 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1232 m_savedir = savedir;
1233 m_map_saving_enabled = false;
1235 m_save_time_counter = mb->addCounter(
1236 "minetest_map_save_time", "Time spent saving blocks (in microseconds)");
1237 m_save_count_counter = mb->addCounter(
1238 "minetest_map_saved_blocks", "Number of blocks saved");
1239 m_loaded_blocks_gauge = mb->addGauge(
1240 "minetest_map_loaded_blocks", "Number of loaded blocks");
1242 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1245 // If directory exists, check contents and load if possible
1246 if (fs::PathExists(m_savedir)) {
1247 // If directory is empty, it is safe to save into it.
1248 if (fs::GetDirListing(m_savedir).empty()) {
1249 infostream<<"ServerMap: Empty save directory is valid."
1251 m_map_saving_enabled = true;
1256 if (settings_mgr.loadMapMeta()) {
1257 infostream << "ServerMap: Metadata loaded from "
1258 << savedir << std::endl;
1260 infostream << "ServerMap: Metadata could not be loaded "
1261 "from " << savedir << ", assuming valid save "
1262 "directory." << std::endl;
1265 m_map_saving_enabled = true;
1266 // Map loaded, not creating new one
1270 // If directory doesn't exist, it is safe to save to it
1272 m_map_saving_enabled = true;
1275 catch(std::exception &e)
1277 warningstream<<"ServerMap: Failed to load map from "<<savedir
1278 <<", exception: "<<e.what()<<std::endl;
1279 infostream<<"Please remove the map or fix it."<<std::endl;
1280 warningstream<<"Map saving will be disabled."<<std::endl;
1284 ServerMap::~ServerMap()
1286 verbosestream<<FUNCTION_NAME<<std::endl;
1290 if (m_map_saving_enabled) {
1291 // Save only changed parts
1292 save(MOD_STATE_WRITE_AT_UNLOAD);
1293 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1295 infostream << "ServerMap: Map not saved" << std::endl;
1298 catch(std::exception &e)
1300 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1301 <<", exception: "<<e.what()<<std::endl;
1305 Close database if it was opened
1311 MapgenParams *ServerMap::getMapgenParams()
1313 // getMapgenParams() should only ever be called after Server is initialized
1314 assert(settings_mgr.mapgen_params != NULL);
1315 return settings_mgr.mapgen_params;
1318 u64 ServerMap::getSeed()
1320 return getMapgenParams()->seed;
1323 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1325 const s16 mapgen_limit_bp = rangelim(
1326 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1328 return p.X < -mapgen_limit_bp ||
1329 p.X > mapgen_limit_bp ||
1330 p.Y < -mapgen_limit_bp ||
1331 p.Y > mapgen_limit_bp ||
1332 p.Z < -mapgen_limit_bp ||
1333 p.Z > mapgen_limit_bp;
1336 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1338 s16 csize = getMapgenParams()->chunksize;
1339 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1340 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1342 if (!m_chunks_in_progress.insert(bpmin).second)
1345 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1346 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1348 v3s16 extra_borders(1, 1, 1);
1349 v3s16 full_bpmin = bpmin - extra_borders;
1350 v3s16 full_bpmax = bpmax + extra_borders;
1352 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1353 if (blockpos_over_mapgen_limit(full_bpmin) ||
1354 blockpos_over_mapgen_limit(full_bpmax))
1357 data->seed = getSeed();
1358 data->blockpos_min = bpmin;
1359 data->blockpos_max = bpmax;
1360 data->nodedef = m_nodedef;
1363 Create the whole area of this and the neighboring blocks
1365 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1366 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1367 v2s16 sectorpos(x, z);
1368 // Sector metadata is loaded from disk if not already loaded.
1369 MapSector *sector = createSector(sectorpos);
1370 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1372 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1375 MapBlock *block = emergeBlock(p, false);
1376 if (block == NULL) {
1377 block = createBlock(p);
1379 // Block gets sunlight if this is true.
1380 // Refer to the map generator heuristics.
1381 bool ug = m_emerge->isBlockUnderground(p);
1382 block->setIsUnderground(ug);
1388 Now we have a big empty area.
1390 Make a ManualMapVoxelManipulator that contains this and the
1394 data->vmanip = new MMVManip(this);
1395 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1397 // Data is ready now.
1401 void ServerMap::finishBlockMake(BlockMakeData *data,
1402 std::map<v3s16, MapBlock*> *changed_blocks)
1404 v3s16 bpmin = data->blockpos_min;
1405 v3s16 bpmax = data->blockpos_max;
1407 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1408 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1411 Blit generated stuff to map
1412 NOTE: blitBackAll adds nearly everything to changed_blocks
1414 data->vmanip->blitBackAll(changed_blocks);
1416 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1417 << changed_blocks->size());
1420 Copy transforming liquid information
1422 while (data->transforming_liquid.size()) {
1423 m_transforming_liquid.push_back(data->transforming_liquid.front());
1424 data->transforming_liquid.pop_front();
1427 for (auto &changed_block : *changed_blocks) {
1428 MapBlock *block = changed_block.second;
1432 Update day/night difference cache of the MapBlocks
1434 block->expireDayNightDiff();
1436 Set block as modified
1438 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1439 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1443 Set central blocks as generated
1445 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1446 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1447 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1448 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1452 block->setGenerated(true);
1456 Save changed parts of map
1457 NOTE: Will be saved later.
1459 //save(MOD_STATE_WRITE_AT_UNLOAD);
1460 m_chunks_in_progress.erase(bpmin);
1463 MapSector *ServerMap::createSector(v2s16 p2d)
1466 Check if it exists already in memory
1468 MapSector *sector = getSectorNoGenerate(p2d);
1473 Do not create over max mapgen limit
1475 if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y)))
1476 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1479 Generate blank sector
1482 sector = new MapSector(this, p2d, m_gamedef);
1487 m_sectors[p2d] = sector;
1492 MapBlock * ServerMap::createBlock(v3s16 p)
1495 Do not create over max mapgen limit
1497 if (blockpos_over_max_limit(p))
1498 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1500 v2s16 p2d(p.X, p.Z);
1503 This will create or load a sector if not found in memory.
1504 If block exists on disk, it will be loaded.
1506 NOTE: On old save formats, this will be slow, as it generates
1507 lighting on blocks for them.
1511 sector = createSector(p2d);
1512 } catch (InvalidPositionException &e) {
1513 infostream<<"createBlock: createSector() failed"<<std::endl;
1518 Try to get a block from the sector
1521 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1526 block = sector->createBlankBlock(block_y);
1531 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1534 MapBlock *block = getBlockNoCreateNoEx(p);
1540 MapBlock *block = loadBlock(p);
1546 MapSector *sector = createSector(v2s16(p.X, p.Z));
1547 MapBlock *block = sector->createBlankBlock(p.Y);
1555 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1557 MapBlock *block = getBlockNoCreateNoEx(p3d);
1559 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1564 bool ServerMap::isBlockInQueue(v3s16 pos)
1566 return m_emerge && m_emerge->isBlockInQueue(pos);
1569 void ServerMap::addNodeAndUpdate(v3s16 p, MapNode n,
1570 std::map<v3s16, MapBlock*> &modified_blocks,
1571 bool remove_metadata)
1573 Map::addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1576 Add neighboring liquid nodes and this node to transform queue.
1577 (it's vital for the node itself to get updated last, if it was removed.)
1580 for (const v3s16 &dir : g_7dirs) {
1583 bool is_valid_position;
1584 MapNode n2 = getNode(p2, &is_valid_position);
1585 if(is_valid_position &&
1586 (m_nodedef->get(n2).isLiquid() ||
1587 n2.getContent() == CONTENT_AIR))
1588 m_transforming_liquid.push_back(p2);
1592 // N.B. This requires no synchronization, since data will not be modified unless
1593 // the VoxelManipulator being updated belongs to the same thread.
1594 void ServerMap::updateVManip(v3s16 pos)
1596 Mapgen *mg = m_emerge->getCurrentMapgen();
1600 MMVManip *vm = mg->vm;
1604 if (!vm->m_area.contains(pos))
1607 s32 idx = vm->m_area.index(pos);
1608 vm->m_data[idx] = getNode(pos);
1609 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1611 vm->m_is_dirty = true;
1614 void ServerMap::reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks)
1616 m_loaded_blocks_gauge->set(all_blocks);
1617 m_save_time_counter->increment(save_time_us);
1618 m_save_count_counter->increment(saved_blocks);
1621 void ServerMap::save(ModifiedState save_level)
1623 if (!m_map_saving_enabled) {
1624 warningstream<<"Not saving map, saving disabled."<<std::endl;
1628 const auto start_time = porting::getTimeUs();
1630 if(save_level == MOD_STATE_CLEAN)
1631 infostream<<"ServerMap: Saving whole map, this can take time."
1634 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1635 if (settings_mgr.saveMapMeta())
1636 m_map_metadata_changed = false;
1639 // Profile modified reasons
1640 Profiler modprofiler;
1642 u32 block_count = 0;
1643 u32 block_count_all = 0; // Number of blocks in memory
1645 // Don't do anything with sqlite unless something is really saved
1646 bool save_started = false;
1648 for (auto §or_it : m_sectors) {
1649 MapSector *sector = sector_it.second;
1651 MapBlockVect blocks;
1652 sector->getBlocks(blocks);
1654 for (MapBlock *block : blocks) {
1657 if(block->getModified() >= (u32)save_level) {
1661 save_started = true;
1664 modprofiler.add(block->getModifiedReasonString(), 1);
1676 Only print if something happened or saved whole map
1678 if(save_level == MOD_STATE_CLEAN
1679 || block_count != 0) {
1680 infostream << "ServerMap: Written: "
1681 << block_count << " blocks"
1682 << ", " << block_count_all << " blocks in memory."
1684 PrintInfo(infostream); // ServerMap/ClientMap:
1685 infostream<<"Blocks modified by: "<<std::endl;
1686 modprofiler.print(infostream);
1689 const auto end_time = porting::getTimeUs();
1690 reportMetrics(end_time - start_time, block_count, block_count_all);
1693 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1695 dbase->listAllLoadableBlocks(dst);
1697 dbase_ro->listAllLoadableBlocks(dst);
1700 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1702 for (auto §or_it : m_sectors) {
1703 MapSector *sector = sector_it.second;
1705 MapBlockVect blocks;
1706 sector->getBlocks(blocks);
1708 for (MapBlock *block : blocks) {
1709 v3s16 p = block->getPos();
1715 MapDatabase *ServerMap::createDatabase(
1716 const std::string &name,
1717 const std::string &savedir,
1720 if (name == "sqlite3")
1721 return new MapDatabaseSQLite3(savedir);
1722 if (name == "dummy")
1723 return new Database_Dummy();
1725 if (name == "leveldb")
1726 return new Database_LevelDB(savedir);
1729 if (name == "redis")
1730 return new Database_Redis(conf);
1733 if (name == "postgresql") {
1734 std::string connect_string;
1735 conf.getNoEx("pgsql_connection", connect_string);
1736 return new MapDatabasePostgreSQL(connect_string);
1740 throw BaseException(std::string("Database backend ") + name + " not supported.");
1743 void ServerMap::beginSave()
1748 void ServerMap::endSave()
1753 bool ServerMap::saveBlock(MapBlock *block)
1755 return saveBlock(block, dbase, m_map_compression_level);
1758 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1760 v3s16 p3d = block->getPos();
1762 // Format used for writing
1763 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1766 [0] u8 serialization version
1769 std::ostringstream o(std::ios_base::binary);
1770 o.write((char*) &version, 1);
1771 block->serialize(o, version, true, compression_level);
1773 bool ret = db->saveBlock(p3d, o.str());
1775 // We just wrote it to the disk so clear modified flag
1776 block->resetModified();
1781 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1784 std::istringstream is(*blob, std::ios_base::binary);
1786 u8 version = SER_FMT_VER_INVALID;
1787 is.read((char*)&version, 1);
1790 throw SerializationError("ServerMap::loadBlock(): Failed"
1791 " to read MapBlock version");
1793 MapBlock *block = NULL;
1794 bool created_new = false;
1795 block = sector->getBlockNoCreateNoEx(p3d.Y);
1798 block = sector->createBlankBlockNoInsert(p3d.Y);
1803 block->deSerialize(is, version, true);
1805 // If it's a new block, insert it to the map
1807 sector->insertBlock(block);
1808 ReflowScan scanner(this, m_emerge->ndef);
1809 scanner.scan(block, &m_transforming_liquid);
1813 Save blocks loaded in old format in new format
1816 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1817 // Only save if asked to; no need to update version
1821 // We just loaded it from, so it's up-to-date.
1822 block->resetModified();
1824 catch(SerializationError &e)
1826 errorstream<<"Invalid block data in database"
1827 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1828 <<" (SerializationError): "<<e.what()<<std::endl;
1830 // TODO: Block should be marked as invalid in memory so that it is
1831 // not touched but the game can run
1833 if(g_settings->getBool("ignore_world_load_errors")){
1834 errorstream<<"Ignoring block load error. Duck and cover! "
1835 <<"(ignore_world_load_errors)"<<std::endl;
1837 throw SerializationError("Invalid block data in database");
1842 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1844 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1846 v2s16 p2d(blockpos.X, blockpos.Z);
1849 dbase->loadBlock(blockpos, &ret);
1851 loadBlock(&ret, blockpos, createSector(p2d), false);
1852 } else if (dbase_ro) {
1853 dbase_ro->loadBlock(blockpos, &ret);
1855 loadBlock(&ret, blockpos, createSector(p2d), false);
1861 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1862 if (created_new && (block != NULL)) {
1863 std::map<v3s16, MapBlock*> modified_blocks;
1864 // Fix lighting if necessary
1865 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1866 if (!modified_blocks.empty()) {
1867 //Modified lighting, send event
1869 event.type = MEET_OTHER;
1870 event.setModifiedBlocks(modified_blocks);
1871 dispatchEvent(event);
1877 bool ServerMap::deleteBlock(v3s16 blockpos)
1879 if (!dbase->deleteBlock(blockpos))
1882 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1884 v2s16 p2d(blockpos.X, blockpos.Z);
1885 MapSector *sector = getSectorNoGenerate(p2d);
1888 sector->deleteBlock(block);
1894 void ServerMap::PrintInfo(std::ostream &out)
1899 bool ServerMap::repairBlockLight(v3s16 blockpos,
1900 std::map<v3s16, MapBlock *> *modified_blocks)
1902 MapBlock *block = emergeBlock(blockpos, false);
1903 if (!block || !block->isGenerated())
1905 voxalgo::repair_block_light(this, block, modified_blocks);
1909 MMVManip::MMVManip(Map *map):
1916 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1917 bool load_if_inexistent)
1919 TimeTaker timer1("initialEmerge", &emerge_time);
1923 // Units of these are MapBlocks
1924 v3s16 p_min = blockpos_min;
1925 v3s16 p_max = blockpos_max;
1927 VoxelArea block_area_nodes
1928 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1930 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1933 infostream<<"initialEmerge: area: ";
1934 block_area_nodes.print(infostream);
1935 infostream<<" ("<<size_MB<<"MB)";
1936 infostream<<std::endl;
1939 addArea(block_area_nodes);
1941 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1942 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1943 for(s32 x=p_min.X; x<=p_max.X; x++)
1948 std::map<v3s16, u8>::iterator n;
1949 n = m_loaded_blocks.find(p);
1950 if(n != m_loaded_blocks.end())
1953 bool block_data_inexistent = false;
1955 TimeTaker timer2("emerge load", &emerge_load_time);
1957 block = m_map->getBlockNoCreateNoEx(p);
1959 block_data_inexistent = true;
1961 block->copyTo(*this);
1964 if(block_data_inexistent)
1967 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1968 ServerMap *svrmap = (ServerMap *)m_map;
1969 block = svrmap->emergeBlock(p, false);
1971 block = svrmap->createBlock(p);
1972 block->copyTo(*this);
1974 flags |= VMANIP_BLOCK_DATA_INEXIST;
1977 Mark area inexistent
1979 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1980 // Fill with VOXELFLAG_NO_DATA
1981 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1982 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1984 s32 i = m_area.index(a.MinEdge.X,y,z);
1985 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1989 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1991 // Mark that block was loaded as blank
1992 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
1995 m_loaded_blocks[p] = flags;
2001 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2002 bool overwrite_generated)
2004 if(m_area.getExtent() == v3s16(0,0,0))
2009 Copy data of all blocks
2011 for (auto &loaded_block : m_loaded_blocks) {
2012 v3s16 p = loaded_block.first;
2013 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2014 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2015 if (!existed || (block == NULL) ||
2016 (!overwrite_generated && block->isGenerated()))
2019 block->copyFrom(*this);
2020 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2023 (*modified_blocks)[p] = block;
2027 MMVManip *MMVManip::clone() const
2029 MMVManip *ret = new MMVManip();
2031 const s32 size = m_area.getVolume();
2032 ret->m_area = m_area;
2034 ret->m_data = new MapNode[size];
2035 memcpy(ret->m_data, m_data, size * sizeof(MapNode));
2038 ret->m_flags = new u8[size];
2039 memcpy(ret->m_flags, m_flags, size * sizeof(u8));
2042 ret->m_is_dirty = m_is_dirty;
2043 // Even if the copy is disconnected from a map object keep the information
2044 // needed to write it back to one
2045 ret->m_loaded_blocks = m_loaded_blocks;
2050 void MMVManip::reparent(Map *map)
2052 assert(map && !m_map);