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(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 fucks up stuff
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 bool ServerMap::loadFromFolders() {
1766 if (!dbase->initialized() &&
1767 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
1772 void ServerMap::createDirs(const std::string &path)
1774 if (!fs::CreateAllDirs(path)) {
1775 m_dout<<"ServerMap: Failed to create directory "
1776 <<"\""<<path<<"\""<<std::endl;
1777 throw BaseException("ServerMap failed to create directory");
1781 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
1787 porting::mt_snprintf(cc, sizeof(cc), "%.4x%.4x",
1788 (unsigned int) pos.X & 0xffff,
1789 (unsigned int) pos.Y & 0xffff);
1791 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
1793 porting::mt_snprintf(cc, sizeof(cc), (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
1794 (unsigned int) pos.X & 0xfff,
1795 (unsigned int) pos.Y & 0xfff);
1797 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
1804 v2s16 ServerMap::getSectorPos(const std::string &dirname)
1806 unsigned int x = 0, y = 0;
1808 std::string component;
1809 fs::RemoveLastPathComponent(dirname, &component, 1);
1810 if(component.size() == 8)
1813 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
1815 else if(component.size() == 3)
1818 fs::RemoveLastPathComponent(dirname, &component, 2);
1819 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
1820 // Sign-extend the 12 bit values up to 16 bits...
1821 if(x & 0x800) x |= 0xF000;
1822 if(y & 0x800) y |= 0xF000;
1829 FATAL_ERROR_IF(r != 2, "getSectorPos()");
1830 v2s16 pos((s16)x, (s16)y);
1834 v3s16 ServerMap::getBlockPos(const std::string §ordir, const std::string &blockfile)
1836 v2s16 p2d = getSectorPos(sectordir);
1838 if(blockfile.size() != 4){
1839 throw InvalidFilenameException("Invalid block filename");
1842 int r = sscanf(blockfile.c_str(), "%4x", &y);
1844 throw InvalidFilenameException("Invalid block filename");
1845 return v3s16(p2d.X, y, p2d.Y);
1848 std::string ServerMap::getBlockFilename(v3s16 p)
1851 porting::mt_snprintf(cc, sizeof(cc), "%.4x", (unsigned int)p.Y&0xffff);
1855 void ServerMap::save(ModifiedState save_level)
1857 if (!m_map_saving_enabled) {
1858 warningstream<<"Not saving map, saving disabled."<<std::endl;
1862 if(save_level == MOD_STATE_CLEAN)
1863 infostream<<"ServerMap: Saving whole map, this can take time."
1866 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1867 if (settings_mgr.saveMapMeta())
1868 m_map_metadata_changed = false;
1871 // Profile modified reasons
1872 Profiler modprofiler;
1874 u32 block_count = 0;
1875 u32 block_count_all = 0; // Number of blocks in memory
1877 // Don't do anything with sqlite unless something is really saved
1878 bool save_started = false;
1880 for (auto §or_it : m_sectors) {
1881 MapSector *sector = sector_it.second;
1883 MapBlockVect blocks;
1884 sector->getBlocks(blocks);
1886 for (MapBlock *block : blocks) {
1889 if(block->getModified() >= (u32)save_level) {
1893 save_started = true;
1896 modprofiler.add(block->getModifiedReasonString(), 1);
1908 Only print if something happened or saved whole map
1910 if(save_level == MOD_STATE_CLEAN
1911 || block_count != 0) {
1912 infostream<<"ServerMap: Written: "
1913 <<block_count<<" block files"
1914 <<", "<<block_count_all<<" blocks in memory."
1916 PrintInfo(infostream); // ServerMap/ClientMap:
1917 infostream<<"Blocks modified by: "<<std::endl;
1918 modprofiler.print(infostream);
1922 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1924 if (loadFromFolders()) {
1925 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
1926 << "all blocks that are stored in flat files." << std::endl;
1928 dbase->listAllLoadableBlocks(dst);
1930 dbase_ro->listAllLoadableBlocks(dst);
1933 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1935 for (auto §or_it : m_sectors) {
1936 MapSector *sector = sector_it.second;
1938 MapBlockVect blocks;
1939 sector->getBlocks(blocks);
1941 for (MapBlock *block : blocks) {
1942 v3s16 p = block->getPos();
1948 MapDatabase *ServerMap::createDatabase(
1949 const std::string &name,
1950 const std::string &savedir,
1953 if (name == "sqlite3")
1954 return new MapDatabaseSQLite3(savedir);
1955 if (name == "dummy")
1956 return new Database_Dummy();
1958 if (name == "leveldb")
1959 return new Database_LevelDB(savedir);
1962 if (name == "redis")
1963 return new Database_Redis(conf);
1966 if (name == "postgresql") {
1967 std::string connect_string;
1968 conf.getNoEx("pgsql_connection", connect_string);
1969 return new MapDatabasePostgreSQL(connect_string);
1973 throw BaseException(std::string("Database backend ") + name + " not supported.");
1976 void ServerMap::beginSave()
1981 void ServerMap::endSave()
1986 bool ServerMap::saveBlock(MapBlock *block)
1988 return saveBlock(block, dbase);
1991 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
1993 v3s16 p3d = block->getPos();
1995 // Dummy blocks are not written
1996 if (block->isDummy()) {
1997 warningstream << "saveBlock: Not writing dummy block "
1998 << PP(p3d) << std::endl;
2002 // Format used for writing
2003 u8 version = SER_FMT_VER_HIGHEST_WRITE;
2006 [0] u8 serialization version
2009 std::ostringstream o(std::ios_base::binary);
2010 o.write((char*) &version, 1);
2011 block->serialize(o, version, true);
2013 bool ret = db->saveBlock(p3d, o.str());
2015 // We just wrote it to the disk so clear modified flag
2016 block->resetModified();
2021 void ServerMap::loadBlock(const std::string §ordir, const std::string &blockfile,
2022 MapSector *sector, bool save_after_load)
2024 std::string fullpath = sectordir + DIR_DELIM + blockfile;
2026 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2028 throw FileNotGoodException("Cannot open block file");
2030 v3s16 p3d = getBlockPos(sectordir, blockfile);
2031 v2s16 p2d(p3d.X, p3d.Z);
2033 assert(sector->getPos() == p2d);
2035 u8 version = SER_FMT_VER_INVALID;
2036 is.read((char*)&version, 1);
2039 throw SerializationError("ServerMap::loadBlock(): Failed"
2040 " to read MapBlock version");
2042 /*u32 block_size = MapBlock::serializedLength(version);
2043 SharedBuffer<u8> data(block_size);
2044 is.read((char*)*data, block_size);*/
2046 // This will always return a sector because we're the server
2047 //MapSector *sector = emergeSector(p2d);
2049 MapBlock *block = NULL;
2050 bool created_new = false;
2051 block = sector->getBlockNoCreateNoEx(p3d.Y);
2054 block = sector->createBlankBlockNoInsert(p3d.Y);
2059 block->deSerialize(is, version, true);
2061 // If it's a new block, insert it to the map
2063 sector->insertBlock(block);
2064 ReflowScan scanner(this, m_emerge->ndef);
2065 scanner.scan(block, &m_transforming_liquid);
2069 Save blocks loaded in old format in new format
2072 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
2076 // Should be in database now, so delete the old file
2077 fs::RecursiveDelete(fullpath);
2080 // We just loaded it from the disk, so it's up-to-date.
2081 block->resetModified();
2084 catch(SerializationError &e)
2086 warningstream<<"Invalid block data on disk "
2087 <<"fullpath="<<fullpath
2088 <<" (SerializationError). "
2089 <<"what()="<<e.what()
2091 // Ignoring. A new one will be generated.
2094 // TODO: Backup file; name is in fullpath.
2098 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
2101 std::istringstream is(*blob, std::ios_base::binary);
2103 u8 version = SER_FMT_VER_INVALID;
2104 is.read((char*)&version, 1);
2107 throw SerializationError("ServerMap::loadBlock(): Failed"
2108 " to read MapBlock version");
2110 MapBlock *block = NULL;
2111 bool created_new = false;
2112 block = sector->getBlockNoCreateNoEx(p3d.Y);
2115 block = sector->createBlankBlockNoInsert(p3d.Y);
2120 block->deSerialize(is, version, true);
2122 // If it's a new block, insert it to the map
2124 sector->insertBlock(block);
2125 ReflowScan scanner(this, m_emerge->ndef);
2126 scanner.scan(block, &m_transforming_liquid);
2130 Save blocks loaded in old format in new format
2133 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
2134 // Only save if asked to; no need to update version
2138 // We just loaded it from, so it's up-to-date.
2139 block->resetModified();
2141 catch(SerializationError &e)
2143 errorstream<<"Invalid block data in database"
2144 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2145 <<" (SerializationError): "<<e.what()<<std::endl;
2147 // TODO: Block should be marked as invalid in memory so that it is
2148 // not touched but the game can run
2150 if(g_settings->getBool("ignore_world_load_errors")){
2151 errorstream<<"Ignoring block load error. Duck and cover! "
2152 <<"(ignore_world_load_errors)"<<std::endl;
2154 throw SerializationError("Invalid block data in database");
2159 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
2161 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
2163 v2s16 p2d(blockpos.X, blockpos.Z);
2166 dbase->loadBlock(blockpos, &ret);
2168 loadBlock(&ret, blockpos, createSector(p2d), false);
2169 } else if (dbase_ro) {
2170 dbase_ro->loadBlock(blockpos, &ret);
2172 loadBlock(&ret, blockpos, createSector(p2d), false);
2175 // Not found in database, try the files
2177 // The directory layout we're going to load from.
2178 // 1 - original sectors/xxxxzzzz/
2179 // 2 - new sectors2/xxx/zzz/
2180 // If we load from anything but the latest structure, we will
2181 // immediately save to the new one, and remove the old.
2182 std::string sectordir1 = getSectorDir(p2d, 1);
2183 std::string sectordir;
2184 if (fs::PathExists(sectordir1)) {
2185 sectordir = sectordir1;
2187 sectordir = getSectorDir(p2d, 2);
2191 Make sure sector is loaded
2194 MapSector *sector = getSectorNoGenerate(p2d);
2197 Make sure file exists
2200 std::string blockfilename = getBlockFilename(blockpos);
2201 if (!fs::PathExists(sectordir + DIR_DELIM + blockfilename))
2205 Load block and save it to the database
2207 loadBlock(sectordir, blockfilename, sector, true);
2210 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2211 if (created_new && (block != NULL)) {
2212 std::map<v3s16, MapBlock*> modified_blocks;
2213 // Fix lighting if necessary
2214 voxalgo::update_block_border_lighting(this, block, modified_blocks);
2215 if (!modified_blocks.empty()) {
2216 //Modified lighting, send event
2218 event.type = MEET_OTHER;
2219 std::map<v3s16, MapBlock *>::iterator it;
2220 for (it = modified_blocks.begin();
2221 it != modified_blocks.end(); ++it)
2222 event.modified_blocks.insert(it->first);
2223 dispatchEvent(&event);
2229 bool ServerMap::deleteBlock(v3s16 blockpos)
2231 if (!dbase->deleteBlock(blockpos))
2234 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2236 v2s16 p2d(blockpos.X, blockpos.Z);
2237 MapSector *sector = getSectorNoGenerate(p2d);
2240 sector->deleteBlock(block);
2246 void ServerMap::PrintInfo(std::ostream &out)
2251 bool ServerMap::repairBlockLight(v3s16 blockpos,
2252 std::map<v3s16, MapBlock *> *modified_blocks)
2254 MapBlock *block = emergeBlock(blockpos, false);
2255 if (!block || !block->isGenerated())
2257 voxalgo::repair_block_light(this, block, modified_blocks);
2261 MMVManip::MMVManip(Map *map):
2267 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2268 bool load_if_inexistent)
2270 TimeTaker timer1("initialEmerge", &emerge_time);
2272 // Units of these are MapBlocks
2273 v3s16 p_min = blockpos_min;
2274 v3s16 p_max = blockpos_max;
2276 VoxelArea block_area_nodes
2277 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2279 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2282 infostream<<"initialEmerge: area: ";
2283 block_area_nodes.print(infostream);
2284 infostream<<" ("<<size_MB<<"MB)";
2285 infostream<<std::endl;
2288 addArea(block_area_nodes);
2290 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2291 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2292 for(s32 x=p_min.X; x<=p_max.X; x++)
2297 std::map<v3s16, u8>::iterator n;
2298 n = m_loaded_blocks.find(p);
2299 if(n != m_loaded_blocks.end())
2302 bool block_data_inexistent = false;
2304 TimeTaker timer2("emerge load", &emerge_load_time);
2306 block = m_map->getBlockNoCreateNoEx(p);
2307 if (!block || block->isDummy())
2308 block_data_inexistent = true;
2310 block->copyTo(*this);
2313 if(block_data_inexistent)
2316 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2317 ServerMap *svrmap = (ServerMap *)m_map;
2318 block = svrmap->emergeBlock(p, false);
2320 block = svrmap->createBlock(p);
2321 block->copyTo(*this);
2323 flags |= VMANIP_BLOCK_DATA_INEXIST;
2326 Mark area inexistent
2328 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2329 // Fill with VOXELFLAG_NO_DATA
2330 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2331 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2333 s32 i = m_area.index(a.MinEdge.X,y,z);
2334 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2338 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2340 // Mark that block was loaded as blank
2341 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2344 m_loaded_blocks[p] = flags;
2350 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2351 bool overwrite_generated)
2353 if(m_area.getExtent() == v3s16(0,0,0))
2357 Copy data of all blocks
2359 for (auto &loaded_block : m_loaded_blocks) {
2360 v3s16 p = loaded_block.first;
2361 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2362 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2363 if (!existed || (block == NULL) ||
2364 (!overwrite_generated && block->isGenerated()))
2367 block->copyFrom(*this);
2368 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2371 (*modified_blocks)[p] = block;