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;
533 while (m_transforming_liquid.size() != 0)
535 // This should be done here so that it is done when continue is used
536 if (loopcount >= initial_size || loopcount >= loop_max)
541 Get a queued transforming liquid node
543 v3s16 p0 = m_transforming_liquid.front();
544 m_transforming_liquid.pop_front();
546 MapNode n0 = getNode(p0);
549 Collect information about current node
551 s8 liquid_level = -1;
552 // The liquid node which will be placed there if
553 // the liquid flows into this node.
554 content_t liquid_kind = CONTENT_IGNORE;
555 // The node which will be placed there if liquid
556 // can't flow into this node.
557 content_t floodable_node = CONTENT_AIR;
558 const ContentFeatures &cf = m_nodedef->get(n0);
559 LiquidType liquid_type = cf.liquid_type;
560 switch (liquid_type) {
562 liquid_level = LIQUID_LEVEL_SOURCE;
563 liquid_kind = cf.liquid_alternative_flowing_id;
566 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
567 liquid_kind = n0.getContent();
570 // if this node is 'floodable', it *could* be transformed
571 // into a liquid, otherwise, continue with the next node.
574 floodable_node = n0.getContent();
575 liquid_kind = CONTENT_AIR;
580 Collect information about the environment
582 NodeNeighbor sources[6]; // surrounding sources
584 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
586 NodeNeighbor airs[6]; // surrounding air
588 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
589 int num_neutrals = 0;
590 bool flowing_down = false;
591 bool ignored_sources = false;
592 for (u16 i = 0; i < 6; i++) {
593 NeighborType nt = NEIGHBOR_SAME_LEVEL;
604 v3s16 npos = p0 + liquid_6dirs[i];
605 NodeNeighbor nb(getNode(npos), nt, npos);
606 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
607 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
609 if (cfnb.floodable) {
610 airs[num_airs++] = nb;
611 // if the current node is a water source the neighbor
612 // should be enqueded for transformation regardless of whether the
613 // current node changes or not.
614 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
615 m_transforming_liquid.push_back(npos);
616 // if the current node happens to be a flowing node, it will start to flow down here.
617 if (nb.t == NEIGHBOR_LOWER)
620 neutrals[num_neutrals++] = nb;
621 if (nb.n.getContent() == CONTENT_IGNORE) {
622 // If node below is ignore prevent water from
623 // spreading outwards and otherwise prevent from
624 // flowing away as ignore node might be the source
625 if (nb.t == NEIGHBOR_LOWER)
628 ignored_sources = true;
633 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
634 if (liquid_kind == CONTENT_AIR)
635 liquid_kind = cfnb.liquid_alternative_flowing_id;
636 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
637 neutrals[num_neutrals++] = nb;
639 // Do not count bottom source, it will screw things up
640 if(nt != NEIGHBOR_LOWER)
641 sources[num_sources++] = nb;
645 if (nb.t != NEIGHBOR_SAME_LEVEL ||
646 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
647 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
648 // but exclude falling liquids on the same level, they cannot flow here anyway
649 if (liquid_kind == CONTENT_AIR)
650 liquid_kind = cfnb.liquid_alternative_flowing_id;
652 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
653 neutrals[num_neutrals++] = nb;
655 flows[num_flows++] = nb;
656 if (nb.t == NEIGHBOR_LOWER)
664 decide on the type (and possibly level) of the current node
666 content_t new_node_content;
667 s8 new_node_level = -1;
668 s8 max_node_level = -1;
670 u8 range = m_nodedef->get(liquid_kind).liquid_range;
671 if (range > LIQUID_LEVEL_MAX + 1)
672 range = LIQUID_LEVEL_MAX + 1;
674 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
675 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
676 // or the flowing alternative of the first of the surrounding sources (if it's air), so
677 // it's perfectly safe to use liquid_kind here to determine the new node content.
678 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
679 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
680 // liquid_kind is set properly, see above
681 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
682 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
683 new_node_content = liquid_kind;
685 new_node_content = floodable_node;
686 } else if (ignored_sources && liquid_level >= 0) {
687 // Maybe there are neighbouring sources that aren't loaded yet
688 // so prevent flowing away.
689 new_node_level = liquid_level;
690 new_node_content = liquid_kind;
692 // no surrounding sources, so get the maximum level that can flow into this node
693 for (u16 i = 0; i < num_flows; i++) {
694 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
695 switch (flows[i].t) {
697 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
698 max_node_level = LIQUID_LEVEL_MAX;
699 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
700 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
701 } else if (nb_liquid_level > max_node_level) {
702 max_node_level = nb_liquid_level;
707 case NEIGHBOR_SAME_LEVEL:
708 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
709 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
710 max_node_level = nb_liquid_level - 1;
715 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
716 if (viscosity > 1 && max_node_level != liquid_level) {
717 // amount to gain, limited by viscosity
718 // must be at least 1 in absolute value
719 s8 level_inc = max_node_level - liquid_level;
720 if (level_inc < -viscosity || level_inc > viscosity)
721 new_node_level = liquid_level + level_inc/viscosity;
722 else if (level_inc < 0)
723 new_node_level = liquid_level - 1;
724 else if (level_inc > 0)
725 new_node_level = liquid_level + 1;
726 if (new_node_level != max_node_level)
727 must_reflow.push_back(p0);
729 new_node_level = max_node_level;
732 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
733 new_node_content = liquid_kind;
735 new_node_content = floodable_node;
740 check if anything has changed. if not, just continue with the next node.
742 if (new_node_content == n0.getContent() &&
743 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
744 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
745 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
751 update the current node
754 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
755 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
756 // set level to last 3 bits, flowing down bit to 4th bit
757 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
759 // set the liquid level and flow bits to 0
760 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
764 n0.setContent(new_node_content);
766 // on_flood() the node
767 if (floodable_node != CONTENT_AIR) {
768 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
772 // Ignore light (because calling voxalgo::update_lighting_nodes)
773 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
774 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
776 // Find out whether there is a suspect for this action
778 if (m_gamedef->rollback())
779 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
781 if (m_gamedef->rollback() && !suspect.empty()) {
783 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
784 // Get old node for rollback
785 RollbackNode rollback_oldnode(this, p0, m_gamedef);
789 RollbackNode rollback_newnode(this, p0, m_gamedef);
790 RollbackAction action;
791 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
792 m_gamedef->rollback()->reportAction(action);
798 v3s16 blockpos = getNodeBlockPos(p0);
799 MapBlock *block = getBlockNoCreateNoEx(blockpos);
801 modified_blocks[blockpos] = block;
802 changed_nodes.emplace_back(p0, n00);
806 enqueue neighbors for update if neccessary
808 switch (m_nodedef->get(n0.getContent()).liquid_type) {
811 // make sure source flows into all neighboring nodes
812 for (u16 i = 0; i < num_flows; i++)
813 if (flows[i].t != NEIGHBOR_UPPER)
814 m_transforming_liquid.push_back(flows[i].p);
815 for (u16 i = 0; i < num_airs; i++)
816 if (airs[i].t != NEIGHBOR_UPPER)
817 m_transforming_liquid.push_back(airs[i].p);
820 // this flow has turned to air; neighboring flows might need to do the same
821 for (u16 i = 0; i < num_flows; i++)
822 m_transforming_liquid.push_back(flows[i].p);
826 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
828 for (auto &iter : must_reflow)
829 m_transforming_liquid.push_back(iter);
831 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
834 /* ----------------------------------------------------------------------
835 * Manage the queue so that it does not grow indefinately
837 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
839 if (time_until_purge == 0)
840 return; // Feature disabled
842 time_until_purge *= 1000; // seconds -> milliseconds
844 u64 curr_time = porting::getTimeMs();
845 u32 prev_unprocessed = m_unprocessed_count;
846 m_unprocessed_count = m_transforming_liquid.size();
848 // if unprocessed block count is decreasing or stable
849 if (m_unprocessed_count <= prev_unprocessed) {
850 m_queue_size_timer_started = false;
852 if (!m_queue_size_timer_started)
853 m_inc_trending_up_start_time = curr_time;
854 m_queue_size_timer_started = true;
857 // Account for curr_time overflowing
858 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
859 m_queue_size_timer_started = false;
861 /* If the queue has been growing for more than liquid_queue_purge_time seconds
862 * and the number of unprocessed blocks is still > liquid_loop_max then we
863 * cannot keep up; dump the oldest blocks from the queue so that the queue
864 * has liquid_loop_max items in it
866 if (m_queue_size_timer_started
867 && curr_time - m_inc_trending_up_start_time > time_until_purge
868 && m_unprocessed_count > liquid_loop_max) {
870 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
872 infostream << "transformLiquids(): DUMPING " << dump_qty
873 << " blocks from the queue" << std::endl;
876 m_transforming_liquid.pop_front();
878 m_queue_size_timer_started = false; // optimistically assume we can keep up now
879 m_unprocessed_count = m_transforming_liquid.size();
883 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
885 std::vector<v3s16> positions_with_meta;
887 sortBoxVerticies(p1, p2);
888 v3s16 bpmin = getNodeBlockPos(p1);
889 v3s16 bpmax = getNodeBlockPos(p2);
891 VoxelArea area(p1, p2);
893 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
894 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
895 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
896 v3s16 blockpos(x, y, z);
898 MapBlock *block = getBlockNoCreateNoEx(blockpos);
900 verbosestream << "Map::getNodeMetadata(): Need to emerge "
901 << PP(blockpos) << std::endl;
902 block = emergeBlock(blockpos, false);
905 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
910 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
911 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
912 for (size_t i = 0; i != keys.size(); i++) {
913 v3s16 p(keys[i] + p_base);
914 if (!area.contains(p))
917 positions_with_meta.push_back(p);
921 return positions_with_meta;
924 NodeMetadata *Map::getNodeMetadata(v3s16 p)
926 v3s16 blockpos = getNodeBlockPos(p);
927 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
928 MapBlock *block = getBlockNoCreateNoEx(blockpos);
930 infostream<<"Map::getNodeMetadata(): Need to emerge "
931 <<PP(blockpos)<<std::endl;
932 block = emergeBlock(blockpos, false);
935 warningstream<<"Map::getNodeMetadata(): Block not found"
939 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
943 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
945 v3s16 blockpos = getNodeBlockPos(p);
946 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
947 MapBlock *block = getBlockNoCreateNoEx(blockpos);
949 infostream<<"Map::setNodeMetadata(): Need to emerge "
950 <<PP(blockpos)<<std::endl;
951 block = emergeBlock(blockpos, false);
954 warningstream<<"Map::setNodeMetadata(): Block not found"
958 block->m_node_metadata.set(p_rel, meta);
962 void Map::removeNodeMetadata(v3s16 p)
964 v3s16 blockpos = getNodeBlockPos(p);
965 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
966 MapBlock *block = getBlockNoCreateNoEx(blockpos);
969 warningstream<<"Map::removeNodeMetadata(): Block not found"
973 block->m_node_metadata.remove(p_rel);
976 NodeTimer Map::getNodeTimer(v3s16 p)
978 v3s16 blockpos = getNodeBlockPos(p);
979 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
980 MapBlock *block = getBlockNoCreateNoEx(blockpos);
982 infostream<<"Map::getNodeTimer(): Need to emerge "
983 <<PP(blockpos)<<std::endl;
984 block = emergeBlock(blockpos, false);
987 warningstream<<"Map::getNodeTimer(): Block not found"
991 NodeTimer t = block->m_node_timers.get(p_rel);
992 NodeTimer nt(t.timeout, t.elapsed, p);
996 void Map::setNodeTimer(const NodeTimer &t)
998 v3s16 p = t.position;
999 v3s16 blockpos = getNodeBlockPos(p);
1000 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1001 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1003 infostream<<"Map::setNodeTimer(): Need to emerge "
1004 <<PP(blockpos)<<std::endl;
1005 block = emergeBlock(blockpos, false);
1008 warningstream<<"Map::setNodeTimer(): Block not found"
1012 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1013 block->m_node_timers.set(nt);
1016 void Map::removeNodeTimer(v3s16 p)
1018 v3s16 blockpos = getNodeBlockPos(p);
1019 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1020 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1023 warningstream<<"Map::removeNodeTimer(): Block not found"
1027 block->m_node_timers.remove(p_rel);
1030 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1031 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1034 This functions determines the node inside the target block that is
1035 closest to the camera position. This increases the occlusion culling
1036 accuracy in straight and diagonal corridors.
1037 The returned position will be occlusion checked first in addition to the
1038 others (8 corners + center).
1039 No position is returned if
1040 - the closest node is a corner, corners are checked anyway.
1041 - the camera is inside the target block, it will never be occluded.
1043 #define CLOSEST_EDGE(pos, bounds, axis) \
1044 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1045 (bounds).MaxEdge.axis
1047 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1048 (pos_camera.X <= block_bounds.MaxEdge.X);
1049 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1050 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1051 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1052 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1054 if (x_inside && y_inside && z_inside)
1055 return false; // Camera inside target mapblock
1058 if (x_inside && y_inside) {
1059 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1060 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1062 } else if (y_inside && z_inside) {
1063 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1064 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1066 } else if (x_inside && z_inside) {
1067 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1068 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1074 check = v3s16(pos_camera.X, 0, 0);
1075 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1076 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1078 } else if (y_inside) {
1079 check = v3s16(0, pos_camera.Y, 0);
1080 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1081 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1083 } else if (z_inside) {
1084 check = v3s16(0, 0, pos_camera.Z);
1085 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1086 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1090 // Closest node would be a corner, none returned
1094 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1095 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1097 v3f direction = intToFloat(pos_target - pos_camera, BS);
1098 float distance = direction.getLength();
1100 // Normalize direction vector
1101 if (distance > 0.0f)
1102 direction /= distance;
1104 v3f pos_origin_f = intToFloat(pos_camera, BS);
1106 bool is_valid_position;
1108 for (; offset < distance + end_offset; offset += step) {
1109 v3f pos_node_f = pos_origin_f + direction * offset;
1110 v3s16 pos_node = floatToInt(pos_node_f, BS);
1112 MapNode node = getNode(pos_node, &is_valid_position);
1114 if (is_valid_position &&
1115 !m_nodedef->get(node).light_propagates) {
1116 // Cannot see through light-blocking nodes --> occluded
1118 if (count >= needed_count)
1126 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1128 // Check occlusion for center and all 8 corners of the mapblock
1129 // Overshoot a little for less flickering
1130 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1131 static const v3s16 dir9[9] = {
1133 v3s16( 1, 1, 1) * bs2,
1134 v3s16( 1, 1, -1) * bs2,
1135 v3s16( 1, -1, 1) * bs2,
1136 v3s16( 1, -1, -1) * bs2,
1137 v3s16(-1, 1, 1) * bs2,
1138 v3s16(-1, 1, -1) * bs2,
1139 v3s16(-1, -1, 1) * bs2,
1140 v3s16(-1, -1, -1) * bs2,
1143 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1145 // Starting step size, value between 1m and sqrt(3)m
1146 float step = BS * 1.2f;
1147 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1148 float stepfac = 1.05f;
1150 float start_offset = BS * 1.0f;
1152 // The occlusion search of 'isOccluded()' must stop short of the target
1153 // point by distance 'end_offset' to not enter the target mapblock.
1154 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1155 // diagonal of a mapblock, because we must consider all view angles.
1156 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1157 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1159 // to reduce the likelihood of falsely occluded blocks
1160 // require at least two solid blocks
1161 // this is a HACK, we should think of a more precise algorithm
1162 u32 needed_count = 2;
1164 // Additional occlusion check, see comments in that function
1166 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1167 // node is always on a side facing the camera, end_offset can be lower
1168 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1169 -1.0f, needed_count))
1173 for (const v3s16 &dir : dir9) {
1174 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1175 start_offset, end_offset, needed_count))
1184 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1185 EmergeManager *emerge, MetricsBackend *mb):
1187 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1190 verbosestream<<FUNCTION_NAME<<std::endl;
1192 // Tell the EmergeManager about our MapSettingsManager
1193 emerge->map_settings_mgr = &settings_mgr;
1196 Try to load map; if not found, create a new one.
1199 // Determine which database backend to use
1200 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1202 bool succeeded = conf.readConfigFile(conf_path.c_str());
1203 if (!succeeded || !conf.exists("backend")) {
1204 // fall back to sqlite3
1205 conf.set("backend", "sqlite3");
1207 std::string backend = conf.get("backend");
1208 dbase = createDatabase(backend, savedir, conf);
1209 if (conf.exists("readonly_backend")) {
1210 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1211 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1213 if (!conf.updateConfigFile(conf_path.c_str()))
1214 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1216 m_savedir = savedir;
1217 m_map_saving_enabled = false;
1219 m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1221 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1224 // If directory exists, check contents and load if possible
1225 if (fs::PathExists(m_savedir)) {
1226 // If directory is empty, it is safe to save into it.
1227 if (fs::GetDirListing(m_savedir).empty()) {
1228 infostream<<"ServerMap: Empty save directory is valid."
1230 m_map_saving_enabled = true;
1235 if (settings_mgr.loadMapMeta()) {
1236 infostream << "ServerMap: Metadata loaded from "
1237 << savedir << std::endl;
1239 infostream << "ServerMap: Metadata could not be loaded "
1240 "from " << savedir << ", assuming valid save "
1241 "directory." << std::endl;
1244 m_map_saving_enabled = true;
1245 // Map loaded, not creating new one
1249 // If directory doesn't exist, it is safe to save to it
1251 m_map_saving_enabled = true;
1254 catch(std::exception &e)
1256 warningstream<<"ServerMap: Failed to load map from "<<savedir
1257 <<", exception: "<<e.what()<<std::endl;
1258 infostream<<"Please remove the map or fix it."<<std::endl;
1259 warningstream<<"Map saving will be disabled."<<std::endl;
1263 ServerMap::~ServerMap()
1265 verbosestream<<FUNCTION_NAME<<std::endl;
1269 if (m_map_saving_enabled) {
1270 // Save only changed parts
1271 save(MOD_STATE_WRITE_AT_UNLOAD);
1272 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1274 infostream << "ServerMap: Map not saved" << std::endl;
1277 catch(std::exception &e)
1279 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1280 <<", exception: "<<e.what()<<std::endl;
1284 Close database if it was opened
1290 MapgenParams *ServerMap::getMapgenParams()
1292 // getMapgenParams() should only ever be called after Server is initialized
1293 assert(settings_mgr.mapgen_params != NULL);
1294 return settings_mgr.mapgen_params;
1297 u64 ServerMap::getSeed()
1299 return getMapgenParams()->seed;
1302 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1304 const s16 mapgen_limit_bp = rangelim(
1305 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1307 return p.X < -mapgen_limit_bp ||
1308 p.X > mapgen_limit_bp ||
1309 p.Y < -mapgen_limit_bp ||
1310 p.Y > mapgen_limit_bp ||
1311 p.Z < -mapgen_limit_bp ||
1312 p.Z > mapgen_limit_bp;
1315 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1317 s16 csize = getMapgenParams()->chunksize;
1318 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1319 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1321 if (!m_chunks_in_progress.insert(bpmin).second)
1324 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1325 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1327 v3s16 extra_borders(1, 1, 1);
1328 v3s16 full_bpmin = bpmin - extra_borders;
1329 v3s16 full_bpmax = bpmax + extra_borders;
1331 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1332 if (blockpos_over_mapgen_limit(full_bpmin) ||
1333 blockpos_over_mapgen_limit(full_bpmax))
1336 data->seed = getSeed();
1337 data->blockpos_min = bpmin;
1338 data->blockpos_max = bpmax;
1339 data->nodedef = m_nodedef;
1342 Create the whole area of this and the neighboring blocks
1344 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1345 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1346 v2s16 sectorpos(x, z);
1347 // Sector metadata is loaded from disk if not already loaded.
1348 MapSector *sector = createSector(sectorpos);
1349 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1351 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1354 MapBlock *block = emergeBlock(p, false);
1355 if (block == NULL) {
1356 block = createBlock(p);
1358 // Block gets sunlight if this is true.
1359 // Refer to the map generator heuristics.
1360 bool ug = m_emerge->isBlockUnderground(p);
1361 block->setIsUnderground(ug);
1367 Now we have a big empty area.
1369 Make a ManualMapVoxelManipulator that contains this and the
1373 data->vmanip = new MMVManip(this);
1374 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1376 // Data is ready now.
1380 void ServerMap::finishBlockMake(BlockMakeData *data,
1381 std::map<v3s16, MapBlock*> *changed_blocks)
1383 v3s16 bpmin = data->blockpos_min;
1384 v3s16 bpmax = data->blockpos_max;
1386 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1387 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1390 Blit generated stuff to map
1391 NOTE: blitBackAll adds nearly everything to changed_blocks
1393 data->vmanip->blitBackAll(changed_blocks);
1395 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1396 << changed_blocks->size());
1399 Copy transforming liquid information
1401 while (data->transforming_liquid.size()) {
1402 m_transforming_liquid.push_back(data->transforming_liquid.front());
1403 data->transforming_liquid.pop_front();
1406 for (auto &changed_block : *changed_blocks) {
1407 MapBlock *block = changed_block.second;
1411 Update day/night difference cache of the MapBlocks
1413 block->expireDayNightDiff();
1415 Set block as modified
1417 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1418 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1422 Set central blocks as generated
1424 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1425 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1426 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1427 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1431 block->setGenerated(true);
1435 Save changed parts of map
1436 NOTE: Will be saved later.
1438 //save(MOD_STATE_WRITE_AT_UNLOAD);
1439 m_chunks_in_progress.erase(bpmin);
1442 MapSector *ServerMap::createSector(v2s16 p2d)
1445 Check if it exists already in memory
1447 MapSector *sector = getSectorNoGenerate(p2d);
1452 Do not create over max mapgen limit
1454 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1455 if (p2d.X < -max_limit_bp ||
1456 p2d.X > max_limit_bp ||
1457 p2d.Y < -max_limit_bp ||
1458 p2d.Y > max_limit_bp)
1459 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1462 Generate blank sector
1465 sector = new MapSector(this, p2d, m_gamedef);
1467 // Sector position on map in nodes
1468 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1473 m_sectors[p2d] = sector;
1478 MapBlock * ServerMap::createBlock(v3s16 p)
1481 Do not create over max mapgen limit
1483 if (blockpos_over_max_limit(p))
1484 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1486 v2s16 p2d(p.X, p.Z);
1489 This will create or load a sector if not found in memory.
1490 If block exists on disk, it will be loaded.
1492 NOTE: On old save formats, this will be slow, as it generates
1493 lighting on blocks for them.
1497 sector = createSector(p2d);
1498 } catch (InvalidPositionException &e) {
1499 infostream<<"createBlock: createSector() failed"<<std::endl;
1504 Try to get a block from the sector
1507 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1509 if(block->isDummy())
1514 block = sector->createBlankBlock(block_y);
1519 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1522 MapBlock *block = getBlockNoCreateNoEx(p);
1523 if (block && !block->isDummy())
1528 MapBlock *block = loadBlock(p);
1534 MapSector *sector = createSector(v2s16(p.X, p.Z));
1535 MapBlock *block = sector->createBlankBlock(p.Y);
1543 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1545 MapBlock *block = getBlockNoCreateNoEx(p3d);
1547 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1552 // N.B. This requires no synchronization, since data will not be modified unless
1553 // the VoxelManipulator being updated belongs to the same thread.
1554 void ServerMap::updateVManip(v3s16 pos)
1556 Mapgen *mg = m_emerge->getCurrentMapgen();
1560 MMVManip *vm = mg->vm;
1564 if (!vm->m_area.contains(pos))
1567 s32 idx = vm->m_area.index(pos);
1568 vm->m_data[idx] = getNode(pos);
1569 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1571 vm->m_is_dirty = true;
1574 void ServerMap::save(ModifiedState save_level)
1576 if (!m_map_saving_enabled) {
1577 warningstream<<"Not saving map, saving disabled."<<std::endl;
1581 u64 start_time = porting::getTimeNs();
1583 if(save_level == MOD_STATE_CLEAN)
1584 infostream<<"ServerMap: Saving whole map, this can take time."
1587 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1588 if (settings_mgr.saveMapMeta())
1589 m_map_metadata_changed = false;
1592 // Profile modified reasons
1593 Profiler modprofiler;
1595 u32 block_count = 0;
1596 u32 block_count_all = 0; // Number of blocks in memory
1598 // Don't do anything with sqlite unless something is really saved
1599 bool save_started = false;
1601 for (auto §or_it : m_sectors) {
1602 MapSector *sector = sector_it.second;
1604 MapBlockVect blocks;
1605 sector->getBlocks(blocks);
1607 for (MapBlock *block : blocks) {
1610 if(block->getModified() >= (u32)save_level) {
1614 save_started = true;
1617 modprofiler.add(block->getModifiedReasonString(), 1);
1629 Only print if something happened or saved whole map
1631 if(save_level == MOD_STATE_CLEAN
1632 || block_count != 0) {
1633 infostream << "ServerMap: Written: "
1634 << block_count << " blocks"
1635 << ", " << block_count_all << " blocks in memory."
1637 PrintInfo(infostream); // ServerMap/ClientMap:
1638 infostream<<"Blocks modified by: "<<std::endl;
1639 modprofiler.print(infostream);
1642 auto end_time = porting::getTimeNs();
1643 m_save_time_counter->increment(end_time - start_time);
1646 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1648 dbase->listAllLoadableBlocks(dst);
1650 dbase_ro->listAllLoadableBlocks(dst);
1653 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1655 for (auto §or_it : m_sectors) {
1656 MapSector *sector = sector_it.second;
1658 MapBlockVect blocks;
1659 sector->getBlocks(blocks);
1661 for (MapBlock *block : blocks) {
1662 v3s16 p = block->getPos();
1668 MapDatabase *ServerMap::createDatabase(
1669 const std::string &name,
1670 const std::string &savedir,
1673 if (name == "sqlite3")
1674 return new MapDatabaseSQLite3(savedir);
1675 if (name == "dummy")
1676 return new Database_Dummy();
1678 if (name == "leveldb")
1679 return new Database_LevelDB(savedir);
1682 if (name == "redis")
1683 return new Database_Redis(conf);
1686 if (name == "postgresql") {
1687 std::string connect_string;
1688 conf.getNoEx("pgsql_connection", connect_string);
1689 return new MapDatabasePostgreSQL(connect_string);
1693 throw BaseException(std::string("Database backend ") + name + " not supported.");
1696 void ServerMap::beginSave()
1701 void ServerMap::endSave()
1706 bool ServerMap::saveBlock(MapBlock *block)
1708 return saveBlock(block, dbase, m_map_compression_level);
1711 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1713 v3s16 p3d = block->getPos();
1715 // Dummy blocks are not written
1716 if (block->isDummy()) {
1717 warningstream << "saveBlock: Not writing dummy block "
1718 << PP(p3d) << std::endl;
1722 // Format used for writing
1723 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1726 [0] u8 serialization version
1729 std::ostringstream o(std::ios_base::binary);
1730 o.write((char*) &version, 1);
1731 block->serialize(o, version, true, compression_level);
1733 bool ret = db->saveBlock(p3d, o.str());
1735 // We just wrote it to the disk so clear modified flag
1736 block->resetModified();
1741 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1744 std::istringstream is(*blob, std::ios_base::binary);
1746 u8 version = SER_FMT_VER_INVALID;
1747 is.read((char*)&version, 1);
1750 throw SerializationError("ServerMap::loadBlock(): Failed"
1751 " to read MapBlock version");
1753 MapBlock *block = NULL;
1754 bool created_new = false;
1755 block = sector->getBlockNoCreateNoEx(p3d.Y);
1758 block = sector->createBlankBlockNoInsert(p3d.Y);
1763 block->deSerialize(is, version, true);
1765 // If it's a new block, insert it to the map
1767 sector->insertBlock(block);
1768 ReflowScan scanner(this, m_emerge->ndef);
1769 scanner.scan(block, &m_transforming_liquid);
1773 Save blocks loaded in old format in new format
1776 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1777 // Only save if asked to; no need to update version
1781 // We just loaded it from, so it's up-to-date.
1782 block->resetModified();
1784 catch(SerializationError &e)
1786 errorstream<<"Invalid block data in database"
1787 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1788 <<" (SerializationError): "<<e.what()<<std::endl;
1790 // TODO: Block should be marked as invalid in memory so that it is
1791 // not touched but the game can run
1793 if(g_settings->getBool("ignore_world_load_errors")){
1794 errorstream<<"Ignoring block load error. Duck and cover! "
1795 <<"(ignore_world_load_errors)"<<std::endl;
1797 throw SerializationError("Invalid block data in database");
1802 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1804 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1806 v2s16 p2d(blockpos.X, blockpos.Z);
1809 dbase->loadBlock(blockpos, &ret);
1811 loadBlock(&ret, blockpos, createSector(p2d), false);
1812 } else if (dbase_ro) {
1813 dbase_ro->loadBlock(blockpos, &ret);
1815 loadBlock(&ret, blockpos, createSector(p2d), false);
1821 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1822 if (created_new && (block != NULL)) {
1823 std::map<v3s16, MapBlock*> modified_blocks;
1824 // Fix lighting if necessary
1825 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1826 if (!modified_blocks.empty()) {
1827 //Modified lighting, send event
1829 event.type = MEET_OTHER;
1830 std::map<v3s16, MapBlock *>::iterator it;
1831 for (it = modified_blocks.begin();
1832 it != modified_blocks.end(); ++it)
1833 event.modified_blocks.insert(it->first);
1834 dispatchEvent(event);
1840 bool ServerMap::deleteBlock(v3s16 blockpos)
1842 if (!dbase->deleteBlock(blockpos))
1845 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1847 v2s16 p2d(blockpos.X, blockpos.Z);
1848 MapSector *sector = getSectorNoGenerate(p2d);
1851 sector->deleteBlock(block);
1857 void ServerMap::PrintInfo(std::ostream &out)
1862 bool ServerMap::repairBlockLight(v3s16 blockpos,
1863 std::map<v3s16, MapBlock *> *modified_blocks)
1865 MapBlock *block = emergeBlock(blockpos, false);
1866 if (!block || !block->isGenerated())
1868 voxalgo::repair_block_light(this, block, modified_blocks);
1872 MMVManip::MMVManip(Map *map):
1878 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1879 bool load_if_inexistent)
1881 TimeTaker timer1("initialEmerge", &emerge_time);
1883 // Units of these are MapBlocks
1884 v3s16 p_min = blockpos_min;
1885 v3s16 p_max = blockpos_max;
1887 VoxelArea block_area_nodes
1888 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1890 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1893 infostream<<"initialEmerge: area: ";
1894 block_area_nodes.print(infostream);
1895 infostream<<" ("<<size_MB<<"MB)";
1896 infostream<<std::endl;
1899 addArea(block_area_nodes);
1901 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1902 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1903 for(s32 x=p_min.X; x<=p_max.X; x++)
1908 std::map<v3s16, u8>::iterator n;
1909 n = m_loaded_blocks.find(p);
1910 if(n != m_loaded_blocks.end())
1913 bool block_data_inexistent = false;
1915 TimeTaker timer2("emerge load", &emerge_load_time);
1917 block = m_map->getBlockNoCreateNoEx(p);
1918 if (!block || block->isDummy())
1919 block_data_inexistent = true;
1921 block->copyTo(*this);
1924 if(block_data_inexistent)
1927 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1928 ServerMap *svrmap = (ServerMap *)m_map;
1929 block = svrmap->emergeBlock(p, false);
1931 block = svrmap->createBlock(p);
1932 block->copyTo(*this);
1934 flags |= VMANIP_BLOCK_DATA_INEXIST;
1937 Mark area inexistent
1939 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1940 // Fill with VOXELFLAG_NO_DATA
1941 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1942 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1944 s32 i = m_area.index(a.MinEdge.X,y,z);
1945 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1949 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1951 // Mark that block was loaded as blank
1952 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
1955 m_loaded_blocks[p] = flags;
1961 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
1962 bool overwrite_generated)
1964 if(m_area.getExtent() == v3s16(0,0,0))
1968 Copy data of all blocks
1970 for (auto &loaded_block : m_loaded_blocks) {
1971 v3s16 p = loaded_block.first;
1972 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1973 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
1974 if (!existed || (block == NULL) ||
1975 (!overwrite_generated && block->isGenerated()))
1978 block->copyFrom(*this);
1979 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
1982 (*modified_blocks)[p] = block;