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(std::ostream &dout, IGameDef *gamedef):
68 m_nodedef(gamedef->ndef())
77 for (auto §or : m_sectors) {
82 void Map::addEventReceiver(MapEventReceiver *event_receiver)
84 m_event_receivers.insert(event_receiver);
87 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
89 m_event_receivers.erase(event_receiver);
92 void Map::dispatchEvent(const MapEditEvent &event)
94 for (MapEventReceiver *event_receiver : m_event_receivers) {
95 event_receiver->onMapEditEvent(event);
99 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
101 if(m_sector_cache != NULL && p == m_sector_cache_p){
102 MapSector * sector = m_sector_cache;
106 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
108 if (n == m_sectors.end())
111 MapSector *sector = n->second;
113 // Cache the last result
114 m_sector_cache_p = p;
115 m_sector_cache = sector;
120 MapSector * Map::getSectorNoGenerate(v2s16 p)
122 return getSectorNoGenerateNoLock(p);
125 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
127 v2s16 p2d(p3d.X, p3d.Z);
128 MapSector * sector = getSectorNoGenerate(p2d);
131 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
135 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
137 MapBlock *block = getBlockNoCreateNoEx(p3d);
139 throw InvalidPositionException();
143 bool Map::isNodeUnderground(v3s16 p)
145 v3s16 blockpos = getNodeBlockPos(p);
146 MapBlock *block = getBlockNoCreateNoEx(blockpos);
147 return block && block->getIsUnderground();
150 bool Map::isValidPosition(v3s16 p)
152 v3s16 blockpos = getNodeBlockPos(p);
153 MapBlock *block = getBlockNoCreateNoEx(blockpos);
154 return (block != NULL);
157 // Returns a CONTENT_IGNORE node if not found
158 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
160 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock *block = getBlockNoCreateNoEx(blockpos);
163 if (is_valid_position != NULL)
164 *is_valid_position = false;
165 return {CONTENT_IGNORE};
168 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
170 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
171 if (is_valid_position != NULL)
172 *is_valid_position = is_valid_p;
176 // throws InvalidPositionException if not found
177 void Map::setNode(v3s16 p, MapNode & n)
179 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock *block = getBlockNoCreate(blockpos);
181 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
182 // Never allow placing CONTENT_IGNORE, it causes problems
183 if(n.getContent() == CONTENT_IGNORE){
185 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
186 <<" while trying to replace \""
187 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
188 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
191 block->setNodeNoCheck(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 // This is needed for updating the lighting
202 MapNode oldnode = getNode(p);
204 // Remove node metadata
205 if (remove_metadata) {
206 removeNodeMetadata(p);
209 // Set the node on the map
210 // Ignore light (because calling voxalgo::update_lighting_nodes)
211 n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
212 n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
216 std::vector<std::pair<v3s16, MapNode> > oldnodes;
217 oldnodes.emplace_back(p, oldnode);
218 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
220 for (auto &modified_block : modified_blocks) {
221 modified_block.second->expireDayNightDiff();
224 // Report for rollback
225 if(m_gamedef->rollback())
227 RollbackNode rollback_newnode(this, p, m_gamedef);
228 RollbackAction action;
229 action.setSetNode(p, rollback_oldnode, rollback_newnode);
230 m_gamedef->rollback()->reportAction(action);
234 Add neighboring liquid nodes and this node to transform queue.
235 (it's vital for the node itself to get updated last, if it was removed.)
238 for (const v3s16 &dir : g_7dirs) {
241 bool is_valid_position;
242 MapNode n2 = getNode(p2, &is_valid_position);
243 if(is_valid_position &&
244 (m_nodedef->get(n2).isLiquid() ||
245 n2.getContent() == CONTENT_AIR))
246 m_transforming_liquid.push_back(p2);
250 void Map::removeNodeAndUpdate(v3s16 p,
251 std::map<v3s16, MapBlock*> &modified_blocks)
253 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
256 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
259 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
263 bool succeeded = true;
265 std::map<v3s16, MapBlock*> modified_blocks;
266 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
268 // Copy modified_blocks to event
269 for (auto &modified_block : modified_blocks) {
270 event.modified_blocks.insert(modified_block.first);
273 catch(InvalidPositionException &e){
277 dispatchEvent(event);
282 bool Map::removeNodeWithEvent(v3s16 p)
285 event.type = MEET_REMOVENODE;
288 bool succeeded = true;
290 std::map<v3s16, MapBlock*> modified_blocks;
291 removeNodeAndUpdate(p, modified_blocks);
293 // Copy modified_blocks to event
294 for (auto &modified_block : modified_blocks) {
295 event.modified_blocks.insert(modified_block.first);
298 catch(InvalidPositionException &e){
302 dispatchEvent(event);
307 struct TimeOrderedMapBlock {
311 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
316 bool operator<(const TimeOrderedMapBlock &b) const
318 return block->getUsageTimer() < b.block->getUsageTimer();
325 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
326 std::vector<v3s16> *unloaded_blocks)
328 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
330 // Profile modified reasons
331 Profiler modprofiler;
333 std::vector<v2s16> sector_deletion_queue;
334 u32 deleted_blocks_count = 0;
335 u32 saved_blocks_count = 0;
336 u32 block_count_all = 0;
340 // If there is no practical limit, we spare creation of mapblock_queue
341 if (max_loaded_blocks == U32_MAX) {
342 for (auto §or_it : m_sectors) {
343 MapSector *sector = sector_it.second;
345 bool all_blocks_deleted = true;
348 sector->getBlocks(blocks);
350 for (MapBlock *block : blocks) {
351 block->incrementUsageTimer(dtime);
353 if (block->refGet() == 0
354 && block->getUsageTimer() > unload_timeout) {
355 v3s16 p = block->getPos();
358 if (block->getModified() != MOD_STATE_CLEAN
359 && save_before_unloading) {
360 modprofiler.add(block->getModifiedReasonString(), 1);
361 if (!saveBlock(block))
363 saved_blocks_count++;
366 // Delete from memory
367 sector->deleteBlock(block);
370 unloaded_blocks->push_back(p);
372 deleted_blocks_count++;
374 all_blocks_deleted = false;
379 if (all_blocks_deleted) {
380 sector_deletion_queue.push_back(sector_it.first);
384 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
385 for (auto §or_it : m_sectors) {
386 MapSector *sector = sector_it.second;
389 sector->getBlocks(blocks);
391 for (MapBlock *block : blocks) {
392 block->incrementUsageTimer(dtime);
393 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
396 block_count_all = mapblock_queue.size();
397 // Delete old blocks, and blocks over the limit from the memory
398 while (!mapblock_queue.empty() && (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++;
427 // Delete empty sectors
428 for (auto §or_it : m_sectors) {
429 if (sector_it.second->empty()) {
430 sector_deletion_queue.push_back(sector_it.first);
436 // Finally delete the empty sectors
437 deleteSectors(sector_deletion_queue);
439 if(deleted_blocks_count != 0)
441 PrintInfo(infostream); // ServerMap/ClientMap:
442 infostream<<"Unloaded "<<deleted_blocks_count
443 <<" blocks from memory";
444 if(save_before_unloading)
445 infostream<<", of which "<<saved_blocks_count<<" were written";
446 infostream<<", "<<block_count_all<<" blocks in memory";
447 infostream<<"."<<std::endl;
448 if(saved_blocks_count != 0){
449 PrintInfo(infostream); // ServerMap/ClientMap:
450 infostream<<"Blocks modified by: "<<std::endl;
451 modprofiler.print(infostream);
456 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
458 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
461 void Map::deleteSectors(std::vector<v2s16> §orList)
463 for (v2s16 j : sectorList) {
464 MapSector *sector = m_sectors[j];
465 // If sector is in sector cache, remove it from there
466 if(m_sector_cache == sector)
467 m_sector_cache = NULL;
468 // Remove from map and delete
474 void Map::PrintInfo(std::ostream &out)
479 #define WATER_DROP_BOOST 4
481 enum NeighborType : u8 {
487 struct NodeNeighbor {
493 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
496 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
503 void Map::transforming_liquid_add(v3s16 p) {
504 m_transforming_liquid.push_back(p);
507 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
508 ServerEnvironment *env)
511 u32 initial_size = m_transforming_liquid.size();
513 /*if(initial_size != 0)
514 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
516 // list of nodes that due to viscosity have not reached their max level height
517 std::deque<v3s16> must_reflow;
519 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
521 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
522 u32 loop_max = liquid_loop_max;
526 /* If liquid_loop_max is not keeping up with the queue size increase
527 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
529 if (m_transforming_liquid.size() > loop_max * 2) {
531 float server_step = g_settings->getFloat("dedicated_server_step");
532 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
533 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
535 m_transforming_liquid_loop_count_multiplier = 1.0;
538 loop_max *= m_transforming_liquid_loop_count_multiplier;
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 = m_nodedef->getId(cf.liquid_alternative_flowing);
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 const v3s16 *dirs = g_6dirs;
591 NodeNeighbor sources[6]; // surrounding sources
593 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
595 NodeNeighbor airs[6]; // surrounding air
597 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
598 int num_neutrals = 0;
599 bool flowing_down = false;
600 bool ignored_sources = false;
601 for (u16 i = 0; i < 6; i++) {
602 NeighborType nt = NEIGHBOR_SAME_LEVEL;
613 v3s16 npos = p0 + dirs[i];
614 NodeNeighbor nb(getNode(npos), nt, npos);
615 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
616 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
618 if (cfnb.floodable) {
619 airs[num_airs++] = nb;
620 // if the current node is a water source the neighbor
621 // should be enqueded for transformation regardless of whether the
622 // current node changes or not.
623 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
624 m_transforming_liquid.push_back(npos);
625 // if the current node happens to be a flowing node, it will start to flow down here.
626 if (nb.t == NEIGHBOR_LOWER)
629 neutrals[num_neutrals++] = nb;
630 if (nb.n.getContent() == CONTENT_IGNORE) {
631 // If node below is ignore prevent water from
632 // spreading outwards and otherwise prevent from
633 // flowing away as ignore node might be the source
634 if (nb.t == NEIGHBOR_LOWER)
637 ignored_sources = true;
642 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
643 if (liquid_kind == CONTENT_AIR)
644 liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing);
645 if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
646 neutrals[num_neutrals++] = nb;
648 // Do not count bottom source, it will screw things up
650 sources[num_sources++] = nb;
654 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
655 if (liquid_kind == CONTENT_AIR)
656 liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing);
657 if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
658 neutrals[num_neutrals++] = nb;
660 flows[num_flows++] = nb;
661 if (nb.t == NEIGHBOR_LOWER)
669 decide on the type (and possibly level) of the current node
671 content_t new_node_content;
672 s8 new_node_level = -1;
673 s8 max_node_level = -1;
675 u8 range = m_nodedef->get(liquid_kind).liquid_range;
676 if (range > LIQUID_LEVEL_MAX + 1)
677 range = LIQUID_LEVEL_MAX + 1;
679 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
680 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
681 // or the flowing alternative of the first of the surrounding sources (if it's air), so
682 // it's perfectly safe to use liquid_kind here to determine the new node content.
683 new_node_content = m_nodedef->getId(m_nodedef->get(liquid_kind).liquid_alternative_source);
684 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
685 // liquid_kind is set properly, see above
686 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
687 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
688 new_node_content = liquid_kind;
690 new_node_content = floodable_node;
691 } else if (ignored_sources && liquid_level >= 0) {
692 // Maybe there are neighbouring sources that aren't loaded yet
693 // so prevent flowing away.
694 new_node_level = liquid_level;
695 new_node_content = liquid_kind;
697 // no surrounding sources, so get the maximum level that can flow into this node
698 for (u16 i = 0; i < num_flows; i++) {
699 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
700 switch (flows[i].t) {
702 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
703 max_node_level = LIQUID_LEVEL_MAX;
704 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
705 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
706 } else if (nb_liquid_level > max_node_level) {
707 max_node_level = nb_liquid_level;
712 case NEIGHBOR_SAME_LEVEL:
713 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
714 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
715 max_node_level = nb_liquid_level - 1;
720 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
721 if (viscosity > 1 && max_node_level != liquid_level) {
722 // amount to gain, limited by viscosity
723 // must be at least 1 in absolute value
724 s8 level_inc = max_node_level - liquid_level;
725 if (level_inc < -viscosity || level_inc > viscosity)
726 new_node_level = liquid_level + level_inc/viscosity;
727 else if (level_inc < 0)
728 new_node_level = liquid_level - 1;
729 else if (level_inc > 0)
730 new_node_level = liquid_level + 1;
731 if (new_node_level != max_node_level)
732 must_reflow.push_back(p0);
734 new_node_level = max_node_level;
737 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
738 new_node_content = liquid_kind;
740 new_node_content = floodable_node;
745 check if anything has changed. if not, just continue with the next node.
747 if (new_node_content == n0.getContent() &&
748 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
749 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
750 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
756 update the current node
759 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
760 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
761 // set level to last 3 bits, flowing down bit to 4th bit
762 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
764 // set the liquid level and flow bits to 0
765 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
769 n0.setContent(new_node_content);
771 // on_flood() the node
772 if (floodable_node != CONTENT_AIR) {
773 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
777 // Ignore light (because calling voxalgo::update_lighting_nodes)
778 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
779 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
781 // Find out whether there is a suspect for this action
783 if (m_gamedef->rollback())
784 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
786 if (m_gamedef->rollback() && !suspect.empty()) {
788 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
789 // Get old node for rollback
790 RollbackNode rollback_oldnode(this, p0, m_gamedef);
794 RollbackNode rollback_newnode(this, p0, m_gamedef);
795 RollbackAction action;
796 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
797 m_gamedef->rollback()->reportAction(action);
803 v3s16 blockpos = getNodeBlockPos(p0);
804 MapBlock *block = getBlockNoCreateNoEx(blockpos);
806 modified_blocks[blockpos] = block;
807 changed_nodes.emplace_back(p0, n00);
811 enqueue neighbors for update if neccessary
813 switch (m_nodedef->get(n0.getContent()).liquid_type) {
816 // make sure source flows into all neighboring nodes
817 for (u16 i = 0; i < num_flows; i++)
818 if (flows[i].t != NEIGHBOR_UPPER)
819 m_transforming_liquid.push_back(flows[i].p);
820 for (u16 i = 0; i < num_airs; i++)
821 if (airs[i].t != NEIGHBOR_UPPER)
822 m_transforming_liquid.push_back(airs[i].p);
825 // this flow has turned to air; neighboring flows might need to do the same
826 for (u16 i = 0; i < num_flows; i++)
827 m_transforming_liquid.push_back(flows[i].p);
831 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
833 for (auto &iter : must_reflow)
834 m_transforming_liquid.push_back(iter);
836 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
839 /* ----------------------------------------------------------------------
840 * Manage the queue so that it does not grow indefinately
842 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
844 if (time_until_purge == 0)
845 return; // Feature disabled
847 time_until_purge *= 1000; // seconds -> milliseconds
849 u64 curr_time = porting::getTimeMs();
850 u32 prev_unprocessed = m_unprocessed_count;
851 m_unprocessed_count = m_transforming_liquid.size();
853 // if unprocessed block count is decreasing or stable
854 if (m_unprocessed_count <= prev_unprocessed) {
855 m_queue_size_timer_started = false;
857 if (!m_queue_size_timer_started)
858 m_inc_trending_up_start_time = curr_time;
859 m_queue_size_timer_started = true;
862 // Account for curr_time overflowing
863 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
864 m_queue_size_timer_started = false;
866 /* If the queue has been growing for more than liquid_queue_purge_time seconds
867 * and the number of unprocessed blocks is still > liquid_loop_max then we
868 * cannot keep up; dump the oldest blocks from the queue so that the queue
869 * has liquid_loop_max items in it
871 if (m_queue_size_timer_started
872 && curr_time - m_inc_trending_up_start_time > time_until_purge
873 && m_unprocessed_count > liquid_loop_max) {
875 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
877 infostream << "transformLiquids(): DUMPING " << dump_qty
878 << " blocks from the queue" << std::endl;
881 m_transforming_liquid.pop_front();
883 m_queue_size_timer_started = false; // optimistically assume we can keep up now
884 m_unprocessed_count = m_transforming_liquid.size();
888 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
890 std::vector<v3s16> positions_with_meta;
892 sortBoxVerticies(p1, p2);
893 v3s16 bpmin = getNodeBlockPos(p1);
894 v3s16 bpmax = getNodeBlockPos(p2);
896 VoxelArea area(p1, p2);
898 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
899 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
900 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
901 v3s16 blockpos(x, y, z);
903 MapBlock *block = getBlockNoCreateNoEx(blockpos);
905 verbosestream << "Map::getNodeMetadata(): Need to emerge "
906 << PP(blockpos) << std::endl;
907 block = emergeBlock(blockpos, false);
910 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
915 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
916 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
917 for (size_t i = 0; i != keys.size(); i++) {
918 v3s16 p(keys[i] + p_base);
919 if (!area.contains(p))
922 positions_with_meta.push_back(p);
926 return positions_with_meta;
929 NodeMetadata *Map::getNodeMetadata(v3s16 p)
931 v3s16 blockpos = getNodeBlockPos(p);
932 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
933 MapBlock *block = getBlockNoCreateNoEx(blockpos);
935 infostream<<"Map::getNodeMetadata(): Need to emerge "
936 <<PP(blockpos)<<std::endl;
937 block = emergeBlock(blockpos, false);
940 warningstream<<"Map::getNodeMetadata(): Block not found"
944 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
948 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
950 v3s16 blockpos = getNodeBlockPos(p);
951 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
952 MapBlock *block = getBlockNoCreateNoEx(blockpos);
954 infostream<<"Map::setNodeMetadata(): Need to emerge "
955 <<PP(blockpos)<<std::endl;
956 block = emergeBlock(blockpos, false);
959 warningstream<<"Map::setNodeMetadata(): Block not found"
963 block->m_node_metadata.set(p_rel, meta);
967 void Map::removeNodeMetadata(v3s16 p)
969 v3s16 blockpos = getNodeBlockPos(p);
970 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
971 MapBlock *block = getBlockNoCreateNoEx(blockpos);
974 warningstream<<"Map::removeNodeMetadata(): Block not found"
978 block->m_node_metadata.remove(p_rel);
981 NodeTimer Map::getNodeTimer(v3s16 p)
983 v3s16 blockpos = getNodeBlockPos(p);
984 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
985 MapBlock *block = getBlockNoCreateNoEx(blockpos);
987 infostream<<"Map::getNodeTimer(): Need to emerge "
988 <<PP(blockpos)<<std::endl;
989 block = emergeBlock(blockpos, false);
992 warningstream<<"Map::getNodeTimer(): Block not found"
996 NodeTimer t = block->m_node_timers.get(p_rel);
997 NodeTimer nt(t.timeout, t.elapsed, p);
1001 void Map::setNodeTimer(const NodeTimer &t)
1003 v3s16 p = t.position;
1004 v3s16 blockpos = getNodeBlockPos(p);
1005 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1006 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1008 infostream<<"Map::setNodeTimer(): Need to emerge "
1009 <<PP(blockpos)<<std::endl;
1010 block = emergeBlock(blockpos, false);
1013 warningstream<<"Map::setNodeTimer(): Block not found"
1017 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1018 block->m_node_timers.set(nt);
1021 void Map::removeNodeTimer(v3s16 p)
1023 v3s16 blockpos = getNodeBlockPos(p);
1024 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1025 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1028 warningstream<<"Map::removeNodeTimer(): Block not found"
1032 block->m_node_timers.remove(p_rel);
1035 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1036 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1039 This functions determines the node inside the target block that is
1040 closest to the camera position. This increases the occlusion culling
1041 accuracy in straight and diagonal corridors.
1042 The returned position will be occlusion checked first in addition to the
1043 others (8 corners + center).
1044 No position is returned if
1045 - the closest node is a corner, corners are checked anyway.
1046 - the camera is inside the target block, it will never be occluded.
1048 #define CLOSEST_EDGE(pos, bounds, axis) \
1049 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1050 (bounds).MaxEdge.axis
1052 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1053 (pos_camera.X <= block_bounds.MaxEdge.X);
1054 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1055 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1056 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1057 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1059 if (x_inside && y_inside && z_inside)
1060 return false; // Camera inside target mapblock
1063 if (x_inside && y_inside) {
1064 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1065 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1067 } else if (y_inside && z_inside) {
1068 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1069 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1071 } else if (x_inside && z_inside) {
1072 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1073 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1079 check = v3s16(pos_camera.X, 0, 0);
1080 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1081 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1083 } else if (y_inside) {
1084 check = v3s16(0, pos_camera.Y, 0);
1085 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1086 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1088 } else if (z_inside) {
1089 check = v3s16(0, 0, pos_camera.Z);
1090 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1091 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1095 // Closest node would be a corner, none returned
1099 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1100 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1102 v3f direction = intToFloat(pos_target - pos_camera, BS);
1103 float distance = direction.getLength();
1105 // Normalize direction vector
1106 if (distance > 0.0f)
1107 direction /= distance;
1109 v3f pos_origin_f = intToFloat(pos_camera, BS);
1111 bool is_valid_position;
1113 for (; offset < distance + end_offset; offset += step) {
1114 v3f pos_node_f = pos_origin_f + direction * offset;
1115 v3s16 pos_node = floatToInt(pos_node_f, BS);
1117 MapNode node = getNode(pos_node, &is_valid_position);
1119 if (is_valid_position &&
1120 !m_nodedef->get(node).light_propagates) {
1121 // Cannot see through light-blocking nodes --> occluded
1123 if (count >= needed_count)
1131 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1133 // Check occlusion for center and all 8 corners of the mapblock
1134 // Overshoot a little for less flickering
1135 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1136 static const v3s16 dir9[9] = {
1138 v3s16( 1, 1, 1) * bs2,
1139 v3s16( 1, 1, -1) * bs2,
1140 v3s16( 1, -1, 1) * bs2,
1141 v3s16( 1, -1, -1) * bs2,
1142 v3s16(-1, 1, 1) * bs2,
1143 v3s16(-1, 1, -1) * bs2,
1144 v3s16(-1, -1, 1) * bs2,
1145 v3s16(-1, -1, -1) * bs2,
1148 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1150 // Starting step size, value between 1m and sqrt(3)m
1151 float step = BS * 1.2f;
1152 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1153 float stepfac = 1.05f;
1155 float start_offset = BS * 1.0f;
1157 // The occlusion search of 'isOccluded()' must stop short of the target
1158 // point by distance 'end_offset' to not enter the target mapblock.
1159 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1160 // diagonal of a mapblock, because we must consider all view angles.
1161 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1162 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1164 // to reduce the likelihood of falsely occluded blocks
1165 // require at least two solid blocks
1166 // this is a HACK, we should think of a more precise algorithm
1167 u32 needed_count = 2;
1169 // Additional occlusion check, see comments in that function
1171 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1172 // node is always on a side facing the camera, end_offset can be lower
1173 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1174 -1.0f, needed_count))
1178 for (const v3s16 &dir : dir9) {
1179 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1180 start_offset, end_offset, needed_count))
1189 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1190 EmergeManager *emerge, MetricsBackend *mb):
1191 Map(dout_server, gamedef),
1192 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1195 verbosestream<<FUNCTION_NAME<<std::endl;
1197 // Tell the EmergeManager about our MapSettingsManager
1198 emerge->map_settings_mgr = &settings_mgr;
1201 Try to load map; if not found, create a new one.
1204 // Determine which database backend to use
1205 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1207 bool succeeded = conf.readConfigFile(conf_path.c_str());
1208 if (!succeeded || !conf.exists("backend")) {
1209 // fall back to sqlite3
1210 conf.set("backend", "sqlite3");
1212 std::string backend = conf.get("backend");
1213 dbase = createDatabase(backend, savedir, conf);
1214 if (conf.exists("readonly_backend")) {
1215 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1216 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1218 if (!conf.updateConfigFile(conf_path.c_str()))
1219 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1221 m_savedir = savedir;
1222 m_map_saving_enabled = false;
1224 m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1227 // If directory exists, check contents and load if possible
1228 if (fs::PathExists(m_savedir)) {
1229 // If directory is empty, it is safe to save into it.
1230 if (fs::GetDirListing(m_savedir).empty()) {
1231 infostream<<"ServerMap: Empty save directory is valid."
1233 m_map_saving_enabled = true;
1238 if (settings_mgr.loadMapMeta()) {
1239 infostream << "ServerMap: Metadata loaded from "
1240 << savedir << std::endl;
1242 infostream << "ServerMap: Metadata could not be loaded "
1243 "from " << savedir << ", assuming valid save "
1244 "directory." << std::endl;
1247 m_map_saving_enabled = true;
1248 // Map loaded, not creating new one
1252 // If directory doesn't exist, it is safe to save to it
1254 m_map_saving_enabled = true;
1257 catch(std::exception &e)
1259 warningstream<<"ServerMap: Failed to load map from "<<savedir
1260 <<", exception: "<<e.what()<<std::endl;
1261 infostream<<"Please remove the map or fix it."<<std::endl;
1262 warningstream<<"Map saving will be disabled."<<std::endl;
1266 ServerMap::~ServerMap()
1268 verbosestream<<FUNCTION_NAME<<std::endl;
1272 if (m_map_saving_enabled) {
1273 // Save only changed parts
1274 save(MOD_STATE_WRITE_AT_UNLOAD);
1275 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1277 infostream << "ServerMap: Map not saved" << std::endl;
1280 catch(std::exception &e)
1282 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1283 <<", exception: "<<e.what()<<std::endl;
1287 Close database if it was opened
1296 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1297 for(; i.atEnd() == false; i++)
1299 MapChunk *chunk = i.getNode()->getValue();
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 s16 ServerMap::getWaterLevel()
1319 return getMapgenParams()->water_level;
1322 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1324 const s16 mapgen_limit_bp = rangelim(
1325 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1327 return p.X < -mapgen_limit_bp ||
1328 p.X > mapgen_limit_bp ||
1329 p.Y < -mapgen_limit_bp ||
1330 p.Y > mapgen_limit_bp ||
1331 p.Z < -mapgen_limit_bp ||
1332 p.Z > mapgen_limit_bp;
1335 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1337 s16 csize = getMapgenParams()->chunksize;
1338 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1339 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1341 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1342 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1344 v3s16 extra_borders(1, 1, 1);
1345 v3s16 full_bpmin = bpmin - extra_borders;
1346 v3s16 full_bpmax = bpmax + extra_borders;
1348 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1349 if (blockpos_over_mapgen_limit(full_bpmin) ||
1350 blockpos_over_mapgen_limit(full_bpmax))
1353 data->seed = getSeed();
1354 data->blockpos_min = bpmin;
1355 data->blockpos_max = bpmax;
1356 data->blockpos_requested = blockpos;
1357 data->nodedef = m_nodedef;
1360 Create the whole area of this and the neighboring blocks
1362 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1363 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1364 v2s16 sectorpos(x, z);
1365 // Sector metadata is loaded from disk if not already loaded.
1366 MapSector *sector = createSector(sectorpos);
1367 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1369 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1372 MapBlock *block = emergeBlock(p, false);
1373 if (block == NULL) {
1374 block = createBlock(p);
1376 // Block gets sunlight if this is true.
1377 // Refer to the map generator heuristics.
1378 bool ug = m_emerge->isBlockUnderground(p);
1379 block->setIsUnderground(ug);
1385 Now we have a big empty area.
1387 Make a ManualMapVoxelManipulator that contains this and the
1391 data->vmanip = new MMVManip(this);
1392 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1394 // Note: we may need this again at some point.
1396 // Ensure none of the blocks to be generated were marked as
1397 // containing CONTENT_IGNORE
1398 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1399 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1400 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1401 core::map<v3s16, u8>::Node *n;
1402 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1405 u8 flags = n->getValue();
1406 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1413 // Data is ready now.
1417 void ServerMap::finishBlockMake(BlockMakeData *data,
1418 std::map<v3s16, MapBlock*> *changed_blocks)
1420 v3s16 bpmin = data->blockpos_min;
1421 v3s16 bpmax = data->blockpos_max;
1423 v3s16 extra_borders(1, 1, 1);
1425 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1426 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1429 Blit generated stuff to map
1430 NOTE: blitBackAll adds nearly everything to changed_blocks
1432 data->vmanip->blitBackAll(changed_blocks);
1434 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1435 << changed_blocks->size());
1438 Copy transforming liquid information
1440 while (data->transforming_liquid.size()) {
1441 m_transforming_liquid.push_back(data->transforming_liquid.front());
1442 data->transforming_liquid.pop_front();
1445 for (auto &changed_block : *changed_blocks) {
1446 MapBlock *block = changed_block.second;
1450 Update day/night difference cache of the MapBlocks
1452 block->expireDayNightDiff();
1454 Set block as modified
1456 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1457 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1461 Set central blocks as generated
1463 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1464 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1465 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1466 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1470 block->setGenerated(true);
1474 Save changed parts of map
1475 NOTE: Will be saved later.
1477 //save(MOD_STATE_WRITE_AT_UNLOAD);
1480 MapSector *ServerMap::createSector(v2s16 p2d)
1483 Check if it exists already in memory
1485 MapSector *sector = getSectorNoGenerate(p2d);
1490 Do not create over max mapgen limit
1492 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1493 if (p2d.X < -max_limit_bp ||
1494 p2d.X > max_limit_bp ||
1495 p2d.Y < -max_limit_bp ||
1496 p2d.Y > max_limit_bp)
1497 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1500 Generate blank sector
1503 sector = new MapSector(this, p2d, m_gamedef);
1505 // Sector position on map in nodes
1506 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1511 m_sectors[p2d] = sector;
1518 This is a quick-hand function for calling makeBlock().
1520 MapBlock * ServerMap::generateBlock(
1522 std::map<v3s16, MapBlock*> &modified_blocks
1525 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
1527 TimeTaker timer("generateBlock");
1529 //MapBlock *block = original_dummy;
1531 v2s16 p2d(p.X, p.Z);
1532 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
1535 Do not generate over-limit
1537 if(blockpos_over_limit(p))
1539 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
1540 throw InvalidPositionException("generateBlock(): pos. over limit");
1544 Create block make data
1547 initBlockMake(&data, p);
1553 TimeTaker t("mapgen::make_block()");
1554 mapgen->makeChunk(&data);
1555 //mapgen::make_block(&data);
1557 if(enable_mapgen_debug_info == false)
1558 t.stop(true); // Hide output
1562 Blit data back on map, update lighting, add mobs and whatever this does
1564 finishBlockMake(&data, modified_blocks);
1569 MapBlock *block = getBlockNoCreateNoEx(p);
1577 bool erroneus_content = false;
1578 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1579 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1580 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1583 MapNode n = block->getNode(p);
1584 if(n.getContent() == CONTENT_IGNORE)
1586 infostream<<"CONTENT_IGNORE at "
1587 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1589 erroneus_content = true;
1593 if(erroneus_content)
1602 Generate a completely empty block
1606 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1607 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1609 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1612 n.setContent(CONTENT_AIR);
1613 block->setNode(v3s16(x0,y0,z0), n);
1619 if(enable_mapgen_debug_info == false)
1620 timer.stop(true); // Hide output
1626 MapBlock * ServerMap::createBlock(v3s16 p)
1629 Do not create over max mapgen limit
1631 if (blockpos_over_max_limit(p))
1632 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1634 v2s16 p2d(p.X, p.Z);
1637 This will create or load a sector if not found in memory.
1638 If block exists on disk, it will be loaded.
1640 NOTE: On old save formats, this will be slow, as it generates
1641 lighting on blocks for them.
1645 sector = createSector(p2d);
1646 } catch (InvalidPositionException &e) {
1647 infostream<<"createBlock: createSector() failed"<<std::endl;
1652 Try to get a block from the sector
1655 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1657 if(block->isDummy())
1662 block = sector->createBlankBlock(block_y);
1667 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1670 MapBlock *block = getBlockNoCreateNoEx(p);
1671 if (block && !block->isDummy())
1676 MapBlock *block = loadBlock(p);
1682 MapSector *sector = createSector(v2s16(p.X, p.Z));
1683 MapBlock *block = sector->createBlankBlock(p.Y);
1691 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1693 MapBlock *block = getBlockNoCreateNoEx(p3d);
1695 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1700 // N.B. This requires no synchronization, since data will not be modified unless
1701 // the VoxelManipulator being updated belongs to the same thread.
1702 void ServerMap::updateVManip(v3s16 pos)
1704 Mapgen *mg = m_emerge->getCurrentMapgen();
1708 MMVManip *vm = mg->vm;
1712 if (!vm->m_area.contains(pos))
1715 s32 idx = vm->m_area.index(pos);
1716 vm->m_data[idx] = getNode(pos);
1717 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1719 vm->m_is_dirty = true;
1722 s16 ServerMap::findGroundLevel(v2s16 p2d)
1726 Uh, just do something random...
1728 // Find existing map from top to down
1731 v3s16 p(p2d.X, max, p2d.Y);
1732 for(; p.Y>min; p.Y--)
1734 MapNode n = getNodeNoEx(p);
1735 if(n.getContent() != CONTENT_IGNORE)
1740 // If this node is not air, go to plan b
1741 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
1743 // Search existing walkable and return it
1744 for(; p.Y>min; p.Y--)
1746 MapNode n = getNodeNoEx(p);
1747 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
1756 Determine from map generator noise functions
1759 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
1762 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
1763 //return (s16)level;
1766 void ServerMap::createDirs(const std::string &path)
1768 if (!fs::CreateAllDirs(path)) {
1769 m_dout<<"ServerMap: Failed to create directory "
1770 <<"\""<<path<<"\""<<std::endl;
1771 throw BaseException("ServerMap failed to create directory");
1775 void ServerMap::save(ModifiedState save_level)
1777 if (!m_map_saving_enabled) {
1778 warningstream<<"Not saving map, saving disabled."<<std::endl;
1782 u64 start_time = porting::getTimeNs();
1784 if(save_level == MOD_STATE_CLEAN)
1785 infostream<<"ServerMap: Saving whole map, this can take time."
1788 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1789 if (settings_mgr.saveMapMeta())
1790 m_map_metadata_changed = false;
1793 // Profile modified reasons
1794 Profiler modprofiler;
1796 u32 block_count = 0;
1797 u32 block_count_all = 0; // Number of blocks in memory
1799 // Don't do anything with sqlite unless something is really saved
1800 bool save_started = false;
1802 for (auto §or_it : m_sectors) {
1803 MapSector *sector = sector_it.second;
1805 MapBlockVect blocks;
1806 sector->getBlocks(blocks);
1808 for (MapBlock *block : blocks) {
1811 if(block->getModified() >= (u32)save_level) {
1815 save_started = true;
1818 modprofiler.add(block->getModifiedReasonString(), 1);
1830 Only print if something happened or saved whole map
1832 if(save_level == MOD_STATE_CLEAN
1833 || block_count != 0) {
1834 infostream << "ServerMap: Written: "
1835 << block_count << " blocks"
1836 << ", " << block_count_all << " blocks in memory."
1838 PrintInfo(infostream); // ServerMap/ClientMap:
1839 infostream<<"Blocks modified by: "<<std::endl;
1840 modprofiler.print(infostream);
1843 auto end_time = porting::getTimeNs();
1844 m_save_time_counter->increment(end_time - start_time);
1847 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1849 dbase->listAllLoadableBlocks(dst);
1851 dbase_ro->listAllLoadableBlocks(dst);
1854 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1856 for (auto §or_it : m_sectors) {
1857 MapSector *sector = sector_it.second;
1859 MapBlockVect blocks;
1860 sector->getBlocks(blocks);
1862 for (MapBlock *block : blocks) {
1863 v3s16 p = block->getPos();
1869 MapDatabase *ServerMap::createDatabase(
1870 const std::string &name,
1871 const std::string &savedir,
1874 if (name == "sqlite3")
1875 return new MapDatabaseSQLite3(savedir);
1876 if (name == "dummy")
1877 return new Database_Dummy();
1879 if (name == "leveldb")
1880 return new Database_LevelDB(savedir);
1883 if (name == "redis")
1884 return new Database_Redis(conf);
1887 if (name == "postgresql") {
1888 std::string connect_string;
1889 conf.getNoEx("pgsql_connection", connect_string);
1890 return new MapDatabasePostgreSQL(connect_string);
1894 throw BaseException(std::string("Database backend ") + name + " not supported.");
1897 void ServerMap::pingDatabase()
1899 dbase->pingDatabase();
1902 void ServerMap::beginSave()
1907 void ServerMap::endSave()
1912 bool ServerMap::saveBlock(MapBlock *block)
1914 return saveBlock(block, dbase);
1917 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
1919 v3s16 p3d = block->getPos();
1921 // Dummy blocks are not written
1922 if (block->isDummy()) {
1923 warningstream << "saveBlock: Not writing dummy block "
1924 << PP(p3d) << std::endl;
1928 // Format used for writing
1929 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1932 [0] u8 serialization version
1935 std::ostringstream o(std::ios_base::binary);
1936 o.write((char*) &version, 1);
1937 block->serialize(o, version, true);
1939 bool ret = db->saveBlock(p3d, o.str());
1941 // We just wrote it to the disk so clear modified flag
1942 block->resetModified();
1947 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1950 std::istringstream is(*blob, std::ios_base::binary);
1952 u8 version = SER_FMT_VER_INVALID;
1953 is.read((char*)&version, 1);
1956 throw SerializationError("ServerMap::loadBlock(): Failed"
1957 " to read MapBlock version");
1959 MapBlock *block = NULL;
1960 bool created_new = false;
1961 block = sector->getBlockNoCreateNoEx(p3d.Y);
1964 block = sector->createBlankBlockNoInsert(p3d.Y);
1969 block->deSerialize(is, version, true);
1971 // If it's a new block, insert it to the map
1973 sector->insertBlock(block);
1974 ReflowScan scanner(this, m_emerge->ndef);
1975 scanner.scan(block, &m_transforming_liquid);
1979 Save blocks loaded in old format in new format
1982 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1983 // Only save if asked to; no need to update version
1987 // We just loaded it from, so it's up-to-date.
1988 block->resetModified();
1990 catch(SerializationError &e)
1992 errorstream<<"Invalid block data in database"
1993 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1994 <<" (SerializationError): "<<e.what()<<std::endl;
1996 // TODO: Block should be marked as invalid in memory so that it is
1997 // not touched but the game can run
1999 if(g_settings->getBool("ignore_world_load_errors")){
2000 errorstream<<"Ignoring block load error. Duck and cover! "
2001 <<"(ignore_world_load_errors)"<<std::endl;
2003 throw SerializationError("Invalid block data in database");
2008 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
2010 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
2012 v2s16 p2d(blockpos.X, blockpos.Z);
2015 dbase->loadBlock(blockpos, &ret);
2017 loadBlock(&ret, blockpos, createSector(p2d), false);
2018 } else if (dbase_ro) {
2019 dbase_ro->loadBlock(blockpos, &ret);
2021 loadBlock(&ret, blockpos, createSector(p2d), false);
2027 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2028 if (created_new && (block != NULL)) {
2029 std::map<v3s16, MapBlock*> modified_blocks;
2030 // Fix lighting if necessary
2031 voxalgo::update_block_border_lighting(this, block, modified_blocks);
2032 if (!modified_blocks.empty()) {
2033 //Modified lighting, send event
2035 event.type = MEET_OTHER;
2036 std::map<v3s16, MapBlock *>::iterator it;
2037 for (it = modified_blocks.begin();
2038 it != modified_blocks.end(); ++it)
2039 event.modified_blocks.insert(it->first);
2040 dispatchEvent(event);
2046 bool ServerMap::deleteBlock(v3s16 blockpos)
2048 if (!dbase->deleteBlock(blockpos))
2051 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2053 v2s16 p2d(blockpos.X, blockpos.Z);
2054 MapSector *sector = getSectorNoGenerate(p2d);
2057 sector->deleteBlock(block);
2063 void ServerMap::PrintInfo(std::ostream &out)
2068 bool ServerMap::repairBlockLight(v3s16 blockpos,
2069 std::map<v3s16, MapBlock *> *modified_blocks)
2071 MapBlock *block = emergeBlock(blockpos, false);
2072 if (!block || !block->isGenerated())
2074 voxalgo::repair_block_light(this, block, modified_blocks);
2078 MMVManip::MMVManip(Map *map):
2084 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2085 bool load_if_inexistent)
2087 TimeTaker timer1("initialEmerge", &emerge_time);
2089 // Units of these are MapBlocks
2090 v3s16 p_min = blockpos_min;
2091 v3s16 p_max = blockpos_max;
2093 VoxelArea block_area_nodes
2094 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2096 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2099 infostream<<"initialEmerge: area: ";
2100 block_area_nodes.print(infostream);
2101 infostream<<" ("<<size_MB<<"MB)";
2102 infostream<<std::endl;
2105 addArea(block_area_nodes);
2107 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2108 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2109 for(s32 x=p_min.X; x<=p_max.X; x++)
2114 std::map<v3s16, u8>::iterator n;
2115 n = m_loaded_blocks.find(p);
2116 if(n != m_loaded_blocks.end())
2119 bool block_data_inexistent = false;
2121 TimeTaker timer2("emerge load", &emerge_load_time);
2123 block = m_map->getBlockNoCreateNoEx(p);
2124 if (!block || block->isDummy())
2125 block_data_inexistent = true;
2127 block->copyTo(*this);
2130 if(block_data_inexistent)
2133 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2134 ServerMap *svrmap = (ServerMap *)m_map;
2135 block = svrmap->emergeBlock(p, false);
2137 block = svrmap->createBlock(p);
2138 block->copyTo(*this);
2140 flags |= VMANIP_BLOCK_DATA_INEXIST;
2143 Mark area inexistent
2145 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2146 // Fill with VOXELFLAG_NO_DATA
2147 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2148 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2150 s32 i = m_area.index(a.MinEdge.X,y,z);
2151 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2155 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2157 // Mark that block was loaded as blank
2158 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2161 m_loaded_blocks[p] = flags;
2167 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2168 bool overwrite_generated)
2170 if(m_area.getExtent() == v3s16(0,0,0))
2174 Copy data of all blocks
2176 for (auto &loaded_block : m_loaded_blocks) {
2177 v3s16 p = loaded_block.first;
2178 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2179 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2180 if (!existed || (block == NULL) ||
2181 (!overwrite_generated && block->isGenerated()))
2184 block->copyFrom(*this);
2185 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2188 (*modified_blocks)[p] = block;