3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
25 #include "voxelalgorithms.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/basic_macros.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
38 #include "reflowscan.h"
40 #include "mapgen/mapgen_v6.h"
41 #include "mapgen/mg_biome.h"
44 #include "database/database.h"
45 #include "database/database-dummy.h"
46 #include "database/database-sqlite3.h"
47 #include "script/scripting_server.h"
51 #include "database/database-leveldb.h"
54 #include "database/database-redis.h"
57 #include "database/database-postgresql.h"
65 Map::Map(IGameDef *gamedef):
67 m_nodedef(gamedef->ndef())
76 for (auto §or : m_sectors) {
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 m_event_receivers.erase(event_receiver);
91 void Map::dispatchEvent(const MapEditEvent &event)
93 for (MapEventReceiver *event_receiver : m_event_receivers) {
94 event_receiver->onMapEditEvent(event);
98 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
100 if(m_sector_cache != NULL && p == m_sector_cache_p){
101 MapSector * sector = m_sector_cache;
105 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
107 if (n == m_sectors.end())
110 MapSector *sector = n->second;
112 // Cache the last result
113 m_sector_cache_p = p;
114 m_sector_cache = sector;
119 MapSector * Map::getSectorNoGenerate(v2s16 p)
121 return getSectorNoGenerateNoLock(p);
124 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
136 MapBlock *block = getBlockNoCreateNoEx(p3d);
138 throw InvalidPositionException();
142 bool Map::isValidPosition(v3s16 p)
144 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock *block = getBlockNoCreateNoEx(blockpos);
146 return (block != NULL);
149 // Returns a CONTENT_IGNORE node if not found
150 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
152 v3s16 blockpos = getNodeBlockPos(p);
153 MapBlock *block = getBlockNoCreateNoEx(blockpos);
155 if (is_valid_position != NULL)
156 *is_valid_position = false;
157 return {CONTENT_IGNORE};
160 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
162 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
163 if (is_valid_position != NULL)
164 *is_valid_position = is_valid_p;
168 // throws InvalidPositionException if not found
169 void Map::setNode(v3s16 p, MapNode & n)
171 v3s16 blockpos = getNodeBlockPos(p);
172 MapBlock *block = getBlockNoCreate(blockpos);
173 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
174 // Never allow placing CONTENT_IGNORE, it causes problems
175 if(n.getContent() == CONTENT_IGNORE){
177 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
178 <<" while trying to replace \""
179 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
180 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
183 block->setNodeNoCheck(relpos, n);
186 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
187 std::map<v3s16, MapBlock*> &modified_blocks,
188 bool remove_metadata)
190 // Collect old node for rollback
191 RollbackNode rollback_oldnode(this, p, m_gamedef);
193 // This is needed for updating the lighting
194 MapNode oldnode = getNode(p);
196 // Remove node metadata
197 if (remove_metadata) {
198 removeNodeMetadata(p);
201 // Set the node on the map
202 // Ignore light (because calling voxalgo::update_lighting_nodes)
203 n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
204 n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
208 std::vector<std::pair<v3s16, MapNode> > oldnodes;
209 oldnodes.emplace_back(p, oldnode);
210 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
212 for (auto &modified_block : modified_blocks) {
213 modified_block.second->expireDayNightDiff();
216 // Report for rollback
217 if(m_gamedef->rollback())
219 RollbackNode rollback_newnode(this, p, m_gamedef);
220 RollbackAction action;
221 action.setSetNode(p, rollback_oldnode, rollback_newnode);
222 m_gamedef->rollback()->reportAction(action);
226 Add neighboring liquid nodes and this node to transform queue.
227 (it's vital for the node itself to get updated last, if it was removed.)
230 for (const v3s16 &dir : g_7dirs) {
233 bool is_valid_position;
234 MapNode n2 = getNode(p2, &is_valid_position);
235 if(is_valid_position &&
236 (m_nodedef->get(n2).isLiquid() ||
237 n2.getContent() == CONTENT_AIR))
238 m_transforming_liquid.push_back(p2);
242 void Map::removeNodeAndUpdate(v3s16 p,
243 std::map<v3s16, MapBlock*> &modified_blocks)
245 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
248 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
251 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
255 bool succeeded = true;
257 std::map<v3s16, MapBlock*> modified_blocks;
258 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
260 // Copy modified_blocks to event
261 for (auto &modified_block : modified_blocks) {
262 event.modified_blocks.insert(modified_block.first);
265 catch(InvalidPositionException &e){
269 dispatchEvent(event);
274 bool Map::removeNodeWithEvent(v3s16 p)
277 event.type = MEET_REMOVENODE;
280 bool succeeded = true;
282 std::map<v3s16, MapBlock*> modified_blocks;
283 removeNodeAndUpdate(p, modified_blocks);
285 // Copy modified_blocks to event
286 for (auto &modified_block : modified_blocks) {
287 event.modified_blocks.insert(modified_block.first);
290 catch(InvalidPositionException &e){
294 dispatchEvent(event);
299 struct TimeOrderedMapBlock {
303 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
308 bool operator<(const TimeOrderedMapBlock &b) const
310 return block->getUsageTimer() < b.block->getUsageTimer();
317 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
318 std::vector<v3s16> *unloaded_blocks)
320 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
322 // Profile modified reasons
323 Profiler modprofiler;
325 std::vector<v2s16> sector_deletion_queue;
326 u32 deleted_blocks_count = 0;
327 u32 saved_blocks_count = 0;
328 u32 block_count_all = 0;
332 // If there is no practical limit, we spare creation of mapblock_queue
333 if (max_loaded_blocks == U32_MAX) {
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 if (all_blocks_deleted) {
372 sector_deletion_queue.push_back(sector_it.first);
376 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
377 for (auto §or_it : m_sectors) {
378 MapSector *sector = sector_it.second;
381 sector->getBlocks(blocks);
383 for (MapBlock *block : blocks) {
384 block->incrementUsageTimer(dtime);
385 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
388 block_count_all = mapblock_queue.size();
389 // Delete old blocks, and blocks over the limit from the memory
390 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
391 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
392 TimeOrderedMapBlock b = mapblock_queue.top();
393 mapblock_queue.pop();
395 MapBlock *block = b.block;
397 if (block->refGet() != 0)
400 v3s16 p = block->getPos();
403 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
404 modprofiler.add(block->getModifiedReasonString(), 1);
405 if (!saveBlock(block))
407 saved_blocks_count++;
410 // Delete from memory
411 b.sect->deleteBlock(block);
414 unloaded_blocks->push_back(p);
416 deleted_blocks_count++;
419 // Delete empty sectors
420 for (auto §or_it : m_sectors) {
421 if (sector_it.second->empty()) {
422 sector_deletion_queue.push_back(sector_it.first);
428 // Finally delete the empty sectors
429 deleteSectors(sector_deletion_queue);
431 if(deleted_blocks_count != 0)
433 PrintInfo(infostream); // ServerMap/ClientMap:
434 infostream<<"Unloaded "<<deleted_blocks_count
435 <<" blocks from memory";
436 if(save_before_unloading)
437 infostream<<", of which "<<saved_blocks_count<<" were written";
438 infostream<<", "<<block_count_all<<" blocks in memory";
439 infostream<<"."<<std::endl;
440 if(saved_blocks_count != 0){
441 PrintInfo(infostream); // ServerMap/ClientMap:
442 infostream<<"Blocks modified by: "<<std::endl;
443 modprofiler.print(infostream);
448 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
450 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
453 void Map::deleteSectors(std::vector<v2s16> §orList)
455 for (v2s16 j : sectorList) {
456 MapSector *sector = m_sectors[j];
457 // If sector is in sector cache, remove it from there
458 if(m_sector_cache == sector)
459 m_sector_cache = NULL;
460 // Remove from map and delete
466 void Map::PrintInfo(std::ostream &out)
471 #define WATER_DROP_BOOST 4
473 const static v3s16 liquid_6dirs[6] = {
474 // order: upper before same level before lower
483 enum NeighborType : u8 {
489 struct NodeNeighbor {
495 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
498 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
505 void Map::transforming_liquid_add(v3s16 p) {
506 m_transforming_liquid.push_back(p);
509 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
510 ServerEnvironment *env)
513 u32 initial_size = m_transforming_liquid.size();
515 /*if(initial_size != 0)
516 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
518 // list of nodes that due to viscosity have not reached their max level height
519 std::deque<v3s16> must_reflow;
521 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
523 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
524 u32 loop_max = liquid_loop_max;
526 while (m_transforming_liquid.size() != 0)
528 // This should be done here so that it is done when continue is used
529 if (loopcount >= initial_size || loopcount >= loop_max)
534 Get a queued transforming liquid node
536 v3s16 p0 = m_transforming_liquid.front();
537 m_transforming_liquid.pop_front();
539 MapNode n0 = getNode(p0);
542 Collect information about current node
544 s8 liquid_level = -1;
545 // The liquid node which will be placed there if
546 // the liquid flows into this node.
547 content_t liquid_kind = CONTENT_IGNORE;
548 // The node which will be placed there if liquid
549 // can't flow into this node.
550 content_t floodable_node = CONTENT_AIR;
551 const ContentFeatures &cf = m_nodedef->get(n0);
552 LiquidType liquid_type = cf.liquid_type;
553 switch (liquid_type) {
555 liquid_level = LIQUID_LEVEL_SOURCE;
556 liquid_kind = cf.liquid_alternative_flowing_id;
559 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
560 liquid_kind = n0.getContent();
563 // if this node is 'floodable', it *could* be transformed
564 // into a liquid, otherwise, continue with the next node.
567 floodable_node = n0.getContent();
568 liquid_kind = CONTENT_AIR;
573 Collect information about the environment
575 NodeNeighbor sources[6]; // surrounding sources
577 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
579 NodeNeighbor airs[6]; // surrounding air
581 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
582 int num_neutrals = 0;
583 bool flowing_down = false;
584 bool ignored_sources = false;
585 for (u16 i = 0; i < 6; i++) {
586 NeighborType nt = NEIGHBOR_SAME_LEVEL;
597 v3s16 npos = p0 + liquid_6dirs[i];
598 NodeNeighbor nb(getNode(npos), nt, npos);
599 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
600 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
602 if (cfnb.floodable) {
603 airs[num_airs++] = nb;
604 // if the current node is a water source the neighbor
605 // should be enqueded for transformation regardless of whether the
606 // current node changes or not.
607 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
608 m_transforming_liquid.push_back(npos);
609 // if the current node happens to be a flowing node, it will start to flow down here.
610 if (nb.t == NEIGHBOR_LOWER)
613 neutrals[num_neutrals++] = nb;
614 if (nb.n.getContent() == CONTENT_IGNORE) {
615 // If node below is ignore prevent water from
616 // spreading outwards and otherwise prevent from
617 // flowing away as ignore node might be the source
618 if (nb.t == NEIGHBOR_LOWER)
621 ignored_sources = true;
626 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
627 if (liquid_kind == CONTENT_AIR)
628 liquid_kind = cfnb.liquid_alternative_flowing_id;
629 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
630 neutrals[num_neutrals++] = nb;
632 // Do not count bottom source, it will screw things up
633 if(nt != NEIGHBOR_LOWER)
634 sources[num_sources++] = nb;
638 if (nb.t != NEIGHBOR_SAME_LEVEL ||
639 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
640 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
641 // but exclude falling liquids on the same level, they cannot flow here anyway
642 if (liquid_kind == CONTENT_AIR)
643 liquid_kind = cfnb.liquid_alternative_flowing_id;
645 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
646 neutrals[num_neutrals++] = nb;
648 flows[num_flows++] = nb;
649 if (nb.t == NEIGHBOR_LOWER)
657 decide on the type (and possibly level) of the current node
659 content_t new_node_content;
660 s8 new_node_level = -1;
661 s8 max_node_level = -1;
663 u8 range = m_nodedef->get(liquid_kind).liquid_range;
664 if (range > LIQUID_LEVEL_MAX + 1)
665 range = LIQUID_LEVEL_MAX + 1;
667 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
668 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
669 // or the flowing alternative of the first of the surrounding sources (if it's air), so
670 // it's perfectly safe to use liquid_kind here to determine the new node content.
671 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
672 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
673 // liquid_kind is set properly, see above
674 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
675 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
676 new_node_content = liquid_kind;
678 new_node_content = floodable_node;
679 } else if (ignored_sources && liquid_level >= 0) {
680 // Maybe there are neighbouring sources that aren't loaded yet
681 // so prevent flowing away.
682 new_node_level = liquid_level;
683 new_node_content = liquid_kind;
685 // no surrounding sources, so get the maximum level that can flow into this node
686 for (u16 i = 0; i < num_flows; i++) {
687 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
688 switch (flows[i].t) {
690 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
691 max_node_level = LIQUID_LEVEL_MAX;
692 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
693 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
694 } else if (nb_liquid_level > max_node_level) {
695 max_node_level = nb_liquid_level;
700 case NEIGHBOR_SAME_LEVEL:
701 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
702 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
703 max_node_level = nb_liquid_level - 1;
708 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
709 if (viscosity > 1 && max_node_level != liquid_level) {
710 // amount to gain, limited by viscosity
711 // must be at least 1 in absolute value
712 s8 level_inc = max_node_level - liquid_level;
713 if (level_inc < -viscosity || level_inc > viscosity)
714 new_node_level = liquid_level + level_inc/viscosity;
715 else if (level_inc < 0)
716 new_node_level = liquid_level - 1;
717 else if (level_inc > 0)
718 new_node_level = liquid_level + 1;
719 if (new_node_level != max_node_level)
720 must_reflow.push_back(p0);
722 new_node_level = max_node_level;
725 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
726 new_node_content = liquid_kind;
728 new_node_content = floodable_node;
733 check if anything has changed. if not, just continue with the next node.
735 if (new_node_content == n0.getContent() &&
736 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
737 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
738 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
744 update the current node
747 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
748 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
749 // set level to last 3 bits, flowing down bit to 4th bit
750 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
752 // set the liquid level and flow bits to 0
753 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
757 n0.setContent(new_node_content);
759 // on_flood() the node
760 if (floodable_node != CONTENT_AIR) {
761 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
765 // Ignore light (because calling voxalgo::update_lighting_nodes)
766 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
767 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
769 // Find out whether there is a suspect for this action
771 if (m_gamedef->rollback())
772 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
774 if (m_gamedef->rollback() && !suspect.empty()) {
776 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
777 // Get old node for rollback
778 RollbackNode rollback_oldnode(this, p0, m_gamedef);
782 RollbackNode rollback_newnode(this, p0, m_gamedef);
783 RollbackAction action;
784 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
785 m_gamedef->rollback()->reportAction(action);
791 v3s16 blockpos = getNodeBlockPos(p0);
792 MapBlock *block = getBlockNoCreateNoEx(blockpos);
794 modified_blocks[blockpos] = block;
795 changed_nodes.emplace_back(p0, n00);
799 enqueue neighbors for update if neccessary
801 switch (m_nodedef->get(n0.getContent()).liquid_type) {
804 // make sure source flows into all neighboring nodes
805 for (u16 i = 0; i < num_flows; i++)
806 if (flows[i].t != NEIGHBOR_UPPER)
807 m_transforming_liquid.push_back(flows[i].p);
808 for (u16 i = 0; i < num_airs; i++)
809 if (airs[i].t != NEIGHBOR_UPPER)
810 m_transforming_liquid.push_back(airs[i].p);
813 // this flow has turned to air; neighboring flows might need to do the same
814 for (u16 i = 0; i < num_flows; i++)
815 m_transforming_liquid.push_back(flows[i].p);
819 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
821 for (auto &iter : must_reflow)
822 m_transforming_liquid.push_back(iter);
824 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
825 env->getScriptIface()->on_liquid_transformed(changed_nodes);
827 /* ----------------------------------------------------------------------
828 * Manage the queue so that it does not grow indefinately
830 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
832 if (time_until_purge == 0)
833 return; // Feature disabled
835 time_until_purge *= 1000; // seconds -> milliseconds
837 u64 curr_time = porting::getTimeMs();
838 u32 prev_unprocessed = m_unprocessed_count;
839 m_unprocessed_count = m_transforming_liquid.size();
841 // if unprocessed block count is decreasing or stable
842 if (m_unprocessed_count <= prev_unprocessed) {
843 m_queue_size_timer_started = false;
845 if (!m_queue_size_timer_started)
846 m_inc_trending_up_start_time = curr_time;
847 m_queue_size_timer_started = true;
850 // Account for curr_time overflowing
851 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
852 m_queue_size_timer_started = false;
854 /* If the queue has been growing for more than liquid_queue_purge_time seconds
855 * and the number of unprocessed blocks is still > liquid_loop_max then we
856 * cannot keep up; dump the oldest blocks from the queue so that the queue
857 * has liquid_loop_max items in it
859 if (m_queue_size_timer_started
860 && curr_time - m_inc_trending_up_start_time > time_until_purge
861 && m_unprocessed_count > liquid_loop_max) {
863 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
865 infostream << "transformLiquids(): DUMPING " << dump_qty
866 << " blocks from the queue" << std::endl;
869 m_transforming_liquid.pop_front();
871 m_queue_size_timer_started = false; // optimistically assume we can keep up now
872 m_unprocessed_count = m_transforming_liquid.size();
876 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
878 std::vector<v3s16> positions_with_meta;
880 sortBoxVerticies(p1, p2);
881 v3s16 bpmin = getNodeBlockPos(p1);
882 v3s16 bpmax = getNodeBlockPos(p2);
884 VoxelArea area(p1, p2);
886 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
887 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
888 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
889 v3s16 blockpos(x, y, z);
891 MapBlock *block = getBlockNoCreateNoEx(blockpos);
893 verbosestream << "Map::getNodeMetadata(): Need to emerge "
894 << PP(blockpos) << std::endl;
895 block = emergeBlock(blockpos, false);
898 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
903 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
904 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
905 for (size_t i = 0; i != keys.size(); i++) {
906 v3s16 p(keys[i] + p_base);
907 if (!area.contains(p))
910 positions_with_meta.push_back(p);
914 return positions_with_meta;
917 NodeMetadata *Map::getNodeMetadata(v3s16 p)
919 v3s16 blockpos = getNodeBlockPos(p);
920 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
921 MapBlock *block = getBlockNoCreateNoEx(blockpos);
923 infostream<<"Map::getNodeMetadata(): Need to emerge "
924 <<PP(blockpos)<<std::endl;
925 block = emergeBlock(blockpos, false);
928 warningstream<<"Map::getNodeMetadata(): Block not found"
932 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
936 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
938 v3s16 blockpos = getNodeBlockPos(p);
939 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
940 MapBlock *block = getBlockNoCreateNoEx(blockpos);
942 infostream<<"Map::setNodeMetadata(): Need to emerge "
943 <<PP(blockpos)<<std::endl;
944 block = emergeBlock(blockpos, false);
947 warningstream<<"Map::setNodeMetadata(): Block not found"
951 block->m_node_metadata.set(p_rel, meta);
955 void Map::removeNodeMetadata(v3s16 p)
957 v3s16 blockpos = getNodeBlockPos(p);
958 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
959 MapBlock *block = getBlockNoCreateNoEx(blockpos);
962 warningstream<<"Map::removeNodeMetadata(): Block not found"
966 block->m_node_metadata.remove(p_rel);
969 NodeTimer Map::getNodeTimer(v3s16 p)
971 v3s16 blockpos = getNodeBlockPos(p);
972 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
973 MapBlock *block = getBlockNoCreateNoEx(blockpos);
975 infostream<<"Map::getNodeTimer(): Need to emerge "
976 <<PP(blockpos)<<std::endl;
977 block = emergeBlock(blockpos, false);
980 warningstream<<"Map::getNodeTimer(): Block not found"
984 NodeTimer t = block->m_node_timers.get(p_rel);
985 NodeTimer nt(t.timeout, t.elapsed, p);
989 void Map::setNodeTimer(const NodeTimer &t)
991 v3s16 p = t.position;
992 v3s16 blockpos = getNodeBlockPos(p);
993 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
994 MapBlock *block = getBlockNoCreateNoEx(blockpos);
996 infostream<<"Map::setNodeTimer(): Need to emerge "
997 <<PP(blockpos)<<std::endl;
998 block = emergeBlock(blockpos, false);
1001 warningstream<<"Map::setNodeTimer(): Block not found"
1005 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1006 block->m_node_timers.set(nt);
1009 void Map::removeNodeTimer(v3s16 p)
1011 v3s16 blockpos = getNodeBlockPos(p);
1012 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1013 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1016 warningstream<<"Map::removeNodeTimer(): Block not found"
1020 block->m_node_timers.remove(p_rel);
1023 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1024 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1027 This functions determines the node inside the target block that is
1028 closest to the camera position. This increases the occlusion culling
1029 accuracy in straight and diagonal corridors.
1030 The returned position will be occlusion checked first in addition to the
1031 others (8 corners + center).
1032 No position is returned if
1033 - the closest node is a corner, corners are checked anyway.
1034 - the camera is inside the target block, it will never be occluded.
1036 #define CLOSEST_EDGE(pos, bounds, axis) \
1037 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1038 (bounds).MaxEdge.axis
1040 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1041 (pos_camera.X <= block_bounds.MaxEdge.X);
1042 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1043 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1044 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1045 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1047 if (x_inside && y_inside && z_inside)
1048 return false; // Camera inside target mapblock
1051 if (x_inside && y_inside) {
1052 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1053 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1055 } else if (y_inside && z_inside) {
1056 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1057 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1059 } else if (x_inside && z_inside) {
1060 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1061 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1067 check = v3s16(pos_camera.X, 0, 0);
1068 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1069 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1071 } else if (y_inside) {
1072 check = v3s16(0, pos_camera.Y, 0);
1073 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1074 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1076 } else if (z_inside) {
1077 check = v3s16(0, 0, pos_camera.Z);
1078 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1079 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1083 // Closest node would be a corner, none returned
1087 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1088 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1090 v3f direction = intToFloat(pos_target - pos_camera, BS);
1091 float distance = direction.getLength();
1093 // Normalize direction vector
1094 if (distance > 0.0f)
1095 direction /= distance;
1097 v3f pos_origin_f = intToFloat(pos_camera, BS);
1099 bool is_valid_position;
1101 for (; offset < distance + end_offset; offset += step) {
1102 v3f pos_node_f = pos_origin_f + direction * offset;
1103 v3s16 pos_node = floatToInt(pos_node_f, BS);
1105 MapNode node = getNode(pos_node, &is_valid_position);
1107 if (is_valid_position &&
1108 !m_nodedef->get(node).light_propagates) {
1109 // Cannot see through light-blocking nodes --> occluded
1111 if (count >= needed_count)
1119 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1121 // Check occlusion for center and all 8 corners of the mapblock
1122 // Overshoot a little for less flickering
1123 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1124 static const v3s16 dir9[9] = {
1126 v3s16( 1, 1, 1) * bs2,
1127 v3s16( 1, 1, -1) * bs2,
1128 v3s16( 1, -1, 1) * bs2,
1129 v3s16( 1, -1, -1) * bs2,
1130 v3s16(-1, 1, 1) * bs2,
1131 v3s16(-1, 1, -1) * bs2,
1132 v3s16(-1, -1, 1) * bs2,
1133 v3s16(-1, -1, -1) * bs2,
1136 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1138 // Starting step size, value between 1m and sqrt(3)m
1139 float step = BS * 1.2f;
1140 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1141 float stepfac = 1.05f;
1143 float start_offset = BS * 1.0f;
1145 // The occlusion search of 'isOccluded()' must stop short of the target
1146 // point by distance 'end_offset' to not enter the target mapblock.
1147 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1148 // diagonal of a mapblock, because we must consider all view angles.
1149 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1150 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1152 // to reduce the likelihood of falsely occluded blocks
1153 // require at least two solid blocks
1154 // this is a HACK, we should think of a more precise algorithm
1155 u32 needed_count = 2;
1157 // Additional occlusion check, see comments in that function
1159 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1160 // node is always on a side facing the camera, end_offset can be lower
1161 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1162 -1.0f, needed_count))
1166 for (const v3s16 &dir : dir9) {
1167 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1168 start_offset, end_offset, needed_count))
1177 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1178 EmergeManager *emerge, MetricsBackend *mb):
1180 settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
1183 verbosestream<<FUNCTION_NAME<<std::endl;
1185 // Tell the EmergeManager about our MapSettingsManager
1186 emerge->map_settings_mgr = &settings_mgr;
1189 Try to load map; if not found, create a new one.
1192 // Determine which database backend to use
1193 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1195 bool succeeded = conf.readConfigFile(conf_path.c_str());
1196 if (!succeeded || !conf.exists("backend")) {
1197 // fall back to sqlite3
1198 conf.set("backend", "sqlite3");
1200 std::string backend = conf.get("backend");
1201 dbase = createDatabase(backend, savedir, conf);
1202 if (conf.exists("readonly_backend")) {
1203 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1204 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1206 if (!conf.updateConfigFile(conf_path.c_str()))
1207 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1209 m_savedir = savedir;
1210 m_map_saving_enabled = false;
1212 m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1214 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1217 // If directory exists, check contents and load if possible
1218 if (fs::PathExists(m_savedir)) {
1219 // If directory is empty, it is safe to save into it.
1220 if (fs::GetDirListing(m_savedir).empty()) {
1221 infostream<<"ServerMap: Empty save directory is valid."
1223 m_map_saving_enabled = true;
1228 if (settings_mgr.loadMapMeta()) {
1229 infostream << "ServerMap: Metadata loaded from "
1230 << savedir << std::endl;
1232 infostream << "ServerMap: Metadata could not be loaded "
1233 "from " << savedir << ", assuming valid save "
1234 "directory." << std::endl;
1237 m_map_saving_enabled = true;
1238 // Map loaded, not creating new one
1242 // If directory doesn't exist, it is safe to save to it
1244 m_map_saving_enabled = true;
1247 catch(std::exception &e)
1249 warningstream<<"ServerMap: Failed to load map from "<<savedir
1250 <<", exception: "<<e.what()<<std::endl;
1251 infostream<<"Please remove the map or fix it."<<std::endl;
1252 warningstream<<"Map saving will be disabled."<<std::endl;
1256 ServerMap::~ServerMap()
1258 verbosestream<<FUNCTION_NAME<<std::endl;
1262 if (m_map_saving_enabled) {
1263 // Save only changed parts
1264 save(MOD_STATE_WRITE_AT_UNLOAD);
1265 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1267 infostream << "ServerMap: Map not saved" << std::endl;
1270 catch(std::exception &e)
1272 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1273 <<", exception: "<<e.what()<<std::endl;
1277 Close database if it was opened
1283 MapgenParams *ServerMap::getMapgenParams()
1285 // getMapgenParams() should only ever be called after Server is initialized
1286 assert(settings_mgr.mapgen_params != NULL);
1287 return settings_mgr.mapgen_params;
1290 u64 ServerMap::getSeed()
1292 return getMapgenParams()->seed;
1295 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1297 const s16 mapgen_limit_bp = rangelim(
1298 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1300 return p.X < -mapgen_limit_bp ||
1301 p.X > mapgen_limit_bp ||
1302 p.Y < -mapgen_limit_bp ||
1303 p.Y > mapgen_limit_bp ||
1304 p.Z < -mapgen_limit_bp ||
1305 p.Z > mapgen_limit_bp;
1308 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1310 s16 csize = getMapgenParams()->chunksize;
1311 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1312 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1314 if (!m_chunks_in_progress.insert(bpmin).second)
1317 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1318 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1320 v3s16 extra_borders(1, 1, 1);
1321 v3s16 full_bpmin = bpmin - extra_borders;
1322 v3s16 full_bpmax = bpmax + extra_borders;
1324 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1325 if (blockpos_over_mapgen_limit(full_bpmin) ||
1326 blockpos_over_mapgen_limit(full_bpmax))
1329 data->seed = getSeed();
1330 data->blockpos_min = bpmin;
1331 data->blockpos_max = bpmax;
1332 data->nodedef = m_nodedef;
1335 Create the whole area of this and the neighboring blocks
1337 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1338 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1339 v2s16 sectorpos(x, z);
1340 // Sector metadata is loaded from disk if not already loaded.
1341 MapSector *sector = createSector(sectorpos);
1342 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1344 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1347 MapBlock *block = emergeBlock(p, false);
1348 if (block == NULL) {
1349 block = createBlock(p);
1351 // Block gets sunlight if this is true.
1352 // Refer to the map generator heuristics.
1353 bool ug = m_emerge->isBlockUnderground(p);
1354 block->setIsUnderground(ug);
1360 Now we have a big empty area.
1362 Make a ManualMapVoxelManipulator that contains this and the
1366 data->vmanip = new MMVManip(this);
1367 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1369 // Data is ready now.
1373 void ServerMap::finishBlockMake(BlockMakeData *data,
1374 std::map<v3s16, MapBlock*> *changed_blocks)
1376 v3s16 bpmin = data->blockpos_min;
1377 v3s16 bpmax = data->blockpos_max;
1379 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1380 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1383 Blit generated stuff to map
1384 NOTE: blitBackAll adds nearly everything to changed_blocks
1386 data->vmanip->blitBackAll(changed_blocks);
1388 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1389 << changed_blocks->size());
1392 Copy transforming liquid information
1394 while (data->transforming_liquid.size()) {
1395 m_transforming_liquid.push_back(data->transforming_liquid.front());
1396 data->transforming_liquid.pop_front();
1399 for (auto &changed_block : *changed_blocks) {
1400 MapBlock *block = changed_block.second;
1404 Update day/night difference cache of the MapBlocks
1406 block->expireDayNightDiff();
1408 Set block as modified
1410 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1411 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1415 Set central blocks as generated
1417 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1418 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1419 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1420 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1424 block->setGenerated(true);
1428 Save changed parts of map
1429 NOTE: Will be saved later.
1431 //save(MOD_STATE_WRITE_AT_UNLOAD);
1432 m_chunks_in_progress.erase(bpmin);
1435 MapSector *ServerMap::createSector(v2s16 p2d)
1438 Check if it exists already in memory
1440 MapSector *sector = getSectorNoGenerate(p2d);
1445 Do not create over max mapgen limit
1447 if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y)))
1448 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1451 Generate blank sector
1454 sector = new MapSector(this, p2d, m_gamedef);
1459 m_sectors[p2d] = sector;
1464 MapBlock * ServerMap::createBlock(v3s16 p)
1467 Do not create over max mapgen limit
1469 if (blockpos_over_max_limit(p))
1470 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1472 v2s16 p2d(p.X, p.Z);
1475 This will create or load a sector if not found in memory.
1476 If block exists on disk, it will be loaded.
1478 NOTE: On old save formats, this will be slow, as it generates
1479 lighting on blocks for them.
1483 sector = createSector(p2d);
1484 } catch (InvalidPositionException &e) {
1485 infostream<<"createBlock: createSector() failed"<<std::endl;
1490 Try to get a block from the sector
1493 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1495 if(block->isDummy())
1500 block = sector->createBlankBlock(block_y);
1505 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1508 MapBlock *block = getBlockNoCreateNoEx(p);
1509 if (block && !block->isDummy())
1514 MapBlock *block = loadBlock(p);
1520 MapSector *sector = createSector(v2s16(p.X, p.Z));
1521 MapBlock *block = sector->createBlankBlock(p.Y);
1529 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1531 MapBlock *block = getBlockNoCreateNoEx(p3d);
1533 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1538 bool ServerMap::isBlockInQueue(v3s16 pos)
1540 return m_emerge && m_emerge->isBlockInQueue(pos);
1543 // N.B. This requires no synchronization, since data will not be modified unless
1544 // the VoxelManipulator being updated belongs to the same thread.
1545 void ServerMap::updateVManip(v3s16 pos)
1547 Mapgen *mg = m_emerge->getCurrentMapgen();
1551 MMVManip *vm = mg->vm;
1555 if (!vm->m_area.contains(pos))
1558 s32 idx = vm->m_area.index(pos);
1559 vm->m_data[idx] = getNode(pos);
1560 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1562 vm->m_is_dirty = true;
1565 void ServerMap::save(ModifiedState save_level)
1567 if (!m_map_saving_enabled) {
1568 warningstream<<"Not saving map, saving disabled."<<std::endl;
1572 u64 start_time = porting::getTimeNs();
1574 if(save_level == MOD_STATE_CLEAN)
1575 infostream<<"ServerMap: Saving whole map, this can take time."
1578 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1579 if (settings_mgr.saveMapMeta())
1580 m_map_metadata_changed = false;
1583 // Profile modified reasons
1584 Profiler modprofiler;
1586 u32 block_count = 0;
1587 u32 block_count_all = 0; // Number of blocks in memory
1589 // Don't do anything with sqlite unless something is really saved
1590 bool save_started = false;
1592 for (auto §or_it : m_sectors) {
1593 MapSector *sector = sector_it.second;
1595 MapBlockVect blocks;
1596 sector->getBlocks(blocks);
1598 for (MapBlock *block : blocks) {
1601 if(block->getModified() >= (u32)save_level) {
1605 save_started = true;
1608 modprofiler.add(block->getModifiedReasonString(), 1);
1620 Only print if something happened or saved whole map
1622 if(save_level == MOD_STATE_CLEAN
1623 || block_count != 0) {
1624 infostream << "ServerMap: Written: "
1625 << block_count << " blocks"
1626 << ", " << block_count_all << " blocks in memory."
1628 PrintInfo(infostream); // ServerMap/ClientMap:
1629 infostream<<"Blocks modified by: "<<std::endl;
1630 modprofiler.print(infostream);
1633 auto end_time = porting::getTimeNs();
1634 m_save_time_counter->increment(end_time - start_time);
1637 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1639 dbase->listAllLoadableBlocks(dst);
1641 dbase_ro->listAllLoadableBlocks(dst);
1644 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1646 for (auto §or_it : m_sectors) {
1647 MapSector *sector = sector_it.second;
1649 MapBlockVect blocks;
1650 sector->getBlocks(blocks);
1652 for (MapBlock *block : blocks) {
1653 v3s16 p = block->getPos();
1659 MapDatabase *ServerMap::createDatabase(
1660 const std::string &name,
1661 const std::string &savedir,
1664 if (name == "sqlite3")
1665 return new MapDatabaseSQLite3(savedir);
1666 if (name == "dummy")
1667 return new Database_Dummy();
1669 if (name == "leveldb")
1670 return new Database_LevelDB(savedir);
1673 if (name == "redis")
1674 return new Database_Redis(conf);
1677 if (name == "postgresql") {
1678 std::string connect_string;
1679 conf.getNoEx("pgsql_connection", connect_string);
1680 return new MapDatabasePostgreSQL(connect_string);
1684 throw BaseException(std::string("Database backend ") + name + " not supported.");
1687 void ServerMap::beginSave()
1692 void ServerMap::endSave()
1697 bool ServerMap::saveBlock(MapBlock *block)
1699 return saveBlock(block, dbase, m_map_compression_level);
1702 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1704 v3s16 p3d = block->getPos();
1706 // Dummy blocks are not written
1707 if (block->isDummy()) {
1708 warningstream << "saveBlock: Not writing dummy block "
1709 << PP(p3d) << std::endl;
1713 // Format used for writing
1714 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1717 [0] u8 serialization version
1720 std::ostringstream o(std::ios_base::binary);
1721 o.write((char*) &version, 1);
1722 block->serialize(o, version, true, compression_level);
1724 bool ret = db->saveBlock(p3d, o.str());
1726 // We just wrote it to the disk so clear modified flag
1727 block->resetModified();
1732 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1735 std::istringstream is(*blob, std::ios_base::binary);
1737 u8 version = SER_FMT_VER_INVALID;
1738 is.read((char*)&version, 1);
1741 throw SerializationError("ServerMap::loadBlock(): Failed"
1742 " to read MapBlock version");
1744 MapBlock *block = NULL;
1745 bool created_new = false;
1746 block = sector->getBlockNoCreateNoEx(p3d.Y);
1749 block = sector->createBlankBlockNoInsert(p3d.Y);
1754 block->deSerialize(is, version, true);
1756 // If it's a new block, insert it to the map
1758 sector->insertBlock(block);
1759 ReflowScan scanner(this, m_emerge->ndef);
1760 scanner.scan(block, &m_transforming_liquid);
1764 Save blocks loaded in old format in new format
1767 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1768 // Only save if asked to; no need to update version
1772 // We just loaded it from, so it's up-to-date.
1773 block->resetModified();
1775 catch(SerializationError &e)
1777 errorstream<<"Invalid block data in database"
1778 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1779 <<" (SerializationError): "<<e.what()<<std::endl;
1781 // TODO: Block should be marked as invalid in memory so that it is
1782 // not touched but the game can run
1784 if(g_settings->getBool("ignore_world_load_errors")){
1785 errorstream<<"Ignoring block load error. Duck and cover! "
1786 <<"(ignore_world_load_errors)"<<std::endl;
1788 throw SerializationError("Invalid block data in database");
1793 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1795 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1797 v2s16 p2d(blockpos.X, blockpos.Z);
1800 dbase->loadBlock(blockpos, &ret);
1802 loadBlock(&ret, blockpos, createSector(p2d), false);
1803 } else if (dbase_ro) {
1804 dbase_ro->loadBlock(blockpos, &ret);
1806 loadBlock(&ret, blockpos, createSector(p2d), false);
1812 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1813 if (created_new && (block != NULL)) {
1814 std::map<v3s16, MapBlock*> modified_blocks;
1815 // Fix lighting if necessary
1816 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1817 if (!modified_blocks.empty()) {
1818 //Modified lighting, send event
1820 event.type = MEET_OTHER;
1821 std::map<v3s16, MapBlock *>::iterator it;
1822 for (it = modified_blocks.begin();
1823 it != modified_blocks.end(); ++it)
1824 event.modified_blocks.insert(it->first);
1825 dispatchEvent(event);
1831 bool ServerMap::deleteBlock(v3s16 blockpos)
1833 if (!dbase->deleteBlock(blockpos))
1836 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1838 v2s16 p2d(blockpos.X, blockpos.Z);
1839 MapSector *sector = getSectorNoGenerate(p2d);
1842 sector->deleteBlock(block);
1848 void ServerMap::PrintInfo(std::ostream &out)
1853 bool ServerMap::repairBlockLight(v3s16 blockpos,
1854 std::map<v3s16, MapBlock *> *modified_blocks)
1856 MapBlock *block = emergeBlock(blockpos, false);
1857 if (!block || !block->isGenerated())
1859 voxalgo::repair_block_light(this, block, modified_blocks);
1863 MMVManip::MMVManip(Map *map):
1869 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1870 bool load_if_inexistent)
1872 TimeTaker timer1("initialEmerge", &emerge_time);
1874 // Units of these are MapBlocks
1875 v3s16 p_min = blockpos_min;
1876 v3s16 p_max = blockpos_max;
1878 VoxelArea block_area_nodes
1879 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1881 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1884 infostream<<"initialEmerge: area: ";
1885 block_area_nodes.print(infostream);
1886 infostream<<" ("<<size_MB<<"MB)";
1887 infostream<<std::endl;
1890 addArea(block_area_nodes);
1892 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1893 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1894 for(s32 x=p_min.X; x<=p_max.X; x++)
1899 std::map<v3s16, u8>::iterator n;
1900 n = m_loaded_blocks.find(p);
1901 if(n != m_loaded_blocks.end())
1904 bool block_data_inexistent = false;
1906 TimeTaker timer2("emerge load", &emerge_load_time);
1908 block = m_map->getBlockNoCreateNoEx(p);
1909 if (!block || block->isDummy())
1910 block_data_inexistent = true;
1912 block->copyTo(*this);
1915 if(block_data_inexistent)
1918 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1919 ServerMap *svrmap = (ServerMap *)m_map;
1920 block = svrmap->emergeBlock(p, false);
1922 block = svrmap->createBlock(p);
1923 block->copyTo(*this);
1925 flags |= VMANIP_BLOCK_DATA_INEXIST;
1928 Mark area inexistent
1930 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1931 // Fill with VOXELFLAG_NO_DATA
1932 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1933 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1935 s32 i = m_area.index(a.MinEdge.X,y,z);
1936 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1940 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1942 // Mark that block was loaded as blank
1943 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
1946 m_loaded_blocks[p] = flags;
1952 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
1953 bool overwrite_generated)
1955 if(m_area.getExtent() == v3s16(0,0,0))
1959 Copy data of all blocks
1961 for (auto &loaded_block : m_loaded_blocks) {
1962 v3s16 p = loaded_block.first;
1963 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1964 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
1965 if (!existed || (block == NULL) ||
1966 (!overwrite_generated && block->isGenerated()))
1969 block->copyFrom(*this);
1970 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
1973 (*modified_blocks)[p] = block;