3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
25 #include "voxelalgorithms.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/basic_macros.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
38 #include "reflowscan.h"
40 #include "mapgen/mapgen_v6.h"
41 #include "mapgen/mg_biome.h"
44 #include "database/database.h"
45 #include "database/database-dummy.h"
46 #include "database/database-sqlite3.h"
47 #include "script/scripting_server.h"
51 #include "database/database-leveldb.h"
54 #include "database/database-redis.h"
57 #include "database/database-postgresql.h"
65 Map::Map(IGameDef *gamedef):
67 m_nodedef(gamedef->ndef())
76 for (auto §or : m_sectors) {
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 m_event_receivers.erase(event_receiver);
91 void Map::dispatchEvent(const MapEditEvent &event)
93 for (MapEventReceiver *event_receiver : m_event_receivers) {
94 event_receiver->onMapEditEvent(event);
98 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
100 if(m_sector_cache != NULL && p == m_sector_cache_p){
101 MapSector * sector = m_sector_cache;
105 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
107 if (n == m_sectors.end())
110 MapSector *sector = n->second;
112 // Cache the last result
113 m_sector_cache_p = p;
114 m_sector_cache = sector;
119 MapSector * Map::getSectorNoGenerate(v2s16 p)
121 return getSectorNoGenerateNoLock(p);
124 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
136 MapBlock *block = getBlockNoCreateNoEx(p3d);
138 throw InvalidPositionException();
142 bool Map::isValidPosition(v3s16 p)
144 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock *block = getBlockNoCreateNoEx(blockpos);
146 return (block != NULL);
149 // Returns a CONTENT_IGNORE node if not found
150 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
152 v3s16 blockpos = getNodeBlockPos(p);
153 MapBlock *block = getBlockNoCreateNoEx(blockpos);
155 if (is_valid_position != NULL)
156 *is_valid_position = false;
157 return {CONTENT_IGNORE};
160 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
162 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
163 if (is_valid_position != NULL)
164 *is_valid_position = is_valid_p;
168 static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n)
170 // Never allow placing CONTENT_IGNORE, it causes problems
171 if(n.getContent() == CONTENT_IGNORE){
172 const NodeDefManager *nodedef = block->getParent()->getNodeDefManager();
173 v3s16 blockpos = block->getPos();
174 v3s16 p = blockpos * MAP_BLOCKSIZE + relpos;
176 errorstream<<"Not allowing to place CONTENT_IGNORE"
177 <<" while trying to replace \""
178 <<nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
179 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
182 block->setNodeNoCheck(relpos, n);
185 // throws InvalidPositionException if not found
186 void Map::setNode(v3s16 p, MapNode & n)
188 v3s16 blockpos = getNodeBlockPos(p);
189 MapBlock *block = getBlockNoCreate(blockpos);
190 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
191 set_node_in_block(block, relpos, n);
194 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
195 std::map<v3s16, MapBlock*> &modified_blocks,
196 bool remove_metadata)
198 // Collect old node for rollback
199 RollbackNode rollback_oldnode(this, p, m_gamedef);
201 v3s16 blockpos = getNodeBlockPos(p);
202 MapBlock *block = getBlockNoCreate(blockpos);
203 if (block->isDummy())
204 throw InvalidPositionException();
205 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
207 // This is needed for updating the lighting
208 MapNode oldnode = block->getNodeUnsafe(relpos);
210 // Remove node metadata
211 if (remove_metadata) {
212 removeNodeMetadata(p);
215 // Set the node on the map
216 const ContentFeatures &cf = m_nodedef->get(n);
217 const ContentFeatures &oldcf = m_nodedef->get(oldnode);
218 if (cf.lightingEquivalent(oldcf)) {
219 // No light update needed, just copy over the old light.
220 n.setLight(LIGHTBANK_DAY, oldnode.getLightRaw(LIGHTBANK_DAY, oldcf), cf);
221 n.setLight(LIGHTBANK_NIGHT, oldnode.getLightRaw(LIGHTBANK_NIGHT, oldcf), cf);
222 set_node_in_block(block, relpos, n);
224 modified_blocks[blockpos] = block;
226 // Ignore light (because calling voxalgo::update_lighting_nodes)
227 n.setLight(LIGHTBANK_DAY, 0, cf);
228 n.setLight(LIGHTBANK_NIGHT, 0, cf);
229 set_node_in_block(block, relpos, n);
232 std::vector<std::pair<v3s16, MapNode> > oldnodes;
233 oldnodes.emplace_back(p, oldnode);
234 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
236 for (auto &modified_block : modified_blocks) {
237 modified_block.second->expireDayNightDiff();
241 // Report for rollback
242 if(m_gamedef->rollback())
244 RollbackNode rollback_newnode(this, p, m_gamedef);
245 RollbackAction action;
246 action.setSetNode(p, rollback_oldnode, rollback_newnode);
247 m_gamedef->rollback()->reportAction(action);
251 void Map::removeNodeAndUpdate(v3s16 p,
252 std::map<v3s16, MapBlock*> &modified_blocks)
254 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
257 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
260 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
264 bool succeeded = true;
266 std::map<v3s16, MapBlock*> modified_blocks;
267 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
269 // Copy modified_blocks to event
270 for (auto &modified_block : modified_blocks) {
271 event.modified_blocks.insert(modified_block.first);
274 catch(InvalidPositionException &e){
278 dispatchEvent(event);
283 bool Map::removeNodeWithEvent(v3s16 p)
286 event.type = MEET_REMOVENODE;
289 bool succeeded = true;
291 std::map<v3s16, MapBlock*> modified_blocks;
292 removeNodeAndUpdate(p, modified_blocks);
294 // Copy modified_blocks to event
295 for (auto &modified_block : modified_blocks) {
296 event.modified_blocks.insert(modified_block.first);
299 catch(InvalidPositionException &e){
303 dispatchEvent(event);
308 struct TimeOrderedMapBlock {
312 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
317 bool operator<(const TimeOrderedMapBlock &b) const
319 return block->getUsageTimer() < b.block->getUsageTimer();
326 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
327 std::vector<v3s16> *unloaded_blocks)
329 bool save_before_unloading = maySaveBlocks();
331 // Profile modified reasons
332 Profiler modprofiler;
334 std::vector<v2s16> sector_deletion_queue;
335 u32 deleted_blocks_count = 0;
336 u32 saved_blocks_count = 0;
337 u32 block_count_all = 0;
339 const auto start_time = porting::getTimeUs();
342 // If there is no practical limit, we spare creation of mapblock_queue
343 if (max_loaded_blocks == U32_MAX) {
344 for (auto §or_it : m_sectors) {
345 MapSector *sector = sector_it.second;
347 bool all_blocks_deleted = true;
350 sector->getBlocks(blocks);
352 for (MapBlock *block : blocks) {
353 block->incrementUsageTimer(dtime);
355 if (block->refGet() == 0
356 && block->getUsageTimer() > unload_timeout) {
357 v3s16 p = block->getPos();
360 if (block->getModified() != MOD_STATE_CLEAN
361 && save_before_unloading) {
362 modprofiler.add(block->getModifiedReasonString(), 1);
363 if (!saveBlock(block))
365 saved_blocks_count++;
368 // Delete from memory
369 sector->deleteBlock(block);
372 unloaded_blocks->push_back(p);
374 deleted_blocks_count++;
376 all_blocks_deleted = false;
381 // Delete sector if we emptied it
382 if (all_blocks_deleted) {
383 sector_deletion_queue.push_back(sector_it.first);
387 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
388 for (auto §or_it : m_sectors) {
389 MapSector *sector = sector_it.second;
392 sector->getBlocks(blocks);
394 for (MapBlock *block : blocks) {
395 block->incrementUsageTimer(dtime);
396 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
399 block_count_all = mapblock_queue.size();
401 // Delete old blocks, and blocks over the limit from the memory
402 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
403 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
404 TimeOrderedMapBlock b = mapblock_queue.top();
405 mapblock_queue.pop();
407 MapBlock *block = b.block;
409 if (block->refGet() != 0)
412 v3s16 p = block->getPos();
415 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
416 modprofiler.add(block->getModifiedReasonString(), 1);
417 if (!saveBlock(block))
419 saved_blocks_count++;
422 // Delete from memory
423 b.sect->deleteBlock(block);
426 unloaded_blocks->push_back(p);
428 deleted_blocks_count++;
432 // Delete empty sectors
433 for (auto §or_it : m_sectors) {
434 if (sector_it.second->empty()) {
435 sector_deletion_queue.push_back(sector_it.first);
441 const auto end_time = porting::getTimeUs();
443 reportMetrics(end_time - start_time, saved_blocks_count, block_count_all);
445 // Finally delete the empty sectors
446 deleteSectors(sector_deletion_queue);
448 if(deleted_blocks_count != 0)
450 PrintInfo(infostream); // ServerMap/ClientMap:
451 infostream<<"Unloaded "<<deleted_blocks_count
452 <<" blocks from memory";
453 if(save_before_unloading)
454 infostream<<", of which "<<saved_blocks_count<<" were written";
455 infostream<<", "<<block_count_all<<" blocks in memory";
456 infostream<<"."<<std::endl;
457 if(saved_blocks_count != 0){
458 PrintInfo(infostream); // ServerMap/ClientMap:
459 infostream<<"Blocks modified by: "<<std::endl;
460 modprofiler.print(infostream);
465 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
467 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
470 void Map::deleteSectors(std::vector<v2s16> §orList)
472 for (v2s16 j : sectorList) {
473 MapSector *sector = m_sectors[j];
474 // If sector is in sector cache, remove it from there
475 if(m_sector_cache == sector)
476 m_sector_cache = NULL;
477 // Remove from map and delete
483 void Map::PrintInfo(std::ostream &out)
488 #define WATER_DROP_BOOST 4
490 const static v3s16 liquid_6dirs[6] = {
491 // order: upper before same level before lower
500 enum NeighborType : u8 {
506 struct NodeNeighbor {
512 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
515 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
522 void ServerMap::transforming_liquid_add(v3s16 p) {
523 m_transforming_liquid.push_back(p);
526 void ServerMap::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
527 ServerEnvironment *env)
530 u32 initial_size = m_transforming_liquid.size();
532 /*if(initial_size != 0)
533 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
535 // list of nodes that due to viscosity have not reached their max level height
536 std::deque<v3s16> must_reflow;
538 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
540 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
541 u32 loop_max = liquid_loop_max;
543 while (m_transforming_liquid.size() != 0)
545 // This should be done here so that it is done when continue is used
546 if (loopcount >= initial_size || loopcount >= loop_max)
551 Get a queued transforming liquid node
553 v3s16 p0 = m_transforming_liquid.front();
554 m_transforming_liquid.pop_front();
556 MapNode n0 = getNode(p0);
559 Collect information about current node
561 s8 liquid_level = -1;
562 // The liquid node which will be placed there if
563 // the liquid flows into this node.
564 content_t liquid_kind = CONTENT_IGNORE;
565 // The node which will be placed there if liquid
566 // can't flow into this node.
567 content_t floodable_node = CONTENT_AIR;
568 const ContentFeatures &cf = m_nodedef->get(n0);
569 LiquidType liquid_type = cf.liquid_type;
570 switch (liquid_type) {
572 liquid_level = LIQUID_LEVEL_SOURCE;
573 liquid_kind = cf.liquid_alternative_flowing_id;
576 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
577 liquid_kind = n0.getContent();
580 // if this node is 'floodable', it *could* be transformed
581 // into a liquid, otherwise, continue with the next node.
584 floodable_node = n0.getContent();
585 liquid_kind = CONTENT_AIR;
590 Collect information about the environment
592 NodeNeighbor sources[6]; // surrounding sources
594 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
596 NodeNeighbor airs[6]; // surrounding air
598 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
599 int num_neutrals = 0;
600 bool flowing_down = false;
601 bool ignored_sources = false;
602 for (u16 i = 0; i < 6; i++) {
603 NeighborType nt = NEIGHBOR_SAME_LEVEL;
614 v3s16 npos = p0 + liquid_6dirs[i];
615 NodeNeighbor nb(getNode(npos), nt, npos);
616 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
617 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
619 if (cfnb.floodable) {
620 airs[num_airs++] = nb;
621 // if the current node is a water source the neighbor
622 // should be enqueded for transformation regardless of whether the
623 // current node changes or not.
624 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
625 m_transforming_liquid.push_back(npos);
626 // if the current node happens to be a flowing node, it will start to flow down here.
627 if (nb.t == NEIGHBOR_LOWER)
630 neutrals[num_neutrals++] = nb;
631 if (nb.n.getContent() == CONTENT_IGNORE) {
632 // If node below is ignore prevent water from
633 // spreading outwards and otherwise prevent from
634 // flowing away as ignore node might be the source
635 if (nb.t == NEIGHBOR_LOWER)
638 ignored_sources = true;
643 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
644 if (liquid_kind == CONTENT_AIR)
645 liquid_kind = cfnb.liquid_alternative_flowing_id;
646 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
647 neutrals[num_neutrals++] = nb;
649 // Do not count bottom source, it will screw things up
650 if(nt != NEIGHBOR_LOWER)
651 sources[num_sources++] = nb;
655 if (nb.t != NEIGHBOR_SAME_LEVEL ||
656 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
657 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
658 // but exclude falling liquids on the same level, they cannot flow here anyway
659 if (liquid_kind == CONTENT_AIR)
660 liquid_kind = cfnb.liquid_alternative_flowing_id;
662 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
663 neutrals[num_neutrals++] = nb;
665 flows[num_flows++] = nb;
666 if (nb.t == NEIGHBOR_LOWER)
674 decide on the type (and possibly level) of the current node
676 content_t new_node_content;
677 s8 new_node_level = -1;
678 s8 max_node_level = -1;
680 u8 range = m_nodedef->get(liquid_kind).liquid_range;
681 if (range > LIQUID_LEVEL_MAX + 1)
682 range = LIQUID_LEVEL_MAX + 1;
684 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
685 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
686 // or the flowing alternative of the first of the surrounding sources (if it's air), so
687 // it's perfectly safe to use liquid_kind here to determine the new node content.
688 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
689 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
690 // liquid_kind is set properly, see above
691 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
692 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
693 new_node_content = liquid_kind;
695 new_node_content = floodable_node;
696 } else if (ignored_sources && liquid_level >= 0) {
697 // Maybe there are neighbouring sources that aren't loaded yet
698 // so prevent flowing away.
699 new_node_level = liquid_level;
700 new_node_content = liquid_kind;
702 // no surrounding sources, so get the maximum level that can flow into this node
703 for (u16 i = 0; i < num_flows; i++) {
704 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
705 switch (flows[i].t) {
707 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
708 max_node_level = LIQUID_LEVEL_MAX;
709 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
710 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
711 } else if (nb_liquid_level > max_node_level) {
712 max_node_level = nb_liquid_level;
717 case NEIGHBOR_SAME_LEVEL:
718 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
719 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
720 max_node_level = nb_liquid_level - 1;
725 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
726 if (viscosity > 1 && max_node_level != liquid_level) {
727 // amount to gain, limited by viscosity
728 // must be at least 1 in absolute value
729 s8 level_inc = max_node_level - liquid_level;
730 if (level_inc < -viscosity || level_inc > viscosity)
731 new_node_level = liquid_level + level_inc/viscosity;
732 else if (level_inc < 0)
733 new_node_level = liquid_level - 1;
734 else if (level_inc > 0)
735 new_node_level = liquid_level + 1;
736 if (new_node_level != max_node_level)
737 must_reflow.push_back(p0);
739 new_node_level = max_node_level;
742 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
743 new_node_content = liquid_kind;
745 new_node_content = floodable_node;
750 check if anything has changed. if not, just continue with the next node.
752 if (new_node_content == n0.getContent() &&
753 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
754 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
755 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
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 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
784 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
786 // Find out whether there is a suspect for this action
788 if (m_gamedef->rollback())
789 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
791 if (m_gamedef->rollback() && !suspect.empty()) {
793 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
794 // Get old node for rollback
795 RollbackNode rollback_oldnode(this, p0, m_gamedef);
799 RollbackNode rollback_newnode(this, p0, m_gamedef);
800 RollbackAction action;
801 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
802 m_gamedef->rollback()->reportAction(action);
808 v3s16 blockpos = getNodeBlockPos(p0);
809 MapBlock *block = getBlockNoCreateNoEx(blockpos);
811 modified_blocks[blockpos] = block;
812 changed_nodes.emplace_back(p0, n00);
816 enqueue neighbors for update if neccessary
818 switch (m_nodedef->get(n0.getContent()).liquid_type) {
821 // make sure source flows into all neighboring nodes
822 for (u16 i = 0; i < num_flows; i++)
823 if (flows[i].t != NEIGHBOR_UPPER)
824 m_transforming_liquid.push_back(flows[i].p);
825 for (u16 i = 0; i < num_airs; i++)
826 if (airs[i].t != NEIGHBOR_UPPER)
827 m_transforming_liquid.push_back(airs[i].p);
830 // this flow has turned to air; neighboring flows might need to do the same
831 for (u16 i = 0; i < num_flows; i++)
832 m_transforming_liquid.push_back(flows[i].p);
836 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
838 for (auto &iter : must_reflow)
839 m_transforming_liquid.push_back(iter);
841 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
842 env->getScriptIface()->on_liquid_transformed(changed_nodes);
844 /* ----------------------------------------------------------------------
845 * Manage the queue so that it does not grow indefinately
847 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
849 if (time_until_purge == 0)
850 return; // Feature disabled
852 time_until_purge *= 1000; // seconds -> milliseconds
854 u64 curr_time = porting::getTimeMs();
855 u32 prev_unprocessed = m_unprocessed_count;
856 m_unprocessed_count = m_transforming_liquid.size();
858 // if unprocessed block count is decreasing or stable
859 if (m_unprocessed_count <= prev_unprocessed) {
860 m_queue_size_timer_started = false;
862 if (!m_queue_size_timer_started)
863 m_inc_trending_up_start_time = curr_time;
864 m_queue_size_timer_started = true;
867 // Account for curr_time overflowing
868 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
869 m_queue_size_timer_started = false;
871 /* If the queue has been growing for more than liquid_queue_purge_time seconds
872 * and the number of unprocessed blocks is still > liquid_loop_max then we
873 * cannot keep up; dump the oldest blocks from the queue so that the queue
874 * has liquid_loop_max items in it
876 if (m_queue_size_timer_started
877 && curr_time - m_inc_trending_up_start_time > time_until_purge
878 && m_unprocessed_count > liquid_loop_max) {
880 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
882 infostream << "transformLiquids(): DUMPING " << dump_qty
883 << " blocks from the queue" << std::endl;
886 m_transforming_liquid.pop_front();
888 m_queue_size_timer_started = false; // optimistically assume we can keep up now
889 m_unprocessed_count = m_transforming_liquid.size();
893 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
895 std::vector<v3s16> positions_with_meta;
897 sortBoxVerticies(p1, p2);
898 v3s16 bpmin = getNodeBlockPos(p1);
899 v3s16 bpmax = getNodeBlockPos(p2);
901 VoxelArea area(p1, p2);
903 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
904 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
905 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
906 v3s16 blockpos(x, y, z);
908 MapBlock *block = getBlockNoCreateNoEx(blockpos);
910 verbosestream << "Map::getNodeMetadata(): Need to emerge "
911 << PP(blockpos) << std::endl;
912 block = emergeBlock(blockpos, false);
915 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
920 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
921 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
922 for (size_t i = 0; i != keys.size(); i++) {
923 v3s16 p(keys[i] + p_base);
924 if (!area.contains(p))
927 positions_with_meta.push_back(p);
931 return positions_with_meta;
934 NodeMetadata *Map::getNodeMetadata(v3s16 p)
936 v3s16 blockpos = getNodeBlockPos(p);
937 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
938 MapBlock *block = getBlockNoCreateNoEx(blockpos);
940 infostream<<"Map::getNodeMetadata(): Need to emerge "
941 <<PP(blockpos)<<std::endl;
942 block = emergeBlock(blockpos, false);
945 warningstream<<"Map::getNodeMetadata(): Block not found"
949 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
953 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
955 v3s16 blockpos = getNodeBlockPos(p);
956 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
957 MapBlock *block = getBlockNoCreateNoEx(blockpos);
959 infostream<<"Map::setNodeMetadata(): Need to emerge "
960 <<PP(blockpos)<<std::endl;
961 block = emergeBlock(blockpos, false);
964 warningstream<<"Map::setNodeMetadata(): Block not found"
968 block->m_node_metadata.set(p_rel, meta);
972 void Map::removeNodeMetadata(v3s16 p)
974 v3s16 blockpos = getNodeBlockPos(p);
975 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
976 MapBlock *block = getBlockNoCreateNoEx(blockpos);
979 warningstream<<"Map::removeNodeMetadata(): Block not found"
983 block->m_node_metadata.remove(p_rel);
986 NodeTimer Map::getNodeTimer(v3s16 p)
988 v3s16 blockpos = getNodeBlockPos(p);
989 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
990 MapBlock *block = getBlockNoCreateNoEx(blockpos);
992 infostream<<"Map::getNodeTimer(): Need to emerge "
993 <<PP(blockpos)<<std::endl;
994 block = emergeBlock(blockpos, false);
997 warningstream<<"Map::getNodeTimer(): Block not found"
1001 NodeTimer t = block->m_node_timers.get(p_rel);
1002 NodeTimer nt(t.timeout, t.elapsed, p);
1006 void Map::setNodeTimer(const NodeTimer &t)
1008 v3s16 p = t.position;
1009 v3s16 blockpos = getNodeBlockPos(p);
1010 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1011 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1013 infostream<<"Map::setNodeTimer(): Need to emerge "
1014 <<PP(blockpos)<<std::endl;
1015 block = emergeBlock(blockpos, false);
1018 warningstream<<"Map::setNodeTimer(): Block not found"
1022 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1023 block->m_node_timers.set(nt);
1026 void Map::removeNodeTimer(v3s16 p)
1028 v3s16 blockpos = getNodeBlockPos(p);
1029 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1030 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1033 warningstream<<"Map::removeNodeTimer(): Block not found"
1037 block->m_node_timers.remove(p_rel);
1040 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1041 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1044 This functions determines the node inside the target block that is
1045 closest to the camera position. This increases the occlusion culling
1046 accuracy in straight and diagonal corridors.
1047 The returned position will be occlusion checked first in addition to the
1048 others (8 corners + center).
1049 No position is returned if
1050 - the closest node is a corner, corners are checked anyway.
1051 - the camera is inside the target block, it will never be occluded.
1053 #define CLOSEST_EDGE(pos, bounds, axis) \
1054 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1055 (bounds).MaxEdge.axis
1057 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1058 (pos_camera.X <= block_bounds.MaxEdge.X);
1059 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1060 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1061 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1062 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1064 if (x_inside && y_inside && z_inside)
1065 return false; // Camera inside target mapblock
1068 if (x_inside && y_inside) {
1069 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1070 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1072 } else if (y_inside && z_inside) {
1073 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1074 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1076 } else if (x_inside && z_inside) {
1077 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1078 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1084 check = v3s16(pos_camera.X, 0, 0);
1085 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1086 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1088 } else if (y_inside) {
1089 check = v3s16(0, pos_camera.Y, 0);
1090 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1091 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1093 } else if (z_inside) {
1094 check = v3s16(0, 0, pos_camera.Z);
1095 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1096 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1100 // Closest node would be a corner, none returned
1104 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1105 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1107 v3f direction = intToFloat(pos_target - pos_camera, BS);
1108 float distance = direction.getLength();
1110 // Normalize direction vector
1111 if (distance > 0.0f)
1112 direction /= distance;
1114 v3f pos_origin_f = intToFloat(pos_camera, BS);
1116 bool is_valid_position;
1118 for (; offset < distance + end_offset; offset += step) {
1119 v3f pos_node_f = pos_origin_f + direction * offset;
1120 v3s16 pos_node = floatToInt(pos_node_f, BS);
1122 MapNode node = getNode(pos_node, &is_valid_position);
1124 if (is_valid_position &&
1125 !m_nodedef->get(node).light_propagates) {
1126 // Cannot see through light-blocking nodes --> occluded
1128 if (count >= needed_count)
1136 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1138 // Check occlusion for center and all 8 corners of the mapblock
1139 // Overshoot a little for less flickering
1140 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1141 static const v3s16 dir9[9] = {
1143 v3s16( 1, 1, 1) * bs2,
1144 v3s16( 1, 1, -1) * bs2,
1145 v3s16( 1, -1, 1) * bs2,
1146 v3s16( 1, -1, -1) * bs2,
1147 v3s16(-1, 1, 1) * bs2,
1148 v3s16(-1, 1, -1) * bs2,
1149 v3s16(-1, -1, 1) * bs2,
1150 v3s16(-1, -1, -1) * bs2,
1153 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1155 // Starting step size, value between 1m and sqrt(3)m
1156 float step = BS * 1.2f;
1157 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1158 float stepfac = 1.05f;
1160 float start_offset = BS * 1.0f;
1162 // The occlusion search of 'isOccluded()' must stop short of the target
1163 // point by distance 'end_offset' to not enter the target mapblock.
1164 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1165 // diagonal of a mapblock, because we must consider all view angles.
1166 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1167 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1169 // to reduce the likelihood of falsely occluded blocks
1170 // require at least two solid blocks
1171 // this is a HACK, we should think of a more precise algorithm
1172 u32 needed_count = 2;
1174 // Additional occlusion check, see comments in that function
1176 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1177 // node is always on a side facing the camera, end_offset can be lower
1178 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1179 -1.0f, needed_count))
1183 for (const v3s16 &dir : dir9) {
1184 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1185 start_offset, end_offset, needed_count))
1194 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1195 EmergeManager *emerge, MetricsBackend *mb):
1197 settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
1200 verbosestream<<FUNCTION_NAME<<std::endl;
1202 // Tell the EmergeManager about our MapSettingsManager
1203 emerge->map_settings_mgr = &settings_mgr;
1206 Try to load map; if not found, create a new one.
1209 // Determine which database backend to use
1210 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1212 bool succeeded = conf.readConfigFile(conf_path.c_str());
1213 if (!succeeded || !conf.exists("backend")) {
1214 // fall back to sqlite3
1215 conf.set("backend", "sqlite3");
1217 std::string backend = conf.get("backend");
1218 dbase = createDatabase(backend, savedir, conf);
1219 if (conf.exists("readonly_backend")) {
1220 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1221 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1223 if (!conf.updateConfigFile(conf_path.c_str()))
1224 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1226 m_savedir = savedir;
1227 m_map_saving_enabled = false;
1229 m_save_time_counter = mb->addCounter(
1230 "minetest_map_save_time", "Time spent saving blocks (in microseconds)");
1231 m_save_count_counter = mb->addCounter(
1232 "minetest_map_saved_blocks", "Number of blocks saved");
1233 m_loaded_blocks_gauge = mb->addGauge(
1234 "minetest_map_loaded_blocks", "Number of loaded blocks");
1236 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1239 // If directory exists, check contents and load if possible
1240 if (fs::PathExists(m_savedir)) {
1241 // If directory is empty, it is safe to save into it.
1242 if (fs::GetDirListing(m_savedir).empty()) {
1243 infostream<<"ServerMap: Empty save directory is valid."
1245 m_map_saving_enabled = true;
1250 if (settings_mgr.loadMapMeta()) {
1251 infostream << "ServerMap: Metadata loaded from "
1252 << savedir << std::endl;
1254 infostream << "ServerMap: Metadata could not be loaded "
1255 "from " << savedir << ", assuming valid save "
1256 "directory." << std::endl;
1259 m_map_saving_enabled = true;
1260 // Map loaded, not creating new one
1264 // If directory doesn't exist, it is safe to save to it
1266 m_map_saving_enabled = true;
1269 catch(std::exception &e)
1271 warningstream<<"ServerMap: Failed to load map from "<<savedir
1272 <<", exception: "<<e.what()<<std::endl;
1273 infostream<<"Please remove the map or fix it."<<std::endl;
1274 warningstream<<"Map saving will be disabled."<<std::endl;
1278 ServerMap::~ServerMap()
1280 verbosestream<<FUNCTION_NAME<<std::endl;
1284 if (m_map_saving_enabled) {
1285 // Save only changed parts
1286 save(MOD_STATE_WRITE_AT_UNLOAD);
1287 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1289 infostream << "ServerMap: Map not saved" << std::endl;
1292 catch(std::exception &e)
1294 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1295 <<", exception: "<<e.what()<<std::endl;
1299 Close database if it was opened
1305 MapgenParams *ServerMap::getMapgenParams()
1307 // getMapgenParams() should only ever be called after Server is initialized
1308 assert(settings_mgr.mapgen_params != NULL);
1309 return settings_mgr.mapgen_params;
1312 u64 ServerMap::getSeed()
1314 return getMapgenParams()->seed;
1317 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1319 const s16 mapgen_limit_bp = rangelim(
1320 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1322 return p.X < -mapgen_limit_bp ||
1323 p.X > mapgen_limit_bp ||
1324 p.Y < -mapgen_limit_bp ||
1325 p.Y > mapgen_limit_bp ||
1326 p.Z < -mapgen_limit_bp ||
1327 p.Z > mapgen_limit_bp;
1330 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1332 s16 csize = getMapgenParams()->chunksize;
1333 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1334 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1336 if (!m_chunks_in_progress.insert(bpmin).second)
1339 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1340 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1342 v3s16 extra_borders(1, 1, 1);
1343 v3s16 full_bpmin = bpmin - extra_borders;
1344 v3s16 full_bpmax = bpmax + extra_borders;
1346 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1347 if (blockpos_over_mapgen_limit(full_bpmin) ||
1348 blockpos_over_mapgen_limit(full_bpmax))
1351 data->seed = getSeed();
1352 data->blockpos_min = bpmin;
1353 data->blockpos_max = bpmax;
1354 data->nodedef = m_nodedef;
1357 Create the whole area of this and the neighboring blocks
1359 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1360 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1361 v2s16 sectorpos(x, z);
1362 // Sector metadata is loaded from disk if not already loaded.
1363 MapSector *sector = createSector(sectorpos);
1364 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1366 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1369 MapBlock *block = emergeBlock(p, false);
1370 if (block == NULL) {
1371 block = createBlock(p);
1373 // Block gets sunlight if this is true.
1374 // Refer to the map generator heuristics.
1375 bool ug = m_emerge->isBlockUnderground(p);
1376 block->setIsUnderground(ug);
1382 Now we have a big empty area.
1384 Make a ManualMapVoxelManipulator that contains this and the
1388 data->vmanip = new MMVManip(this);
1389 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1391 // Data is ready now.
1395 void ServerMap::finishBlockMake(BlockMakeData *data,
1396 std::map<v3s16, MapBlock*> *changed_blocks)
1398 v3s16 bpmin = data->blockpos_min;
1399 v3s16 bpmax = data->blockpos_max;
1401 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1402 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1405 Blit generated stuff to map
1406 NOTE: blitBackAll adds nearly everything to changed_blocks
1408 data->vmanip->blitBackAll(changed_blocks);
1410 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1411 << changed_blocks->size());
1414 Copy transforming liquid information
1416 while (data->transforming_liquid.size()) {
1417 m_transforming_liquid.push_back(data->transforming_liquid.front());
1418 data->transforming_liquid.pop_front();
1421 for (auto &changed_block : *changed_blocks) {
1422 MapBlock *block = changed_block.second;
1426 Update day/night difference cache of the MapBlocks
1428 block->expireDayNightDiff();
1430 Set block as modified
1432 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1433 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1437 Set central blocks as generated
1439 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1440 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1441 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1442 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1446 block->setGenerated(true);
1450 Save changed parts of map
1451 NOTE: Will be saved later.
1453 //save(MOD_STATE_WRITE_AT_UNLOAD);
1454 m_chunks_in_progress.erase(bpmin);
1457 MapSector *ServerMap::createSector(v2s16 p2d)
1460 Check if it exists already in memory
1462 MapSector *sector = getSectorNoGenerate(p2d);
1467 Do not create over max mapgen limit
1469 if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y)))
1470 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1473 Generate blank sector
1476 sector = new MapSector(this, p2d, m_gamedef);
1481 m_sectors[p2d] = sector;
1486 MapBlock * ServerMap::createBlock(v3s16 p)
1489 Do not create over max mapgen limit
1491 if (blockpos_over_max_limit(p))
1492 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1494 v2s16 p2d(p.X, p.Z);
1497 This will create or load a sector if not found in memory.
1498 If block exists on disk, it will be loaded.
1500 NOTE: On old save formats, this will be slow, as it generates
1501 lighting on blocks for them.
1505 sector = createSector(p2d);
1506 } catch (InvalidPositionException &e) {
1507 infostream<<"createBlock: createSector() failed"<<std::endl;
1512 Try to get a block from the sector
1515 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1517 if(block->isDummy())
1522 block = sector->createBlankBlock(block_y);
1527 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1530 MapBlock *block = getBlockNoCreateNoEx(p);
1531 if (block && !block->isDummy())
1536 MapBlock *block = loadBlock(p);
1542 MapSector *sector = createSector(v2s16(p.X, p.Z));
1543 MapBlock *block = sector->createBlankBlock(p.Y);
1551 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1553 MapBlock *block = getBlockNoCreateNoEx(p3d);
1555 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1560 bool ServerMap::isBlockInQueue(v3s16 pos)
1562 return m_emerge && m_emerge->isBlockInQueue(pos);
1565 void ServerMap::addNodeAndUpdate(v3s16 p, MapNode n,
1566 std::map<v3s16, MapBlock*> &modified_blocks,
1567 bool remove_metadata)
1569 Map::addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1572 Add neighboring liquid nodes and this node to transform queue.
1573 (it's vital for the node itself to get updated last, if it was removed.)
1576 for (const v3s16 &dir : g_7dirs) {
1579 bool is_valid_position;
1580 MapNode n2 = getNode(p2, &is_valid_position);
1581 if(is_valid_position &&
1582 (m_nodedef->get(n2).isLiquid() ||
1583 n2.getContent() == CONTENT_AIR))
1584 m_transforming_liquid.push_back(p2);
1588 // N.B. This requires no synchronization, since data will not be modified unless
1589 // the VoxelManipulator being updated belongs to the same thread.
1590 void ServerMap::updateVManip(v3s16 pos)
1592 Mapgen *mg = m_emerge->getCurrentMapgen();
1596 MMVManip *vm = mg->vm;
1600 if (!vm->m_area.contains(pos))
1603 s32 idx = vm->m_area.index(pos);
1604 vm->m_data[idx] = getNode(pos);
1605 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1607 vm->m_is_dirty = true;
1610 void ServerMap::reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks)
1612 m_loaded_blocks_gauge->set(all_blocks);
1613 m_save_time_counter->increment(save_time_us);
1614 m_save_count_counter->increment(saved_blocks);
1617 void ServerMap::save(ModifiedState save_level)
1619 if (!m_map_saving_enabled) {
1620 warningstream<<"Not saving map, saving disabled."<<std::endl;
1624 const auto start_time = porting::getTimeUs();
1626 if(save_level == MOD_STATE_CLEAN)
1627 infostream<<"ServerMap: Saving whole map, this can take time."
1630 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1631 if (settings_mgr.saveMapMeta())
1632 m_map_metadata_changed = false;
1635 // Profile modified reasons
1636 Profiler modprofiler;
1638 u32 block_count = 0;
1639 u32 block_count_all = 0; // Number of blocks in memory
1641 // Don't do anything with sqlite unless something is really saved
1642 bool save_started = false;
1644 for (auto §or_it : m_sectors) {
1645 MapSector *sector = sector_it.second;
1647 MapBlockVect blocks;
1648 sector->getBlocks(blocks);
1650 for (MapBlock *block : blocks) {
1653 if(block->getModified() >= (u32)save_level) {
1657 save_started = true;
1660 modprofiler.add(block->getModifiedReasonString(), 1);
1672 Only print if something happened or saved whole map
1674 if(save_level == MOD_STATE_CLEAN
1675 || block_count != 0) {
1676 infostream << "ServerMap: Written: "
1677 << block_count << " blocks"
1678 << ", " << block_count_all << " blocks in memory."
1680 PrintInfo(infostream); // ServerMap/ClientMap:
1681 infostream<<"Blocks modified by: "<<std::endl;
1682 modprofiler.print(infostream);
1685 const auto end_time = porting::getTimeUs();
1686 reportMetrics(end_time - start_time, block_count, block_count_all);
1689 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1691 dbase->listAllLoadableBlocks(dst);
1693 dbase_ro->listAllLoadableBlocks(dst);
1696 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1698 for (auto §or_it : m_sectors) {
1699 MapSector *sector = sector_it.second;
1701 MapBlockVect blocks;
1702 sector->getBlocks(blocks);
1704 for (MapBlock *block : blocks) {
1705 v3s16 p = block->getPos();
1711 MapDatabase *ServerMap::createDatabase(
1712 const std::string &name,
1713 const std::string &savedir,
1716 if (name == "sqlite3")
1717 return new MapDatabaseSQLite3(savedir);
1718 if (name == "dummy")
1719 return new Database_Dummy();
1721 if (name == "leveldb")
1722 return new Database_LevelDB(savedir);
1725 if (name == "redis")
1726 return new Database_Redis(conf);
1729 if (name == "postgresql") {
1730 std::string connect_string;
1731 conf.getNoEx("pgsql_connection", connect_string);
1732 return new MapDatabasePostgreSQL(connect_string);
1736 throw BaseException(std::string("Database backend ") + name + " not supported.");
1739 void ServerMap::beginSave()
1744 void ServerMap::endSave()
1749 bool ServerMap::saveBlock(MapBlock *block)
1751 return saveBlock(block, dbase, m_map_compression_level);
1754 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1756 v3s16 p3d = block->getPos();
1758 // Dummy blocks are not written
1759 if (block->isDummy()) {
1760 warningstream << "saveBlock: Not writing dummy block "
1761 << PP(p3d) << std::endl;
1765 // Format used for writing
1766 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1769 [0] u8 serialization version
1772 std::ostringstream o(std::ios_base::binary);
1773 o.write((char*) &version, 1);
1774 block->serialize(o, version, true, compression_level);
1776 bool ret = db->saveBlock(p3d, o.str());
1778 // We just wrote it to the disk so clear modified flag
1779 block->resetModified();
1784 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1787 std::istringstream is(*blob, std::ios_base::binary);
1789 u8 version = SER_FMT_VER_INVALID;
1790 is.read((char*)&version, 1);
1793 throw SerializationError("ServerMap::loadBlock(): Failed"
1794 " to read MapBlock version");
1796 MapBlock *block = NULL;
1797 bool created_new = false;
1798 block = sector->getBlockNoCreateNoEx(p3d.Y);
1801 block = sector->createBlankBlockNoInsert(p3d.Y);
1806 block->deSerialize(is, version, true);
1808 // If it's a new block, insert it to the map
1810 sector->insertBlock(block);
1811 ReflowScan scanner(this, m_emerge->ndef);
1812 scanner.scan(block, &m_transforming_liquid);
1816 Save blocks loaded in old format in new format
1819 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1820 // Only save if asked to; no need to update version
1824 // We just loaded it from, so it's up-to-date.
1825 block->resetModified();
1827 catch(SerializationError &e)
1829 errorstream<<"Invalid block data in database"
1830 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1831 <<" (SerializationError): "<<e.what()<<std::endl;
1833 // TODO: Block should be marked as invalid in memory so that it is
1834 // not touched but the game can run
1836 if(g_settings->getBool("ignore_world_load_errors")){
1837 errorstream<<"Ignoring block load error. Duck and cover! "
1838 <<"(ignore_world_load_errors)"<<std::endl;
1840 throw SerializationError("Invalid block data in database");
1845 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1847 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1849 v2s16 p2d(blockpos.X, blockpos.Z);
1852 dbase->loadBlock(blockpos, &ret);
1854 loadBlock(&ret, blockpos, createSector(p2d), false);
1855 } else if (dbase_ro) {
1856 dbase_ro->loadBlock(blockpos, &ret);
1858 loadBlock(&ret, blockpos, createSector(p2d), false);
1864 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1865 if (created_new && (block != NULL)) {
1866 std::map<v3s16, MapBlock*> modified_blocks;
1867 // Fix lighting if necessary
1868 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1869 if (!modified_blocks.empty()) {
1870 //Modified lighting, send event
1872 event.type = MEET_OTHER;
1873 std::map<v3s16, MapBlock *>::iterator it;
1874 for (it = modified_blocks.begin();
1875 it != modified_blocks.end(); ++it)
1876 event.modified_blocks.insert(it->first);
1877 dispatchEvent(event);
1883 bool ServerMap::deleteBlock(v3s16 blockpos)
1885 if (!dbase->deleteBlock(blockpos))
1888 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1890 v2s16 p2d(blockpos.X, blockpos.Z);
1891 MapSector *sector = getSectorNoGenerate(p2d);
1894 sector->deleteBlock(block);
1900 void ServerMap::PrintInfo(std::ostream &out)
1905 bool ServerMap::repairBlockLight(v3s16 blockpos,
1906 std::map<v3s16, MapBlock *> *modified_blocks)
1908 MapBlock *block = emergeBlock(blockpos, false);
1909 if (!block || !block->isGenerated())
1911 voxalgo::repair_block_light(this, block, modified_blocks);
1915 MMVManip::MMVManip(Map *map):
1922 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1923 bool load_if_inexistent)
1925 TimeTaker timer1("initialEmerge", &emerge_time);
1929 // Units of these are MapBlocks
1930 v3s16 p_min = blockpos_min;
1931 v3s16 p_max = blockpos_max;
1933 VoxelArea block_area_nodes
1934 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1936 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1939 infostream<<"initialEmerge: area: ";
1940 block_area_nodes.print(infostream);
1941 infostream<<" ("<<size_MB<<"MB)";
1942 infostream<<std::endl;
1945 addArea(block_area_nodes);
1947 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1948 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1949 for(s32 x=p_min.X; x<=p_max.X; x++)
1954 std::map<v3s16, u8>::iterator n;
1955 n = m_loaded_blocks.find(p);
1956 if(n != m_loaded_blocks.end())
1959 bool block_data_inexistent = false;
1961 TimeTaker timer2("emerge load", &emerge_load_time);
1963 block = m_map->getBlockNoCreateNoEx(p);
1964 if (!block || block->isDummy())
1965 block_data_inexistent = true;
1967 block->copyTo(*this);
1970 if(block_data_inexistent)
1973 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1974 ServerMap *svrmap = (ServerMap *)m_map;
1975 block = svrmap->emergeBlock(p, false);
1977 block = svrmap->createBlock(p);
1978 block->copyTo(*this);
1980 flags |= VMANIP_BLOCK_DATA_INEXIST;
1983 Mark area inexistent
1985 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1986 // Fill with VOXELFLAG_NO_DATA
1987 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1988 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1990 s32 i = m_area.index(a.MinEdge.X,y,z);
1991 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1995 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1997 // Mark that block was loaded as blank
1998 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2001 m_loaded_blocks[p] = flags;
2007 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2008 bool overwrite_generated)
2010 if(m_area.getExtent() == v3s16(0,0,0))
2015 Copy data of all blocks
2017 for (auto &loaded_block : m_loaded_blocks) {
2018 v3s16 p = loaded_block.first;
2019 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2020 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2021 if (!existed || (block == NULL) ||
2022 (!overwrite_generated && block->isGenerated()))
2025 block->copyFrom(*this);
2026 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2029 (*modified_blocks)[p] = block;
2033 MMVManip *MMVManip::clone() const
2035 MMVManip *ret = new MMVManip();
2037 const s32 size = m_area.getVolume();
2038 ret->m_area = m_area;
2040 ret->m_data = new MapNode[size];
2041 memcpy(ret->m_data, m_data, size * sizeof(MapNode));
2044 ret->m_flags = new u8[size];
2045 memcpy(ret->m_flags, m_flags, size * sizeof(u8));
2048 ret->m_is_dirty = m_is_dirty;
2049 // Even if the copy is disconnected from a map object keep the information
2050 // needed to write it back to one
2051 ret->m_loaded_blocks = m_loaded_blocks;
2056 void MMVManip::reparent(Map *map)
2058 assert(map && !m_map);