3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
25 #include "voxelalgorithms.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/basic_macros.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
38 #include "reflowscan.h"
40 #include "mapgen/mapgen_v6.h"
41 #include "mapgen/mg_biome.h"
44 #include "database/database.h"
45 #include "database/database-dummy.h"
46 #include "database/database-sqlite3.h"
47 #include "script/scripting_server.h"
51 #include "database/database-leveldb.h"
54 #include "database/database-redis.h"
57 #include "database/database-postgresql.h"
65 Map::Map(IGameDef *gamedef):
67 m_nodedef(gamedef->ndef())
76 for (auto §or : m_sectors) {
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 m_event_receivers.erase(event_receiver);
91 void Map::dispatchEvent(const MapEditEvent &event)
93 for (MapEventReceiver *event_receiver : m_event_receivers) {
94 event_receiver->onMapEditEvent(event);
98 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
100 if(m_sector_cache != NULL && p == m_sector_cache_p){
101 MapSector * sector = m_sector_cache;
105 auto n = m_sectors.find(p);
107 if (n == m_sectors.end())
110 MapSector *sector = n->second;
112 // Cache the last result
113 m_sector_cache_p = p;
114 m_sector_cache = sector;
119 MapSector * Map::getSectorNoGenerate(v2s16 p)
121 return getSectorNoGenerateNoLock(p);
124 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
136 MapBlock *block = getBlockNoCreateNoEx(p3d);
138 throw InvalidPositionException();
142 bool Map::isValidPosition(v3s16 p)
144 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock *block = getBlockNoCreateNoEx(blockpos);
146 return (block != NULL);
149 // Returns a CONTENT_IGNORE node if not found
150 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
152 v3s16 blockpos = getNodeBlockPos(p);
153 MapBlock *block = getBlockNoCreateNoEx(blockpos);
155 if (is_valid_position != NULL)
156 *is_valid_position = false;
157 return {CONTENT_IGNORE};
160 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
161 MapNode node = block->getNodeNoCheck(relpos);
162 if (is_valid_position != NULL)
163 *is_valid_position = true;
167 static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n)
169 // Never allow placing CONTENT_IGNORE, it causes problems
170 if(n.getContent() == CONTENT_IGNORE){
171 const NodeDefManager *nodedef = block->getParent()->getNodeDefManager();
172 v3s16 blockpos = block->getPos();
173 v3s16 p = blockpos * MAP_BLOCKSIZE + relpos;
174 errorstream<<"Not allowing to place CONTENT_IGNORE"
175 <<" while trying to replace \""
176 <<nodedef->get(block->getNodeNoCheck(relpos)).name
177 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
180 block->setNodeNoCheck(relpos, n);
183 // throws InvalidPositionException if not found
184 void Map::setNode(v3s16 p, MapNode n)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreate(blockpos);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 set_node_in_block(block, relpos, n);
192 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
193 std::map<v3s16, MapBlock*> &modified_blocks,
194 bool remove_metadata)
196 // Collect old node for rollback
197 RollbackNode rollback_oldnode(this, p, m_gamedef);
199 v3s16 blockpos = getNodeBlockPos(p);
200 MapBlock *block = getBlockNoCreate(blockpos);
201 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
203 // This is needed for updating the lighting
204 MapNode oldnode = block->getNodeNoCheck(relpos);
206 // Remove node metadata
207 if (remove_metadata) {
208 removeNodeMetadata(p);
211 // Set the node on the map
212 ContentLightingFlags f = m_nodedef->getLightingFlags(n);
213 ContentLightingFlags oldf = m_nodedef->getLightingFlags(oldnode);
215 // No light update needed, just copy over the old light.
216 n.setLight(LIGHTBANK_DAY, oldnode.getLightRaw(LIGHTBANK_DAY, oldf), f);
217 n.setLight(LIGHTBANK_NIGHT, oldnode.getLightRaw(LIGHTBANK_NIGHT, oldf), f);
218 set_node_in_block(block, relpos, n);
220 modified_blocks[blockpos] = block;
222 // Ignore light (because calling voxalgo::update_lighting_nodes)
223 n.setLight(LIGHTBANK_DAY, 0, f);
224 n.setLight(LIGHTBANK_NIGHT, 0, f);
225 set_node_in_block(block, relpos, n);
228 std::vector<std::pair<v3s16, MapNode> > oldnodes;
229 oldnodes.emplace_back(p, oldnode);
230 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
232 for (auto &modified_block : modified_blocks) {
233 modified_block.second->expireDayNightDiff();
237 // Report for rollback
238 if(m_gamedef->rollback())
240 RollbackNode rollback_newnode(this, p, m_gamedef);
241 RollbackAction action;
242 action.setSetNode(p, rollback_oldnode, rollback_newnode);
243 m_gamedef->rollback()->reportAction(action);
247 void Map::removeNodeAndUpdate(v3s16 p,
248 std::map<v3s16, MapBlock*> &modified_blocks)
250 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
253 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
256 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
260 bool succeeded = true;
262 std::map<v3s16, MapBlock*> modified_blocks;
263 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
265 // Copy modified_blocks to event
266 for (auto &modified_block : modified_blocks) {
267 event.modified_blocks.insert(modified_block.first);
270 catch(InvalidPositionException &e){
274 dispatchEvent(event);
279 bool Map::removeNodeWithEvent(v3s16 p)
282 event.type = MEET_REMOVENODE;
285 bool succeeded = true;
287 std::map<v3s16, MapBlock*> modified_blocks;
288 removeNodeAndUpdate(p, modified_blocks);
290 // Copy modified_blocks to event
291 for (auto &modified_block : modified_blocks) {
292 event.modified_blocks.insert(modified_block.first);
295 catch(InvalidPositionException &e){
299 dispatchEvent(event);
304 struct TimeOrderedMapBlock {
308 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
313 bool operator<(const TimeOrderedMapBlock &b) const
315 return block->getUsageTimer() < b.block->getUsageTimer();
322 void Map::timerUpdate(float dtime, float unload_timeout, s32 max_loaded_blocks,
323 std::vector<v3s16> *unloaded_blocks)
325 bool save_before_unloading = maySaveBlocks();
327 // Profile modified reasons
328 Profiler modprofiler;
330 std::vector<v2s16> sector_deletion_queue;
331 u32 deleted_blocks_count = 0;
332 u32 saved_blocks_count = 0;
333 u32 block_count_all = 0;
335 const auto start_time = porting::getTimeUs();
338 // If there is no practical limit, we spare creation of mapblock_queue
339 if (max_loaded_blocks < 0) {
340 for (auto §or_it : m_sectors) {
341 MapSector *sector = sector_it.second;
343 bool all_blocks_deleted = true;
346 sector->getBlocks(blocks);
348 for (MapBlock *block : blocks) {
349 block->incrementUsageTimer(dtime);
351 if (block->refGet() == 0
352 && block->getUsageTimer() > unload_timeout) {
353 v3s16 p = block->getPos();
356 if (block->getModified() != MOD_STATE_CLEAN
357 && save_before_unloading) {
358 modprofiler.add(block->getModifiedReasonString(), 1);
359 if (!saveBlock(block))
361 saved_blocks_count++;
364 // Delete from memory
365 sector->deleteBlock(block);
368 unloaded_blocks->push_back(p);
370 deleted_blocks_count++;
372 all_blocks_deleted = false;
377 // Delete sector if we emptied it
378 if (all_blocks_deleted) {
379 sector_deletion_queue.push_back(sector_it.first);
383 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
384 for (auto §or_it : m_sectors) {
385 MapSector *sector = sector_it.second;
388 sector->getBlocks(blocks);
390 for (MapBlock *block : blocks) {
391 block->incrementUsageTimer(dtime);
392 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
395 block_count_all = mapblock_queue.size();
397 // Delete old blocks, and blocks over the limit from the memory
398 while (!mapblock_queue.empty() && ((s32)mapblock_queue.size() > max_loaded_blocks
399 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
400 TimeOrderedMapBlock b = mapblock_queue.top();
401 mapblock_queue.pop();
403 MapBlock *block = b.block;
405 if (block->refGet() != 0)
408 v3s16 p = block->getPos();
411 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
412 modprofiler.add(block->getModifiedReasonString(), 1);
413 if (!saveBlock(block))
415 saved_blocks_count++;
418 // Delete from memory
419 b.sect->deleteBlock(block);
422 unloaded_blocks->push_back(p);
424 deleted_blocks_count++;
428 // Delete empty sectors
429 for (auto §or_it : m_sectors) {
430 if (sector_it.second->empty()) {
431 sector_deletion_queue.push_back(sector_it.first);
437 const auto end_time = porting::getTimeUs();
439 reportMetrics(end_time - start_time, saved_blocks_count, block_count_all);
441 // Finally delete the empty sectors
442 deleteSectors(sector_deletion_queue);
444 if(deleted_blocks_count != 0)
446 PrintInfo(infostream); // ServerMap/ClientMap:
447 infostream<<"Unloaded "<<deleted_blocks_count
448 <<" blocks from memory";
449 if(save_before_unloading)
450 infostream<<", of which "<<saved_blocks_count<<" were written";
451 infostream<<", "<<block_count_all<<" blocks in memory";
452 infostream<<"."<<std::endl;
453 if(saved_blocks_count != 0){
454 PrintInfo(infostream); // ServerMap/ClientMap:
455 infostream<<"Blocks modified by: "<<std::endl;
456 modprofiler.print(infostream);
461 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
463 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
466 void Map::deleteSectors(std::vector<v2s16> §orList)
468 for (v2s16 j : sectorList) {
469 MapSector *sector = m_sectors[j];
470 // If sector is in sector cache, remove it from there
471 if(m_sector_cache == sector)
472 m_sector_cache = NULL;
473 // Remove from map and delete
479 void Map::PrintInfo(std::ostream &out)
484 #define WATER_DROP_BOOST 4
486 const static v3s16 liquid_6dirs[6] = {
487 // order: upper before same level before lower
496 enum NeighborType : u8 {
502 struct NodeNeighbor {
508 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
511 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
518 void ServerMap::transforming_liquid_add(v3s16 p) {
519 m_transforming_liquid.push_back(p);
522 void ServerMap::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
523 ServerEnvironment *env)
526 u32 initial_size = m_transforming_liquid.size();
528 /*if(initial_size != 0)
529 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
531 // list of nodes that due to viscosity have not reached their max level height
532 std::vector<v3s16> must_reflow;
534 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
536 std::vector<v3s16> check_for_falling;
538 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
539 u32 loop_max = liquid_loop_max;
541 while (m_transforming_liquid.size() != 0)
543 // This should be done here so that it is done when continue is used
544 if (loopcount >= initial_size || loopcount >= loop_max)
549 Get a queued transforming liquid node
551 v3s16 p0 = m_transforming_liquid.front();
552 m_transforming_liquid.pop_front();
554 MapNode n0 = getNode(p0);
557 Collect information about current node
559 s8 liquid_level = -1;
560 // The liquid node which will be placed there if
561 // the liquid flows into this node.
562 content_t liquid_kind = CONTENT_IGNORE;
563 // The node which will be placed there if liquid
564 // can't flow into this node.
565 content_t floodable_node = CONTENT_AIR;
566 const ContentFeatures &cf = m_nodedef->get(n0);
567 LiquidType liquid_type = cf.liquid_type;
568 switch (liquid_type) {
570 liquid_level = LIQUID_LEVEL_SOURCE;
571 liquid_kind = cf.liquid_alternative_flowing_id;
574 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
575 liquid_kind = n0.getContent();
578 // if this node is 'floodable', it *could* be transformed
579 // into a liquid, otherwise, continue with the next node.
582 floodable_node = n0.getContent();
583 liquid_kind = CONTENT_AIR;
588 Collect information about the environment
590 NodeNeighbor sources[6]; // surrounding sources
592 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
594 NodeNeighbor airs[6]; // surrounding air
596 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
597 int num_neutrals = 0;
598 bool flowing_down = false;
599 bool ignored_sources = false;
600 bool floating_node_above = false;
601 for (u16 i = 0; i < 6; i++) {
602 NeighborType nt = NEIGHBOR_SAME_LEVEL;
613 v3s16 npos = p0 + liquid_6dirs[i];
614 NodeNeighbor nb(getNode(npos), nt, npos);
615 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
616 if (nt == NEIGHBOR_UPPER && cfnb.floats)
617 floating_node_above = true;
618 switch (cfnb.liquid_type) {
620 if (cfnb.floodable) {
621 airs[num_airs++] = nb;
622 // if the current node is a water source the neighbor
623 // should be enqueded for transformation regardless of whether the
624 // current node changes or not.
625 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
626 m_transforming_liquid.push_back(npos);
627 // if the current node happens to be a flowing node, it will start to flow down here.
628 if (nb.t == NEIGHBOR_LOWER)
631 neutrals[num_neutrals++] = nb;
632 if (nb.n.getContent() == CONTENT_IGNORE) {
633 // If node below is ignore prevent water from
634 // spreading outwards and otherwise prevent from
635 // flowing away as ignore node might be the source
636 if (nb.t == NEIGHBOR_LOWER)
639 ignored_sources = true;
644 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
645 if (liquid_kind == CONTENT_AIR)
646 liquid_kind = cfnb.liquid_alternative_flowing_id;
647 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
648 neutrals[num_neutrals++] = nb;
650 // Do not count bottom source, it will screw things up
651 if(nt != NEIGHBOR_LOWER)
652 sources[num_sources++] = nb;
656 if (nb.t != NEIGHBOR_SAME_LEVEL ||
657 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
658 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
659 // but exclude falling liquids on the same level, they cannot flow here anyway
660 if (liquid_kind == CONTENT_AIR)
661 liquid_kind = cfnb.liquid_alternative_flowing_id;
663 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
664 neutrals[num_neutrals++] = nb;
666 flows[num_flows++] = nb;
667 if (nb.t == NEIGHBOR_LOWER)
675 decide on the type (and possibly level) of the current node
677 content_t new_node_content;
678 s8 new_node_level = -1;
679 s8 max_node_level = -1;
681 u8 range = m_nodedef->get(liquid_kind).liquid_range;
682 if (range > LIQUID_LEVEL_MAX + 1)
683 range = LIQUID_LEVEL_MAX + 1;
685 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
686 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
687 // or the flowing alternative of the first of the surrounding sources (if it's air), so
688 // it's perfectly safe to use liquid_kind here to determine the new node content.
689 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
690 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
691 // liquid_kind is set properly, see above
692 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
693 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
694 new_node_content = liquid_kind;
696 new_node_content = floodable_node;
697 } else if (ignored_sources && liquid_level >= 0) {
698 // Maybe there are neighboring sources that aren't loaded yet
699 // so prevent flowing away.
700 new_node_level = liquid_level;
701 new_node_content = liquid_kind;
703 // no surrounding sources, so get the maximum level that can flow into this node
704 for (u16 i = 0; i < num_flows; i++) {
705 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
706 switch (flows[i].t) {
708 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
709 max_node_level = LIQUID_LEVEL_MAX;
710 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
711 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
712 } else if (nb_liquid_level > max_node_level) {
713 max_node_level = nb_liquid_level;
718 case NEIGHBOR_SAME_LEVEL:
719 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
720 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
721 max_node_level = nb_liquid_level - 1;
726 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
727 if (viscosity > 1 && max_node_level != liquid_level) {
728 // amount to gain, limited by viscosity
729 // must be at least 1 in absolute value
730 s8 level_inc = max_node_level - liquid_level;
731 if (level_inc < -viscosity || level_inc > viscosity)
732 new_node_level = liquid_level + level_inc/viscosity;
733 else if (level_inc < 0)
734 new_node_level = liquid_level - 1;
735 else if (level_inc > 0)
736 new_node_level = liquid_level + 1;
737 if (new_node_level != max_node_level)
738 must_reflow.push_back(p0);
740 new_node_level = max_node_level;
743 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
744 new_node_content = liquid_kind;
746 new_node_content = floodable_node;
751 check if anything has changed. if not, just continue with the next node.
753 if (new_node_content == n0.getContent() &&
754 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
755 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
756 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
761 check if there is a floating node above that needs to be updated.
763 if (floating_node_above && new_node_content == CONTENT_AIR)
764 check_for_falling.push_back(p0);
767 update the current node
770 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
771 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
772 // set level to last 3 bits, flowing down bit to 4th bit
773 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
775 // set the liquid level and flow bits to 0
776 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
780 n0.setContent(new_node_content);
782 // on_flood() the node
783 if (floodable_node != CONTENT_AIR) {
784 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
788 // Ignore light (because calling voxalgo::update_lighting_nodes)
789 ContentLightingFlags f0 = m_nodedef->getLightingFlags(n0);
790 n0.setLight(LIGHTBANK_DAY, 0, f0);
791 n0.setLight(LIGHTBANK_NIGHT, 0, f0);
793 // Find out whether there is a suspect for this action
795 if (m_gamedef->rollback())
796 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
798 if (m_gamedef->rollback() && !suspect.empty()) {
800 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
801 // Get old node for rollback
802 RollbackNode rollback_oldnode(this, p0, m_gamedef);
806 RollbackNode rollback_newnode(this, p0, m_gamedef);
807 RollbackAction action;
808 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
809 m_gamedef->rollback()->reportAction(action);
815 v3s16 blockpos = getNodeBlockPos(p0);
816 MapBlock *block = getBlockNoCreateNoEx(blockpos);
818 modified_blocks[blockpos] = block;
819 changed_nodes.emplace_back(p0, n00);
823 enqueue neighbors for update if necessary
825 switch (m_nodedef->get(n0.getContent()).liquid_type) {
828 // make sure source flows into all neighboring nodes
829 for (u16 i = 0; i < num_flows; i++)
830 if (flows[i].t != NEIGHBOR_UPPER)
831 m_transforming_liquid.push_back(flows[i].p);
832 for (u16 i = 0; i < num_airs; i++)
833 if (airs[i].t != NEIGHBOR_UPPER)
834 m_transforming_liquid.push_back(airs[i].p);
837 // this flow has turned to air; neighboring flows might need to do the same
838 for (u16 i = 0; i < num_flows; i++)
839 m_transforming_liquid.push_back(flows[i].p);
843 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
845 for (const auto &iter : must_reflow)
846 m_transforming_liquid.push_back(iter);
848 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
850 for (const v3s16 &p : check_for_falling) {
851 env->getScriptIface()->check_for_falling(p);
854 env->getScriptIface()->on_liquid_transformed(changed_nodes);
856 /* ----------------------------------------------------------------------
857 * Manage the queue so that it does not grow indefinitely
859 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
861 if (time_until_purge == 0)
862 return; // Feature disabled
864 time_until_purge *= 1000; // seconds -> milliseconds
866 u64 curr_time = porting::getTimeMs();
867 u32 prev_unprocessed = m_unprocessed_count;
868 m_unprocessed_count = m_transforming_liquid.size();
870 // if unprocessed block count is decreasing or stable
871 if (m_unprocessed_count <= prev_unprocessed) {
872 m_queue_size_timer_started = false;
874 if (!m_queue_size_timer_started)
875 m_inc_trending_up_start_time = curr_time;
876 m_queue_size_timer_started = true;
879 // Account for curr_time overflowing
880 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
881 m_queue_size_timer_started = false;
883 /* If the queue has been growing for more than liquid_queue_purge_time seconds
884 * and the number of unprocessed blocks is still > liquid_loop_max then we
885 * cannot keep up; dump the oldest blocks from the queue so that the queue
886 * has liquid_loop_max items in it
888 if (m_queue_size_timer_started
889 && curr_time - m_inc_trending_up_start_time > time_until_purge
890 && m_unprocessed_count > liquid_loop_max) {
892 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
894 infostream << "transformLiquids(): DUMPING " << dump_qty
895 << " blocks from the queue" << std::endl;
898 m_transforming_liquid.pop_front();
900 m_queue_size_timer_started = false; // optimistically assume we can keep up now
901 m_unprocessed_count = m_transforming_liquid.size();
905 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
907 std::vector<v3s16> positions_with_meta;
909 sortBoxVerticies(p1, p2);
910 v3s16 bpmin = getNodeBlockPos(p1);
911 v3s16 bpmax = getNodeBlockPos(p2);
913 VoxelArea area(p1, p2);
915 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
916 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
917 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
918 v3s16 blockpos(x, y, z);
920 MapBlock *block = getBlockNoCreateNoEx(blockpos);
922 verbosestream << "Map::getNodeMetadata(): Need to emerge "
923 << PP(blockpos) << std::endl;
924 block = emergeBlock(blockpos, false);
927 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
932 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
933 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
934 for (size_t i = 0; i != keys.size(); i++) {
935 v3s16 p(keys[i] + p_base);
936 if (!area.contains(p))
939 positions_with_meta.push_back(p);
943 return positions_with_meta;
946 NodeMetadata *Map::getNodeMetadata(v3s16 p)
948 v3s16 blockpos = getNodeBlockPos(p);
949 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
950 MapBlock *block = getBlockNoCreateNoEx(blockpos);
952 infostream<<"Map::getNodeMetadata(): Need to emerge "
953 <<PP(blockpos)<<std::endl;
954 block = emergeBlock(blockpos, false);
957 warningstream<<"Map::getNodeMetadata(): Block not found"
961 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
965 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
967 v3s16 blockpos = getNodeBlockPos(p);
968 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
969 MapBlock *block = getBlockNoCreateNoEx(blockpos);
971 infostream<<"Map::setNodeMetadata(): Need to emerge "
972 <<PP(blockpos)<<std::endl;
973 block = emergeBlock(blockpos, false);
976 warningstream<<"Map::setNodeMetadata(): Block not found"
980 block->m_node_metadata.set(p_rel, meta);
984 void Map::removeNodeMetadata(v3s16 p)
986 v3s16 blockpos = getNodeBlockPos(p);
987 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
988 MapBlock *block = getBlockNoCreateNoEx(blockpos);
991 warningstream<<"Map::removeNodeMetadata(): Block not found"
995 block->m_node_metadata.remove(p_rel);
998 NodeTimer Map::getNodeTimer(v3s16 p)
1000 v3s16 blockpos = getNodeBlockPos(p);
1001 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1002 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1004 infostream<<"Map::getNodeTimer(): Need to emerge "
1005 <<PP(blockpos)<<std::endl;
1006 block = emergeBlock(blockpos, false);
1009 warningstream<<"Map::getNodeTimer(): Block not found"
1013 NodeTimer t = block->getNodeTimer(p_rel);
1014 NodeTimer nt(t.timeout, t.elapsed, p);
1018 void Map::setNodeTimer(const NodeTimer &t)
1020 v3s16 p = t.position;
1021 v3s16 blockpos = getNodeBlockPos(p);
1022 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1023 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1025 infostream<<"Map::setNodeTimer(): Need to emerge "
1026 <<PP(blockpos)<<std::endl;
1027 block = emergeBlock(blockpos, false);
1030 warningstream<<"Map::setNodeTimer(): Block not found"
1034 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1035 block->setNodeTimer(nt);
1038 void Map::removeNodeTimer(v3s16 p)
1040 v3s16 blockpos = getNodeBlockPos(p);
1041 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1042 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1045 warningstream<<"Map::removeNodeTimer(): Block not found"
1049 block->removeNodeTimer(p_rel);
1052 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1053 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1056 This functions determines the node inside the target block that is
1057 closest to the camera position. This increases the occlusion culling
1058 accuracy in straight and diagonal corridors.
1059 The returned position will be occlusion checked first in addition to the
1060 others (8 corners + center).
1061 No position is returned if
1062 - the closest node is a corner, corners are checked anyway.
1063 - the camera is inside the target block, it will never be occluded.
1065 #define CLOSEST_EDGE(pos, bounds, axis) \
1066 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1067 (bounds).MaxEdge.axis
1069 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1070 (pos_camera.X <= block_bounds.MaxEdge.X);
1071 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1072 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1073 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1074 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1076 if (x_inside && y_inside && z_inside)
1077 return false; // Camera inside target mapblock
1080 if (x_inside && y_inside) {
1081 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1082 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1084 } else if (y_inside && z_inside) {
1085 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1086 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1088 } else if (x_inside && z_inside) {
1089 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1090 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1096 check = v3s16(pos_camera.X, 0, 0);
1097 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1098 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1100 } else if (y_inside) {
1101 check = v3s16(0, pos_camera.Y, 0);
1102 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1103 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1105 } else if (z_inside) {
1106 check = v3s16(0, 0, pos_camera.Z);
1107 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1108 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1112 // Closest node would be a corner, none returned
1116 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1117 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1119 v3f direction = intToFloat(pos_target - pos_camera, BS);
1120 float distance = direction.getLength();
1122 // Normalize direction vector
1123 if (distance > 0.0f)
1124 direction /= distance;
1126 v3f pos_origin_f = intToFloat(pos_camera, BS);
1128 bool is_valid_position;
1130 for (; offset < distance + end_offset; offset += step) {
1131 v3f pos_node_f = pos_origin_f + direction * offset;
1132 v3s16 pos_node = floatToInt(pos_node_f, BS);
1134 MapNode node = getNode(pos_node, &is_valid_position);
1136 if (is_valid_position &&
1137 !m_nodedef->getLightingFlags(node).light_propagates) {
1138 // Cannot see through light-blocking nodes --> occluded
1140 if (count >= needed_count)
1148 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1150 // Check occlusion for center and all 8 corners of the mapblock
1151 // Overshoot a little for less flickering
1152 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1153 static const v3s16 dir9[9] = {
1155 v3s16( 1, 1, 1) * bs2,
1156 v3s16( 1, 1, -1) * bs2,
1157 v3s16( 1, -1, 1) * bs2,
1158 v3s16( 1, -1, -1) * bs2,
1159 v3s16(-1, 1, 1) * bs2,
1160 v3s16(-1, 1, -1) * bs2,
1161 v3s16(-1, -1, 1) * bs2,
1162 v3s16(-1, -1, -1) * bs2,
1165 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1167 // Starting step size, value between 1m and sqrt(3)m
1168 float step = BS * 1.2f;
1169 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1170 float stepfac = 1.05f;
1172 float start_offset = BS * 1.0f;
1174 // The occlusion search of 'isOccluded()' must stop short of the target
1175 // point by distance 'end_offset' to not enter the target mapblock.
1176 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1177 // diagonal of a mapblock, because we must consider all view angles.
1178 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1179 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1181 // to reduce the likelihood of falsely occluded blocks
1182 // require at least two solid blocks
1183 // this is a HACK, we should think of a more precise algorithm
1184 u32 needed_count = 2;
1186 // Additional occlusion check, see comments in that function
1188 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1189 // node is always on a side facing the camera, end_offset can be lower
1190 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1191 -1.0f, needed_count))
1195 for (const v3s16 &dir : dir9) {
1196 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1197 start_offset, end_offset, needed_count))
1206 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1207 EmergeManager *emerge, MetricsBackend *mb):
1209 settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
1212 verbosestream<<FUNCTION_NAME<<std::endl;
1214 // Tell the EmergeManager about our MapSettingsManager
1215 emerge->map_settings_mgr = &settings_mgr;
1218 Try to load map; if not found, create a new one.
1221 // Determine which database backend to use
1222 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1224 bool succeeded = conf.readConfigFile(conf_path.c_str());
1225 if (!succeeded || !conf.exists("backend")) {
1226 // fall back to sqlite3
1227 conf.set("backend", "sqlite3");
1229 std::string backend = conf.get("backend");
1230 dbase = createDatabase(backend, savedir, conf);
1231 if (conf.exists("readonly_backend")) {
1232 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1233 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1235 if (!conf.updateConfigFile(conf_path.c_str()))
1236 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1238 m_savedir = savedir;
1239 m_map_saving_enabled = false;
1241 m_save_time_counter = mb->addCounter(
1242 "minetest_map_save_time", "Time spent saving blocks (in microseconds)");
1243 m_save_count_counter = mb->addCounter(
1244 "minetest_map_saved_blocks", "Number of blocks saved");
1245 m_loaded_blocks_gauge = mb->addGauge(
1246 "minetest_map_loaded_blocks", "Number of loaded blocks");
1248 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1251 // If directory exists, check contents and load if possible
1252 if (fs::PathExists(m_savedir)) {
1253 // If directory is empty, it is safe to save into it.
1254 if (fs::GetDirListing(m_savedir).empty()) {
1255 infostream<<"ServerMap: Empty save directory is valid."
1257 m_map_saving_enabled = true;
1262 if (settings_mgr.loadMapMeta()) {
1263 infostream << "ServerMap: Metadata loaded from "
1264 << savedir << std::endl;
1266 infostream << "ServerMap: Metadata could not be loaded "
1267 "from " << savedir << ", assuming valid save "
1268 "directory." << std::endl;
1271 m_map_saving_enabled = true;
1272 // Map loaded, not creating new one
1276 // If directory doesn't exist, it is safe to save to it
1278 m_map_saving_enabled = true;
1281 catch(std::exception &e)
1283 warningstream<<"ServerMap: Failed to load map from "<<savedir
1284 <<", exception: "<<e.what()<<std::endl;
1285 infostream<<"Please remove the map or fix it."<<std::endl;
1286 warningstream<<"Map saving will be disabled."<<std::endl;
1290 ServerMap::~ServerMap()
1292 verbosestream<<FUNCTION_NAME<<std::endl;
1296 if (m_map_saving_enabled) {
1297 // Save only changed parts
1298 save(MOD_STATE_WRITE_AT_UNLOAD);
1299 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1301 infostream << "ServerMap: Map not saved" << std::endl;
1304 catch(std::exception &e)
1306 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1307 <<", exception: "<<e.what()<<std::endl;
1311 Close database if it was opened
1317 MapgenParams *ServerMap::getMapgenParams()
1319 // getMapgenParams() should only ever be called after Server is initialized
1320 assert(settings_mgr.mapgen_params != NULL);
1321 return settings_mgr.mapgen_params;
1324 u64 ServerMap::getSeed()
1326 return getMapgenParams()->seed;
1329 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1331 const s16 mapgen_limit_bp = rangelim(
1332 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1334 return p.X < -mapgen_limit_bp ||
1335 p.X > mapgen_limit_bp ||
1336 p.Y < -mapgen_limit_bp ||
1337 p.Y > mapgen_limit_bp ||
1338 p.Z < -mapgen_limit_bp ||
1339 p.Z > mapgen_limit_bp;
1342 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1344 s16 csize = getMapgenParams()->chunksize;
1345 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1346 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1348 if (!m_chunks_in_progress.insert(bpmin).second)
1351 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1352 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1354 v3s16 extra_borders(1, 1, 1);
1355 v3s16 full_bpmin = bpmin - extra_borders;
1356 v3s16 full_bpmax = bpmax + extra_borders;
1358 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1359 if (blockpos_over_mapgen_limit(full_bpmin) ||
1360 blockpos_over_mapgen_limit(full_bpmax))
1363 data->seed = getSeed();
1364 data->blockpos_min = bpmin;
1365 data->blockpos_max = bpmax;
1366 data->nodedef = m_nodedef;
1369 Create the whole area of this and the neighboring blocks
1371 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1372 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1373 v2s16 sectorpos(x, z);
1374 // Sector metadata is loaded from disk if not already loaded.
1375 MapSector *sector = createSector(sectorpos);
1376 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1378 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1381 MapBlock *block = emergeBlock(p, false);
1382 if (block == NULL) {
1383 block = createBlock(p);
1385 // Block gets sunlight if this is true.
1386 // Refer to the map generator heuristics.
1387 bool ug = m_emerge->isBlockUnderground(p);
1388 block->setIsUnderground(ug);
1394 Now we have a big empty area.
1396 Make a ManualMapVoxelManipulator that contains this and the
1400 data->vmanip = new MMVManip(this);
1401 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1403 // Data is ready now.
1407 void ServerMap::finishBlockMake(BlockMakeData *data,
1408 std::map<v3s16, MapBlock*> *changed_blocks)
1410 v3s16 bpmin = data->blockpos_min;
1411 v3s16 bpmax = data->blockpos_max;
1413 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1414 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1417 Blit generated stuff to map
1418 NOTE: blitBackAll adds nearly everything to changed_blocks
1420 data->vmanip->blitBackAll(changed_blocks);
1422 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1423 << changed_blocks->size());
1426 Copy transforming liquid information
1428 while (data->transforming_liquid.size()) {
1429 m_transforming_liquid.push_back(data->transforming_liquid.front());
1430 data->transforming_liquid.pop_front();
1433 for (auto &changed_block : *changed_blocks) {
1434 MapBlock *block = changed_block.second;
1438 Update day/night difference cache of the MapBlocks
1440 block->expireDayNightDiff();
1442 Set block as modified
1444 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1445 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1449 Set central blocks as generated
1451 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1452 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1453 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1454 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1458 block->setGenerated(true);
1462 Save changed parts of map
1463 NOTE: Will be saved later.
1465 //save(MOD_STATE_WRITE_AT_UNLOAD);
1466 m_chunks_in_progress.erase(bpmin);
1469 MapSector *ServerMap::createSector(v2s16 p2d)
1472 Check if it exists already in memory
1474 MapSector *sector = getSectorNoGenerate(p2d);
1479 Do not create over max mapgen limit
1481 if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y)))
1482 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1485 Generate blank sector
1488 sector = new MapSector(this, p2d, m_gamedef);
1493 m_sectors[p2d] = sector;
1498 MapBlock * ServerMap::createBlock(v3s16 p)
1501 Do not create over max mapgen limit
1503 if (blockpos_over_max_limit(p))
1504 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1506 v2s16 p2d(p.X, p.Z);
1509 This will create or load a sector if not found in memory.
1510 If block exists on disk, it will be loaded.
1512 NOTE: On old save formats, this will be slow, as it generates
1513 lighting on blocks for them.
1517 sector = createSector(p2d);
1518 } catch (InvalidPositionException &e) {
1519 infostream<<"createBlock: createSector() failed"<<std::endl;
1524 Try to get a block from the sector
1527 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1532 block = sector->createBlankBlock(block_y);
1537 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1540 MapBlock *block = getBlockNoCreateNoEx(p);
1546 MapBlock *block = loadBlock(p);
1552 MapSector *sector = createSector(v2s16(p.X, p.Z));
1553 MapBlock *block = sector->createBlankBlock(p.Y);
1561 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1563 MapBlock *block = getBlockNoCreateNoEx(p3d);
1565 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1570 bool ServerMap::isBlockInQueue(v3s16 pos)
1572 return m_emerge && m_emerge->isBlockInQueue(pos);
1575 void ServerMap::addNodeAndUpdate(v3s16 p, MapNode n,
1576 std::map<v3s16, MapBlock*> &modified_blocks,
1577 bool remove_metadata)
1579 Map::addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1582 Add neighboring liquid nodes and this node to transform queue.
1583 (it's vital for the node itself to get updated last, if it was removed.)
1586 for (const v3s16 &dir : g_7dirs) {
1589 bool is_valid_position;
1590 MapNode n2 = getNode(p2, &is_valid_position);
1591 if(is_valid_position &&
1592 (m_nodedef->get(n2).isLiquid() ||
1593 n2.getContent() == CONTENT_AIR))
1594 m_transforming_liquid.push_back(p2);
1598 // N.B. This requires no synchronization, since data will not be modified unless
1599 // the VoxelManipulator being updated belongs to the same thread.
1600 void ServerMap::updateVManip(v3s16 pos)
1602 Mapgen *mg = m_emerge->getCurrentMapgen();
1606 MMVManip *vm = mg->vm;
1610 if (!vm->m_area.contains(pos))
1613 s32 idx = vm->m_area.index(pos);
1614 vm->m_data[idx] = getNode(pos);
1615 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1617 vm->m_is_dirty = true;
1620 void ServerMap::reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks)
1622 m_loaded_blocks_gauge->set(all_blocks);
1623 m_save_time_counter->increment(save_time_us);
1624 m_save_count_counter->increment(saved_blocks);
1627 void ServerMap::save(ModifiedState save_level)
1629 if (!m_map_saving_enabled) {
1630 warningstream<<"Not saving map, saving disabled."<<std::endl;
1634 const auto start_time = porting::getTimeUs();
1636 if(save_level == MOD_STATE_CLEAN)
1637 infostream<<"ServerMap: Saving whole map, this can take time."
1640 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1641 if (settings_mgr.saveMapMeta())
1642 m_map_metadata_changed = false;
1645 // Profile modified reasons
1646 Profiler modprofiler;
1648 u32 block_count = 0;
1649 u32 block_count_all = 0; // Number of blocks in memory
1651 // Don't do anything with sqlite unless something is really saved
1652 bool save_started = false;
1654 for (auto §or_it : m_sectors) {
1655 MapSector *sector = sector_it.second;
1657 MapBlockVect blocks;
1658 sector->getBlocks(blocks);
1660 for (MapBlock *block : blocks) {
1663 if(block->getModified() >= (u32)save_level) {
1667 save_started = true;
1670 modprofiler.add(block->getModifiedReasonString(), 1);
1682 Only print if something happened or saved whole map
1684 if(save_level == MOD_STATE_CLEAN
1685 || block_count != 0) {
1686 infostream << "ServerMap: Written: "
1687 << block_count << " blocks"
1688 << ", " << block_count_all << " blocks in memory."
1690 PrintInfo(infostream); // ServerMap/ClientMap:
1691 infostream<<"Blocks modified by: "<<std::endl;
1692 modprofiler.print(infostream);
1695 const auto end_time = porting::getTimeUs();
1696 reportMetrics(end_time - start_time, block_count, block_count_all);
1699 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1701 dbase->listAllLoadableBlocks(dst);
1703 dbase_ro->listAllLoadableBlocks(dst);
1706 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1708 for (auto §or_it : m_sectors) {
1709 MapSector *sector = sector_it.second;
1711 MapBlockVect blocks;
1712 sector->getBlocks(blocks);
1714 for (MapBlock *block : blocks) {
1715 v3s16 p = block->getPos();
1721 MapDatabase *ServerMap::createDatabase(
1722 const std::string &name,
1723 const std::string &savedir,
1726 if (name == "sqlite3")
1727 return new MapDatabaseSQLite3(savedir);
1728 if (name == "dummy")
1729 return new Database_Dummy();
1731 if (name == "leveldb")
1732 return new Database_LevelDB(savedir);
1735 if (name == "redis")
1736 return new Database_Redis(conf);
1739 if (name == "postgresql") {
1740 std::string connect_string;
1741 conf.getNoEx("pgsql_connection", connect_string);
1742 return new MapDatabasePostgreSQL(connect_string);
1746 throw BaseException(std::string("Database backend ") + name + " not supported.");
1749 void ServerMap::beginSave()
1754 void ServerMap::endSave()
1759 bool ServerMap::saveBlock(MapBlock *block)
1761 return saveBlock(block, dbase, m_map_compression_level);
1764 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1766 v3s16 p3d = block->getPos();
1768 // Format used for writing
1769 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1772 [0] u8 serialization version
1775 std::ostringstream o(std::ios_base::binary);
1776 o.write((char*) &version, 1);
1777 block->serialize(o, version, true, compression_level);
1779 bool ret = db->saveBlock(p3d, o.str());
1781 // We just wrote it to the disk so clear modified flag
1782 block->resetModified();
1787 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1790 std::istringstream is(*blob, std::ios_base::binary);
1792 u8 version = SER_FMT_VER_INVALID;
1793 is.read((char*)&version, 1);
1796 throw SerializationError("ServerMap::loadBlock(): Failed"
1797 " to read MapBlock version");
1799 MapBlock *block = NULL;
1800 bool created_new = false;
1801 block = sector->getBlockNoCreateNoEx(p3d.Y);
1804 block = sector->createBlankBlockNoInsert(p3d.Y);
1809 block->deSerialize(is, version, true);
1811 // If it's a new block, insert it to the map
1813 sector->insertBlock(block);
1814 ReflowScan scanner(this, m_emerge->ndef);
1815 scanner.scan(block, &m_transforming_liquid);
1819 Save blocks loaded in old format in new format
1822 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1823 // Only save if asked to; no need to update version
1827 // We just loaded it from, so it's up-to-date.
1828 block->resetModified();
1830 catch(SerializationError &e)
1832 errorstream<<"Invalid block data in database"
1833 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1834 <<" (SerializationError): "<<e.what()<<std::endl;
1836 // TODO: Block should be marked as invalid in memory so that it is
1837 // not touched but the game can run
1839 if(g_settings->getBool("ignore_world_load_errors")){
1840 errorstream<<"Ignoring block load error. Duck and cover! "
1841 <<"(ignore_world_load_errors)"<<std::endl;
1843 throw SerializationError("Invalid block data in database");
1848 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1850 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1852 v2s16 p2d(blockpos.X, blockpos.Z);
1855 dbase->loadBlock(blockpos, &ret);
1857 loadBlock(&ret, blockpos, createSector(p2d), false);
1858 } else if (dbase_ro) {
1859 dbase_ro->loadBlock(blockpos, &ret);
1861 loadBlock(&ret, blockpos, createSector(p2d), false);
1867 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1868 if (created_new && (block != NULL)) {
1869 std::map<v3s16, MapBlock*> modified_blocks;
1870 // Fix lighting if necessary
1871 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1872 if (!modified_blocks.empty()) {
1873 //Modified lighting, send event
1875 event.type = MEET_OTHER;
1876 std::map<v3s16, MapBlock *>::iterator it;
1877 for (it = modified_blocks.begin();
1878 it != modified_blocks.end(); ++it)
1879 event.modified_blocks.insert(it->first);
1880 dispatchEvent(event);
1886 bool ServerMap::deleteBlock(v3s16 blockpos)
1888 if (!dbase->deleteBlock(blockpos))
1891 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1893 v2s16 p2d(blockpos.X, blockpos.Z);
1894 MapSector *sector = getSectorNoGenerate(p2d);
1897 sector->deleteBlock(block);
1903 void ServerMap::PrintInfo(std::ostream &out)
1908 bool ServerMap::repairBlockLight(v3s16 blockpos,
1909 std::map<v3s16, MapBlock *> *modified_blocks)
1911 MapBlock *block = emergeBlock(blockpos, false);
1912 if (!block || !block->isGenerated())
1914 voxalgo::repair_block_light(this, block, modified_blocks);
1918 MMVManip::MMVManip(Map *map):
1925 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1926 bool load_if_inexistent)
1928 TimeTaker timer1("initialEmerge", &emerge_time);
1932 // Units of these are MapBlocks
1933 v3s16 p_min = blockpos_min;
1934 v3s16 p_max = blockpos_max;
1936 VoxelArea block_area_nodes
1937 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1939 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1942 infostream<<"initialEmerge: area: ";
1943 block_area_nodes.print(infostream);
1944 infostream<<" ("<<size_MB<<"MB)";
1945 infostream<<std::endl;
1948 addArea(block_area_nodes);
1950 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1951 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1952 for(s32 x=p_min.X; x<=p_max.X; x++)
1957 std::map<v3s16, u8>::iterator n;
1958 n = m_loaded_blocks.find(p);
1959 if(n != m_loaded_blocks.end())
1962 bool block_data_inexistent = false;
1964 TimeTaker timer2("emerge load", &emerge_load_time);
1966 block = m_map->getBlockNoCreateNoEx(p);
1968 block_data_inexistent = true;
1970 block->copyTo(*this);
1973 if(block_data_inexistent)
1976 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1977 ServerMap *svrmap = (ServerMap *)m_map;
1978 block = svrmap->emergeBlock(p, false);
1980 block = svrmap->createBlock(p);
1981 block->copyTo(*this);
1983 flags |= VMANIP_BLOCK_DATA_INEXIST;
1986 Mark area inexistent
1988 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1989 // Fill with VOXELFLAG_NO_DATA
1990 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1991 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1993 s32 i = m_area.index(a.MinEdge.X,y,z);
1994 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1998 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2000 // Mark that block was loaded as blank
2001 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2004 m_loaded_blocks[p] = flags;
2010 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2011 bool overwrite_generated)
2013 if(m_area.getExtent() == v3s16(0,0,0))
2018 Copy data of all blocks
2020 for (auto &loaded_block : m_loaded_blocks) {
2021 v3s16 p = loaded_block.first;
2022 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2023 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2024 if (!existed || (block == NULL) ||
2025 (!overwrite_generated && block->isGenerated()))
2028 block->copyFrom(*this);
2029 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2032 (*modified_blocks)[p] = block;
2036 MMVManip *MMVManip::clone() const
2038 MMVManip *ret = new MMVManip();
2040 const s32 size = m_area.getVolume();
2041 ret->m_area = m_area;
2043 ret->m_data = new MapNode[size];
2044 memcpy(ret->m_data, m_data, size * sizeof(MapNode));
2047 ret->m_flags = new u8[size];
2048 memcpy(ret->m_flags, m_flags, size * sizeof(u8));
2051 ret->m_is_dirty = m_is_dirty;
2052 // Even if the copy is disconnected from a map object keep the information
2053 // needed to write it back to one
2054 ret->m_loaded_blocks = m_loaded_blocks;
2059 void MMVManip::reparent(Map *map)
2061 assert(map && !m_map);