3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
25 #include "voxelalgorithms.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/basic_macros.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
38 #include "reflowscan.h"
40 #include "mapgen/mapgen_v6.h"
41 #include "mapgen/mg_biome.h"
44 #include "database/database.h"
45 #include "database/database-dummy.h"
46 #include "database/database-sqlite3.h"
47 #include "script/scripting_server.h"
51 #include "database/database-leveldb.h"
54 #include "database/database-redis.h"
57 #include "database/database-postgresql.h"
65 Map::Map(IGameDef *gamedef):
67 m_nodedef(gamedef->ndef())
76 for (auto §or : m_sectors) {
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 m_event_receivers.erase(event_receiver);
91 void Map::dispatchEvent(const MapEditEvent &event)
93 for (MapEventReceiver *event_receiver : m_event_receivers) {
94 event_receiver->onMapEditEvent(event);
98 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
100 if(m_sector_cache != NULL && p == m_sector_cache_p){
101 MapSector * sector = m_sector_cache;
105 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
107 if (n == m_sectors.end())
110 MapSector *sector = n->second;
112 // Cache the last result
113 m_sector_cache_p = p;
114 m_sector_cache = sector;
119 MapSector * Map::getSectorNoGenerate(v2s16 p)
121 return getSectorNoGenerateNoLock(p);
124 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
136 MapBlock *block = getBlockNoCreateNoEx(p3d);
138 throw InvalidPositionException();
142 bool Map::isNodeUnderground(v3s16 p)
144 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock *block = getBlockNoCreateNoEx(blockpos);
146 return block && block->getIsUnderground();
149 bool Map::isValidPosition(v3s16 p)
151 v3s16 blockpos = getNodeBlockPos(p);
152 MapBlock *block = getBlockNoCreateNoEx(blockpos);
153 return (block != NULL);
156 // Returns a CONTENT_IGNORE node if not found
157 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
159 v3s16 blockpos = getNodeBlockPos(p);
160 MapBlock *block = getBlockNoCreateNoEx(blockpos);
162 if (is_valid_position != NULL)
163 *is_valid_position = false;
164 return {CONTENT_IGNORE};
167 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
169 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
170 if (is_valid_position != NULL)
171 *is_valid_position = is_valid_p;
175 // throws InvalidPositionException if not found
176 void Map::setNode(v3s16 p, MapNode & n)
178 v3s16 blockpos = getNodeBlockPos(p);
179 MapBlock *block = getBlockNoCreate(blockpos);
180 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
181 // Never allow placing CONTENT_IGNORE, it causes problems
182 if(n.getContent() == CONTENT_IGNORE){
184 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
185 <<" while trying to replace \""
186 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
187 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
190 block->setNodeNoCheck(relpos, n);
193 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
194 std::map<v3s16, MapBlock*> &modified_blocks,
195 bool remove_metadata)
197 // Collect old node for rollback
198 RollbackNode rollback_oldnode(this, p, m_gamedef);
200 // This is needed for updating the lighting
201 MapNode oldnode = getNode(p);
203 // Remove node metadata
204 if (remove_metadata) {
205 removeNodeMetadata(p);
208 // Set the node on the map
209 // Ignore light (because calling voxalgo::update_lighting_nodes)
210 n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
211 n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
215 std::vector<std::pair<v3s16, MapNode> > oldnodes;
216 oldnodes.emplace_back(p, oldnode);
217 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
219 for (auto &modified_block : modified_blocks) {
220 modified_block.second->expireDayNightDiff();
223 // Report for rollback
224 if(m_gamedef->rollback())
226 RollbackNode rollback_newnode(this, p, m_gamedef);
227 RollbackAction action;
228 action.setSetNode(p, rollback_oldnode, rollback_newnode);
229 m_gamedef->rollback()->reportAction(action);
233 Add neighboring liquid nodes and this node to transform queue.
234 (it's vital for the node itself to get updated last, if it was removed.)
237 for (const v3s16 &dir : g_7dirs) {
240 bool is_valid_position;
241 MapNode n2 = getNode(p2, &is_valid_position);
242 if(is_valid_position &&
243 (m_nodedef->get(n2).isLiquid() ||
244 n2.getContent() == CONTENT_AIR))
245 m_transforming_liquid.push_back(p2);
249 void Map::removeNodeAndUpdate(v3s16 p,
250 std::map<v3s16, MapBlock*> &modified_blocks)
252 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
255 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
258 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
262 bool succeeded = true;
264 std::map<v3s16, MapBlock*> modified_blocks;
265 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
267 // Copy modified_blocks to event
268 for (auto &modified_block : modified_blocks) {
269 event.modified_blocks.insert(modified_block.first);
272 catch(InvalidPositionException &e){
276 dispatchEvent(event);
281 bool Map::removeNodeWithEvent(v3s16 p)
284 event.type = MEET_REMOVENODE;
287 bool succeeded = true;
289 std::map<v3s16, MapBlock*> modified_blocks;
290 removeNodeAndUpdate(p, modified_blocks);
292 // Copy modified_blocks to event
293 for (auto &modified_block : modified_blocks) {
294 event.modified_blocks.insert(modified_block.first);
297 catch(InvalidPositionException &e){
301 dispatchEvent(event);
306 struct TimeOrderedMapBlock {
310 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
315 bool operator<(const TimeOrderedMapBlock &b) const
317 return block->getUsageTimer() < b.block->getUsageTimer();
324 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
325 std::vector<v3s16> *unloaded_blocks)
327 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
329 // Profile modified reasons
330 Profiler modprofiler;
332 std::vector<v2s16> sector_deletion_queue;
333 u32 deleted_blocks_count = 0;
334 u32 saved_blocks_count = 0;
335 u32 block_count_all = 0;
339 // If there is no practical limit, we spare creation of mapblock_queue
340 if (max_loaded_blocks == U32_MAX) {
341 for (auto §or_it : m_sectors) {
342 MapSector *sector = sector_it.second;
344 bool all_blocks_deleted = true;
347 sector->getBlocks(blocks);
349 for (MapBlock *block : blocks) {
350 block->incrementUsageTimer(dtime);
352 if (block->refGet() == 0
353 && block->getUsageTimer() > unload_timeout) {
354 v3s16 p = block->getPos();
357 if (block->getModified() != MOD_STATE_CLEAN
358 && save_before_unloading) {
359 modprofiler.add(block->getModifiedReasonString(), 1);
360 if (!saveBlock(block))
362 saved_blocks_count++;
365 // Delete from memory
366 sector->deleteBlock(block);
369 unloaded_blocks->push_back(p);
371 deleted_blocks_count++;
373 all_blocks_deleted = false;
378 if (all_blocks_deleted) {
379 sector_deletion_queue.push_back(sector_it.first);
383 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
384 for (auto §or_it : m_sectors) {
385 MapSector *sector = sector_it.second;
388 sector->getBlocks(blocks);
390 for (MapBlock *block : blocks) {
391 block->incrementUsageTimer(dtime);
392 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
395 block_count_all = mapblock_queue.size();
396 // Delete old blocks, and blocks over the limit from the memory
397 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
398 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
399 TimeOrderedMapBlock b = mapblock_queue.top();
400 mapblock_queue.pop();
402 MapBlock *block = b.block;
404 if (block->refGet() != 0)
407 v3s16 p = block->getPos();
410 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
411 modprofiler.add(block->getModifiedReasonString(), 1);
412 if (!saveBlock(block))
414 saved_blocks_count++;
417 // Delete from memory
418 b.sect->deleteBlock(block);
421 unloaded_blocks->push_back(p);
423 deleted_blocks_count++;
426 // Delete empty sectors
427 for (auto §or_it : m_sectors) {
428 if (sector_it.second->empty()) {
429 sector_deletion_queue.push_back(sector_it.first);
435 // Finally delete the empty sectors
436 deleteSectors(sector_deletion_queue);
438 if(deleted_blocks_count != 0)
440 PrintInfo(infostream); // ServerMap/ClientMap:
441 infostream<<"Unloaded "<<deleted_blocks_count
442 <<" blocks from memory";
443 if(save_before_unloading)
444 infostream<<", of which "<<saved_blocks_count<<" were written";
445 infostream<<", "<<block_count_all<<" blocks in memory";
446 infostream<<"."<<std::endl;
447 if(saved_blocks_count != 0){
448 PrintInfo(infostream); // ServerMap/ClientMap:
449 infostream<<"Blocks modified by: "<<std::endl;
450 modprofiler.print(infostream);
455 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
457 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
460 void Map::deleteSectors(std::vector<v2s16> §orList)
462 for (v2s16 j : sectorList) {
463 MapSector *sector = m_sectors[j];
464 // If sector is in sector cache, remove it from there
465 if(m_sector_cache == sector)
466 m_sector_cache = NULL;
467 // Remove from map and delete
473 void Map::PrintInfo(std::ostream &out)
478 #define WATER_DROP_BOOST 4
480 const static v3s16 liquid_6dirs[6] = {
481 // order: upper before same level before lower
490 enum NeighborType : u8 {
496 struct NodeNeighbor {
502 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
505 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
512 void Map::transforming_liquid_add(v3s16 p) {
513 m_transforming_liquid.push_back(p);
516 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
517 ServerEnvironment *env)
520 u32 initial_size = m_transforming_liquid.size();
522 /*if(initial_size != 0)
523 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
525 // list of nodes that due to viscosity have not reached their max level height
526 std::deque<v3s16> must_reflow;
528 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
530 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
531 u32 loop_max = liquid_loop_max;
535 /* If liquid_loop_max is not keeping up with the queue size increase
536 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
538 if (m_transforming_liquid.size() > loop_max * 2) {
540 float server_step = g_settings->getFloat("dedicated_server_step");
541 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
542 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
544 m_transforming_liquid_loop_count_multiplier = 1.0;
547 loop_max *= m_transforming_liquid_loop_count_multiplier;
550 while (m_transforming_liquid.size() != 0)
552 // This should be done here so that it is done when continue is used
553 if (loopcount >= initial_size || loopcount >= loop_max)
558 Get a queued transforming liquid node
560 v3s16 p0 = m_transforming_liquid.front();
561 m_transforming_liquid.pop_front();
563 MapNode n0 = getNode(p0);
566 Collect information about current node
568 s8 liquid_level = -1;
569 // The liquid node which will be placed there if
570 // the liquid flows into this node.
571 content_t liquid_kind = CONTENT_IGNORE;
572 // The node which will be placed there if liquid
573 // can't flow into this node.
574 content_t floodable_node = CONTENT_AIR;
575 const ContentFeatures &cf = m_nodedef->get(n0);
576 LiquidType liquid_type = cf.liquid_type;
577 switch (liquid_type) {
579 liquid_level = LIQUID_LEVEL_SOURCE;
580 liquid_kind = cf.liquid_alternative_flowing_id;
583 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
584 liquid_kind = n0.getContent();
587 // if this node is 'floodable', it *could* be transformed
588 // into a liquid, otherwise, continue with the next node.
591 floodable_node = n0.getContent();
592 liquid_kind = CONTENT_AIR;
597 Collect information about the environment
599 NodeNeighbor sources[6]; // surrounding sources
601 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
603 NodeNeighbor airs[6]; // surrounding air
605 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
606 int num_neutrals = 0;
607 bool flowing_down = false;
608 bool ignored_sources = false;
609 for (u16 i = 0; i < 6; i++) {
610 NeighborType nt = NEIGHBOR_SAME_LEVEL;
621 v3s16 npos = p0 + liquid_6dirs[i];
622 NodeNeighbor nb(getNode(npos), nt, npos);
623 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
624 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
626 if (cfnb.floodable) {
627 airs[num_airs++] = nb;
628 // if the current node is a water source the neighbor
629 // should be enqueded for transformation regardless of whether the
630 // current node changes or not.
631 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
632 m_transforming_liquid.push_back(npos);
633 // if the current node happens to be a flowing node, it will start to flow down here.
634 if (nb.t == NEIGHBOR_LOWER)
637 neutrals[num_neutrals++] = nb;
638 if (nb.n.getContent() == CONTENT_IGNORE) {
639 // If node below is ignore prevent water from
640 // spreading outwards and otherwise prevent from
641 // flowing away as ignore node might be the source
642 if (nb.t == NEIGHBOR_LOWER)
645 ignored_sources = true;
650 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
651 if (liquid_kind == CONTENT_AIR)
652 liquid_kind = cfnb.liquid_alternative_flowing_id;
653 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
654 neutrals[num_neutrals++] = nb;
656 // Do not count bottom source, it will screw things up
657 if(nt != NEIGHBOR_LOWER)
658 sources[num_sources++] = nb;
662 if (nb.t != NEIGHBOR_SAME_LEVEL ||
663 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
664 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
665 // but exclude falling liquids on the same level, they cannot flow here anyway
666 if (liquid_kind == CONTENT_AIR)
667 liquid_kind = cfnb.liquid_alternative_flowing_id;
669 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
670 neutrals[num_neutrals++] = nb;
672 flows[num_flows++] = nb;
673 if (nb.t == NEIGHBOR_LOWER)
681 decide on the type (and possibly level) of the current node
683 content_t new_node_content;
684 s8 new_node_level = -1;
685 s8 max_node_level = -1;
687 u8 range = m_nodedef->get(liquid_kind).liquid_range;
688 if (range > LIQUID_LEVEL_MAX + 1)
689 range = LIQUID_LEVEL_MAX + 1;
691 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
692 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
693 // or the flowing alternative of the first of the surrounding sources (if it's air), so
694 // it's perfectly safe to use liquid_kind here to determine the new node content.
695 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
696 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
697 // liquid_kind is set properly, see above
698 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
699 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
700 new_node_content = liquid_kind;
702 new_node_content = floodable_node;
703 } else if (ignored_sources && liquid_level >= 0) {
704 // Maybe there are neighbouring sources that aren't loaded yet
705 // so prevent flowing away.
706 new_node_level = liquid_level;
707 new_node_content = liquid_kind;
709 // no surrounding sources, so get the maximum level that can flow into this node
710 for (u16 i = 0; i < num_flows; i++) {
711 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
712 switch (flows[i].t) {
714 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
715 max_node_level = LIQUID_LEVEL_MAX;
716 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
717 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
718 } else if (nb_liquid_level > max_node_level) {
719 max_node_level = nb_liquid_level;
724 case NEIGHBOR_SAME_LEVEL:
725 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
726 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
727 max_node_level = nb_liquid_level - 1;
732 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
733 if (viscosity > 1 && max_node_level != liquid_level) {
734 // amount to gain, limited by viscosity
735 // must be at least 1 in absolute value
736 s8 level_inc = max_node_level - liquid_level;
737 if (level_inc < -viscosity || level_inc > viscosity)
738 new_node_level = liquid_level + level_inc/viscosity;
739 else if (level_inc < 0)
740 new_node_level = liquid_level - 1;
741 else if (level_inc > 0)
742 new_node_level = liquid_level + 1;
743 if (new_node_level != max_node_level)
744 must_reflow.push_back(p0);
746 new_node_level = max_node_level;
749 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
750 new_node_content = liquid_kind;
752 new_node_content = floodable_node;
757 check if anything has changed. if not, just continue with the next node.
759 if (new_node_content == n0.getContent() &&
760 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
761 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
762 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
768 update the current node
771 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
772 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
773 // set level to last 3 bits, flowing down bit to 4th bit
774 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
776 // set the liquid level and flow bits to 0
777 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
781 n0.setContent(new_node_content);
783 // on_flood() the node
784 if (floodable_node != CONTENT_AIR) {
785 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
789 // Ignore light (because calling voxalgo::update_lighting_nodes)
790 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
791 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
793 // Find out whether there is a suspect for this action
795 if (m_gamedef->rollback())
796 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
798 if (m_gamedef->rollback() && !suspect.empty()) {
800 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
801 // Get old node for rollback
802 RollbackNode rollback_oldnode(this, p0, m_gamedef);
806 RollbackNode rollback_newnode(this, p0, m_gamedef);
807 RollbackAction action;
808 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
809 m_gamedef->rollback()->reportAction(action);
815 v3s16 blockpos = getNodeBlockPos(p0);
816 MapBlock *block = getBlockNoCreateNoEx(blockpos);
818 modified_blocks[blockpos] = block;
819 changed_nodes.emplace_back(p0, n00);
823 enqueue neighbors for update if neccessary
825 switch (m_nodedef->get(n0.getContent()).liquid_type) {
828 // make sure source flows into all neighboring nodes
829 for (u16 i = 0; i < num_flows; i++)
830 if (flows[i].t != NEIGHBOR_UPPER)
831 m_transforming_liquid.push_back(flows[i].p);
832 for (u16 i = 0; i < num_airs; i++)
833 if (airs[i].t != NEIGHBOR_UPPER)
834 m_transforming_liquid.push_back(airs[i].p);
837 // this flow has turned to air; neighboring flows might need to do the same
838 for (u16 i = 0; i < num_flows; i++)
839 m_transforming_liquid.push_back(flows[i].p);
843 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
845 for (auto &iter : must_reflow)
846 m_transforming_liquid.push_back(iter);
848 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
851 /* ----------------------------------------------------------------------
852 * Manage the queue so that it does not grow indefinately
854 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
856 if (time_until_purge == 0)
857 return; // Feature disabled
859 time_until_purge *= 1000; // seconds -> milliseconds
861 u64 curr_time = porting::getTimeMs();
862 u32 prev_unprocessed = m_unprocessed_count;
863 m_unprocessed_count = m_transforming_liquid.size();
865 // if unprocessed block count is decreasing or stable
866 if (m_unprocessed_count <= prev_unprocessed) {
867 m_queue_size_timer_started = false;
869 if (!m_queue_size_timer_started)
870 m_inc_trending_up_start_time = curr_time;
871 m_queue_size_timer_started = true;
874 // Account for curr_time overflowing
875 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
876 m_queue_size_timer_started = false;
878 /* If the queue has been growing for more than liquid_queue_purge_time seconds
879 * and the number of unprocessed blocks is still > liquid_loop_max then we
880 * cannot keep up; dump the oldest blocks from the queue so that the queue
881 * has liquid_loop_max items in it
883 if (m_queue_size_timer_started
884 && curr_time - m_inc_trending_up_start_time > time_until_purge
885 && m_unprocessed_count > liquid_loop_max) {
887 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
889 infostream << "transformLiquids(): DUMPING " << dump_qty
890 << " blocks from the queue" << std::endl;
893 m_transforming_liquid.pop_front();
895 m_queue_size_timer_started = false; // optimistically assume we can keep up now
896 m_unprocessed_count = m_transforming_liquid.size();
900 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
902 std::vector<v3s16> positions_with_meta;
904 sortBoxVerticies(p1, p2);
905 v3s16 bpmin = getNodeBlockPos(p1);
906 v3s16 bpmax = getNodeBlockPos(p2);
908 VoxelArea area(p1, p2);
910 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
911 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
912 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
913 v3s16 blockpos(x, y, z);
915 MapBlock *block = getBlockNoCreateNoEx(blockpos);
917 verbosestream << "Map::getNodeMetadata(): Need to emerge "
918 << PP(blockpos) << std::endl;
919 block = emergeBlock(blockpos, false);
922 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
927 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
928 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
929 for (size_t i = 0; i != keys.size(); i++) {
930 v3s16 p(keys[i] + p_base);
931 if (!area.contains(p))
934 positions_with_meta.push_back(p);
938 return positions_with_meta;
941 NodeMetadata *Map::getNodeMetadata(v3s16 p)
943 v3s16 blockpos = getNodeBlockPos(p);
944 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
945 MapBlock *block = getBlockNoCreateNoEx(blockpos);
947 infostream<<"Map::getNodeMetadata(): Need to emerge "
948 <<PP(blockpos)<<std::endl;
949 block = emergeBlock(blockpos, false);
952 warningstream<<"Map::getNodeMetadata(): Block not found"
956 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
960 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
962 v3s16 blockpos = getNodeBlockPos(p);
963 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
964 MapBlock *block = getBlockNoCreateNoEx(blockpos);
966 infostream<<"Map::setNodeMetadata(): Need to emerge "
967 <<PP(blockpos)<<std::endl;
968 block = emergeBlock(blockpos, false);
971 warningstream<<"Map::setNodeMetadata(): Block not found"
975 block->m_node_metadata.set(p_rel, meta);
979 void Map::removeNodeMetadata(v3s16 p)
981 v3s16 blockpos = getNodeBlockPos(p);
982 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
983 MapBlock *block = getBlockNoCreateNoEx(blockpos);
986 warningstream<<"Map::removeNodeMetadata(): Block not found"
990 block->m_node_metadata.remove(p_rel);
993 NodeTimer Map::getNodeTimer(v3s16 p)
995 v3s16 blockpos = getNodeBlockPos(p);
996 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
997 MapBlock *block = getBlockNoCreateNoEx(blockpos);
999 infostream<<"Map::getNodeTimer(): Need to emerge "
1000 <<PP(blockpos)<<std::endl;
1001 block = emergeBlock(blockpos, false);
1004 warningstream<<"Map::getNodeTimer(): Block not found"
1008 NodeTimer t = block->m_node_timers.get(p_rel);
1009 NodeTimer nt(t.timeout, t.elapsed, p);
1013 void Map::setNodeTimer(const NodeTimer &t)
1015 v3s16 p = t.position;
1016 v3s16 blockpos = getNodeBlockPos(p);
1017 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1018 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1020 infostream<<"Map::setNodeTimer(): Need to emerge "
1021 <<PP(blockpos)<<std::endl;
1022 block = emergeBlock(blockpos, false);
1025 warningstream<<"Map::setNodeTimer(): Block not found"
1029 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1030 block->m_node_timers.set(nt);
1033 void Map::removeNodeTimer(v3s16 p)
1035 v3s16 blockpos = getNodeBlockPos(p);
1036 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1037 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1040 warningstream<<"Map::removeNodeTimer(): Block not found"
1044 block->m_node_timers.remove(p_rel);
1047 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1048 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1051 This functions determines the node inside the target block that is
1052 closest to the camera position. This increases the occlusion culling
1053 accuracy in straight and diagonal corridors.
1054 The returned position will be occlusion checked first in addition to the
1055 others (8 corners + center).
1056 No position is returned if
1057 - the closest node is a corner, corners are checked anyway.
1058 - the camera is inside the target block, it will never be occluded.
1060 #define CLOSEST_EDGE(pos, bounds, axis) \
1061 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1062 (bounds).MaxEdge.axis
1064 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1065 (pos_camera.X <= block_bounds.MaxEdge.X);
1066 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1067 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1068 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1069 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1071 if (x_inside && y_inside && z_inside)
1072 return false; // Camera inside target mapblock
1075 if (x_inside && y_inside) {
1076 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1077 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1079 } else if (y_inside && z_inside) {
1080 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1081 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1083 } else if (x_inside && z_inside) {
1084 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1085 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1091 check = v3s16(pos_camera.X, 0, 0);
1092 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1093 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1095 } else if (y_inside) {
1096 check = v3s16(0, pos_camera.Y, 0);
1097 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1098 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1100 } else if (z_inside) {
1101 check = v3s16(0, 0, pos_camera.Z);
1102 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1103 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1107 // Closest node would be a corner, none returned
1111 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1112 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1114 v3f direction = intToFloat(pos_target - pos_camera, BS);
1115 float distance = direction.getLength();
1117 // Normalize direction vector
1118 if (distance > 0.0f)
1119 direction /= distance;
1121 v3f pos_origin_f = intToFloat(pos_camera, BS);
1123 bool is_valid_position;
1125 for (; offset < distance + end_offset; offset += step) {
1126 v3f pos_node_f = pos_origin_f + direction * offset;
1127 v3s16 pos_node = floatToInt(pos_node_f, BS);
1129 MapNode node = getNode(pos_node, &is_valid_position);
1131 if (is_valid_position &&
1132 !m_nodedef->get(node).light_propagates) {
1133 // Cannot see through light-blocking nodes --> occluded
1135 if (count >= needed_count)
1143 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1145 // Check occlusion for center and all 8 corners of the mapblock
1146 // Overshoot a little for less flickering
1147 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1148 static const v3s16 dir9[9] = {
1150 v3s16( 1, 1, 1) * bs2,
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,
1160 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1162 // Starting step size, value between 1m and sqrt(3)m
1163 float step = BS * 1.2f;
1164 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1165 float stepfac = 1.05f;
1167 float start_offset = BS * 1.0f;
1169 // The occlusion search of 'isOccluded()' must stop short of the target
1170 // point by distance 'end_offset' to not enter the target mapblock.
1171 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1172 // diagonal of a mapblock, because we must consider all view angles.
1173 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1174 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1176 // to reduce the likelihood of falsely occluded blocks
1177 // require at least two solid blocks
1178 // this is a HACK, we should think of a more precise algorithm
1179 u32 needed_count = 2;
1181 // Additional occlusion check, see comments in that function
1183 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1184 // node is always on a side facing the camera, end_offset can be lower
1185 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1186 -1.0f, needed_count))
1190 for (const v3s16 &dir : dir9) {
1191 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1192 start_offset, end_offset, needed_count))
1201 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1202 EmergeManager *emerge, MetricsBackend *mb):
1204 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1207 verbosestream<<FUNCTION_NAME<<std::endl;
1209 // Tell the EmergeManager about our MapSettingsManager
1210 emerge->map_settings_mgr = &settings_mgr;
1213 Try to load map; if not found, create a new one.
1216 // Determine which database backend to use
1217 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1219 bool succeeded = conf.readConfigFile(conf_path.c_str());
1220 if (!succeeded || !conf.exists("backend")) {
1221 // fall back to sqlite3
1222 conf.set("backend", "sqlite3");
1224 std::string backend = conf.get("backend");
1225 dbase = createDatabase(backend, savedir, conf);
1226 if (conf.exists("readonly_backend")) {
1227 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1228 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1230 if (!conf.updateConfigFile(conf_path.c_str()))
1231 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1233 m_savedir = savedir;
1234 m_map_saving_enabled = false;
1236 m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1238 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1241 // If directory exists, check contents and load if possible
1242 if (fs::PathExists(m_savedir)) {
1243 // If directory is empty, it is safe to save into it.
1244 if (fs::GetDirListing(m_savedir).empty()) {
1245 infostream<<"ServerMap: Empty save directory is valid."
1247 m_map_saving_enabled = true;
1252 if (settings_mgr.loadMapMeta()) {
1253 infostream << "ServerMap: Metadata loaded from "
1254 << savedir << std::endl;
1256 infostream << "ServerMap: Metadata could not be loaded "
1257 "from " << savedir << ", assuming valid save "
1258 "directory." << std::endl;
1261 m_map_saving_enabled = true;
1262 // Map loaded, not creating new one
1266 // If directory doesn't exist, it is safe to save to it
1268 m_map_saving_enabled = true;
1271 catch(std::exception &e)
1273 warningstream<<"ServerMap: Failed to load map from "<<savedir
1274 <<", exception: "<<e.what()<<std::endl;
1275 infostream<<"Please remove the map or fix it."<<std::endl;
1276 warningstream<<"Map saving will be disabled."<<std::endl;
1280 ServerMap::~ServerMap()
1282 verbosestream<<FUNCTION_NAME<<std::endl;
1286 if (m_map_saving_enabled) {
1287 // Save only changed parts
1288 save(MOD_STATE_WRITE_AT_UNLOAD);
1289 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1291 infostream << "ServerMap: Map not saved" << std::endl;
1294 catch(std::exception &e)
1296 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1297 <<", exception: "<<e.what()<<std::endl;
1301 Close database if it was opened
1310 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1311 for(; i.atEnd() == false; i++)
1313 MapChunk *chunk = i.getNode()->getValue();
1319 MapgenParams *ServerMap::getMapgenParams()
1321 // getMapgenParams() should only ever be called after Server is initialized
1322 assert(settings_mgr.mapgen_params != NULL);
1323 return settings_mgr.mapgen_params;
1326 u64 ServerMap::getSeed()
1328 return getMapgenParams()->seed;
1331 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1333 const s16 mapgen_limit_bp = rangelim(
1334 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1336 return p.X < -mapgen_limit_bp ||
1337 p.X > mapgen_limit_bp ||
1338 p.Y < -mapgen_limit_bp ||
1339 p.Y > mapgen_limit_bp ||
1340 p.Z < -mapgen_limit_bp ||
1341 p.Z > mapgen_limit_bp;
1344 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1346 s16 csize = getMapgenParams()->chunksize;
1347 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1348 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1350 if (!m_chunks_in_progress.insert(bpmin).second)
1353 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1354 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1356 v3s16 extra_borders(1, 1, 1);
1357 v3s16 full_bpmin = bpmin - extra_borders;
1358 v3s16 full_bpmax = bpmax + extra_borders;
1360 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1361 if (blockpos_over_mapgen_limit(full_bpmin) ||
1362 blockpos_over_mapgen_limit(full_bpmax))
1365 data->seed = getSeed();
1366 data->blockpos_min = bpmin;
1367 data->blockpos_max = bpmax;
1368 data->nodedef = m_nodedef;
1371 Create the whole area of this and the neighboring blocks
1373 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1374 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1375 v2s16 sectorpos(x, z);
1376 // Sector metadata is loaded from disk if not already loaded.
1377 MapSector *sector = createSector(sectorpos);
1378 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1380 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1383 MapBlock *block = emergeBlock(p, false);
1384 if (block == NULL) {
1385 block = createBlock(p);
1387 // Block gets sunlight if this is true.
1388 // Refer to the map generator heuristics.
1389 bool ug = m_emerge->isBlockUnderground(p);
1390 block->setIsUnderground(ug);
1396 Now we have a big empty area.
1398 Make a ManualMapVoxelManipulator that contains this and the
1402 data->vmanip = new MMVManip(this);
1403 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1405 // Note: we may need this again at some point.
1407 // Ensure none of the blocks to be generated were marked as
1408 // containing CONTENT_IGNORE
1409 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1410 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1411 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1412 core::map<v3s16, u8>::Node *n;
1413 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1416 u8 flags = n->getValue();
1417 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1424 // Data is ready now.
1428 void ServerMap::finishBlockMake(BlockMakeData *data,
1429 std::map<v3s16, MapBlock*> *changed_blocks)
1431 v3s16 bpmin = data->blockpos_min;
1432 v3s16 bpmax = data->blockpos_max;
1434 v3s16 extra_borders(1, 1, 1);
1436 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1437 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1440 Blit generated stuff to map
1441 NOTE: blitBackAll adds nearly everything to changed_blocks
1443 data->vmanip->blitBackAll(changed_blocks);
1445 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1446 << changed_blocks->size());
1449 Copy transforming liquid information
1451 while (data->transforming_liquid.size()) {
1452 m_transforming_liquid.push_back(data->transforming_liquid.front());
1453 data->transforming_liquid.pop_front();
1456 for (auto &changed_block : *changed_blocks) {
1457 MapBlock *block = changed_block.second;
1461 Update day/night difference cache of the MapBlocks
1463 block->expireDayNightDiff();
1465 Set block as modified
1467 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1468 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1472 Set central blocks as generated
1474 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1475 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1476 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1477 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1481 block->setGenerated(true);
1485 Save changed parts of map
1486 NOTE: Will be saved later.
1488 //save(MOD_STATE_WRITE_AT_UNLOAD);
1489 m_chunks_in_progress.erase(bpmin);
1492 MapSector *ServerMap::createSector(v2s16 p2d)
1495 Check if it exists already in memory
1497 MapSector *sector = getSectorNoGenerate(p2d);
1502 Do not create over max mapgen limit
1504 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1505 if (p2d.X < -max_limit_bp ||
1506 p2d.X > max_limit_bp ||
1507 p2d.Y < -max_limit_bp ||
1508 p2d.Y > max_limit_bp)
1509 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1512 Generate blank sector
1515 sector = new MapSector(this, p2d, m_gamedef);
1517 // Sector position on map in nodes
1518 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1523 m_sectors[p2d] = sector;
1530 This is a quick-hand function for calling makeBlock().
1532 MapBlock * ServerMap::generateBlock(
1534 std::map<v3s16, MapBlock*> &modified_blocks
1537 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
1539 TimeTaker timer("generateBlock");
1541 //MapBlock *block = original_dummy;
1543 v2s16 p2d(p.X, p.Z);
1544 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
1547 Do not generate over-limit
1549 if(blockpos_over_limit(p))
1551 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
1552 throw InvalidPositionException("generateBlock(): pos. over limit");
1556 Create block make data
1559 initBlockMake(&data, p);
1565 TimeTaker t("mapgen::make_block()");
1566 mapgen->makeChunk(&data);
1567 //mapgen::make_block(&data);
1569 if(enable_mapgen_debug_info == false)
1570 t.stop(true); // Hide output
1574 Blit data back on map, update lighting, add mobs and whatever this does
1576 finishBlockMake(&data, modified_blocks);
1581 MapBlock *block = getBlockNoCreateNoEx(p);
1589 bool erroneus_content = false;
1590 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1591 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1592 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1595 MapNode n = block->getNode(p);
1596 if(n.getContent() == CONTENT_IGNORE)
1598 infostream<<"CONTENT_IGNORE at "
1599 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1601 erroneus_content = true;
1605 if(erroneus_content)
1614 Generate a completely empty block
1618 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1619 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1621 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1624 n.setContent(CONTENT_AIR);
1625 block->setNode(v3s16(x0,y0,z0), n);
1631 if(enable_mapgen_debug_info == false)
1632 timer.stop(true); // Hide output
1638 MapBlock * ServerMap::createBlock(v3s16 p)
1641 Do not create over max mapgen limit
1643 if (blockpos_over_max_limit(p))
1644 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1646 v2s16 p2d(p.X, p.Z);
1649 This will create or load a sector if not found in memory.
1650 If block exists on disk, it will be loaded.
1652 NOTE: On old save formats, this will be slow, as it generates
1653 lighting on blocks for them.
1657 sector = createSector(p2d);
1658 } catch (InvalidPositionException &e) {
1659 infostream<<"createBlock: createSector() failed"<<std::endl;
1664 Try to get a block from the sector
1667 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1669 if(block->isDummy())
1674 block = sector->createBlankBlock(block_y);
1679 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1682 MapBlock *block = getBlockNoCreateNoEx(p);
1683 if (block && !block->isDummy())
1688 MapBlock *block = loadBlock(p);
1694 MapSector *sector = createSector(v2s16(p.X, p.Z));
1695 MapBlock *block = sector->createBlankBlock(p.Y);
1703 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1705 MapBlock *block = getBlockNoCreateNoEx(p3d);
1707 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1712 // N.B. This requires no synchronization, since data will not be modified unless
1713 // the VoxelManipulator being updated belongs to the same thread.
1714 void ServerMap::updateVManip(v3s16 pos)
1716 Mapgen *mg = m_emerge->getCurrentMapgen();
1720 MMVManip *vm = mg->vm;
1724 if (!vm->m_area.contains(pos))
1727 s32 idx = vm->m_area.index(pos);
1728 vm->m_data[idx] = getNode(pos);
1729 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1731 vm->m_is_dirty = true;
1734 void ServerMap::save(ModifiedState save_level)
1736 if (!m_map_saving_enabled) {
1737 warningstream<<"Not saving map, saving disabled."<<std::endl;
1741 u64 start_time = porting::getTimeNs();
1743 if(save_level == MOD_STATE_CLEAN)
1744 infostream<<"ServerMap: Saving whole map, this can take time."
1747 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1748 if (settings_mgr.saveMapMeta())
1749 m_map_metadata_changed = false;
1752 // Profile modified reasons
1753 Profiler modprofiler;
1755 u32 block_count = 0;
1756 u32 block_count_all = 0; // Number of blocks in memory
1758 // Don't do anything with sqlite unless something is really saved
1759 bool save_started = false;
1761 for (auto §or_it : m_sectors) {
1762 MapSector *sector = sector_it.second;
1764 MapBlockVect blocks;
1765 sector->getBlocks(blocks);
1767 for (MapBlock *block : blocks) {
1770 if(block->getModified() >= (u32)save_level) {
1774 save_started = true;
1777 modprofiler.add(block->getModifiedReasonString(), 1);
1789 Only print if something happened or saved whole map
1791 if(save_level == MOD_STATE_CLEAN
1792 || block_count != 0) {
1793 infostream << "ServerMap: Written: "
1794 << block_count << " blocks"
1795 << ", " << block_count_all << " blocks in memory."
1797 PrintInfo(infostream); // ServerMap/ClientMap:
1798 infostream<<"Blocks modified by: "<<std::endl;
1799 modprofiler.print(infostream);
1802 auto end_time = porting::getTimeNs();
1803 m_save_time_counter->increment(end_time - start_time);
1806 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1808 dbase->listAllLoadableBlocks(dst);
1810 dbase_ro->listAllLoadableBlocks(dst);
1813 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
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) {
1822 v3s16 p = block->getPos();
1828 MapDatabase *ServerMap::createDatabase(
1829 const std::string &name,
1830 const std::string &savedir,
1833 if (name == "sqlite3")
1834 return new MapDatabaseSQLite3(savedir);
1835 if (name == "dummy")
1836 return new Database_Dummy();
1838 if (name == "leveldb")
1839 return new Database_LevelDB(savedir);
1842 if (name == "redis")
1843 return new Database_Redis(conf);
1846 if (name == "postgresql") {
1847 std::string connect_string;
1848 conf.getNoEx("pgsql_connection", connect_string);
1849 return new MapDatabasePostgreSQL(connect_string);
1853 throw BaseException(std::string("Database backend ") + name + " not supported.");
1856 void ServerMap::beginSave()
1861 void ServerMap::endSave()
1866 bool ServerMap::saveBlock(MapBlock *block)
1868 return saveBlock(block, dbase, m_map_compression_level);
1871 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1873 v3s16 p3d = block->getPos();
1875 // Dummy blocks are not written
1876 if (block->isDummy()) {
1877 warningstream << "saveBlock: Not writing dummy block "
1878 << PP(p3d) << std::endl;
1882 // Format used for writing
1883 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1886 [0] u8 serialization version
1889 std::ostringstream o(std::ios_base::binary);
1890 o.write((char*) &version, 1);
1891 block->serialize(o, version, true, compression_level);
1893 bool ret = db->saveBlock(p3d, o.str());
1895 // We just wrote it to the disk so clear modified flag
1896 block->resetModified();
1901 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1904 std::istringstream is(*blob, std::ios_base::binary);
1906 u8 version = SER_FMT_VER_INVALID;
1907 is.read((char*)&version, 1);
1910 throw SerializationError("ServerMap::loadBlock(): Failed"
1911 " to read MapBlock version");
1913 MapBlock *block = NULL;
1914 bool created_new = false;
1915 block = sector->getBlockNoCreateNoEx(p3d.Y);
1918 block = sector->createBlankBlockNoInsert(p3d.Y);
1923 block->deSerialize(is, version, true);
1925 // If it's a new block, insert it to the map
1927 sector->insertBlock(block);
1928 ReflowScan scanner(this, m_emerge->ndef);
1929 scanner.scan(block, &m_transforming_liquid);
1933 Save blocks loaded in old format in new format
1936 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1937 // Only save if asked to; no need to update version
1941 // We just loaded it from, so it's up-to-date.
1942 block->resetModified();
1944 catch(SerializationError &e)
1946 errorstream<<"Invalid block data in database"
1947 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1948 <<" (SerializationError): "<<e.what()<<std::endl;
1950 // TODO: Block should be marked as invalid in memory so that it is
1951 // not touched but the game can run
1953 if(g_settings->getBool("ignore_world_load_errors")){
1954 errorstream<<"Ignoring block load error. Duck and cover! "
1955 <<"(ignore_world_load_errors)"<<std::endl;
1957 throw SerializationError("Invalid block data in database");
1962 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1964 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1966 v2s16 p2d(blockpos.X, blockpos.Z);
1969 dbase->loadBlock(blockpos, &ret);
1971 loadBlock(&ret, blockpos, createSector(p2d), false);
1972 } else if (dbase_ro) {
1973 dbase_ro->loadBlock(blockpos, &ret);
1975 loadBlock(&ret, blockpos, createSector(p2d), false);
1981 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1982 if (created_new && (block != NULL)) {
1983 std::map<v3s16, MapBlock*> modified_blocks;
1984 // Fix lighting if necessary
1985 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1986 if (!modified_blocks.empty()) {
1987 //Modified lighting, send event
1989 event.type = MEET_OTHER;
1990 std::map<v3s16, MapBlock *>::iterator it;
1991 for (it = modified_blocks.begin();
1992 it != modified_blocks.end(); ++it)
1993 event.modified_blocks.insert(it->first);
1994 dispatchEvent(event);
2000 bool ServerMap::deleteBlock(v3s16 blockpos)
2002 if (!dbase->deleteBlock(blockpos))
2005 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2007 v2s16 p2d(blockpos.X, blockpos.Z);
2008 MapSector *sector = getSectorNoGenerate(p2d);
2011 sector->deleteBlock(block);
2017 void ServerMap::PrintInfo(std::ostream &out)
2022 bool ServerMap::repairBlockLight(v3s16 blockpos,
2023 std::map<v3s16, MapBlock *> *modified_blocks)
2025 MapBlock *block = emergeBlock(blockpos, false);
2026 if (!block || !block->isGenerated())
2028 voxalgo::repair_block_light(this, block, modified_blocks);
2032 MMVManip::MMVManip(Map *map):
2038 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2039 bool load_if_inexistent)
2041 TimeTaker timer1("initialEmerge", &emerge_time);
2043 // Units of these are MapBlocks
2044 v3s16 p_min = blockpos_min;
2045 v3s16 p_max = blockpos_max;
2047 VoxelArea block_area_nodes
2048 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2050 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2053 infostream<<"initialEmerge: area: ";
2054 block_area_nodes.print(infostream);
2055 infostream<<" ("<<size_MB<<"MB)";
2056 infostream<<std::endl;
2059 addArea(block_area_nodes);
2061 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2062 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2063 for(s32 x=p_min.X; x<=p_max.X; x++)
2068 std::map<v3s16, u8>::iterator n;
2069 n = m_loaded_blocks.find(p);
2070 if(n != m_loaded_blocks.end())
2073 bool block_data_inexistent = false;
2075 TimeTaker timer2("emerge load", &emerge_load_time);
2077 block = m_map->getBlockNoCreateNoEx(p);
2078 if (!block || block->isDummy())
2079 block_data_inexistent = true;
2081 block->copyTo(*this);
2084 if(block_data_inexistent)
2087 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2088 ServerMap *svrmap = (ServerMap *)m_map;
2089 block = svrmap->emergeBlock(p, false);
2091 block = svrmap->createBlock(p);
2092 block->copyTo(*this);
2094 flags |= VMANIP_BLOCK_DATA_INEXIST;
2097 Mark area inexistent
2099 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2100 // Fill with VOXELFLAG_NO_DATA
2101 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2102 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2104 s32 i = m_area.index(a.MinEdge.X,y,z);
2105 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2109 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2111 // Mark that block was loaded as blank
2112 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2115 m_loaded_blocks[p] = flags;
2121 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2122 bool overwrite_generated)
2124 if(m_area.getExtent() == v3s16(0,0,0))
2128 Copy data of all blocks
2130 for (auto &loaded_block : m_loaded_blocks) {
2131 v3s16 p = loaded_block.first;
2132 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2133 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2134 if (!existed || (block == NULL) ||
2135 (!overwrite_generated && block->isGenerated()))
2138 block->copyFrom(*this);
2139 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2142 (*modified_blocks)[p] = block;