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)");
1239 // If directory exists, check contents and load if possible
1240 if (fs::PathExists(m_savedir)) {
1241 // If directory is empty, it is safe to save into it.
1242 if (fs::GetDirListing(m_savedir).empty()) {
1243 infostream<<"ServerMap: Empty save directory is valid."
1245 m_map_saving_enabled = true;
1250 if (settings_mgr.loadMapMeta()) {
1251 infostream << "ServerMap: Metadata loaded from "
1252 << savedir << std::endl;
1254 infostream << "ServerMap: Metadata could not be loaded "
1255 "from " << savedir << ", assuming valid save "
1256 "directory." << std::endl;
1259 m_map_saving_enabled = true;
1260 // Map loaded, not creating new one
1264 // If directory doesn't exist, it is safe to save to it
1266 m_map_saving_enabled = true;
1269 catch(std::exception &e)
1271 warningstream<<"ServerMap: Failed to load map from "<<savedir
1272 <<", exception: "<<e.what()<<std::endl;
1273 infostream<<"Please remove the map or fix it."<<std::endl;
1274 warningstream<<"Map saving will be disabled."<<std::endl;
1278 ServerMap::~ServerMap()
1280 verbosestream<<FUNCTION_NAME<<std::endl;
1284 if (m_map_saving_enabled) {
1285 // Save only changed parts
1286 save(MOD_STATE_WRITE_AT_UNLOAD);
1287 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1289 infostream << "ServerMap: Map not saved" << std::endl;
1292 catch(std::exception &e)
1294 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1295 <<", exception: "<<e.what()<<std::endl;
1299 Close database if it was opened
1308 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1309 for(; i.atEnd() == false; i++)
1311 MapChunk *chunk = i.getNode()->getValue();
1317 MapgenParams *ServerMap::getMapgenParams()
1319 // getMapgenParams() should only ever be called after Server is initialized
1320 assert(settings_mgr.mapgen_params != NULL);
1321 return settings_mgr.mapgen_params;
1324 u64 ServerMap::getSeed()
1326 return getMapgenParams()->seed;
1329 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1331 const s16 mapgen_limit_bp = rangelim(
1332 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1334 return p.X < -mapgen_limit_bp ||
1335 p.X > mapgen_limit_bp ||
1336 p.Y < -mapgen_limit_bp ||
1337 p.Y > mapgen_limit_bp ||
1338 p.Z < -mapgen_limit_bp ||
1339 p.Z > mapgen_limit_bp;
1342 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1344 s16 csize = getMapgenParams()->chunksize;
1345 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1346 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1348 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1349 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1351 v3s16 extra_borders(1, 1, 1);
1352 v3s16 full_bpmin = bpmin - extra_borders;
1353 v3s16 full_bpmax = bpmax + extra_borders;
1355 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1356 if (blockpos_over_mapgen_limit(full_bpmin) ||
1357 blockpos_over_mapgen_limit(full_bpmax))
1360 data->seed = getSeed();
1361 data->blockpos_min = bpmin;
1362 data->blockpos_max = bpmax;
1363 data->blockpos_requested = blockpos;
1364 data->nodedef = m_nodedef;
1367 Create the whole area of this and the neighboring blocks
1369 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1370 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1371 v2s16 sectorpos(x, z);
1372 // Sector metadata is loaded from disk if not already loaded.
1373 MapSector *sector = createSector(sectorpos);
1374 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1376 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1379 MapBlock *block = emergeBlock(p, false);
1380 if (block == NULL) {
1381 block = createBlock(p);
1383 // Block gets sunlight if this is true.
1384 // Refer to the map generator heuristics.
1385 bool ug = m_emerge->isBlockUnderground(p);
1386 block->setIsUnderground(ug);
1392 Now we have a big empty area.
1394 Make a ManualMapVoxelManipulator that contains this and the
1398 data->vmanip = new MMVManip(this);
1399 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1401 // Note: we may need this again at some point.
1403 // Ensure none of the blocks to be generated were marked as
1404 // containing CONTENT_IGNORE
1405 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1406 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1407 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1408 core::map<v3s16, u8>::Node *n;
1409 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1412 u8 flags = n->getValue();
1413 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1420 // Data is ready now.
1424 void ServerMap::finishBlockMake(BlockMakeData *data,
1425 std::map<v3s16, MapBlock*> *changed_blocks)
1427 v3s16 bpmin = data->blockpos_min;
1428 v3s16 bpmax = data->blockpos_max;
1430 v3s16 extra_borders(1, 1, 1);
1432 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1433 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1436 Blit generated stuff to map
1437 NOTE: blitBackAll adds nearly everything to changed_blocks
1439 data->vmanip->blitBackAll(changed_blocks);
1441 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1442 << changed_blocks->size());
1445 Copy transforming liquid information
1447 while (data->transforming_liquid.size()) {
1448 m_transforming_liquid.push_back(data->transforming_liquid.front());
1449 data->transforming_liquid.pop_front();
1452 for (auto &changed_block : *changed_blocks) {
1453 MapBlock *block = changed_block.second;
1457 Update day/night difference cache of the MapBlocks
1459 block->expireDayNightDiff();
1461 Set block as modified
1463 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1464 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1468 Set central blocks as generated
1470 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1471 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1472 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1473 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1477 block->setGenerated(true);
1481 Save changed parts of map
1482 NOTE: Will be saved later.
1484 //save(MOD_STATE_WRITE_AT_UNLOAD);
1487 MapSector *ServerMap::createSector(v2s16 p2d)
1490 Check if it exists already in memory
1492 MapSector *sector = getSectorNoGenerate(p2d);
1497 Do not create over max mapgen limit
1499 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1500 if (p2d.X < -max_limit_bp ||
1501 p2d.X > max_limit_bp ||
1502 p2d.Y < -max_limit_bp ||
1503 p2d.Y > max_limit_bp)
1504 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1507 Generate blank sector
1510 sector = new MapSector(this, p2d, m_gamedef);
1512 // Sector position on map in nodes
1513 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1518 m_sectors[p2d] = sector;
1525 This is a quick-hand function for calling makeBlock().
1527 MapBlock * ServerMap::generateBlock(
1529 std::map<v3s16, MapBlock*> &modified_blocks
1532 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
1534 TimeTaker timer("generateBlock");
1536 //MapBlock *block = original_dummy;
1538 v2s16 p2d(p.X, p.Z);
1539 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
1542 Do not generate over-limit
1544 if(blockpos_over_limit(p))
1546 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
1547 throw InvalidPositionException("generateBlock(): pos. over limit");
1551 Create block make data
1554 initBlockMake(&data, p);
1560 TimeTaker t("mapgen::make_block()");
1561 mapgen->makeChunk(&data);
1562 //mapgen::make_block(&data);
1564 if(enable_mapgen_debug_info == false)
1565 t.stop(true); // Hide output
1569 Blit data back on map, update lighting, add mobs and whatever this does
1571 finishBlockMake(&data, modified_blocks);
1576 MapBlock *block = getBlockNoCreateNoEx(p);
1584 bool erroneus_content = false;
1585 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1586 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1587 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1590 MapNode n = block->getNode(p);
1591 if(n.getContent() == CONTENT_IGNORE)
1593 infostream<<"CONTENT_IGNORE at "
1594 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1596 erroneus_content = true;
1600 if(erroneus_content)
1609 Generate a completely empty block
1613 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1614 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1616 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1619 n.setContent(CONTENT_AIR);
1620 block->setNode(v3s16(x0,y0,z0), n);
1626 if(enable_mapgen_debug_info == false)
1627 timer.stop(true); // Hide output
1633 MapBlock * ServerMap::createBlock(v3s16 p)
1636 Do not create over max mapgen limit
1638 if (blockpos_over_max_limit(p))
1639 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1641 v2s16 p2d(p.X, p.Z);
1644 This will create or load a sector if not found in memory.
1645 If block exists on disk, it will be loaded.
1647 NOTE: On old save formats, this will be slow, as it generates
1648 lighting on blocks for them.
1652 sector = createSector(p2d);
1653 } catch (InvalidPositionException &e) {
1654 infostream<<"createBlock: createSector() failed"<<std::endl;
1659 Try to get a block from the sector
1662 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1664 if(block->isDummy())
1669 block = sector->createBlankBlock(block_y);
1674 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1677 MapBlock *block = getBlockNoCreateNoEx(p);
1678 if (block && !block->isDummy())
1683 MapBlock *block = loadBlock(p);
1689 MapSector *sector = createSector(v2s16(p.X, p.Z));
1690 MapBlock *block = sector->createBlankBlock(p.Y);
1698 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1700 MapBlock *block = getBlockNoCreateNoEx(p3d);
1702 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1707 // N.B. This requires no synchronization, since data will not be modified unless
1708 // the VoxelManipulator being updated belongs to the same thread.
1709 void ServerMap::updateVManip(v3s16 pos)
1711 Mapgen *mg = m_emerge->getCurrentMapgen();
1715 MMVManip *vm = mg->vm;
1719 if (!vm->m_area.contains(pos))
1722 s32 idx = vm->m_area.index(pos);
1723 vm->m_data[idx] = getNode(pos);
1724 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1726 vm->m_is_dirty = true;
1729 void ServerMap::save(ModifiedState save_level)
1731 if (!m_map_saving_enabled) {
1732 warningstream<<"Not saving map, saving disabled."<<std::endl;
1736 u64 start_time = porting::getTimeNs();
1738 if(save_level == MOD_STATE_CLEAN)
1739 infostream<<"ServerMap: Saving whole map, this can take time."
1742 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1743 if (settings_mgr.saveMapMeta())
1744 m_map_metadata_changed = false;
1747 // Profile modified reasons
1748 Profiler modprofiler;
1750 u32 block_count = 0;
1751 u32 block_count_all = 0; // Number of blocks in memory
1753 // Don't do anything with sqlite unless something is really saved
1754 bool save_started = false;
1756 for (auto §or_it : m_sectors) {
1757 MapSector *sector = sector_it.second;
1759 MapBlockVect blocks;
1760 sector->getBlocks(blocks);
1762 for (MapBlock *block : blocks) {
1765 if(block->getModified() >= (u32)save_level) {
1769 save_started = true;
1772 modprofiler.add(block->getModifiedReasonString(), 1);
1784 Only print if something happened or saved whole map
1786 if(save_level == MOD_STATE_CLEAN
1787 || block_count != 0) {
1788 infostream << "ServerMap: Written: "
1789 << block_count << " blocks"
1790 << ", " << block_count_all << " blocks in memory."
1792 PrintInfo(infostream); // ServerMap/ClientMap:
1793 infostream<<"Blocks modified by: "<<std::endl;
1794 modprofiler.print(infostream);
1797 auto end_time = porting::getTimeNs();
1798 m_save_time_counter->increment(end_time - start_time);
1801 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1803 dbase->listAllLoadableBlocks(dst);
1805 dbase_ro->listAllLoadableBlocks(dst);
1808 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1810 for (auto §or_it : m_sectors) {
1811 MapSector *sector = sector_it.second;
1813 MapBlockVect blocks;
1814 sector->getBlocks(blocks);
1816 for (MapBlock *block : blocks) {
1817 v3s16 p = block->getPos();
1823 MapDatabase *ServerMap::createDatabase(
1824 const std::string &name,
1825 const std::string &savedir,
1828 if (name == "sqlite3")
1829 return new MapDatabaseSQLite3(savedir);
1830 if (name == "dummy")
1831 return new Database_Dummy();
1833 if (name == "leveldb")
1834 return new Database_LevelDB(savedir);
1837 if (name == "redis")
1838 return new Database_Redis(conf);
1841 if (name == "postgresql") {
1842 std::string connect_string;
1843 conf.getNoEx("pgsql_connection", connect_string);
1844 return new MapDatabasePostgreSQL(connect_string);
1848 throw BaseException(std::string("Database backend ") + name + " not supported.");
1851 void ServerMap::beginSave()
1856 void ServerMap::endSave()
1861 bool ServerMap::saveBlock(MapBlock *block)
1863 return saveBlock(block, dbase);
1866 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
1868 v3s16 p3d = block->getPos();
1870 // Dummy blocks are not written
1871 if (block->isDummy()) {
1872 warningstream << "saveBlock: Not writing dummy block "
1873 << PP(p3d) << std::endl;
1877 // Format used for writing
1878 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1881 [0] u8 serialization version
1884 std::ostringstream o(std::ios_base::binary);
1885 o.write((char*) &version, 1);
1886 block->serialize(o, version, true);
1888 bool ret = db->saveBlock(p3d, o.str());
1890 // We just wrote it to the disk so clear modified flag
1891 block->resetModified();
1896 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1899 std::istringstream is(*blob, std::ios_base::binary);
1901 u8 version = SER_FMT_VER_INVALID;
1902 is.read((char*)&version, 1);
1905 throw SerializationError("ServerMap::loadBlock(): Failed"
1906 " to read MapBlock version");
1908 MapBlock *block = NULL;
1909 bool created_new = false;
1910 block = sector->getBlockNoCreateNoEx(p3d.Y);
1913 block = sector->createBlankBlockNoInsert(p3d.Y);
1918 block->deSerialize(is, version, true);
1920 // If it's a new block, insert it to the map
1922 sector->insertBlock(block);
1923 ReflowScan scanner(this, m_emerge->ndef);
1924 scanner.scan(block, &m_transforming_liquid);
1928 Save blocks loaded in old format in new format
1931 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1932 // Only save if asked to; no need to update version
1936 // We just loaded it from, so it's up-to-date.
1937 block->resetModified();
1939 catch(SerializationError &e)
1941 errorstream<<"Invalid block data in database"
1942 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1943 <<" (SerializationError): "<<e.what()<<std::endl;
1945 // TODO: Block should be marked as invalid in memory so that it is
1946 // not touched but the game can run
1948 if(g_settings->getBool("ignore_world_load_errors")){
1949 errorstream<<"Ignoring block load error. Duck and cover! "
1950 <<"(ignore_world_load_errors)"<<std::endl;
1952 throw SerializationError("Invalid block data in database");
1957 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1959 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1961 v2s16 p2d(blockpos.X, blockpos.Z);
1964 dbase->loadBlock(blockpos, &ret);
1966 loadBlock(&ret, blockpos, createSector(p2d), false);
1967 } else if (dbase_ro) {
1968 dbase_ro->loadBlock(blockpos, &ret);
1970 loadBlock(&ret, blockpos, createSector(p2d), false);
1976 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1977 if (created_new && (block != NULL)) {
1978 std::map<v3s16, MapBlock*> modified_blocks;
1979 // Fix lighting if necessary
1980 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1981 if (!modified_blocks.empty()) {
1982 //Modified lighting, send event
1984 event.type = MEET_OTHER;
1985 std::map<v3s16, MapBlock *>::iterator it;
1986 for (it = modified_blocks.begin();
1987 it != modified_blocks.end(); ++it)
1988 event.modified_blocks.insert(it->first);
1989 dispatchEvent(event);
1995 bool ServerMap::deleteBlock(v3s16 blockpos)
1997 if (!dbase->deleteBlock(blockpos))
2000 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2002 v2s16 p2d(blockpos.X, blockpos.Z);
2003 MapSector *sector = getSectorNoGenerate(p2d);
2006 sector->deleteBlock(block);
2012 void ServerMap::PrintInfo(std::ostream &out)
2017 bool ServerMap::repairBlockLight(v3s16 blockpos,
2018 std::map<v3s16, MapBlock *> *modified_blocks)
2020 MapBlock *block = emergeBlock(blockpos, false);
2021 if (!block || !block->isGenerated())
2023 voxalgo::repair_block_light(this, block, modified_blocks);
2027 MMVManip::MMVManip(Map *map):
2033 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2034 bool load_if_inexistent)
2036 TimeTaker timer1("initialEmerge", &emerge_time);
2038 // Units of these are MapBlocks
2039 v3s16 p_min = blockpos_min;
2040 v3s16 p_max = blockpos_max;
2042 VoxelArea block_area_nodes
2043 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2045 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2048 infostream<<"initialEmerge: area: ";
2049 block_area_nodes.print(infostream);
2050 infostream<<" ("<<size_MB<<"MB)";
2051 infostream<<std::endl;
2054 addArea(block_area_nodes);
2056 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2057 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2058 for(s32 x=p_min.X; x<=p_max.X; x++)
2063 std::map<v3s16, u8>::iterator n;
2064 n = m_loaded_blocks.find(p);
2065 if(n != m_loaded_blocks.end())
2068 bool block_data_inexistent = false;
2070 TimeTaker timer2("emerge load", &emerge_load_time);
2072 block = m_map->getBlockNoCreateNoEx(p);
2073 if (!block || block->isDummy())
2074 block_data_inexistent = true;
2076 block->copyTo(*this);
2079 if(block_data_inexistent)
2082 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2083 ServerMap *svrmap = (ServerMap *)m_map;
2084 block = svrmap->emergeBlock(p, false);
2086 block = svrmap->createBlock(p);
2087 block->copyTo(*this);
2089 flags |= VMANIP_BLOCK_DATA_INEXIST;
2092 Mark area inexistent
2094 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2095 // Fill with VOXELFLAG_NO_DATA
2096 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2097 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2099 s32 i = m_area.index(a.MinEdge.X,y,z);
2100 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2104 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2106 // Mark that block was loaded as blank
2107 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2110 m_loaded_blocks[p] = flags;
2116 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2117 bool overwrite_generated)
2119 if(m_area.getExtent() == v3s16(0,0,0))
2123 Copy data of all blocks
2125 for (auto &loaded_block : m_loaded_blocks) {
2126 v3s16 p = loaded_block.first;
2127 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2128 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2129 if (!existed || (block == NULL) ||
2130 (!overwrite_generated && block->isGenerated()))
2133 block->copyFrom(*this);
2134 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2137 (*modified_blocks)[p] = block;