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 void Map::listAllLoadedBlocks(std::vector<v3s16> &dst)
144 for (auto §or_it : m_sectors) {
145 MapSector *sector = sector_it.second;
148 sector->getBlocks(blocks);
150 for (MapBlock *block : blocks) {
151 v3s16 p = block->getPos();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
160 MapBlock *block = getBlockNoCreateNoEx(blockpos);
161 return block && block->getIsUnderground();
164 bool Map::isValidPosition(v3s16 p)
166 v3s16 blockpos = getNodeBlockPos(p);
167 MapBlock *block = getBlockNoCreateNoEx(blockpos);
168 return (block != NULL);
171 // Returns a CONTENT_IGNORE node if not found
172 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
174 v3s16 blockpos = getNodeBlockPos(p);
175 MapBlock *block = getBlockNoCreateNoEx(blockpos);
177 if (is_valid_position != NULL)
178 *is_valid_position = false;
179 return {CONTENT_IGNORE};
182 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
184 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
185 if (is_valid_position != NULL)
186 *is_valid_position = is_valid_p;
190 // throws InvalidPositionException if not found
191 void Map::setNode(v3s16 p, MapNode & n)
193 v3s16 blockpos = getNodeBlockPos(p);
194 MapBlock *block = getBlockNoCreate(blockpos);
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 // Never allow placing CONTENT_IGNORE, it causes problems
197 if(n.getContent() == CONTENT_IGNORE){
199 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
200 <<" while trying to replace \""
201 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
202 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
205 block->setNodeNoCheck(relpos, n);
208 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
209 std::map<v3s16, MapBlock*> &modified_blocks,
210 bool remove_metadata)
212 // Collect old node for rollback
213 RollbackNode rollback_oldnode(this, p, m_gamedef);
215 // This is needed for updating the lighting
216 MapNode oldnode = getNode(p);
218 // Remove node metadata
219 if (remove_metadata) {
220 removeNodeMetadata(p);
223 // Set the node on the map
224 // Ignore light (because calling voxalgo::update_lighting_nodes)
225 n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
226 n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
230 std::vector<std::pair<v3s16, MapNode> > oldnodes;
231 oldnodes.emplace_back(p, oldnode);
232 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
234 for (auto &modified_block : modified_blocks) {
235 modified_block.second->expireDayNightDiff();
238 // Report for rollback
239 if(m_gamedef->rollback())
241 RollbackNode rollback_newnode(this, p, m_gamedef);
242 RollbackAction action;
243 action.setSetNode(p, rollback_oldnode, rollback_newnode);
244 m_gamedef->rollback()->reportAction(action);
248 Add neighboring liquid nodes and this node to transform queue.
249 (it's vital for the node itself to get updated last, if it was removed.)
252 for (const v3s16 &dir : g_7dirs) {
255 bool is_valid_position;
256 MapNode n2 = getNode(p2, &is_valid_position);
257 if(is_valid_position &&
258 (m_nodedef->get(n2).isLiquid() ||
259 n2.getContent() == CONTENT_AIR))
260 m_transforming_liquid.push_back(p2);
264 void Map::removeNodeAndUpdate(v3s16 p,
265 std::map<v3s16, MapBlock*> &modified_blocks)
267 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
270 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
273 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
277 bool succeeded = true;
279 std::map<v3s16, MapBlock*> modified_blocks;
280 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
282 // Copy modified_blocks to event
283 for (auto &modified_block : modified_blocks) {
284 event.modified_blocks.insert(modified_block.first);
287 catch(InvalidPositionException &e){
291 dispatchEvent(event);
296 bool Map::removeNodeWithEvent(v3s16 p)
299 event.type = MEET_REMOVENODE;
302 bool succeeded = true;
304 std::map<v3s16, MapBlock*> modified_blocks;
305 removeNodeAndUpdate(p, modified_blocks);
307 // Copy modified_blocks to event
308 for (auto &modified_block : modified_blocks) {
309 event.modified_blocks.insert(modified_block.first);
312 catch(InvalidPositionException &e){
316 dispatchEvent(event);
321 struct TimeOrderedMapBlock {
325 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
330 bool operator<(const TimeOrderedMapBlock &b) const
332 return block->getUsageTimer() < b.block->getUsageTimer();
339 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
340 std::vector<v3s16> *unloaded_blocks)
342 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
344 // Profile modified reasons
345 Profiler modprofiler;
347 std::vector<v2s16> sector_deletion_queue;
348 u32 deleted_blocks_count = 0;
349 u32 saved_blocks_count = 0;
350 u32 block_count_all = 0;
354 // If there is no practical limit, we spare creation of mapblock_queue
355 if (max_loaded_blocks == U32_MAX) {
356 for (auto §or_it : m_sectors) {
357 MapSector *sector = sector_it.second;
359 bool all_blocks_deleted = true;
362 sector->getBlocks(blocks);
364 for (MapBlock *block : blocks) {
365 block->incrementUsageTimer(dtime);
367 if (block->refGet() == 0
368 && block->getUsageTimer() > unload_timeout) {
369 v3s16 p = block->getPos();
372 if (block->getModified() != MOD_STATE_CLEAN
373 && save_before_unloading) {
374 modprofiler.add(block->getModifiedReasonString(), 1);
375 if (!saveBlock(block))
377 saved_blocks_count++;
380 // Delete from memory
381 sector->deleteBlock(block);
384 unloaded_blocks->push_back(p);
386 deleted_blocks_count++;
388 all_blocks_deleted = false;
393 if (all_blocks_deleted) {
394 sector_deletion_queue.push_back(sector_it.first);
398 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
399 for (auto §or_it : m_sectors) {
400 MapSector *sector = sector_it.second;
403 sector->getBlocks(blocks);
405 for (MapBlock *block : blocks) {
406 block->incrementUsageTimer(dtime);
407 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
410 block_count_all = mapblock_queue.size();
411 // Delete old blocks, and blocks over the limit from the memory
412 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
413 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
414 TimeOrderedMapBlock b = mapblock_queue.top();
415 mapblock_queue.pop();
417 MapBlock *block = b.block;
419 if (block->refGet() != 0)
422 v3s16 p = block->getPos();
425 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
426 modprofiler.add(block->getModifiedReasonString(), 1);
427 if (!saveBlock(block))
429 saved_blocks_count++;
432 // Delete from memory
433 b.sect->deleteBlock(block);
436 unloaded_blocks->push_back(p);
438 deleted_blocks_count++;
441 // Delete empty sectors
442 for (auto §or_it : m_sectors) {
443 if (sector_it.second->empty()) {
444 sector_deletion_queue.push_back(sector_it.first);
450 // Finally delete the empty sectors
451 deleteSectors(sector_deletion_queue);
453 if(deleted_blocks_count != 0)
455 PrintInfo(infostream); // ServerMap/ClientMap:
456 infostream<<"Unloaded "<<deleted_blocks_count
457 <<" blocks from memory";
458 if(save_before_unloading)
459 infostream<<", of which "<<saved_blocks_count<<" were written";
460 infostream<<", "<<block_count_all<<" blocks in memory";
461 infostream<<"."<<std::endl;
462 if(saved_blocks_count != 0){
463 PrintInfo(infostream); // ServerMap/ClientMap:
464 infostream<<"Blocks modified by: "<<std::endl;
465 modprofiler.print(infostream);
470 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
472 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
475 void Map::deleteSectors(std::vector<v2s16> §orList)
477 for (v2s16 j : sectorList) {
478 MapSector *sector = m_sectors[j];
479 // If sector is in sector cache, remove it from there
480 if(m_sector_cache == sector)
481 m_sector_cache = NULL;
482 // Remove from map and delete
488 void Map::PrintInfo(std::ostream &out)
493 #define WATER_DROP_BOOST 4
495 const static v3s16 liquid_6dirs[6] = {
496 // order: upper before same level before lower
505 enum NeighborType : u8 {
511 struct NodeNeighbor {
517 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
520 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
527 void Map::transforming_liquid_add(v3s16 p) {
528 m_transforming_liquid.push_back(p);
531 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
532 ServerEnvironment *env)
535 u32 initial_size = m_transforming_liquid.size();
537 /*if(initial_size != 0)
538 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
540 // list of nodes that due to viscosity have not reached their max level height
541 std::deque<v3s16> must_reflow;
543 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
545 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
546 u32 loop_max = liquid_loop_max;
548 while (m_transforming_liquid.size() != 0)
550 // This should be done here so that it is done when continue is used
551 if (loopcount >= initial_size || loopcount >= loop_max)
556 Get a queued transforming liquid node
558 v3s16 p0 = m_transforming_liquid.front();
559 m_transforming_liquid.pop_front();
561 MapNode n0 = getNode(p0);
564 Collect information about current node
566 s8 liquid_level = -1;
567 // The liquid node which will be placed there if
568 // the liquid flows into this node.
569 content_t liquid_kind = CONTENT_IGNORE;
570 // The node which will be placed there if liquid
571 // can't flow into this node.
572 content_t floodable_node = CONTENT_AIR;
573 const ContentFeatures &cf = m_nodedef->get(n0);
574 LiquidType liquid_type = cf.liquid_type;
575 switch (liquid_type) {
577 liquid_level = LIQUID_LEVEL_SOURCE;
578 liquid_kind = cf.liquid_alternative_flowing_id;
581 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
582 liquid_kind = n0.getContent();
585 // if this node is 'floodable', it *could* be transformed
586 // into a liquid, otherwise, continue with the next node.
589 floodable_node = n0.getContent();
590 liquid_kind = CONTENT_AIR;
595 Collect information about the environment
597 NodeNeighbor sources[6]; // surrounding sources
599 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
601 NodeNeighbor airs[6]; // surrounding air
603 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
604 int num_neutrals = 0;
605 bool flowing_down = false;
606 bool ignored_sources = false;
607 for (u16 i = 0; i < 6; i++) {
608 NeighborType nt = NEIGHBOR_SAME_LEVEL;
619 v3s16 npos = p0 + liquid_6dirs[i];
620 NodeNeighbor nb(getNode(npos), nt, npos);
621 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
622 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
624 if (cfnb.floodable) {
625 airs[num_airs++] = nb;
626 // if the current node is a water source the neighbor
627 // should be enqueded for transformation regardless of whether the
628 // current node changes or not.
629 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
630 m_transforming_liquid.push_back(npos);
631 // if the current node happens to be a flowing node, it will start to flow down here.
632 if (nb.t == NEIGHBOR_LOWER)
635 neutrals[num_neutrals++] = nb;
636 if (nb.n.getContent() == CONTENT_IGNORE) {
637 // If node below is ignore prevent water from
638 // spreading outwards and otherwise prevent from
639 // flowing away as ignore node might be the source
640 if (nb.t == NEIGHBOR_LOWER)
643 ignored_sources = true;
648 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
649 if (liquid_kind == CONTENT_AIR)
650 liquid_kind = cfnb.liquid_alternative_flowing_id;
651 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
652 neutrals[num_neutrals++] = nb;
654 // Do not count bottom source, it will screw things up
655 if(nt != NEIGHBOR_LOWER)
656 sources[num_sources++] = nb;
660 if (nb.t != NEIGHBOR_SAME_LEVEL ||
661 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
662 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
663 // but exclude falling liquids on the same level, they cannot flow here anyway
664 if (liquid_kind == CONTENT_AIR)
665 liquid_kind = cfnb.liquid_alternative_flowing_id;
667 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
668 neutrals[num_neutrals++] = nb;
670 flows[num_flows++] = nb;
671 if (nb.t == NEIGHBOR_LOWER)
679 decide on the type (and possibly level) of the current node
681 content_t new_node_content;
682 s8 new_node_level = -1;
683 s8 max_node_level = -1;
685 u8 range = m_nodedef->get(liquid_kind).liquid_range;
686 if (range > LIQUID_LEVEL_MAX + 1)
687 range = LIQUID_LEVEL_MAX + 1;
689 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
690 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
691 // or the flowing alternative of the first of the surrounding sources (if it's air), so
692 // it's perfectly safe to use liquid_kind here to determine the new node content.
693 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
694 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
695 // liquid_kind is set properly, see above
696 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
697 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
698 new_node_content = liquid_kind;
700 new_node_content = floodable_node;
701 } else if (ignored_sources && liquid_level >= 0) {
702 // Maybe there are neighbouring sources that aren't loaded yet
703 // so prevent flowing away.
704 new_node_level = liquid_level;
705 new_node_content = liquid_kind;
707 // no surrounding sources, so get the maximum level that can flow into this node
708 for (u16 i = 0; i < num_flows; i++) {
709 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
710 switch (flows[i].t) {
712 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
713 max_node_level = LIQUID_LEVEL_MAX;
714 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
715 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
716 } else if (nb_liquid_level > max_node_level) {
717 max_node_level = nb_liquid_level;
722 case NEIGHBOR_SAME_LEVEL:
723 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
724 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
725 max_node_level = nb_liquid_level - 1;
730 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
731 if (viscosity > 1 && max_node_level != liquid_level) {
732 // amount to gain, limited by viscosity
733 // must be at least 1 in absolute value
734 s8 level_inc = max_node_level - liquid_level;
735 if (level_inc < -viscosity || level_inc > viscosity)
736 new_node_level = liquid_level + level_inc/viscosity;
737 else if (level_inc < 0)
738 new_node_level = liquid_level - 1;
739 else if (level_inc > 0)
740 new_node_level = liquid_level + 1;
741 if (new_node_level != max_node_level)
742 must_reflow.push_back(p0);
744 new_node_level = max_node_level;
747 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
748 new_node_content = liquid_kind;
750 new_node_content = floodable_node;
755 check if anything has changed. if not, just continue with the next node.
757 if (new_node_content == n0.getContent() &&
758 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
759 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
760 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
766 update the current node
769 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
770 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
771 // set level to last 3 bits, flowing down bit to 4th bit
772 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
774 // set the liquid level and flow bits to 0
775 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
779 n0.setContent(new_node_content);
781 // on_flood() the node
782 if (floodable_node != CONTENT_AIR) {
783 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
787 // Ignore light (because calling voxalgo::update_lighting_nodes)
788 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
789 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
791 // Find out whether there is a suspect for this action
793 if (m_gamedef->rollback())
794 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
796 if (m_gamedef->rollback() && !suspect.empty()) {
798 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
799 // Get old node for rollback
800 RollbackNode rollback_oldnode(this, p0, m_gamedef);
804 RollbackNode rollback_newnode(this, p0, m_gamedef);
805 RollbackAction action;
806 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
807 m_gamedef->rollback()->reportAction(action);
813 v3s16 blockpos = getNodeBlockPos(p0);
814 MapBlock *block = getBlockNoCreateNoEx(blockpos);
816 modified_blocks[blockpos] = block;
817 changed_nodes.emplace_back(p0, n00);
821 enqueue neighbors for update if neccessary
823 switch (m_nodedef->get(n0.getContent()).liquid_type) {
826 // make sure source flows into all neighboring nodes
827 for (u16 i = 0; i < num_flows; i++)
828 if (flows[i].t != NEIGHBOR_UPPER)
829 m_transforming_liquid.push_back(flows[i].p);
830 for (u16 i = 0; i < num_airs; i++)
831 if (airs[i].t != NEIGHBOR_UPPER)
832 m_transforming_liquid.push_back(airs[i].p);
835 // this flow has turned to air; neighboring flows might need to do the same
836 for (u16 i = 0; i < num_flows; i++)
837 m_transforming_liquid.push_back(flows[i].p);
841 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
843 for (auto &iter : must_reflow)
844 m_transforming_liquid.push_back(iter);
846 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
847 env->getScriptIface()->on_liquid_transformed(changed_nodes);
849 /* ----------------------------------------------------------------------
850 * Manage the queue so that it does not grow indefinately
852 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
854 if (time_until_purge == 0)
855 return; // Feature disabled
857 time_until_purge *= 1000; // seconds -> milliseconds
859 u64 curr_time = porting::getTimeMs();
860 u32 prev_unprocessed = m_unprocessed_count;
861 m_unprocessed_count = m_transforming_liquid.size();
863 // if unprocessed block count is decreasing or stable
864 if (m_unprocessed_count <= prev_unprocessed) {
865 m_queue_size_timer_started = false;
867 if (!m_queue_size_timer_started)
868 m_inc_trending_up_start_time = curr_time;
869 m_queue_size_timer_started = true;
872 // Account for curr_time overflowing
873 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
874 m_queue_size_timer_started = false;
876 /* If the queue has been growing for more than liquid_queue_purge_time seconds
877 * and the number of unprocessed blocks is still > liquid_loop_max then we
878 * cannot keep up; dump the oldest blocks from the queue so that the queue
879 * has liquid_loop_max items in it
881 if (m_queue_size_timer_started
882 && curr_time - m_inc_trending_up_start_time > time_until_purge
883 && m_unprocessed_count > liquid_loop_max) {
885 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
887 infostream << "transformLiquids(): DUMPING " << dump_qty
888 << " blocks from the queue" << std::endl;
891 m_transforming_liquid.pop_front();
893 m_queue_size_timer_started = false; // optimistically assume we can keep up now
894 m_unprocessed_count = m_transforming_liquid.size();
898 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
900 std::vector<v3s16> positions_with_meta;
902 sortBoxVerticies(p1, p2);
903 v3s16 bpmin = getNodeBlockPos(p1);
904 v3s16 bpmax = getNodeBlockPos(p2);
906 VoxelArea area(p1, p2);
908 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
909 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
910 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
911 v3s16 blockpos(x, y, z);
913 MapBlock *block = getBlockNoCreateNoEx(blockpos);
915 verbosestream << "Map::getNodeMetadata(): Need to emerge "
916 << PP(blockpos) << std::endl;
917 block = emergeBlock(blockpos, false);
920 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
925 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
926 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
927 for (size_t i = 0; i != keys.size(); i++) {
928 v3s16 p(keys[i] + p_base);
929 if (!area.contains(p))
932 positions_with_meta.push_back(p);
936 return positions_with_meta;
939 NodeMetadata *Map::getNodeMetadata(v3s16 p)
941 v3s16 blockpos = getNodeBlockPos(p);
942 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
943 MapBlock *block = getBlockNoCreateNoEx(blockpos);
945 infostream<<"Map::getNodeMetadata(): Need to emerge "
946 <<PP(blockpos)<<std::endl;
947 block = emergeBlock(blockpos, false);
950 warningstream<<"Map::getNodeMetadata(): Block not found"
954 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
958 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
960 v3s16 blockpos = getNodeBlockPos(p);
961 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
962 MapBlock *block = getBlockNoCreateNoEx(blockpos);
964 infostream<<"Map::setNodeMetadata(): Need to emerge "
965 <<PP(blockpos)<<std::endl;
966 block = emergeBlock(blockpos, false);
969 warningstream<<"Map::setNodeMetadata(): Block not found"
973 block->m_node_metadata.set(p_rel, meta);
977 void Map::removeNodeMetadata(v3s16 p)
979 v3s16 blockpos = getNodeBlockPos(p);
980 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
981 MapBlock *block = getBlockNoCreateNoEx(blockpos);
984 warningstream<<"Map::removeNodeMetadata(): Block not found"
988 block->m_node_metadata.remove(p_rel);
991 NodeTimer Map::getNodeTimer(v3s16 p)
993 v3s16 blockpos = getNodeBlockPos(p);
994 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
995 MapBlock *block = getBlockNoCreateNoEx(blockpos);
997 infostream<<"Map::getNodeTimer(): Need to emerge "
998 <<PP(blockpos)<<std::endl;
999 block = emergeBlock(blockpos, false);
1002 warningstream<<"Map::getNodeTimer(): Block not found"
1006 NodeTimer t = block->m_node_timers.get(p_rel);
1007 NodeTimer nt(t.timeout, t.elapsed, p);
1011 void Map::setNodeTimer(const NodeTimer &t)
1013 v3s16 p = t.position;
1014 v3s16 blockpos = getNodeBlockPos(p);
1015 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1016 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1018 infostream<<"Map::setNodeTimer(): Need to emerge "
1019 <<PP(blockpos)<<std::endl;
1020 block = emergeBlock(blockpos, false);
1023 warningstream<<"Map::setNodeTimer(): Block not found"
1027 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1028 block->m_node_timers.set(nt);
1031 void Map::removeNodeTimer(v3s16 p)
1033 v3s16 blockpos = getNodeBlockPos(p);
1034 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1035 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1038 warningstream<<"Map::removeNodeTimer(): Block not found"
1042 block->m_node_timers.remove(p_rel);
1045 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1046 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1049 This functions determines the node inside the target block that is
1050 closest to the camera position. This increases the occlusion culling
1051 accuracy in straight and diagonal corridors.
1052 The returned position will be occlusion checked first in addition to the
1053 others (8 corners + center).
1054 No position is returned if
1055 - the closest node is a corner, corners are checked anyway.
1056 - the camera is inside the target block, it will never be occluded.
1058 #define CLOSEST_EDGE(pos, bounds, axis) \
1059 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1060 (bounds).MaxEdge.axis
1062 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1063 (pos_camera.X <= block_bounds.MaxEdge.X);
1064 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1065 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1066 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1067 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1069 if (x_inside && y_inside && z_inside)
1070 return false; // Camera inside target mapblock
1073 if (x_inside && y_inside) {
1074 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1075 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1077 } else if (y_inside && z_inside) {
1078 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1079 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1081 } else if (x_inside && z_inside) {
1082 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1083 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1089 check = v3s16(pos_camera.X, 0, 0);
1090 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1091 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1093 } else if (y_inside) {
1094 check = v3s16(0, pos_camera.Y, 0);
1095 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1096 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1098 } else if (z_inside) {
1099 check = v3s16(0, 0, pos_camera.Z);
1100 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1101 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1105 // Closest node would be a corner, none returned
1109 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1110 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1112 v3f direction = intToFloat(pos_target - pos_camera, BS);
1113 float distance = direction.getLength();
1115 // Normalize direction vector
1116 if (distance > 0.0f)
1117 direction /= distance;
1119 v3f pos_origin_f = intToFloat(pos_camera, BS);
1121 bool is_valid_position;
1123 for (; offset < distance + end_offset; offset += step) {
1124 v3f pos_node_f = pos_origin_f + direction * offset;
1125 v3s16 pos_node = floatToInt(pos_node_f, BS);
1127 MapNode node = getNode(pos_node, &is_valid_position);
1129 if (is_valid_position &&
1130 !m_nodedef->get(node).light_propagates) {
1131 // Cannot see through light-blocking nodes --> occluded
1133 if (count >= needed_count)
1141 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1143 // Check occlusion for center and all 8 corners of the mapblock
1144 // Overshoot a little for less flickering
1145 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1146 static const v3s16 dir9[9] = {
1148 v3s16( 1, 1, 1) * bs2,
1149 v3s16( 1, 1, -1) * bs2,
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,
1158 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1160 // Starting step size, value between 1m and sqrt(3)m
1161 float step = BS * 1.2f;
1162 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1163 float stepfac = 1.05f;
1165 float start_offset = BS * 1.0f;
1167 // The occlusion search of 'isOccluded()' must stop short of the target
1168 // point by distance 'end_offset' to not enter the target mapblock.
1169 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1170 // diagonal of a mapblock, because we must consider all view angles.
1171 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1172 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1174 // to reduce the likelihood of falsely occluded blocks
1175 // require at least two solid blocks
1176 // this is a HACK, we should think of a more precise algorithm
1177 u32 needed_count = 2;
1179 // Additional occlusion check, see comments in that function
1181 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1182 // node is always on a side facing the camera, end_offset can be lower
1183 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1184 -1.0f, needed_count))
1188 for (const v3s16 &dir : dir9) {
1189 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1190 start_offset, end_offset, needed_count))
1199 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1200 EmergeManager *emerge, MetricsBackend *mb):
1202 settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
1205 verbosestream<<FUNCTION_NAME<<std::endl;
1207 // Tell the EmergeManager about our MapSettingsManager
1208 emerge->map_settings_mgr = &settings_mgr;
1211 Try to load map; if not found, create a new one.
1214 // Determine which database backend to use
1215 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1217 bool succeeded = conf.readConfigFile(conf_path.c_str());
1218 if (!succeeded || !conf.exists("backend")) {
1219 // fall back to sqlite3
1220 conf.set("backend", "sqlite3");
1222 std::string backend = conf.get("backend");
1223 dbase = createDatabase(backend, savedir, conf);
1224 if (conf.exists("readonly_backend")) {
1225 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1226 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1228 if (!conf.updateConfigFile(conf_path.c_str()))
1229 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1231 m_savedir = savedir;
1232 m_map_saving_enabled = false;
1234 m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1236 m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
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
1305 MapgenParams *ServerMap::getMapgenParams()
1307 // getMapgenParams() should only ever be called after Server is initialized
1308 assert(settings_mgr.mapgen_params != NULL);
1309 return settings_mgr.mapgen_params;
1312 u64 ServerMap::getSeed()
1314 return getMapgenParams()->seed;
1317 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1319 const s16 mapgen_limit_bp = rangelim(
1320 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1322 return p.X < -mapgen_limit_bp ||
1323 p.X > mapgen_limit_bp ||
1324 p.Y < -mapgen_limit_bp ||
1325 p.Y > mapgen_limit_bp ||
1326 p.Z < -mapgen_limit_bp ||
1327 p.Z > mapgen_limit_bp;
1330 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1332 s16 csize = getMapgenParams()->chunksize;
1333 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1334 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1336 if (!m_chunks_in_progress.insert(bpmin).second)
1339 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1340 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1342 v3s16 extra_borders(1, 1, 1);
1343 v3s16 full_bpmin = bpmin - extra_borders;
1344 v3s16 full_bpmax = bpmax + extra_borders;
1346 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1347 if (blockpos_over_mapgen_limit(full_bpmin) ||
1348 blockpos_over_mapgen_limit(full_bpmax))
1351 data->seed = getSeed();
1352 data->blockpos_min = bpmin;
1353 data->blockpos_max = bpmax;
1354 data->nodedef = m_nodedef;
1357 Create the whole area of this and the neighboring blocks
1359 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1360 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1361 v2s16 sectorpos(x, z);
1362 // Sector metadata is loaded from disk if not already loaded.
1363 MapSector *sector = createSector(sectorpos);
1364 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1366 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1369 MapBlock *block = emergeBlock(p, false);
1370 if (block == NULL) {
1371 block = createBlock(p);
1373 // Block gets sunlight if this is true.
1374 // Refer to the map generator heuristics.
1375 bool ug = m_emerge->isBlockUnderground(p);
1376 block->setIsUnderground(ug);
1382 Now we have a big empty area.
1384 Make a ManualMapVoxelManipulator that contains this and the
1388 data->vmanip = new MMVManip(this);
1389 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1391 // Data is ready now.
1395 void ServerMap::finishBlockMake(BlockMakeData *data,
1396 std::map<v3s16, MapBlock*> *changed_blocks)
1398 v3s16 bpmin = data->blockpos_min;
1399 v3s16 bpmax = data->blockpos_max;
1401 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1402 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1405 Blit generated stuff to map
1406 NOTE: blitBackAll adds nearly everything to changed_blocks
1408 data->vmanip->blitBackAll(changed_blocks);
1410 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1411 << changed_blocks->size());
1414 Copy transforming liquid information
1416 while (data->transforming_liquid.size()) {
1417 m_transforming_liquid.push_back(data->transforming_liquid.front());
1418 data->transforming_liquid.pop_front();
1421 for (auto &changed_block : *changed_blocks) {
1422 MapBlock *block = changed_block.second;
1426 Update day/night difference cache of the MapBlocks
1428 block->expireDayNightDiff();
1430 Set block as modified
1432 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1433 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1437 Set central blocks as generated
1439 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1440 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1441 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1442 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1446 block->setGenerated(true);
1450 Save changed parts of map
1451 NOTE: Will be saved later.
1453 //save(MOD_STATE_WRITE_AT_UNLOAD);
1454 m_chunks_in_progress.erase(bpmin);
1457 MapSector *ServerMap::createSector(v2s16 p2d)
1460 Check if it exists already in memory
1462 MapSector *sector = getSectorNoGenerate(p2d);
1467 Do not create over max mapgen limit
1469 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1470 if (p2d.X < -max_limit_bp ||
1471 p2d.X > max_limit_bp ||
1472 p2d.Y < -max_limit_bp ||
1473 p2d.Y > max_limit_bp)
1474 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1477 Generate blank sector
1480 sector = new MapSector(this, p2d, m_gamedef);
1482 // Sector position on map in nodes
1483 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1488 m_sectors[p2d] = sector;
1493 MapBlock * ServerMap::createBlock(v3s16 p)
1496 Do not create over max mapgen limit
1498 if (blockpos_over_max_limit(p))
1499 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1501 v2s16 p2d(p.X, p.Z);
1504 This will create or load a sector if not found in memory.
1505 If block exists on disk, it will be loaded.
1507 NOTE: On old save formats, this will be slow, as it generates
1508 lighting on blocks for them.
1512 sector = createSector(p2d);
1513 } catch (InvalidPositionException &e) {
1514 infostream<<"createBlock: createSector() failed"<<std::endl;
1519 Try to get a block from the sector
1522 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1524 if(block->isDummy())
1529 block = sector->createBlankBlock(block_y);
1534 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1537 MapBlock *block = getBlockNoCreateNoEx(p);
1538 if (block && !block->isDummy())
1543 MapBlock *block = loadBlock(p);
1549 MapSector *sector = createSector(v2s16(p.X, p.Z));
1550 MapBlock *block = sector->createBlankBlock(p.Y);
1558 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1560 MapBlock *block = getBlockNoCreateNoEx(p3d);
1562 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1567 bool ServerMap::isBlockInQueue(v3s16 pos)
1569 return m_emerge && m_emerge->isBlockInQueue(pos);
1572 // N.B. This requires no synchronization, since data will not be modified unless
1573 // the VoxelManipulator being updated belongs to the same thread.
1574 void ServerMap::updateVManip(v3s16 pos)
1576 Mapgen *mg = m_emerge->getCurrentMapgen();
1580 MMVManip *vm = mg->vm;
1584 if (!vm->m_area.contains(pos))
1587 s32 idx = vm->m_area.index(pos);
1588 vm->m_data[idx] = getNode(pos);
1589 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1591 vm->m_is_dirty = true;
1594 void ServerMap::save(ModifiedState save_level)
1596 if (!m_map_saving_enabled) {
1597 warningstream<<"Not saving map, saving disabled."<<std::endl;
1601 u64 start_time = porting::getTimeNs();
1603 if(save_level == MOD_STATE_CLEAN)
1604 infostream<<"ServerMap: Saving whole map, this can take time."
1607 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1608 if (settings_mgr.saveMapMeta())
1609 m_map_metadata_changed = false;
1612 // Profile modified reasons
1613 Profiler modprofiler;
1615 u32 block_count = 0;
1616 u32 block_count_all = 0; // Number of blocks in memory
1618 // Don't do anything with sqlite unless something is really saved
1619 bool save_started = false;
1621 for (auto §or_it : m_sectors) {
1622 MapSector *sector = sector_it.second;
1624 MapBlockVect blocks;
1625 sector->getBlocks(blocks);
1627 for (MapBlock *block : blocks) {
1630 if(block->getModified() >= (u32)save_level) {
1634 save_started = true;
1637 modprofiler.add(block->getModifiedReasonString(), 1);
1649 Only print if something happened or saved whole map
1651 if(save_level == MOD_STATE_CLEAN
1652 || block_count != 0) {
1653 infostream << "ServerMap: Written: "
1654 << block_count << " blocks"
1655 << ", " << block_count_all << " blocks in memory."
1657 PrintInfo(infostream); // ServerMap/ClientMap:
1658 infostream<<"Blocks modified by: "<<std::endl;
1659 modprofiler.print(infostream);
1662 auto end_time = porting::getTimeNs();
1663 m_save_time_counter->increment(end_time - start_time);
1666 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1668 dbase->listAllLoadableBlocks(dst);
1670 dbase_ro->listAllLoadableBlocks(dst);
1673 MapDatabase *ServerMap::createDatabase(
1674 const std::string &name,
1675 const std::string &savedir,
1678 if (name == "sqlite3")
1679 return new MapDatabaseSQLite3(savedir);
1680 if (name == "dummy")
1681 return new Database_Dummy();
1683 if (name == "leveldb")
1684 return new Database_LevelDB(savedir);
1687 if (name == "redis")
1688 return new Database_Redis(conf);
1691 if (name == "postgresql") {
1692 std::string connect_string;
1693 conf.getNoEx("pgsql_connection", connect_string);
1694 return new MapDatabasePostgreSQL(connect_string);
1698 throw BaseException(std::string("Database backend ") + name + " not supported.");
1701 void ServerMap::beginSave()
1706 void ServerMap::endSave()
1711 bool ServerMap::saveBlock(MapBlock *block)
1713 return saveBlock(block, dbase, m_map_compression_level);
1716 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1718 v3s16 p3d = block->getPos();
1720 // Dummy blocks are not written
1721 if (block->isDummy()) {
1722 warningstream << "saveBlock: Not writing dummy block "
1723 << PP(p3d) << std::endl;
1727 // Format used for writing
1728 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1731 [0] u8 serialization version
1734 std::ostringstream o(std::ios_base::binary);
1735 o.write((char*) &version, 1);
1736 block->serialize(o, version, true, compression_level);
1738 bool ret = db->saveBlock(p3d, o.str());
1740 // We just wrote it to the disk so clear modified flag
1741 block->resetModified();
1746 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1749 std::istringstream is(*blob, std::ios_base::binary);
1751 u8 version = SER_FMT_VER_INVALID;
1752 is.read((char*)&version, 1);
1755 throw SerializationError("ServerMap::loadBlock(): Failed"
1756 " to read MapBlock version");
1758 MapBlock *block = NULL;
1759 bool created_new = false;
1760 block = sector->getBlockNoCreateNoEx(p3d.Y);
1763 block = sector->createBlankBlockNoInsert(p3d.Y);
1768 block->deSerialize(is, version, true);
1770 // If it's a new block, insert it to the map
1772 sector->insertBlock(block);
1773 ReflowScan scanner(this, m_emerge->ndef);
1774 scanner.scan(block, &m_transforming_liquid);
1778 Save blocks loaded in old format in new format
1781 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1782 // Only save if asked to; no need to update version
1786 // We just loaded it from, so it's up-to-date.
1787 block->resetModified();
1789 catch(SerializationError &e)
1791 errorstream<<"Invalid block data in database"
1792 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1793 <<" (SerializationError): "<<e.what()<<std::endl;
1795 // TODO: Block should be marked as invalid in memory so that it is
1796 // not touched but the game can run
1798 if(g_settings->getBool("ignore_world_load_errors")){
1799 errorstream<<"Ignoring block load error. Duck and cover! "
1800 <<"(ignore_world_load_errors)"<<std::endl;
1802 throw SerializationError("Invalid block data in database");
1807 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1809 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1811 v2s16 p2d(blockpos.X, blockpos.Z);
1814 dbase->loadBlock(blockpos, &ret);
1816 loadBlock(&ret, blockpos, createSector(p2d), false);
1817 } else if (dbase_ro) {
1818 dbase_ro->loadBlock(blockpos, &ret);
1820 loadBlock(&ret, blockpos, createSector(p2d), false);
1826 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1827 if (created_new && (block != NULL)) {
1828 std::map<v3s16, MapBlock*> modified_blocks;
1829 // Fix lighting if necessary
1830 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1831 if (!modified_blocks.empty()) {
1832 //Modified lighting, send event
1834 event.type = MEET_OTHER;
1835 std::map<v3s16, MapBlock *>::iterator it;
1836 for (it = modified_blocks.begin();
1837 it != modified_blocks.end(); ++it)
1838 event.modified_blocks.insert(it->first);
1839 dispatchEvent(event);
1845 bool ServerMap::deleteBlock(v3s16 blockpos)
1847 if (!dbase->deleteBlock(blockpos))
1850 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1852 v2s16 p2d(blockpos.X, blockpos.Z);
1853 MapSector *sector = getSectorNoGenerate(p2d);
1856 sector->deleteBlock(block);
1862 void ServerMap::PrintInfo(std::ostream &out)
1867 bool ServerMap::repairBlockLight(v3s16 blockpos,
1868 std::map<v3s16, MapBlock *> *modified_blocks)
1870 MapBlock *block = emergeBlock(blockpos, false);
1871 if (!block || !block->isGenerated())
1873 voxalgo::repair_block_light(this, block, modified_blocks);
1877 MMVManip::MMVManip(Map *map):
1883 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1884 bool load_if_inexistent)
1886 TimeTaker timer1("initialEmerge", &emerge_time);
1888 // Units of these are MapBlocks
1889 v3s16 p_min = blockpos_min;
1890 v3s16 p_max = blockpos_max;
1892 VoxelArea block_area_nodes
1893 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1895 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1898 infostream<<"initialEmerge: area: ";
1899 block_area_nodes.print(infostream);
1900 infostream<<" ("<<size_MB<<"MB)";
1901 infostream<<std::endl;
1904 addArea(block_area_nodes);
1906 for(s32 z=p_min.Z; z<=p_max.Z; z++)
1907 for(s32 y=p_min.Y; y<=p_max.Y; y++)
1908 for(s32 x=p_min.X; x<=p_max.X; x++)
1913 std::map<v3s16, u8>::iterator n;
1914 n = m_loaded_blocks.find(p);
1915 if(n != m_loaded_blocks.end())
1918 bool block_data_inexistent = false;
1920 TimeTaker timer2("emerge load", &emerge_load_time);
1922 block = m_map->getBlockNoCreateNoEx(p);
1923 if (!block || block->isDummy())
1924 block_data_inexistent = true;
1926 block->copyTo(*this);
1929 if(block_data_inexistent)
1932 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1933 ServerMap *svrmap = (ServerMap *)m_map;
1934 block = svrmap->emergeBlock(p, false);
1936 block = svrmap->createBlock(p);
1937 block->copyTo(*this);
1939 flags |= VMANIP_BLOCK_DATA_INEXIST;
1942 Mark area inexistent
1944 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1945 // Fill with VOXELFLAG_NO_DATA
1946 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1947 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1949 s32 i = m_area.index(a.MinEdge.X,y,z);
1950 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1954 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1956 // Mark that block was loaded as blank
1957 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
1960 m_loaded_blocks[p] = flags;
1966 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
1967 bool overwrite_generated)
1969 if(m_area.getExtent() == v3s16(0,0,0))
1973 Copy data of all blocks
1975 for (auto &loaded_block : m_loaded_blocks) {
1976 v3s16 p = loaded_block.first;
1977 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1978 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
1979 if (!existed || (block == NULL) ||
1980 (!overwrite_generated && block->isGenerated()))
1983 block->copyFrom(*this);
1984 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
1987 (*modified_blocks)[p] = block;