3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
25 #include "voxelalgorithms.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/basic_macros.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
38 #include "reflowscan.h"
40 #include "mapgen/mapgen_v6.h"
41 #include "mapgen/mg_biome.h"
44 #include "database/database.h"
45 #include "database/database-dummy.h"
46 #include "database/database-sqlite3.h"
47 #include "script/scripting_server.h"
51 #include "database/database-leveldb.h"
54 #include "database/database-redis.h"
57 #include "database/database-postgresql.h"
65 Map::Map(std::ostream &dout, IGameDef *gamedef):
68 m_nodedef(gamedef->ndef())
77 for (auto §or : m_sectors) {
82 void Map::addEventReceiver(MapEventReceiver *event_receiver)
84 m_event_receivers.insert(event_receiver);
87 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
89 m_event_receivers.erase(event_receiver);
92 void Map::dispatchEvent(const MapEditEvent &event)
94 for (MapEventReceiver *event_receiver : m_event_receivers) {
95 event_receiver->onMapEditEvent(event);
99 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
101 if(m_sector_cache != NULL && p == m_sector_cache_p){
102 MapSector * sector = m_sector_cache;
106 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
108 if (n == m_sectors.end())
111 MapSector *sector = n->second;
113 // Cache the last result
114 m_sector_cache_p = p;
115 m_sector_cache = sector;
120 MapSector * Map::getSectorNoGenerate(v2s16 p)
122 return getSectorNoGenerateNoLock(p);
125 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
127 v2s16 p2d(p3d.X, p3d.Z);
128 MapSector * sector = getSectorNoGenerate(p2d);
131 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
135 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
137 MapBlock *block = getBlockNoCreateNoEx(p3d);
139 throw InvalidPositionException();
143 bool Map::isNodeUnderground(v3s16 p)
145 v3s16 blockpos = getNodeBlockPos(p);
146 MapBlock *block = getBlockNoCreateNoEx(blockpos);
147 return block && block->getIsUnderground();
150 bool Map::isValidPosition(v3s16 p)
152 v3s16 blockpos = getNodeBlockPos(p);
153 MapBlock *block = getBlockNoCreateNoEx(blockpos);
154 return (block != NULL);
157 // Returns a CONTENT_IGNORE node if not found
158 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
160 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock *block = getBlockNoCreateNoEx(blockpos);
163 if (is_valid_position != NULL)
164 *is_valid_position = false;
165 return {CONTENT_IGNORE};
168 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
170 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
171 if (is_valid_position != NULL)
172 *is_valid_position = is_valid_p;
176 // throws InvalidPositionException if not found
177 void Map::setNode(v3s16 p, MapNode & n)
179 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock *block = getBlockNoCreate(blockpos);
181 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
182 // Never allow placing CONTENT_IGNORE, it causes problems
183 if(n.getContent() == CONTENT_IGNORE){
185 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
186 <<" while trying to replace \""
187 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
188 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
191 block->setNodeNoCheck(relpos, n);
194 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
195 std::map<v3s16, MapBlock*> &modified_blocks,
196 bool remove_metadata)
198 // Collect old node for rollback
199 RollbackNode rollback_oldnode(this, p, m_gamedef);
201 // This is needed for updating the lighting
202 MapNode oldnode = getNode(p);
204 // Remove node metadata
205 if (remove_metadata) {
206 removeNodeMetadata(p);
209 // Set the node on the map
210 // Ignore light (because calling voxalgo::update_lighting_nodes)
211 n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
212 n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
216 std::vector<std::pair<v3s16, MapNode> > oldnodes;
217 oldnodes.emplace_back(p, oldnode);
218 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
220 for (auto &modified_block : modified_blocks) {
221 modified_block.second->expireDayNightDiff();
224 // Report for rollback
225 if(m_gamedef->rollback())
227 RollbackNode rollback_newnode(this, p, m_gamedef);
228 RollbackAction action;
229 action.setSetNode(p, rollback_oldnode, rollback_newnode);
230 m_gamedef->rollback()->reportAction(action);
234 Add neighboring liquid nodes and this node to transform queue.
235 (it's vital for the node itself to get updated last, if it was removed.)
238 for (const v3s16 &dir : g_7dirs) {
241 bool is_valid_position;
242 MapNode n2 = getNode(p2, &is_valid_position);
243 if(is_valid_position &&
244 (m_nodedef->get(n2).isLiquid() ||
245 n2.getContent() == CONTENT_AIR))
246 m_transforming_liquid.push_back(p2);
250 void Map::removeNodeAndUpdate(v3s16 p,
251 std::map<v3s16, MapBlock*> &modified_blocks)
253 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
256 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
259 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
263 bool succeeded = true;
265 std::map<v3s16, MapBlock*> modified_blocks;
266 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
268 // Copy modified_blocks to event
269 for (auto &modified_block : modified_blocks) {
270 event.modified_blocks.insert(modified_block.first);
273 catch(InvalidPositionException &e){
277 dispatchEvent(event);
282 bool Map::removeNodeWithEvent(v3s16 p)
285 event.type = MEET_REMOVENODE;
288 bool succeeded = true;
290 std::map<v3s16, MapBlock*> modified_blocks;
291 removeNodeAndUpdate(p, modified_blocks);
293 // Copy modified_blocks to event
294 for (auto &modified_block : modified_blocks) {
295 event.modified_blocks.insert(modified_block.first);
298 catch(InvalidPositionException &e){
302 dispatchEvent(event);
307 struct TimeOrderedMapBlock {
311 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
316 bool operator<(const TimeOrderedMapBlock &b) const
318 return block->getUsageTimer() < b.block->getUsageTimer();
325 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
326 std::vector<v3s16> *unloaded_blocks)
328 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
330 // Profile modified reasons
331 Profiler modprofiler;
333 std::vector<v2s16> sector_deletion_queue;
334 u32 deleted_blocks_count = 0;
335 u32 saved_blocks_count = 0;
336 u32 block_count_all = 0;
340 // If there is no practical limit, we spare creation of mapblock_queue
341 if (max_loaded_blocks == U32_MAX) {
342 for (auto §or_it : m_sectors) {
343 MapSector *sector = sector_it.second;
345 bool all_blocks_deleted = true;
348 sector->getBlocks(blocks);
350 for (MapBlock *block : blocks) {
351 block->incrementUsageTimer(dtime);
353 if (block->refGet() == 0
354 && block->getUsageTimer() > unload_timeout) {
355 v3s16 p = block->getPos();
358 if (block->getModified() != MOD_STATE_CLEAN
359 && save_before_unloading) {
360 modprofiler.add(block->getModifiedReasonString(), 1);
361 if (!saveBlock(block))
363 saved_blocks_count++;
366 // Delete from memory
367 sector->deleteBlock(block);
370 unloaded_blocks->push_back(p);
372 deleted_blocks_count++;
374 all_blocks_deleted = false;
379 if (all_blocks_deleted) {
380 sector_deletion_queue.push_back(sector_it.first);
384 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
385 for (auto §or_it : m_sectors) {
386 MapSector *sector = sector_it.second;
389 sector->getBlocks(blocks);
391 for (MapBlock *block : blocks) {
392 block->incrementUsageTimer(dtime);
393 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
396 block_count_all = mapblock_queue.size();
397 // Delete old blocks, and blocks over the limit from the memory
398 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
399 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
400 TimeOrderedMapBlock b = mapblock_queue.top();
401 mapblock_queue.pop();
403 MapBlock *block = b.block;
405 if (block->refGet() != 0)
408 v3s16 p = block->getPos();
411 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
412 modprofiler.add(block->getModifiedReasonString(), 1);
413 if (!saveBlock(block))
415 saved_blocks_count++;
418 // Delete from memory
419 b.sect->deleteBlock(block);
422 unloaded_blocks->push_back(p);
424 deleted_blocks_count++;
427 // Delete empty sectors
428 for (auto §or_it : m_sectors) {
429 if (sector_it.second->empty()) {
430 sector_deletion_queue.push_back(sector_it.first);
436 // Finally delete the empty sectors
437 deleteSectors(sector_deletion_queue);
439 if(deleted_blocks_count != 0)
441 PrintInfo(infostream); // ServerMap/ClientMap:
442 infostream<<"Unloaded "<<deleted_blocks_count
443 <<" blocks from memory";
444 if(save_before_unloading)
445 infostream<<", of which "<<saved_blocks_count<<" were written";
446 infostream<<", "<<block_count_all<<" blocks in memory";
447 infostream<<"."<<std::endl;
448 if(saved_blocks_count != 0){
449 PrintInfo(infostream); // ServerMap/ClientMap:
450 infostream<<"Blocks modified by: "<<std::endl;
451 modprofiler.print(infostream);
456 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
458 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
461 void Map::deleteSectors(std::vector<v2s16> §orList)
463 for (v2s16 j : sectorList) {
464 MapSector *sector = m_sectors[j];
465 // If sector is in sector cache, remove it from there
466 if(m_sector_cache == sector)
467 m_sector_cache = NULL;
468 // Remove from map and delete
474 void Map::PrintInfo(std::ostream &out)
479 #define WATER_DROP_BOOST 4
481 enum NeighborType : u8 {
487 struct NodeNeighbor {
493 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
496 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
503 void Map::transforming_liquid_add(v3s16 p) {
504 m_transforming_liquid.push_back(p);
507 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
508 ServerEnvironment *env)
511 u32 initial_size = m_transforming_liquid.size();
513 /*if(initial_size != 0)
514 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
516 // list of nodes that due to viscosity have not reached their max level height
517 std::deque<v3s16> must_reflow;
519 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
521 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
522 u32 loop_max = liquid_loop_max;
526 /* If liquid_loop_max is not keeping up with the queue size increase
527 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
529 if (m_transforming_liquid.size() > loop_max * 2) {
531 float server_step = g_settings->getFloat("dedicated_server_step");
532 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
533 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
535 m_transforming_liquid_loop_count_multiplier = 1.0;
538 loop_max *= m_transforming_liquid_loop_count_multiplier;
541 while (m_transforming_liquid.size() != 0)
543 // This should be done here so that it is done when continue is used
544 if (loopcount >= initial_size || loopcount >= loop_max)
549 Get a queued transforming liquid node
551 v3s16 p0 = m_transforming_liquid.front();
552 m_transforming_liquid.pop_front();
554 MapNode n0 = getNode(p0);
557 Collect information about current node
559 s8 liquid_level = -1;
560 // The liquid node which will be placed there if
561 // the liquid flows into this node.
562 content_t liquid_kind = CONTENT_IGNORE;
563 // The node which will be placed there if liquid
564 // can't flow into this node.
565 content_t floodable_node = CONTENT_AIR;
566 const ContentFeatures &cf = m_nodedef->get(n0);
567 LiquidType liquid_type = cf.liquid_type;
568 switch (liquid_type) {
570 liquid_level = LIQUID_LEVEL_SOURCE;
571 liquid_kind = m_nodedef->getId(cf.liquid_alternative_flowing);
574 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
575 liquid_kind = n0.getContent();
578 // if this node is 'floodable', it *could* be transformed
579 // into a liquid, otherwise, continue with the next node.
582 floodable_node = n0.getContent();
583 liquid_kind = CONTENT_AIR;
588 Collect information about the environment
590 const v3s16 *dirs = g_6dirs;
591 NodeNeighbor sources[6]; // surrounding sources
593 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
595 NodeNeighbor airs[6]; // surrounding air
597 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
598 int num_neutrals = 0;
599 bool flowing_down = false;
600 bool ignored_sources = false;
601 for (u16 i = 0; i < 6; i++) {
602 NeighborType nt = NEIGHBOR_SAME_LEVEL;
613 v3s16 npos = p0 + dirs[i];
614 NodeNeighbor nb(getNode(npos), nt, npos);
615 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
616 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
618 if (cfnb.floodable) {
619 airs[num_airs++] = nb;
620 // if the current node is a water source the neighbor
621 // should be enqueded for transformation regardless of whether the
622 // current node changes or not.
623 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
624 m_transforming_liquid.push_back(npos);
625 // if the current node happens to be a flowing node, it will start to flow down here.
626 if (nb.t == NEIGHBOR_LOWER)
629 neutrals[num_neutrals++] = nb;
630 if (nb.n.getContent() == CONTENT_IGNORE) {
631 // If node below is ignore prevent water from
632 // spreading outwards and otherwise prevent from
633 // flowing away as ignore node might be the source
634 if (nb.t == NEIGHBOR_LOWER)
637 ignored_sources = true;
642 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
643 if (liquid_kind == CONTENT_AIR)
644 liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing);
645 if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
646 neutrals[num_neutrals++] = nb;
648 // Do not count bottom source, it will screw things up
650 sources[num_sources++] = nb;
654 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
655 if (liquid_kind == CONTENT_AIR)
656 liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing);
657 if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
658 neutrals[num_neutrals++] = nb;
660 flows[num_flows++] = nb;
661 if (nb.t == NEIGHBOR_LOWER)
669 decide on the type (and possibly level) of the current node
671 content_t new_node_content;
672 s8 new_node_level = -1;
673 s8 max_node_level = -1;
675 u8 range = m_nodedef->get(liquid_kind).liquid_range;
676 if (range > LIQUID_LEVEL_MAX + 1)
677 range = LIQUID_LEVEL_MAX + 1;
679 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
680 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
681 // or the flowing alternative of the first of the surrounding sources (if it's air), so
682 // it's perfectly safe to use liquid_kind here to determine the new node content.
683 new_node_content = m_nodedef->getId(m_nodedef->get(liquid_kind).liquid_alternative_source);
684 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
685 // liquid_kind is set properly, see above
686 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
687 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
688 new_node_content = liquid_kind;
690 new_node_content = floodable_node;
691 } else if (ignored_sources && liquid_level >= 0) {
692 // Maybe there are neighbouring sources that aren't loaded yet
693 // so prevent flowing away.
694 new_node_level = liquid_level;
695 new_node_content = liquid_kind;
697 // no surrounding sources, so get the maximum level that can flow into this node
698 for (u16 i = 0; i < num_flows; i++) {
699 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
700 switch (flows[i].t) {
702 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
703 max_node_level = LIQUID_LEVEL_MAX;
704 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
705 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
706 } else if (nb_liquid_level > max_node_level) {
707 max_node_level = nb_liquid_level;
712 case NEIGHBOR_SAME_LEVEL:
713 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
714 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
715 max_node_level = nb_liquid_level - 1;
720 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
721 if (viscosity > 1 && max_node_level != liquid_level) {
722 // amount to gain, limited by viscosity
723 // must be at least 1 in absolute value
724 s8 level_inc = max_node_level - liquid_level;
725 if (level_inc < -viscosity || level_inc > viscosity)
726 new_node_level = liquid_level + level_inc/viscosity;
727 else if (level_inc < 0)
728 new_node_level = liquid_level - 1;
729 else if (level_inc > 0)
730 new_node_level = liquid_level + 1;
731 if (new_node_level != max_node_level)
732 must_reflow.push_back(p0);
734 new_node_level = max_node_level;
737 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
738 new_node_content = liquid_kind;
740 new_node_content = floodable_node;
745 check if anything has changed. if not, just continue with the next node.
747 if (new_node_content == n0.getContent() &&
748 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
749 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
750 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
756 update the current node
759 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
760 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
761 // set level to last 3 bits, flowing down bit to 4th bit
762 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
764 // set the liquid level and flow bits to 0
765 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
769 n0.setContent(new_node_content);
771 // on_flood() the node
772 if (floodable_node != CONTENT_AIR) {
773 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
777 // Ignore light (because calling voxalgo::update_lighting_nodes)
778 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
779 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
781 // Find out whether there is a suspect for this action
783 if (m_gamedef->rollback())
784 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
786 if (m_gamedef->rollback() && !suspect.empty()) {
788 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
789 // Get old node for rollback
790 RollbackNode rollback_oldnode(this, p0, m_gamedef);
794 RollbackNode rollback_newnode(this, p0, m_gamedef);
795 RollbackAction action;
796 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
797 m_gamedef->rollback()->reportAction(action);
803 v3s16 blockpos = getNodeBlockPos(p0);
804 MapBlock *block = getBlockNoCreateNoEx(blockpos);
806 modified_blocks[blockpos] = block;
807 changed_nodes.emplace_back(p0, n00);
811 enqueue neighbors for update if neccessary
813 switch (m_nodedef->get(n0.getContent()).liquid_type) {
816 // make sure source flows into all neighboring nodes
817 for (u16 i = 0; i < num_flows; i++)
818 if (flows[i].t != NEIGHBOR_UPPER)
819 m_transforming_liquid.push_back(flows[i].p);
820 for (u16 i = 0; i < num_airs; i++)
821 if (airs[i].t != NEIGHBOR_UPPER)
822 m_transforming_liquid.push_back(airs[i].p);
825 // this flow has turned to air; neighboring flows might need to do the same
826 for (u16 i = 0; i < num_flows; i++)
827 m_transforming_liquid.push_back(flows[i].p);
831 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
833 for (auto &iter : must_reflow)
834 m_transforming_liquid.push_back(iter);
836 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
839 /* ----------------------------------------------------------------------
840 * Manage the queue so that it does not grow indefinately
842 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
844 if (time_until_purge == 0)
845 return; // Feature disabled
847 time_until_purge *= 1000; // seconds -> milliseconds
849 u64 curr_time = porting::getTimeMs();
850 u32 prev_unprocessed = m_unprocessed_count;
851 m_unprocessed_count = m_transforming_liquid.size();
853 // if unprocessed block count is decreasing or stable
854 if (m_unprocessed_count <= prev_unprocessed) {
855 m_queue_size_timer_started = false;
857 if (!m_queue_size_timer_started)
858 m_inc_trending_up_start_time = curr_time;
859 m_queue_size_timer_started = true;
862 // Account for curr_time overflowing
863 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
864 m_queue_size_timer_started = false;
866 /* If the queue has been growing for more than liquid_queue_purge_time seconds
867 * and the number of unprocessed blocks is still > liquid_loop_max then we
868 * cannot keep up; dump the oldest blocks from the queue so that the queue
869 * has liquid_loop_max items in it
871 if (m_queue_size_timer_started
872 && curr_time - m_inc_trending_up_start_time > time_until_purge
873 && m_unprocessed_count > liquid_loop_max) {
875 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
877 infostream << "transformLiquids(): DUMPING " << dump_qty
878 << " blocks from the queue" << std::endl;
881 m_transforming_liquid.pop_front();
883 m_queue_size_timer_started = false; // optimistically assume we can keep up now
884 m_unprocessed_count = m_transforming_liquid.size();
888 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
890 std::vector<v3s16> positions_with_meta;
892 sortBoxVerticies(p1, p2);
893 v3s16 bpmin = getNodeBlockPos(p1);
894 v3s16 bpmax = getNodeBlockPos(p2);
896 VoxelArea area(p1, p2);
898 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
899 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
900 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
901 v3s16 blockpos(x, y, z);
903 MapBlock *block = getBlockNoCreateNoEx(blockpos);
905 verbosestream << "Map::getNodeMetadata(): Need to emerge "
906 << PP(blockpos) << std::endl;
907 block = emergeBlock(blockpos, false);
910 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
915 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
916 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
917 for (size_t i = 0; i != keys.size(); i++) {
918 v3s16 p(keys[i] + p_base);
919 if (!area.contains(p))
922 positions_with_meta.push_back(p);
926 return positions_with_meta;
929 NodeMetadata *Map::getNodeMetadata(v3s16 p)
931 v3s16 blockpos = getNodeBlockPos(p);
932 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
933 MapBlock *block = getBlockNoCreateNoEx(blockpos);
935 infostream<<"Map::getNodeMetadata(): Need to emerge "
936 <<PP(blockpos)<<std::endl;
937 block = emergeBlock(blockpos, false);
940 warningstream<<"Map::getNodeMetadata(): Block not found"
944 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
948 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
950 v3s16 blockpos = getNodeBlockPos(p);
951 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
952 MapBlock *block = getBlockNoCreateNoEx(blockpos);
954 infostream<<"Map::setNodeMetadata(): Need to emerge "
955 <<PP(blockpos)<<std::endl;
956 block = emergeBlock(blockpos, false);
959 warningstream<<"Map::setNodeMetadata(): Block not found"
963 block->m_node_metadata.set(p_rel, meta);
967 void Map::removeNodeMetadata(v3s16 p)
969 v3s16 blockpos = getNodeBlockPos(p);
970 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
971 MapBlock *block = getBlockNoCreateNoEx(blockpos);
974 warningstream<<"Map::removeNodeMetadata(): Block not found"
978 block->m_node_metadata.remove(p_rel);
981 NodeTimer Map::getNodeTimer(v3s16 p)
983 v3s16 blockpos = getNodeBlockPos(p);
984 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
985 MapBlock *block = getBlockNoCreateNoEx(blockpos);
987 infostream<<"Map::getNodeTimer(): Need to emerge "
988 <<PP(blockpos)<<std::endl;
989 block = emergeBlock(blockpos, false);
992 warningstream<<"Map::getNodeTimer(): Block not found"
996 NodeTimer t = block->m_node_timers.get(p_rel);
997 NodeTimer nt(t.timeout, t.elapsed, p);
1001 void Map::setNodeTimer(const NodeTimer &t)
1003 v3s16 p = t.position;
1004 v3s16 blockpos = getNodeBlockPos(p);
1005 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1006 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1008 infostream<<"Map::setNodeTimer(): Need to emerge "
1009 <<PP(blockpos)<<std::endl;
1010 block = emergeBlock(blockpos, false);
1013 warningstream<<"Map::setNodeTimer(): Block not found"
1017 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1018 block->m_node_timers.set(nt);
1021 void Map::removeNodeTimer(v3s16 p)
1023 v3s16 blockpos = getNodeBlockPos(p);
1024 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1025 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1028 warningstream<<"Map::removeNodeTimer(): Block not found"
1032 block->m_node_timers.remove(p_rel);
1035 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1036 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1039 This functions determines the node inside the target block that is
1040 closest to the camera position. This increases the occlusion culling
1041 accuracy in straight and diagonal corridors.
1042 The returned position will be occlusion checked first in addition to the
1043 others (8 corners + center).
1044 No position is returned if
1045 - the closest node is a corner, corners are checked anyway.
1046 - the camera is inside the target block, it will never be occluded.
1048 #define CLOSEST_EDGE(pos, bounds, axis) \
1049 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1050 (bounds).MaxEdge.axis
1052 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1053 (pos_camera.X <= block_bounds.MaxEdge.X);
1054 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1055 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1056 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1057 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1059 if (x_inside && y_inside && z_inside)
1060 return false; // Camera inside target mapblock
1063 if (x_inside && y_inside) {
1064 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1065 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1067 } else if (y_inside && z_inside) {
1068 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1069 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1071 } else if (x_inside && z_inside) {
1072 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1073 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1079 check = v3s16(pos_camera.X, 0, 0);
1080 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1081 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1083 } else if (y_inside) {
1084 check = v3s16(0, pos_camera.Y, 0);
1085 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1086 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1088 } else if (z_inside) {
1089 check = v3s16(0, 0, pos_camera.Z);
1090 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1091 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1095 // Closest node would be a corner, none returned
1099 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1100 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1102 v3f direction = intToFloat(pos_target - pos_camera, BS);
1103 float distance = direction.getLength();
1105 // Normalize direction vector
1106 if (distance > 0.0f)
1107 direction /= distance;
1109 v3f pos_origin_f = intToFloat(pos_camera, BS);
1111 bool is_valid_position;
1113 for (; offset < distance + end_offset; offset += step) {
1114 v3f pos_node_f = pos_origin_f + direction * offset;
1115 v3s16 pos_node = floatToInt(pos_node_f, BS);
1117 MapNode node = getNode(pos_node, &is_valid_position);
1119 if (is_valid_position &&
1120 !m_nodedef->get(node).light_propagates) {
1121 // Cannot see through light-blocking nodes --> occluded
1123 if (count >= needed_count)
1131 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1133 // Check occlusion for center and all 8 corners of the mapblock
1134 // Overshoot a little for less flickering
1135 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1136 static const v3s16 dir9[9] = {
1138 v3s16( 1, 1, 1) * bs2,
1139 v3s16( 1, 1, -1) * bs2,
1140 v3s16( 1, -1, 1) * bs2,
1141 v3s16( 1, -1, -1) * bs2,
1142 v3s16(-1, 1, 1) * bs2,
1143 v3s16(-1, 1, -1) * bs2,
1144 v3s16(-1, -1, 1) * bs2,
1145 v3s16(-1, -1, -1) * bs2,
1148 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1150 // Starting step size, value between 1m and sqrt(3)m
1151 float step = BS * 1.2f;
1152 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1153 float stepfac = 1.05f;
1155 float start_offset = BS * 1.0f;
1157 // The occlusion search of 'isOccluded()' must stop short of the target
1158 // point by distance 'end_offset' to not enter the target mapblock.
1159 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1160 // diagonal of a mapblock, because we must consider all view angles.
1161 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1162 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1164 // to reduce the likelihood of falsely occluded blocks
1165 // require at least two solid blocks
1166 // this is a HACK, we should think of a more precise algorithm
1167 u32 needed_count = 2;
1169 // Additional occlusion check, see comments in that function
1171 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1172 // node is always on a side facing the camera, end_offset can be lower
1173 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1174 -1.0f, needed_count))
1178 for (const v3s16 &dir : dir9) {
1179 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1180 start_offset, end_offset, needed_count))
1189 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1190 EmergeManager *emerge):
1191 Map(dout_server, gamedef),
1192 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1195 verbosestream<<FUNCTION_NAME<<std::endl;
1197 // Tell the EmergeManager about our MapSettingsManager
1198 emerge->map_settings_mgr = &settings_mgr;
1201 Try to load map; if not found, create a new one.
1204 // Determine which database backend to use
1205 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1207 bool succeeded = conf.readConfigFile(conf_path.c_str());
1208 if (!succeeded || !conf.exists("backend")) {
1209 // fall back to sqlite3
1210 conf.set("backend", "sqlite3");
1212 std::string backend = conf.get("backend");
1213 dbase = createDatabase(backend, savedir, conf);
1214 if (conf.exists("readonly_backend")) {
1215 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1216 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1218 if (!conf.updateConfigFile(conf_path.c_str()))
1219 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1221 m_savedir = savedir;
1222 m_map_saving_enabled = false;
1225 // If directory exists, check contents and load if possible
1226 if (fs::PathExists(m_savedir)) {
1227 // If directory is empty, it is safe to save into it.
1228 if (fs::GetDirListing(m_savedir).empty()) {
1229 infostream<<"ServerMap: Empty save directory is valid."
1231 m_map_saving_enabled = true;
1236 if (settings_mgr.loadMapMeta()) {
1237 infostream << "ServerMap: Metadata loaded from "
1238 << savedir << std::endl;
1240 infostream << "ServerMap: Metadata could not be loaded "
1241 "from " << savedir << ", assuming valid save "
1242 "directory." << std::endl;
1245 m_map_saving_enabled = true;
1246 // Map loaded, not creating new one
1250 // If directory doesn't exist, it is safe to save to it
1252 m_map_saving_enabled = true;
1255 catch(std::exception &e)
1257 warningstream<<"ServerMap: Failed to load map from "<<savedir
1258 <<", exception: "<<e.what()<<std::endl;
1259 infostream<<"Please remove the map or fix it."<<std::endl;
1260 warningstream<<"Map saving will be disabled."<<std::endl;
1264 ServerMap::~ServerMap()
1266 verbosestream<<FUNCTION_NAME<<std::endl;
1270 if (m_map_saving_enabled) {
1271 // Save only changed parts
1272 save(MOD_STATE_WRITE_AT_UNLOAD);
1273 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1275 infostream << "ServerMap: Map not saved" << std::endl;
1278 catch(std::exception &e)
1280 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1281 <<", exception: "<<e.what()<<std::endl;
1285 Close database if it was opened
1294 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1295 for(; i.atEnd() == false; i++)
1297 MapChunk *chunk = i.getNode()->getValue();
1303 MapgenParams *ServerMap::getMapgenParams()
1305 // getMapgenParams() should only ever be called after Server is initialized
1306 assert(settings_mgr.mapgen_params != NULL);
1307 return settings_mgr.mapgen_params;
1310 u64 ServerMap::getSeed()
1312 return getMapgenParams()->seed;
1315 s16 ServerMap::getWaterLevel()
1317 return getMapgenParams()->water_level;
1320 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1322 const s16 mapgen_limit_bp = rangelim(
1323 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1325 return p.X < -mapgen_limit_bp ||
1326 p.X > mapgen_limit_bp ||
1327 p.Y < -mapgen_limit_bp ||
1328 p.Y > mapgen_limit_bp ||
1329 p.Z < -mapgen_limit_bp ||
1330 p.Z > mapgen_limit_bp;
1333 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1335 s16 csize = getMapgenParams()->chunksize;
1336 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1337 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
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->blockpos_requested = blockpos;
1355 data->nodedef = m_nodedef;
1358 Create the whole area of this and the neighboring blocks
1360 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1361 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1362 v2s16 sectorpos(x, z);
1363 // Sector metadata is loaded from disk if not already loaded.
1364 MapSector *sector = createSector(sectorpos);
1365 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1367 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1370 MapBlock *block = emergeBlock(p, false);
1371 if (block == NULL) {
1372 block = createBlock(p);
1374 // Block gets sunlight if this is true.
1375 // Refer to the map generator heuristics.
1376 bool ug = m_emerge->isBlockUnderground(p);
1377 block->setIsUnderground(ug);
1383 Now we have a big empty area.
1385 Make a ManualMapVoxelManipulator that contains this and the
1389 data->vmanip = new MMVManip(this);
1390 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1392 // Note: we may need this again at some point.
1394 // Ensure none of the blocks to be generated were marked as
1395 // containing CONTENT_IGNORE
1396 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1397 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1398 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1399 core::map<v3s16, u8>::Node *n;
1400 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1403 u8 flags = n->getValue();
1404 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1411 // Data is ready now.
1415 void ServerMap::finishBlockMake(BlockMakeData *data,
1416 std::map<v3s16, MapBlock*> *changed_blocks)
1418 v3s16 bpmin = data->blockpos_min;
1419 v3s16 bpmax = data->blockpos_max;
1421 v3s16 extra_borders(1, 1, 1);
1423 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1424 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1427 Blit generated stuff to map
1428 NOTE: blitBackAll adds nearly everything to changed_blocks
1430 data->vmanip->blitBackAll(changed_blocks);
1432 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1433 << changed_blocks->size());
1436 Copy transforming liquid information
1438 while (data->transforming_liquid.size()) {
1439 m_transforming_liquid.push_back(data->transforming_liquid.front());
1440 data->transforming_liquid.pop_front();
1443 for (auto &changed_block : *changed_blocks) {
1444 MapBlock *block = changed_block.second;
1448 Update day/night difference cache of the MapBlocks
1450 block->expireDayNightDiff();
1452 Set block as modified
1454 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1455 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1459 Set central blocks as generated
1461 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1462 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1463 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1464 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1468 block->setGenerated(true);
1472 Save changed parts of map
1473 NOTE: Will be saved later.
1475 //save(MOD_STATE_WRITE_AT_UNLOAD);
1478 MapSector *ServerMap::createSector(v2s16 p2d)
1481 Check if it exists already in memory
1483 MapSector *sector = getSectorNoGenerate(p2d);
1488 Do not create over max mapgen limit
1490 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1491 if (p2d.X < -max_limit_bp ||
1492 p2d.X > max_limit_bp ||
1493 p2d.Y < -max_limit_bp ||
1494 p2d.Y > max_limit_bp)
1495 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1498 Generate blank sector
1501 sector = new MapSector(this, p2d, m_gamedef);
1503 // Sector position on map in nodes
1504 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1509 m_sectors[p2d] = sector;
1516 This is a quick-hand function for calling makeBlock().
1518 MapBlock * ServerMap::generateBlock(
1520 std::map<v3s16, MapBlock*> &modified_blocks
1523 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
1525 TimeTaker timer("generateBlock");
1527 //MapBlock *block = original_dummy;
1529 v2s16 p2d(p.X, p.Z);
1530 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
1533 Do not generate over-limit
1535 if(blockpos_over_limit(p))
1537 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
1538 throw InvalidPositionException("generateBlock(): pos. over limit");
1542 Create block make data
1545 initBlockMake(&data, p);
1551 TimeTaker t("mapgen::make_block()");
1552 mapgen->makeChunk(&data);
1553 //mapgen::make_block(&data);
1555 if(enable_mapgen_debug_info == false)
1556 t.stop(true); // Hide output
1560 Blit data back on map, update lighting, add mobs and whatever this does
1562 finishBlockMake(&data, modified_blocks);
1567 MapBlock *block = getBlockNoCreateNoEx(p);
1575 bool erroneus_content = false;
1576 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1577 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1578 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1581 MapNode n = block->getNode(p);
1582 if(n.getContent() == CONTENT_IGNORE)
1584 infostream<<"CONTENT_IGNORE at "
1585 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1587 erroneus_content = true;
1591 if(erroneus_content)
1600 Generate a completely empty block
1604 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1605 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1607 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1610 n.setContent(CONTENT_AIR);
1611 block->setNode(v3s16(x0,y0,z0), n);
1617 if(enable_mapgen_debug_info == false)
1618 timer.stop(true); // Hide output
1624 MapBlock * ServerMap::createBlock(v3s16 p)
1627 Do not create over max mapgen limit
1629 if (blockpos_over_max_limit(p))
1630 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1632 v2s16 p2d(p.X, p.Z);
1635 This will create or load a sector if not found in memory.
1636 If block exists on disk, it will be loaded.
1638 NOTE: On old save formats, this will be slow, as it generates
1639 lighting on blocks for them.
1643 sector = createSector(p2d);
1644 } catch (InvalidPositionException &e) {
1645 infostream<<"createBlock: createSector() failed"<<std::endl;
1650 Try to get a block from the sector
1653 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1655 if(block->isDummy())
1660 block = sector->createBlankBlock(block_y);
1665 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1668 MapBlock *block = getBlockNoCreateNoEx(p);
1669 if (block && !block->isDummy())
1674 MapBlock *block = loadBlock(p);
1680 MapSector *sector = createSector(v2s16(p.X, p.Z));
1681 MapBlock *block = sector->createBlankBlock(p.Y);
1689 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1691 MapBlock *block = getBlockNoCreateNoEx(p3d);
1693 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1698 // N.B. This requires no synchronization, since data will not be modified unless
1699 // the VoxelManipulator being updated belongs to the same thread.
1700 void ServerMap::updateVManip(v3s16 pos)
1702 Mapgen *mg = m_emerge->getCurrentMapgen();
1706 MMVManip *vm = mg->vm;
1710 if (!vm->m_area.contains(pos))
1713 s32 idx = vm->m_area.index(pos);
1714 vm->m_data[idx] = getNode(pos);
1715 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1717 vm->m_is_dirty = true;
1720 s16 ServerMap::findGroundLevel(v2s16 p2d)
1724 Uh, just do something random...
1726 // Find existing map from top to down
1729 v3s16 p(p2d.X, max, p2d.Y);
1730 for(; p.Y>min; p.Y--)
1732 MapNode n = getNodeNoEx(p);
1733 if(n.getContent() != CONTENT_IGNORE)
1738 // If this node is not air, go to plan b
1739 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
1741 // Search existing walkable and return it
1742 for(; p.Y>min; p.Y--)
1744 MapNode n = getNodeNoEx(p);
1745 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
1754 Determine from map generator noise functions
1757 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
1760 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
1761 //return (s16)level;
1764 void ServerMap::createDirs(const std::string &path)
1766 if (!fs::CreateAllDirs(path)) {
1767 m_dout<<"ServerMap: Failed to create directory "
1768 <<"\""<<path<<"\""<<std::endl;
1769 throw BaseException("ServerMap failed to create directory");
1773 void ServerMap::save(ModifiedState save_level)
1775 if (!m_map_saving_enabled) {
1776 warningstream<<"Not saving map, saving disabled."<<std::endl;
1780 if(save_level == MOD_STATE_CLEAN)
1781 infostream<<"ServerMap: Saving whole map, this can take time."
1784 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1785 if (settings_mgr.saveMapMeta())
1786 m_map_metadata_changed = false;
1789 // Profile modified reasons
1790 Profiler modprofiler;
1792 u32 block_count = 0;
1793 u32 block_count_all = 0; // Number of blocks in memory
1795 // Don't do anything with sqlite unless something is really saved
1796 bool save_started = false;
1798 for (auto §or_it : m_sectors) {
1799 MapSector *sector = sector_it.second;
1801 MapBlockVect blocks;
1802 sector->getBlocks(blocks);
1804 for (MapBlock *block : blocks) {
1807 if(block->getModified() >= (u32)save_level) {
1811 save_started = true;
1814 modprofiler.add(block->getModifiedReasonString(), 1);
1826 Only print if something happened or saved whole map
1828 if(save_level == MOD_STATE_CLEAN
1829 || block_count != 0) {
1830 infostream << "ServerMap: Written: "
1831 << block_count << " blocks"
1832 << ", " << block_count_all << " blocks in memory."
1834 PrintInfo(infostream); // ServerMap/ClientMap:
1835 infostream<<"Blocks modified by: "<<std::endl;
1836 modprofiler.print(infostream);
1840 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1842 dbase->listAllLoadableBlocks(dst);
1844 dbase_ro->listAllLoadableBlocks(dst);
1847 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1849 for (auto §or_it : m_sectors) {
1850 MapSector *sector = sector_it.second;
1852 MapBlockVect blocks;
1853 sector->getBlocks(blocks);
1855 for (MapBlock *block : blocks) {
1856 v3s16 p = block->getPos();
1862 MapDatabase *ServerMap::createDatabase(
1863 const std::string &name,
1864 const std::string &savedir,
1867 if (name == "sqlite3")
1868 return new MapDatabaseSQLite3(savedir);
1869 if (name == "dummy")
1870 return new Database_Dummy();
1872 if (name == "leveldb")
1873 return new Database_LevelDB(savedir);
1876 if (name == "redis")
1877 return new Database_Redis(conf);
1880 if (name == "postgresql") {
1881 std::string connect_string;
1882 conf.getNoEx("pgsql_connection", connect_string);
1883 return new MapDatabasePostgreSQL(connect_string);
1887 throw BaseException(std::string("Database backend ") + name + " not supported.");
1890 void ServerMap::beginSave()
1895 void ServerMap::endSave()
1900 bool ServerMap::saveBlock(MapBlock *block)
1902 return saveBlock(block, dbase);
1905 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
1907 v3s16 p3d = block->getPos();
1909 // Dummy blocks are not written
1910 if (block->isDummy()) {
1911 warningstream << "saveBlock: Not writing dummy block "
1912 << PP(p3d) << std::endl;
1916 // Format used for writing
1917 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1920 [0] u8 serialization version
1923 std::ostringstream o(std::ios_base::binary);
1924 o.write((char*) &version, 1);
1925 block->serialize(o, version, true);
1927 bool ret = db->saveBlock(p3d, o.str());
1929 // We just wrote it to the disk so clear modified flag
1930 block->resetModified();
1935 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1938 std::istringstream is(*blob, std::ios_base::binary);
1940 u8 version = SER_FMT_VER_INVALID;
1941 is.read((char*)&version, 1);
1944 throw SerializationError("ServerMap::loadBlock(): Failed"
1945 " to read MapBlock version");
1947 MapBlock *block = NULL;
1948 bool created_new = false;
1949 block = sector->getBlockNoCreateNoEx(p3d.Y);
1952 block = sector->createBlankBlockNoInsert(p3d.Y);
1957 block->deSerialize(is, version, true);
1959 // If it's a new block, insert it to the map
1961 sector->insertBlock(block);
1962 ReflowScan scanner(this, m_emerge->ndef);
1963 scanner.scan(block, &m_transforming_liquid);
1967 Save blocks loaded in old format in new format
1970 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1971 // Only save if asked to; no need to update version
1975 // We just loaded it from, so it's up-to-date.
1976 block->resetModified();
1978 catch(SerializationError &e)
1980 errorstream<<"Invalid block data in database"
1981 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1982 <<" (SerializationError): "<<e.what()<<std::endl;
1984 // TODO: Block should be marked as invalid in memory so that it is
1985 // not touched but the game can run
1987 if(g_settings->getBool("ignore_world_load_errors")){
1988 errorstream<<"Ignoring block load error. Duck and cover! "
1989 <<"(ignore_world_load_errors)"<<std::endl;
1991 throw SerializationError("Invalid block data in database");
1996 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1998 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
2000 v2s16 p2d(blockpos.X, blockpos.Z);
2003 dbase->loadBlock(blockpos, &ret);
2005 loadBlock(&ret, blockpos, createSector(p2d), false);
2006 } else if (dbase_ro) {
2007 dbase_ro->loadBlock(blockpos, &ret);
2009 loadBlock(&ret, blockpos, createSector(p2d), false);
2015 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2016 if (created_new && (block != NULL)) {
2017 std::map<v3s16, MapBlock*> modified_blocks;
2018 // Fix lighting if necessary
2019 voxalgo::update_block_border_lighting(this, block, modified_blocks);
2020 if (!modified_blocks.empty()) {
2021 //Modified lighting, send event
2023 event.type = MEET_OTHER;
2024 std::map<v3s16, MapBlock *>::iterator it;
2025 for (it = modified_blocks.begin();
2026 it != modified_blocks.end(); ++it)
2027 event.modified_blocks.insert(it->first);
2028 dispatchEvent(event);
2034 bool ServerMap::deleteBlock(v3s16 blockpos)
2036 if (!dbase->deleteBlock(blockpos))
2039 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2041 v2s16 p2d(blockpos.X, blockpos.Z);
2042 MapSector *sector = getSectorNoGenerate(p2d);
2045 sector->deleteBlock(block);
2051 void ServerMap::PrintInfo(std::ostream &out)
2056 bool ServerMap::repairBlockLight(v3s16 blockpos,
2057 std::map<v3s16, MapBlock *> *modified_blocks)
2059 MapBlock *block = emergeBlock(blockpos, false);
2060 if (!block || !block->isGenerated())
2062 voxalgo::repair_block_light(this, block, modified_blocks);
2066 MMVManip::MMVManip(Map *map):
2072 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2073 bool load_if_inexistent)
2075 TimeTaker timer1("initialEmerge", &emerge_time);
2077 // Units of these are MapBlocks
2078 v3s16 p_min = blockpos_min;
2079 v3s16 p_max = blockpos_max;
2081 VoxelArea block_area_nodes
2082 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2084 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2087 infostream<<"initialEmerge: area: ";
2088 block_area_nodes.print(infostream);
2089 infostream<<" ("<<size_MB<<"MB)";
2090 infostream<<std::endl;
2093 addArea(block_area_nodes);
2095 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2096 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2097 for(s32 x=p_min.X; x<=p_max.X; x++)
2102 std::map<v3s16, u8>::iterator n;
2103 n = m_loaded_blocks.find(p);
2104 if(n != m_loaded_blocks.end())
2107 bool block_data_inexistent = false;
2109 TimeTaker timer2("emerge load", &emerge_load_time);
2111 block = m_map->getBlockNoCreateNoEx(p);
2112 if (!block || block->isDummy())
2113 block_data_inexistent = true;
2115 block->copyTo(*this);
2118 if(block_data_inexistent)
2121 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2122 ServerMap *svrmap = (ServerMap *)m_map;
2123 block = svrmap->emergeBlock(p, false);
2125 block = svrmap->createBlock(p);
2126 block->copyTo(*this);
2128 flags |= VMANIP_BLOCK_DATA_INEXIST;
2131 Mark area inexistent
2133 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2134 // Fill with VOXELFLAG_NO_DATA
2135 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2136 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2138 s32 i = m_area.index(a.MinEdge.X,y,z);
2139 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2143 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2145 // Mark that block was loaded as blank
2146 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2149 m_loaded_blocks[p] = flags;
2155 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2156 bool overwrite_generated)
2158 if(m_area.getExtent() == v3s16(0,0,0))
2162 Copy data of all blocks
2164 for (auto &loaded_block : m_loaded_blocks) {
2165 v3s16 p = loaded_block.first;
2166 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2167 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2168 if (!existed || (block == NULL) ||
2169 (!overwrite_generated && block->isGenerated()))
2172 block->copyFrom(*this);
2173 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2176 (*modified_blocks)[p] = block;