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 bit 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
1295 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1296 for(; i.atEnd() == false; i++)
1298 MapChunk *chunk = i.getNode()->getValue();
1304 MapgenParams *ServerMap::getMapgenParams()
1306 // getMapgenParams() should only ever be called after Server is initialized
1307 assert(settings_mgr.mapgen_params != NULL);
1308 return settings_mgr.mapgen_params;
1311 u64 ServerMap::getSeed()
1313 return getMapgenParams()->seed;
1316 s16 ServerMap::getWaterLevel()
1318 return getMapgenParams()->water_level;
1321 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1323 const s16 mapgen_limit_bp = rangelim(
1324 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1326 return p.X < -mapgen_limit_bp ||
1327 p.X > mapgen_limit_bp ||
1328 p.Y < -mapgen_limit_bp ||
1329 p.Y > mapgen_limit_bp ||
1330 p.Z < -mapgen_limit_bp ||
1331 p.Z > mapgen_limit_bp;
1334 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1336 s16 csize = getMapgenParams()->chunksize;
1337 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1338 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1340 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1341 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1343 v3s16 extra_borders(1, 1, 1);
1344 v3s16 full_bpmin = bpmin - extra_borders;
1345 v3s16 full_bpmax = bpmax + extra_borders;
1347 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1348 if (blockpos_over_mapgen_limit(full_bpmin) ||
1349 blockpos_over_mapgen_limit(full_bpmax))
1352 data->seed = getSeed();
1353 data->blockpos_min = bpmin;
1354 data->blockpos_max = bpmax;
1355 data->blockpos_requested = blockpos;
1356 data->nodedef = m_nodedef;
1359 Create the whole area of this and the neighboring blocks
1361 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1362 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1363 v2s16 sectorpos(x, z);
1364 // Sector metadata is loaded from disk if not already loaded.
1365 MapSector *sector = createSector(sectorpos);
1366 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1368 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1371 MapBlock *block = emergeBlock(p, false);
1372 if (block == NULL) {
1373 block = createBlock(p);
1375 // Block gets sunlight if this is true.
1376 // Refer to the map generator heuristics.
1377 bool ug = m_emerge->isBlockUnderground(p);
1378 block->setIsUnderground(ug);
1384 Now we have a big empty area.
1386 Make a ManualMapVoxelManipulator that contains this and the
1390 data->vmanip = new MMVManip(this);
1391 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1393 // Note: we may need this again at some point.
1395 // Ensure none of the blocks to be generated were marked as
1396 // containing CONTENT_IGNORE
1397 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1398 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1399 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1400 core::map<v3s16, u8>::Node *n;
1401 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1404 u8 flags = n->getValue();
1405 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1412 // Data is ready now.
1416 void ServerMap::finishBlockMake(BlockMakeData *data,
1417 std::map<v3s16, MapBlock*> *changed_blocks)
1419 v3s16 bpmin = data->blockpos_min;
1420 v3s16 bpmax = data->blockpos_max;
1422 v3s16 extra_borders(1, 1, 1);
1424 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1425 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1428 Blit generated stuff to map
1429 NOTE: blitBackAll adds nearly everything to changed_blocks
1431 data->vmanip->blitBackAll(changed_blocks);
1433 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1434 << changed_blocks->size());
1437 Copy transforming liquid information
1439 while (data->transforming_liquid.size()) {
1440 m_transforming_liquid.push_back(data->transforming_liquid.front());
1441 data->transforming_liquid.pop_front();
1444 for (auto &changed_block : *changed_blocks) {
1445 MapBlock *block = changed_block.second;
1449 Update day/night difference cache of the MapBlocks
1451 block->expireDayNightDiff();
1453 Set block as modified
1455 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1456 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1460 Set central blocks as generated
1462 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1463 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1464 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1465 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1469 block->setGenerated(true);
1473 Save changed parts of map
1474 NOTE: Will be saved later.
1476 //save(MOD_STATE_WRITE_AT_UNLOAD);
1479 MapSector *ServerMap::createSector(v2s16 p2d)
1482 Check if it exists already in memory
1484 MapSector *sector = getSectorNoGenerate(p2d);
1489 Do not create over max mapgen limit
1491 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1492 if (p2d.X < -max_limit_bp ||
1493 p2d.X > max_limit_bp ||
1494 p2d.Y < -max_limit_bp ||
1495 p2d.Y > max_limit_bp)
1496 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1499 Generate blank sector
1502 sector = new MapSector(this, p2d, m_gamedef);
1504 // Sector position on map in nodes
1505 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1510 m_sectors[p2d] = sector;
1517 This is a quick-hand function for calling makeBlock().
1519 MapBlock * ServerMap::generateBlock(
1521 std::map<v3s16, MapBlock*> &modified_blocks
1524 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
1526 TimeTaker timer("generateBlock");
1528 //MapBlock *block = original_dummy;
1530 v2s16 p2d(p.X, p.Z);
1531 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
1534 Do not generate over-limit
1536 if(blockpos_over_limit(p))
1538 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
1539 throw InvalidPositionException("generateBlock(): pos. over limit");
1543 Create block make data
1546 initBlockMake(&data, p);
1552 TimeTaker t("mapgen::make_block()");
1553 mapgen->makeChunk(&data);
1554 //mapgen::make_block(&data);
1556 if(enable_mapgen_debug_info == false)
1557 t.stop(true); // Hide output
1561 Blit data back on map, update lighting, add mobs and whatever this does
1563 finishBlockMake(&data, modified_blocks);
1568 MapBlock *block = getBlockNoCreateNoEx(p);
1576 bool erroneus_content = false;
1577 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1578 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1579 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1582 MapNode n = block->getNode(p);
1583 if(n.getContent() == CONTENT_IGNORE)
1585 infostream<<"CONTENT_IGNORE at "
1586 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1588 erroneus_content = true;
1592 if(erroneus_content)
1601 Generate a completely empty block
1605 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1606 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1608 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1611 n.setContent(CONTENT_AIR);
1612 block->setNode(v3s16(x0,y0,z0), n);
1618 if(enable_mapgen_debug_info == false)
1619 timer.stop(true); // Hide output
1625 MapBlock * ServerMap::createBlock(v3s16 p)
1628 Do not create over max mapgen limit
1630 if (blockpos_over_max_limit(p))
1631 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1633 v2s16 p2d(p.X, p.Z);
1636 This will create or load a sector if not found in memory.
1637 If block exists on disk, it will be loaded.
1639 NOTE: On old save formats, this will be slow, as it generates
1640 lighting on blocks for them.
1644 sector = createSector(p2d);
1645 } catch (InvalidPositionException &e) {
1646 infostream<<"createBlock: createSector() failed"<<std::endl;
1651 Try to get a block from the sector
1654 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1656 if(block->isDummy())
1661 block = sector->createBlankBlock(block_y);
1666 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1669 MapBlock *block = getBlockNoCreateNoEx(p);
1670 if (block && !block->isDummy())
1675 MapBlock *block = loadBlock(p);
1681 MapSector *sector = createSector(v2s16(p.X, p.Z));
1682 MapBlock *block = sector->createBlankBlock(p.Y);
1690 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1692 MapBlock *block = getBlockNoCreateNoEx(p3d);
1694 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1699 // N.B. This requires no synchronization, since data will not be modified unless
1700 // the VoxelManipulator being updated belongs to the same thread.
1701 void ServerMap::updateVManip(v3s16 pos)
1703 Mapgen *mg = m_emerge->getCurrentMapgen();
1707 MMVManip *vm = mg->vm;
1711 if (!vm->m_area.contains(pos))
1714 s32 idx = vm->m_area.index(pos);
1715 vm->m_data[idx] = getNode(pos);
1716 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1718 vm->m_is_dirty = true;
1721 s16 ServerMap::findGroundLevel(v2s16 p2d)
1725 Uh, just do something random...
1727 // Find existing map from top to down
1730 v3s16 p(p2d.X, max, p2d.Y);
1731 for(; p.Y>min; p.Y--)
1733 MapNode n = getNodeNoEx(p);
1734 if(n.getContent() != CONTENT_IGNORE)
1739 // If this node is not air, go to plan b
1740 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
1742 // Search existing walkable and return it
1743 for(; p.Y>min; p.Y--)
1745 MapNode n = getNodeNoEx(p);
1746 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
1755 Determine from map generator noise functions
1758 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
1761 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
1762 //return (s16)level;
1765 void ServerMap::createDirs(const std::string &path)
1767 if (!fs::CreateAllDirs(path)) {
1768 m_dout<<"ServerMap: Failed to create directory "
1769 <<"\""<<path<<"\""<<std::endl;
1770 throw BaseException("ServerMap failed to create directory");
1774 void ServerMap::save(ModifiedState save_level)
1776 if (!m_map_saving_enabled) {
1777 warningstream<<"Not saving map, saving disabled."<<std::endl;
1781 if(save_level == MOD_STATE_CLEAN)
1782 infostream<<"ServerMap: Saving whole map, this can take time."
1785 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1786 if (settings_mgr.saveMapMeta())
1787 m_map_metadata_changed = false;
1790 // Profile modified reasons
1791 Profiler modprofiler;
1793 u32 block_count = 0;
1794 u32 block_count_all = 0; // Number of blocks in memory
1796 // Don't do anything with sqlite unless something is really saved
1797 bool save_started = false;
1799 for (auto §or_it : m_sectors) {
1800 MapSector *sector = sector_it.second;
1802 MapBlockVect blocks;
1803 sector->getBlocks(blocks);
1805 for (MapBlock *block : blocks) {
1808 if(block->getModified() >= (u32)save_level) {
1812 save_started = true;
1815 modprofiler.add(block->getModifiedReasonString(), 1);
1827 Only print if something happened or saved whole map
1829 if(save_level == MOD_STATE_CLEAN
1830 || block_count != 0) {
1831 infostream<<"ServerMap: Written: "
1832 <<block_count<<" block files"
1833 <<", "<<block_count_all<<" blocks in memory."
1835 PrintInfo(infostream); // ServerMap/ClientMap:
1836 infostream<<"Blocks modified by: "<<std::endl;
1837 modprofiler.print(infostream);
1841 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1843 dbase->listAllLoadableBlocks(dst);
1845 dbase_ro->listAllLoadableBlocks(dst);
1848 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1850 for (auto §or_it : m_sectors) {
1851 MapSector *sector = sector_it.second;
1853 MapBlockVect blocks;
1854 sector->getBlocks(blocks);
1856 for (MapBlock *block : blocks) {
1857 v3s16 p = block->getPos();
1863 MapDatabase *ServerMap::createDatabase(
1864 const std::string &name,
1865 const std::string &savedir,
1868 if (name == "sqlite3")
1869 return new MapDatabaseSQLite3(savedir);
1870 if (name == "dummy")
1871 return new Database_Dummy();
1873 if (name == "leveldb")
1874 return new Database_LevelDB(savedir);
1877 if (name == "redis")
1878 return new Database_Redis(conf);
1881 if (name == "postgresql") {
1882 std::string connect_string;
1883 conf.getNoEx("pgsql_connection", connect_string);
1884 return new MapDatabasePostgreSQL(connect_string);
1888 throw BaseException(std::string("Database backend ") + name + " not supported.");
1891 void ServerMap::beginSave()
1896 void ServerMap::endSave()
1901 bool ServerMap::saveBlock(MapBlock *block)
1903 return saveBlock(block, dbase);
1906 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
1908 v3s16 p3d = block->getPos();
1910 // Dummy blocks are not written
1911 if (block->isDummy()) {
1912 warningstream << "saveBlock: Not writing dummy block "
1913 << PP(p3d) << std::endl;
1917 // Format used for writing
1918 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1921 [0] u8 serialization version
1924 std::ostringstream o(std::ios_base::binary);
1925 o.write((char*) &version, 1);
1926 block->serialize(o, version, true);
1928 bool ret = db->saveBlock(p3d, o.str());
1930 // We just wrote it to the disk so clear modified flag
1931 block->resetModified();
1936 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1939 std::istringstream is(*blob, std::ios_base::binary);
1941 u8 version = SER_FMT_VER_INVALID;
1942 is.read((char*)&version, 1);
1945 throw SerializationError("ServerMap::loadBlock(): Failed"
1946 " to read MapBlock version");
1948 MapBlock *block = NULL;
1949 bool created_new = false;
1950 block = sector->getBlockNoCreateNoEx(p3d.Y);
1953 block = sector->createBlankBlockNoInsert(p3d.Y);
1958 block->deSerialize(is, version, true);
1960 // If it's a new block, insert it to the map
1962 sector->insertBlock(block);
1963 ReflowScan scanner(this, m_emerge->ndef);
1964 scanner.scan(block, &m_transforming_liquid);
1968 Save blocks loaded in old format in new format
1971 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1972 // Only save if asked to; no need to update version
1976 // We just loaded it from, so it's up-to-date.
1977 block->resetModified();
1979 catch(SerializationError &e)
1981 errorstream<<"Invalid block data in database"
1982 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1983 <<" (SerializationError): "<<e.what()<<std::endl;
1985 // TODO: Block should be marked as invalid in memory so that it is
1986 // not touched but the game can run
1988 if(g_settings->getBool("ignore_world_load_errors")){
1989 errorstream<<"Ignoring block load error. Duck and cover! "
1990 <<"(ignore_world_load_errors)"<<std::endl;
1992 throw SerializationError("Invalid block data in database");
1997 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1999 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
2001 v2s16 p2d(blockpos.X, blockpos.Z);
2004 dbase->loadBlock(blockpos, &ret);
2006 loadBlock(&ret, blockpos, createSector(p2d), false);
2007 } else if (dbase_ro) {
2008 dbase_ro->loadBlock(blockpos, &ret);
2010 loadBlock(&ret, blockpos, createSector(p2d), false);
2016 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2017 if (created_new && (block != NULL)) {
2018 std::map<v3s16, MapBlock*> modified_blocks;
2019 // Fix lighting if necessary
2020 voxalgo::update_block_border_lighting(this, block, modified_blocks);
2021 if (!modified_blocks.empty()) {
2022 //Modified lighting, send event
2024 event.type = MEET_OTHER;
2025 std::map<v3s16, MapBlock *>::iterator it;
2026 for (it = modified_blocks.begin();
2027 it != modified_blocks.end(); ++it)
2028 event.modified_blocks.insert(it->first);
2029 dispatchEvent(event);
2035 bool ServerMap::deleteBlock(v3s16 blockpos)
2037 if (!dbase->deleteBlock(blockpos))
2040 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2042 v2s16 p2d(blockpos.X, blockpos.Z);
2043 MapSector *sector = getSectorNoGenerate(p2d);
2046 sector->deleteBlock(block);
2052 void ServerMap::PrintInfo(std::ostream &out)
2057 bool ServerMap::repairBlockLight(v3s16 blockpos,
2058 std::map<v3s16, MapBlock *> *modified_blocks)
2060 MapBlock *block = emergeBlock(blockpos, false);
2061 if (!block || !block->isGenerated())
2063 voxalgo::repair_block_light(this, block, modified_blocks);
2067 MMVManip::MMVManip(Map *map):
2073 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2074 bool load_if_inexistent)
2076 TimeTaker timer1("initialEmerge", &emerge_time);
2078 // Units of these are MapBlocks
2079 v3s16 p_min = blockpos_min;
2080 v3s16 p_max = blockpos_max;
2082 VoxelArea block_area_nodes
2083 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2085 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2088 infostream<<"initialEmerge: area: ";
2089 block_area_nodes.print(infostream);
2090 infostream<<" ("<<size_MB<<"MB)";
2091 infostream<<std::endl;
2094 addArea(block_area_nodes);
2096 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2097 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2098 for(s32 x=p_min.X; x<=p_max.X; x++)
2103 std::map<v3s16, u8>::iterator n;
2104 n = m_loaded_blocks.find(p);
2105 if(n != m_loaded_blocks.end())
2108 bool block_data_inexistent = false;
2110 TimeTaker timer2("emerge load", &emerge_load_time);
2112 block = m_map->getBlockNoCreateNoEx(p);
2113 if (!block || block->isDummy())
2114 block_data_inexistent = true;
2116 block->copyTo(*this);
2119 if(block_data_inexistent)
2122 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2123 ServerMap *svrmap = (ServerMap *)m_map;
2124 block = svrmap->emergeBlock(p, false);
2126 block = svrmap->createBlock(p);
2127 block->copyTo(*this);
2129 flags |= VMANIP_BLOCK_DATA_INEXIST;
2132 Mark area inexistent
2134 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2135 // Fill with VOXELFLAG_NO_DATA
2136 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2137 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2139 s32 i = m_area.index(a.MinEdge.X,y,z);
2140 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2144 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2146 // Mark that block was loaded as blank
2147 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2150 m_loaded_blocks[p] = flags;
2156 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2157 bool overwrite_generated)
2159 if(m_area.getExtent() == v3s16(0,0,0))
2163 Copy data of all blocks
2165 for (auto &loaded_block : m_loaded_blocks) {
2166 v3s16 p = loaded_block.first;
2167 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2168 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2169 if (!existed || (block == NULL) ||
2170 (!overwrite_generated && block->isGenerated()))
2173 block->copyFrom(*this);
2174 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2177 (*modified_blocks)[p] = block;