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 const static v3s16 liquid_6dirs[6] = {
482 // order: upper before same level before lower
491 enum NeighborType : u8 {
497 struct NodeNeighbor {
503 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
506 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
513 void Map::transforming_liquid_add(v3s16 p) {
514 m_transforming_liquid.push_back(p);
517 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
518 ServerEnvironment *env)
521 u32 initial_size = m_transforming_liquid.size();
523 /*if(initial_size != 0)
524 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
526 // list of nodes that due to viscosity have not reached their max level height
527 std::deque<v3s16> must_reflow;
529 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
531 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
532 u32 loop_max = liquid_loop_max;
536 /* If liquid_loop_max is not keeping up with the queue size increase
537 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
539 if (m_transforming_liquid.size() > loop_max * 2) {
541 float server_step = g_settings->getFloat("dedicated_server_step");
542 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
543 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
545 m_transforming_liquid_loop_count_multiplier = 1.0;
548 loop_max *= m_transforming_liquid_loop_count_multiplier;
551 while (m_transforming_liquid.size() != 0)
553 // This should be done here so that it is done when continue is used
554 if (loopcount >= initial_size || loopcount >= loop_max)
559 Get a queued transforming liquid node
561 v3s16 p0 = m_transforming_liquid.front();
562 m_transforming_liquid.pop_front();
564 MapNode n0 = getNode(p0);
567 Collect information about current node
569 s8 liquid_level = -1;
570 // The liquid node which will be placed there if
571 // the liquid flows into this node.
572 content_t liquid_kind = CONTENT_IGNORE;
573 // The node which will be placed there if liquid
574 // can't flow into this node.
575 content_t floodable_node = CONTENT_AIR;
576 const ContentFeatures &cf = m_nodedef->get(n0);
577 LiquidType liquid_type = cf.liquid_type;
578 switch (liquid_type) {
580 liquid_level = LIQUID_LEVEL_SOURCE;
581 liquid_kind = cf.liquid_alternative_flowing_id;
584 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
585 liquid_kind = n0.getContent();
588 // if this node is 'floodable', it *could* be transformed
589 // into a liquid, otherwise, continue with the next node.
592 floodable_node = n0.getContent();
593 liquid_kind = CONTENT_AIR;
598 Collect information about the environment
600 NodeNeighbor sources[6]; // surrounding sources
602 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
604 NodeNeighbor airs[6]; // surrounding air
606 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
607 int num_neutrals = 0;
608 bool flowing_down = false;
609 bool ignored_sources = false;
610 for (u16 i = 0; i < 6; i++) {
611 NeighborType nt = NEIGHBOR_SAME_LEVEL;
622 v3s16 npos = p0 + liquid_6dirs[i];
623 NodeNeighbor nb(getNode(npos), nt, npos);
624 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
625 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
627 if (cfnb.floodable) {
628 airs[num_airs++] = nb;
629 // if the current node is a water source the neighbor
630 // should be enqueded for transformation regardless of whether the
631 // current node changes or not.
632 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
633 m_transforming_liquid.push_back(npos);
634 // if the current node happens to be a flowing node, it will start to flow down here.
635 if (nb.t == NEIGHBOR_LOWER)
638 neutrals[num_neutrals++] = nb;
639 if (nb.n.getContent() == CONTENT_IGNORE) {
640 // If node below is ignore prevent water from
641 // spreading outwards and otherwise prevent from
642 // flowing away as ignore node might be the source
643 if (nb.t == NEIGHBOR_LOWER)
646 ignored_sources = true;
651 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
652 if (liquid_kind == CONTENT_AIR)
653 liquid_kind = cfnb.liquid_alternative_flowing_id;
654 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
655 neutrals[num_neutrals++] = nb;
657 // Do not count bottom source, it will screw things up
658 if(nt != NEIGHBOR_LOWER)
659 sources[num_sources++] = nb;
663 if (nb.t != NEIGHBOR_SAME_LEVEL ||
664 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
665 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
666 // but exclude falling liquids on the same level, they cannot flow here anyway
667 if (liquid_kind == CONTENT_AIR)
668 liquid_kind = cfnb.liquid_alternative_flowing_id;
670 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
671 neutrals[num_neutrals++] = nb;
673 flows[num_flows++] = nb;
674 if (nb.t == NEIGHBOR_LOWER)
682 decide on the type (and possibly level) of the current node
684 content_t new_node_content;
685 s8 new_node_level = -1;
686 s8 max_node_level = -1;
688 u8 range = m_nodedef->get(liquid_kind).liquid_range;
689 if (range > LIQUID_LEVEL_MAX + 1)
690 range = LIQUID_LEVEL_MAX + 1;
692 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
693 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
694 // or the flowing alternative of the first of the surrounding sources (if it's air), so
695 // it's perfectly safe to use liquid_kind here to determine the new node content.
696 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
697 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
698 // liquid_kind is set properly, see above
699 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
700 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
701 new_node_content = liquid_kind;
703 new_node_content = floodable_node;
704 } else if (ignored_sources && liquid_level >= 0) {
705 // Maybe there are neighbouring sources that aren't loaded yet
706 // so prevent flowing away.
707 new_node_level = liquid_level;
708 new_node_content = liquid_kind;
710 // no surrounding sources, so get the maximum level that can flow into this node
711 for (u16 i = 0; i < num_flows; i++) {
712 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
713 switch (flows[i].t) {
715 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
716 max_node_level = LIQUID_LEVEL_MAX;
717 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
718 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
719 } else if (nb_liquid_level > max_node_level) {
720 max_node_level = nb_liquid_level;
725 case NEIGHBOR_SAME_LEVEL:
726 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
727 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
728 max_node_level = nb_liquid_level - 1;
733 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
734 if (viscosity > 1 && max_node_level != liquid_level) {
735 // amount to gain, limited by viscosity
736 // must be at least 1 in absolute value
737 s8 level_inc = max_node_level - liquid_level;
738 if (level_inc < -viscosity || level_inc > viscosity)
739 new_node_level = liquid_level + level_inc/viscosity;
740 else if (level_inc < 0)
741 new_node_level = liquid_level - 1;
742 else if (level_inc > 0)
743 new_node_level = liquid_level + 1;
744 if (new_node_level != max_node_level)
745 must_reflow.push_back(p0);
747 new_node_level = max_node_level;
750 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
751 new_node_content = liquid_kind;
753 new_node_content = floodable_node;
758 check if anything has changed. if not, just continue with the next node.
760 if (new_node_content == n0.getContent() &&
761 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
762 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
763 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
769 update the current node
772 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
773 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
774 // set level to last 3 bits, flowing down bit to 4th bit
775 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
777 // set the liquid level and flow bits to 0
778 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
782 n0.setContent(new_node_content);
784 // on_flood() the node
785 if (floodable_node != CONTENT_AIR) {
786 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
790 // Ignore light (because calling voxalgo::update_lighting_nodes)
791 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
792 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
794 // Find out whether there is a suspect for this action
796 if (m_gamedef->rollback())
797 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
799 if (m_gamedef->rollback() && !suspect.empty()) {
801 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
802 // Get old node for rollback
803 RollbackNode rollback_oldnode(this, p0, m_gamedef);
807 RollbackNode rollback_newnode(this, p0, m_gamedef);
808 RollbackAction action;
809 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
810 m_gamedef->rollback()->reportAction(action);
816 v3s16 blockpos = getNodeBlockPos(p0);
817 MapBlock *block = getBlockNoCreateNoEx(blockpos);
819 modified_blocks[blockpos] = block;
820 changed_nodes.emplace_back(p0, n00);
824 enqueue neighbors for update if neccessary
826 switch (m_nodedef->get(n0.getContent()).liquid_type) {
829 // make sure source flows into all neighboring nodes
830 for (u16 i = 0; i < num_flows; i++)
831 if (flows[i].t != NEIGHBOR_UPPER)
832 m_transforming_liquid.push_back(flows[i].p);
833 for (u16 i = 0; i < num_airs; i++)
834 if (airs[i].t != NEIGHBOR_UPPER)
835 m_transforming_liquid.push_back(airs[i].p);
838 // this flow has turned to air; neighboring flows might need to do the same
839 for (u16 i = 0; i < num_flows; i++)
840 m_transforming_liquid.push_back(flows[i].p);
844 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
846 for (auto &iter : must_reflow)
847 m_transforming_liquid.push_back(iter);
849 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
852 /* ----------------------------------------------------------------------
853 * Manage the queue so that it does not grow indefinately
855 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
857 if (time_until_purge == 0)
858 return; // Feature disabled
860 time_until_purge *= 1000; // seconds -> milliseconds
862 u64 curr_time = porting::getTimeMs();
863 u32 prev_unprocessed = m_unprocessed_count;
864 m_unprocessed_count = m_transforming_liquid.size();
866 // if unprocessed block count is decreasing or stable
867 if (m_unprocessed_count <= prev_unprocessed) {
868 m_queue_size_timer_started = false;
870 if (!m_queue_size_timer_started)
871 m_inc_trending_up_start_time = curr_time;
872 m_queue_size_timer_started = true;
875 // Account for curr_time overflowing
876 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
877 m_queue_size_timer_started = false;
879 /* If the queue has been growing for more than liquid_queue_purge_time seconds
880 * and the number of unprocessed blocks is still > liquid_loop_max then we
881 * cannot keep up; dump the oldest blocks from the queue so that the queue
882 * has liquid_loop_max items in it
884 if (m_queue_size_timer_started
885 && curr_time - m_inc_trending_up_start_time > time_until_purge
886 && m_unprocessed_count > liquid_loop_max) {
888 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
890 infostream << "transformLiquids(): DUMPING " << dump_qty
891 << " blocks from the queue" << std::endl;
894 m_transforming_liquid.pop_front();
896 m_queue_size_timer_started = false; // optimistically assume we can keep up now
897 m_unprocessed_count = m_transforming_liquid.size();
901 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
903 std::vector<v3s16> positions_with_meta;
905 sortBoxVerticies(p1, p2);
906 v3s16 bpmin = getNodeBlockPos(p1);
907 v3s16 bpmax = getNodeBlockPos(p2);
909 VoxelArea area(p1, p2);
911 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
912 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
913 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
914 v3s16 blockpos(x, y, z);
916 MapBlock *block = getBlockNoCreateNoEx(blockpos);
918 verbosestream << "Map::getNodeMetadata(): Need to emerge "
919 << PP(blockpos) << std::endl;
920 block = emergeBlock(blockpos, false);
923 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
928 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
929 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
930 for (size_t i = 0; i != keys.size(); i++) {
931 v3s16 p(keys[i] + p_base);
932 if (!area.contains(p))
935 positions_with_meta.push_back(p);
939 return positions_with_meta;
942 NodeMetadata *Map::getNodeMetadata(v3s16 p)
944 v3s16 blockpos = getNodeBlockPos(p);
945 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
946 MapBlock *block = getBlockNoCreateNoEx(blockpos);
948 infostream<<"Map::getNodeMetadata(): Need to emerge "
949 <<PP(blockpos)<<std::endl;
950 block = emergeBlock(blockpos, false);
953 warningstream<<"Map::getNodeMetadata(): Block not found"
957 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
961 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
963 v3s16 blockpos = getNodeBlockPos(p);
964 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
965 MapBlock *block = getBlockNoCreateNoEx(blockpos);
967 infostream<<"Map::setNodeMetadata(): Need to emerge "
968 <<PP(blockpos)<<std::endl;
969 block = emergeBlock(blockpos, false);
972 warningstream<<"Map::setNodeMetadata(): Block not found"
976 block->m_node_metadata.set(p_rel, meta);
980 void Map::removeNodeMetadata(v3s16 p)
982 v3s16 blockpos = getNodeBlockPos(p);
983 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
984 MapBlock *block = getBlockNoCreateNoEx(blockpos);
987 warningstream<<"Map::removeNodeMetadata(): Block not found"
991 block->m_node_metadata.remove(p_rel);
994 NodeTimer Map::getNodeTimer(v3s16 p)
996 v3s16 blockpos = getNodeBlockPos(p);
997 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
998 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1000 infostream<<"Map::getNodeTimer(): Need to emerge "
1001 <<PP(blockpos)<<std::endl;
1002 block = emergeBlock(blockpos, false);
1005 warningstream<<"Map::getNodeTimer(): Block not found"
1009 NodeTimer t = block->m_node_timers.get(p_rel);
1010 NodeTimer nt(t.timeout, t.elapsed, p);
1014 void Map::setNodeTimer(const NodeTimer &t)
1016 v3s16 p = t.position;
1017 v3s16 blockpos = getNodeBlockPos(p);
1018 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1019 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1021 infostream<<"Map::setNodeTimer(): Need to emerge "
1022 <<PP(blockpos)<<std::endl;
1023 block = emergeBlock(blockpos, false);
1026 warningstream<<"Map::setNodeTimer(): Block not found"
1030 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1031 block->m_node_timers.set(nt);
1034 void Map::removeNodeTimer(v3s16 p)
1036 v3s16 blockpos = getNodeBlockPos(p);
1037 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1038 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1041 warningstream<<"Map::removeNodeTimer(): Block not found"
1045 block->m_node_timers.remove(p_rel);
1048 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1049 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1052 This functions determines the node inside the target block that is
1053 closest to the camera position. This increases the occlusion culling
1054 accuracy in straight and diagonal corridors.
1055 The returned position will be occlusion checked first in addition to the
1056 others (8 corners + center).
1057 No position is returned if
1058 - the closest node is a corner, corners are checked anyway.
1059 - the camera is inside the target block, it will never be occluded.
1061 #define CLOSEST_EDGE(pos, bounds, axis) \
1062 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1063 (bounds).MaxEdge.axis
1065 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1066 (pos_camera.X <= block_bounds.MaxEdge.X);
1067 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1068 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1069 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1070 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1072 if (x_inside && y_inside && z_inside)
1073 return false; // Camera inside target mapblock
1076 if (x_inside && y_inside) {
1077 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1078 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1080 } else if (y_inside && z_inside) {
1081 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1082 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1084 } else if (x_inside && z_inside) {
1085 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1086 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1092 check = v3s16(pos_camera.X, 0, 0);
1093 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1094 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1096 } else if (y_inside) {
1097 check = v3s16(0, pos_camera.Y, 0);
1098 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1099 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1101 } else if (z_inside) {
1102 check = v3s16(0, 0, pos_camera.Z);
1103 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1104 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1108 // Closest node would be a corner, none returned
1112 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1113 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1115 v3f direction = intToFloat(pos_target - pos_camera, BS);
1116 float distance = direction.getLength();
1118 // Normalize direction vector
1119 if (distance > 0.0f)
1120 direction /= distance;
1122 v3f pos_origin_f = intToFloat(pos_camera, BS);
1124 bool is_valid_position;
1126 for (; offset < distance + end_offset; offset += step) {
1127 v3f pos_node_f = pos_origin_f + direction * offset;
1128 v3s16 pos_node = floatToInt(pos_node_f, BS);
1130 MapNode node = getNode(pos_node, &is_valid_position);
1132 if (is_valid_position &&
1133 !m_nodedef->get(node).light_propagates) {
1134 // Cannot see through light-blocking nodes --> occluded
1136 if (count >= needed_count)
1144 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1146 // Check occlusion for center and all 8 corners of the mapblock
1147 // Overshoot a little for less flickering
1148 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1149 static const v3s16 dir9[9] = {
1151 v3s16( 1, 1, 1) * bs2,
1152 v3s16( 1, 1, -1) * bs2,
1153 v3s16( 1, -1, 1) * bs2,
1154 v3s16( 1, -1, -1) * bs2,
1155 v3s16(-1, 1, 1) * bs2,
1156 v3s16(-1, 1, -1) * bs2,
1157 v3s16(-1, -1, 1) * bs2,
1158 v3s16(-1, -1, -1) * bs2,
1161 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1163 // Starting step size, value between 1m and sqrt(3)m
1164 float step = BS * 1.2f;
1165 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1166 float stepfac = 1.05f;
1168 float start_offset = BS * 1.0f;
1170 // The occlusion search of 'isOccluded()' must stop short of the target
1171 // point by distance 'end_offset' to not enter the target mapblock.
1172 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1173 // diagonal of a mapblock, because we must consider all view angles.
1174 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1175 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1177 // to reduce the likelihood of falsely occluded blocks
1178 // require at least two solid blocks
1179 // this is a HACK, we should think of a more precise algorithm
1180 u32 needed_count = 2;
1182 // Additional occlusion check, see comments in that function
1184 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1185 // node is always on a side facing the camera, end_offset can be lower
1186 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1187 -1.0f, needed_count))
1191 for (const v3s16 &dir : dir9) {
1192 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1193 start_offset, end_offset, needed_count))
1202 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1203 EmergeManager *emerge, MetricsBackend *mb):
1204 Map(dout_server, gamedef),
1205 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1208 verbosestream<<FUNCTION_NAME<<std::endl;
1210 // Tell the EmergeManager about our MapSettingsManager
1211 emerge->map_settings_mgr = &settings_mgr;
1214 Try to load map; if not found, create a new one.
1217 // Determine which database backend to use
1218 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1220 bool succeeded = conf.readConfigFile(conf_path.c_str());
1221 if (!succeeded || !conf.exists("backend")) {
1222 // fall back to sqlite3
1223 conf.set("backend", "sqlite3");
1225 std::string backend = conf.get("backend");
1226 dbase = createDatabase(backend, savedir, conf);
1227 if (conf.exists("readonly_backend")) {
1228 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1229 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1231 if (!conf.updateConfigFile(conf_path.c_str()))
1232 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1234 m_savedir = savedir;
1235 m_map_saving_enabled = false;
1237 m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1240 // If directory exists, check contents and load if possible
1241 if (fs::PathExists(m_savedir)) {
1242 // If directory is empty, it is safe to save into it.
1243 if (fs::GetDirListing(m_savedir).empty()) {
1244 infostream<<"ServerMap: Empty save directory is valid."
1246 m_map_saving_enabled = true;
1251 if (settings_mgr.loadMapMeta()) {
1252 infostream << "ServerMap: Metadata loaded from "
1253 << savedir << std::endl;
1255 infostream << "ServerMap: Metadata could not be loaded "
1256 "from " << savedir << ", assuming valid save "
1257 "directory." << std::endl;
1260 m_map_saving_enabled = true;
1261 // Map loaded, not creating new one
1265 // If directory doesn't exist, it is safe to save to it
1267 m_map_saving_enabled = true;
1270 catch(std::exception &e)
1272 warningstream<<"ServerMap: Failed to load map from "<<savedir
1273 <<", exception: "<<e.what()<<std::endl;
1274 infostream<<"Please remove the map or fix it."<<std::endl;
1275 warningstream<<"Map saving will be disabled."<<std::endl;
1279 ServerMap::~ServerMap()
1281 verbosestream<<FUNCTION_NAME<<std::endl;
1285 if (m_map_saving_enabled) {
1286 // Save only changed parts
1287 save(MOD_STATE_WRITE_AT_UNLOAD);
1288 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1290 infostream << "ServerMap: Map not saved" << std::endl;
1293 catch(std::exception &e)
1295 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1296 <<", exception: "<<e.what()<<std::endl;
1300 Close database if it was opened
1309 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1310 for(; i.atEnd() == false; i++)
1312 MapChunk *chunk = i.getNode()->getValue();
1318 MapgenParams *ServerMap::getMapgenParams()
1320 // getMapgenParams() should only ever be called after Server is initialized
1321 assert(settings_mgr.mapgen_params != NULL);
1322 return settings_mgr.mapgen_params;
1325 u64 ServerMap::getSeed()
1327 return getMapgenParams()->seed;
1330 s16 ServerMap::getWaterLevel()
1332 return getMapgenParams()->water_level;
1335 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1337 const s16 mapgen_limit_bp = rangelim(
1338 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1340 return p.X < -mapgen_limit_bp ||
1341 p.X > mapgen_limit_bp ||
1342 p.Y < -mapgen_limit_bp ||
1343 p.Y > mapgen_limit_bp ||
1344 p.Z < -mapgen_limit_bp ||
1345 p.Z > mapgen_limit_bp;
1348 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1350 s16 csize = getMapgenParams()->chunksize;
1351 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1352 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1354 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1355 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1357 v3s16 extra_borders(1, 1, 1);
1358 v3s16 full_bpmin = bpmin - extra_borders;
1359 v3s16 full_bpmax = bpmax + extra_borders;
1361 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1362 if (blockpos_over_mapgen_limit(full_bpmin) ||
1363 blockpos_over_mapgen_limit(full_bpmax))
1366 data->seed = getSeed();
1367 data->blockpos_min = bpmin;
1368 data->blockpos_max = bpmax;
1369 data->blockpos_requested = blockpos;
1370 data->nodedef = m_nodedef;
1373 Create the whole area of this and the neighboring blocks
1375 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1376 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1377 v2s16 sectorpos(x, z);
1378 // Sector metadata is loaded from disk if not already loaded.
1379 MapSector *sector = createSector(sectorpos);
1380 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1382 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1385 MapBlock *block = emergeBlock(p, false);
1386 if (block == NULL) {
1387 block = createBlock(p);
1389 // Block gets sunlight if this is true.
1390 // Refer to the map generator heuristics.
1391 bool ug = m_emerge->isBlockUnderground(p);
1392 block->setIsUnderground(ug);
1398 Now we have a big empty area.
1400 Make a ManualMapVoxelManipulator that contains this and the
1404 data->vmanip = new MMVManip(this);
1405 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1407 // Note: we may need this again at some point.
1409 // Ensure none of the blocks to be generated were marked as
1410 // containing CONTENT_IGNORE
1411 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1412 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1413 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1414 core::map<v3s16, u8>::Node *n;
1415 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1418 u8 flags = n->getValue();
1419 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1426 // Data is ready now.
1430 void ServerMap::finishBlockMake(BlockMakeData *data,
1431 std::map<v3s16, MapBlock*> *changed_blocks)
1433 v3s16 bpmin = data->blockpos_min;
1434 v3s16 bpmax = data->blockpos_max;
1436 v3s16 extra_borders(1, 1, 1);
1438 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1439 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1442 Blit generated stuff to map
1443 NOTE: blitBackAll adds nearly everything to changed_blocks
1445 data->vmanip->blitBackAll(changed_blocks);
1447 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1448 << changed_blocks->size());
1451 Copy transforming liquid information
1453 while (data->transforming_liquid.size()) {
1454 m_transforming_liquid.push_back(data->transforming_liquid.front());
1455 data->transforming_liquid.pop_front();
1458 for (auto &changed_block : *changed_blocks) {
1459 MapBlock *block = changed_block.second;
1463 Update day/night difference cache of the MapBlocks
1465 block->expireDayNightDiff();
1467 Set block as modified
1469 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1470 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1474 Set central blocks as generated
1476 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1477 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1478 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1479 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1483 block->setGenerated(true);
1487 Save changed parts of map
1488 NOTE: Will be saved later.
1490 //save(MOD_STATE_WRITE_AT_UNLOAD);
1493 MapSector *ServerMap::createSector(v2s16 p2d)
1496 Check if it exists already in memory
1498 MapSector *sector = getSectorNoGenerate(p2d);
1503 Do not create over max mapgen limit
1505 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1506 if (p2d.X < -max_limit_bp ||
1507 p2d.X > max_limit_bp ||
1508 p2d.Y < -max_limit_bp ||
1509 p2d.Y > max_limit_bp)
1510 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1513 Generate blank sector
1516 sector = new MapSector(this, p2d, m_gamedef);
1518 // Sector position on map in nodes
1519 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1524 m_sectors[p2d] = sector;
1531 This is a quick-hand function for calling makeBlock().
1533 MapBlock * ServerMap::generateBlock(
1535 std::map<v3s16, MapBlock*> &modified_blocks
1538 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
1540 TimeTaker timer("generateBlock");
1542 //MapBlock *block = original_dummy;
1544 v2s16 p2d(p.X, p.Z);
1545 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
1548 Do not generate over-limit
1550 if(blockpos_over_limit(p))
1552 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
1553 throw InvalidPositionException("generateBlock(): pos. over limit");
1557 Create block make data
1560 initBlockMake(&data, p);
1566 TimeTaker t("mapgen::make_block()");
1567 mapgen->makeChunk(&data);
1568 //mapgen::make_block(&data);
1570 if(enable_mapgen_debug_info == false)
1571 t.stop(true); // Hide output
1575 Blit data back on map, update lighting, add mobs and whatever this does
1577 finishBlockMake(&data, modified_blocks);
1582 MapBlock *block = getBlockNoCreateNoEx(p);
1590 bool erroneus_content = false;
1591 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1592 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1593 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1596 MapNode n = block->getNode(p);
1597 if(n.getContent() == CONTENT_IGNORE)
1599 infostream<<"CONTENT_IGNORE at "
1600 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1602 erroneus_content = true;
1606 if(erroneus_content)
1615 Generate a completely empty block
1619 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1620 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1622 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1625 n.setContent(CONTENT_AIR);
1626 block->setNode(v3s16(x0,y0,z0), n);
1632 if(enable_mapgen_debug_info == false)
1633 timer.stop(true); // Hide output
1639 MapBlock * ServerMap::createBlock(v3s16 p)
1642 Do not create over max mapgen limit
1644 if (blockpos_over_max_limit(p))
1645 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1647 v2s16 p2d(p.X, p.Z);
1650 This will create or load a sector if not found in memory.
1651 If block exists on disk, it will be loaded.
1653 NOTE: On old save formats, this will be slow, as it generates
1654 lighting on blocks for them.
1658 sector = createSector(p2d);
1659 } catch (InvalidPositionException &e) {
1660 infostream<<"createBlock: createSector() failed"<<std::endl;
1665 Try to get a block from the sector
1668 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1670 if(block->isDummy())
1675 block = sector->createBlankBlock(block_y);
1680 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1683 MapBlock *block = getBlockNoCreateNoEx(p);
1684 if (block && !block->isDummy())
1689 MapBlock *block = loadBlock(p);
1695 MapSector *sector = createSector(v2s16(p.X, p.Z));
1696 MapBlock *block = sector->createBlankBlock(p.Y);
1704 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1706 MapBlock *block = getBlockNoCreateNoEx(p3d);
1708 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1713 // N.B. This requires no synchronization, since data will not be modified unless
1714 // the VoxelManipulator being updated belongs to the same thread.
1715 void ServerMap::updateVManip(v3s16 pos)
1717 Mapgen *mg = m_emerge->getCurrentMapgen();
1721 MMVManip *vm = mg->vm;
1725 if (!vm->m_area.contains(pos))
1728 s32 idx = vm->m_area.index(pos);
1729 vm->m_data[idx] = getNode(pos);
1730 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1732 vm->m_is_dirty = true;
1735 s16 ServerMap::findGroundLevel(v2s16 p2d)
1739 Uh, just do something random...
1741 // Find existing map from top to down
1744 v3s16 p(p2d.X, max, p2d.Y);
1745 for(; p.Y>min; p.Y--)
1747 MapNode n = getNodeNoEx(p);
1748 if(n.getContent() != CONTENT_IGNORE)
1753 // If this node is not air, go to plan b
1754 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
1756 // Search existing walkable and return it
1757 for(; p.Y>min; p.Y--)
1759 MapNode n = getNodeNoEx(p);
1760 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
1769 Determine from map generator noise functions
1772 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
1775 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
1776 //return (s16)level;
1779 void ServerMap::createDirs(const std::string &path)
1781 if (!fs::CreateAllDirs(path)) {
1782 m_dout<<"ServerMap: Failed to create directory "
1783 <<"\""<<path<<"\""<<std::endl;
1784 throw BaseException("ServerMap failed to create directory");
1788 void ServerMap::save(ModifiedState save_level)
1790 if (!m_map_saving_enabled) {
1791 warningstream<<"Not saving map, saving disabled."<<std::endl;
1795 u64 start_time = porting::getTimeNs();
1797 if(save_level == MOD_STATE_CLEAN)
1798 infostream<<"ServerMap: Saving whole map, this can take time."
1801 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1802 if (settings_mgr.saveMapMeta())
1803 m_map_metadata_changed = false;
1806 // Profile modified reasons
1807 Profiler modprofiler;
1809 u32 block_count = 0;
1810 u32 block_count_all = 0; // Number of blocks in memory
1812 // Don't do anything with sqlite unless something is really saved
1813 bool save_started = false;
1815 for (auto §or_it : m_sectors) {
1816 MapSector *sector = sector_it.second;
1818 MapBlockVect blocks;
1819 sector->getBlocks(blocks);
1821 for (MapBlock *block : blocks) {
1824 if(block->getModified() >= (u32)save_level) {
1828 save_started = true;
1831 modprofiler.add(block->getModifiedReasonString(), 1);
1843 Only print if something happened or saved whole map
1845 if(save_level == MOD_STATE_CLEAN
1846 || block_count != 0) {
1847 infostream << "ServerMap: Written: "
1848 << block_count << " blocks"
1849 << ", " << block_count_all << " blocks in memory."
1851 PrintInfo(infostream); // ServerMap/ClientMap:
1852 infostream<<"Blocks modified by: "<<std::endl;
1853 modprofiler.print(infostream);
1856 auto end_time = porting::getTimeNs();
1857 m_save_time_counter->increment(end_time - start_time);
1860 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1862 dbase->listAllLoadableBlocks(dst);
1864 dbase_ro->listAllLoadableBlocks(dst);
1867 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1869 for (auto §or_it : m_sectors) {
1870 MapSector *sector = sector_it.second;
1872 MapBlockVect blocks;
1873 sector->getBlocks(blocks);
1875 for (MapBlock *block : blocks) {
1876 v3s16 p = block->getPos();
1882 MapDatabase *ServerMap::createDatabase(
1883 const std::string &name,
1884 const std::string &savedir,
1887 if (name == "sqlite3")
1888 return new MapDatabaseSQLite3(savedir);
1889 if (name == "dummy")
1890 return new Database_Dummy();
1892 if (name == "leveldb")
1893 return new Database_LevelDB(savedir);
1896 if (name == "redis")
1897 return new Database_Redis(conf);
1900 if (name == "postgresql") {
1901 std::string connect_string;
1902 conf.getNoEx("pgsql_connection", connect_string);
1903 return new MapDatabasePostgreSQL(connect_string);
1907 throw BaseException(std::string("Database backend ") + name + " not supported.");
1910 void ServerMap::pingDatabase()
1912 dbase->pingDatabase();
1915 void ServerMap::beginSave()
1920 void ServerMap::endSave()
1925 bool ServerMap::saveBlock(MapBlock *block)
1927 return saveBlock(block, dbase);
1930 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
1932 v3s16 p3d = block->getPos();
1934 // Dummy blocks are not written
1935 if (block->isDummy()) {
1936 warningstream << "saveBlock: Not writing dummy block "
1937 << PP(p3d) << std::endl;
1941 // Format used for writing
1942 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1945 [0] u8 serialization version
1948 std::ostringstream o(std::ios_base::binary);
1949 o.write((char*) &version, 1);
1950 block->serialize(o, version, true);
1952 bool ret = db->saveBlock(p3d, o.str());
1954 // We just wrote it to the disk so clear modified flag
1955 block->resetModified();
1960 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1963 std::istringstream is(*blob, std::ios_base::binary);
1965 u8 version = SER_FMT_VER_INVALID;
1966 is.read((char*)&version, 1);
1969 throw SerializationError("ServerMap::loadBlock(): Failed"
1970 " to read MapBlock version");
1972 MapBlock *block = NULL;
1973 bool created_new = false;
1974 block = sector->getBlockNoCreateNoEx(p3d.Y);
1977 block = sector->createBlankBlockNoInsert(p3d.Y);
1982 block->deSerialize(is, version, true);
1984 // If it's a new block, insert it to the map
1986 sector->insertBlock(block);
1987 ReflowScan scanner(this, m_emerge->ndef);
1988 scanner.scan(block, &m_transforming_liquid);
1992 Save blocks loaded in old format in new format
1995 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1996 // Only save if asked to; no need to update version
2000 // We just loaded it from, so it's up-to-date.
2001 block->resetModified();
2003 catch(SerializationError &e)
2005 errorstream<<"Invalid block data in database"
2006 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2007 <<" (SerializationError): "<<e.what()<<std::endl;
2009 // TODO: Block should be marked as invalid in memory so that it is
2010 // not touched but the game can run
2012 if(g_settings->getBool("ignore_world_load_errors")){
2013 errorstream<<"Ignoring block load error. Duck and cover! "
2014 <<"(ignore_world_load_errors)"<<std::endl;
2016 throw SerializationError("Invalid block data in database");
2021 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
2023 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
2025 v2s16 p2d(blockpos.X, blockpos.Z);
2028 dbase->loadBlock(blockpos, &ret);
2030 loadBlock(&ret, blockpos, createSector(p2d), false);
2031 } else if (dbase_ro) {
2032 dbase_ro->loadBlock(blockpos, &ret);
2034 loadBlock(&ret, blockpos, createSector(p2d), false);
2040 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2041 if (created_new && (block != NULL)) {
2042 std::map<v3s16, MapBlock*> modified_blocks;
2043 // Fix lighting if necessary
2044 voxalgo::update_block_border_lighting(this, block, modified_blocks);
2045 if (!modified_blocks.empty()) {
2046 //Modified lighting, send event
2048 event.type = MEET_OTHER;
2049 std::map<v3s16, MapBlock *>::iterator it;
2050 for (it = modified_blocks.begin();
2051 it != modified_blocks.end(); ++it)
2052 event.modified_blocks.insert(it->first);
2053 dispatchEvent(event);
2059 bool ServerMap::deleteBlock(v3s16 blockpos)
2061 if (!dbase->deleteBlock(blockpos))
2064 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2066 v2s16 p2d(blockpos.X, blockpos.Z);
2067 MapSector *sector = getSectorNoGenerate(p2d);
2070 sector->deleteBlock(block);
2076 void ServerMap::PrintInfo(std::ostream &out)
2081 bool ServerMap::repairBlockLight(v3s16 blockpos,
2082 std::map<v3s16, MapBlock *> *modified_blocks)
2084 MapBlock *block = emergeBlock(blockpos, false);
2085 if (!block || !block->isGenerated())
2087 voxalgo::repair_block_light(this, block, modified_blocks);
2091 MMVManip::MMVManip(Map *map):
2097 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2098 bool load_if_inexistent)
2100 TimeTaker timer1("initialEmerge", &emerge_time);
2102 // Units of these are MapBlocks
2103 v3s16 p_min = blockpos_min;
2104 v3s16 p_max = blockpos_max;
2106 VoxelArea block_area_nodes
2107 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2109 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2112 infostream<<"initialEmerge: area: ";
2113 block_area_nodes.print(infostream);
2114 infostream<<" ("<<size_MB<<"MB)";
2115 infostream<<std::endl;
2118 addArea(block_area_nodes);
2120 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2121 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2122 for(s32 x=p_min.X; x<=p_max.X; x++)
2127 std::map<v3s16, u8>::iterator n;
2128 n = m_loaded_blocks.find(p);
2129 if(n != m_loaded_blocks.end())
2132 bool block_data_inexistent = false;
2134 TimeTaker timer2("emerge load", &emerge_load_time);
2136 block = m_map->getBlockNoCreateNoEx(p);
2137 if (!block || block->isDummy())
2138 block_data_inexistent = true;
2140 block->copyTo(*this);
2143 if(block_data_inexistent)
2146 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2147 ServerMap *svrmap = (ServerMap *)m_map;
2148 block = svrmap->emergeBlock(p, false);
2150 block = svrmap->createBlock(p);
2151 block->copyTo(*this);
2153 flags |= VMANIP_BLOCK_DATA_INEXIST;
2156 Mark area inexistent
2158 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2159 // Fill with VOXELFLAG_NO_DATA
2160 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2161 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2163 s32 i = m_area.index(a.MinEdge.X,y,z);
2164 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2168 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2170 // Mark that block was loaded as blank
2171 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2174 m_loaded_blocks[p] = flags;
2180 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2181 bool overwrite_generated)
2183 if(m_area.getExtent() == v3s16(0,0,0))
2187 Copy data of all blocks
2189 for (auto &loaded_block : m_loaded_blocks) {
2190 v3s16 p = loaded_block.first;
2191 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2192 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2193 if (!existed || (block == NULL) ||
2194 (!overwrite_generated && block->isGenerated()))
2197 block->copyFrom(*this);
2198 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2201 (*modified_blocks)[p] = block;