3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
25 #include "voxelalgorithms.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/basic_macros.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
38 #include "reflowscan.h"
40 #include "mapgen/mapgen_v6.h"
41 #include "mapgen/mg_biome.h"
44 #include "database/database.h"
45 #include "database/database-dummy.h"
46 #include "database/database-sqlite3.h"
47 #include "script/scripting_server.h"
51 #include "database/database-leveldb.h"
54 #include "database/database-redis.h"
57 #include "database/database-postgresql.h"
65 Map::Map(IGameDef *gamedef):
67 m_nodedef(gamedef->ndef())
76 for (auto §or : m_sectors) {
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 m_event_receivers.erase(event_receiver);
91 void Map::dispatchEvent(const MapEditEvent &event)
93 for (MapEventReceiver *event_receiver : m_event_receivers) {
94 event_receiver->onMapEditEvent(event);
98 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
100 if(m_sector_cache != NULL && p == m_sector_cache_p){
101 MapSector * sector = m_sector_cache;
105 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
107 if (n == m_sectors.end())
110 MapSector *sector = n->second;
112 // Cache the last result
113 m_sector_cache_p = p;
114 m_sector_cache = sector;
119 MapSector * Map::getSectorNoGenerate(v2s16 p)
121 return getSectorNoGenerateNoLock(p);
124 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
136 MapBlock *block = getBlockNoCreateNoEx(p3d);
138 throw InvalidPositionException();
142 void Map::listAllLoadedBlocks(std::vector<v3s16> &dst)
144 for (auto §or_it : m_sectors) {
145 MapSector *sector = sector_it.second;
148 sector->getBlocks(blocks);
150 for (MapBlock *block : blocks) {
151 v3s16 p = block->getPos();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
160 MapBlock *block = getBlockNoCreateNoEx(blockpos);
161 return block && block->getIsUnderground();
164 bool Map::isValidPosition(v3s16 p)
166 v3s16 blockpos = getNodeBlockPos(p);
167 MapBlock *block = getBlockNoCreateNoEx(blockpos);
168 return (block != NULL);
171 // Returns a CONTENT_IGNORE node if not found
172 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
174 v3s16 blockpos = getNodeBlockPos(p);
175 MapBlock *block = getBlockNoCreateNoEx(blockpos);
177 if (is_valid_position != NULL)
178 *is_valid_position = false;
179 return {CONTENT_IGNORE};
182 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
184 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
185 if (is_valid_position != NULL)
186 *is_valid_position = is_valid_p;
190 // throws InvalidPositionException if not found
191 void Map::setNode(v3s16 p, MapNode & n)
193 v3s16 blockpos = getNodeBlockPos(p);
194 MapBlock *block = getBlockNoCreate(blockpos);
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 // Never allow placing CONTENT_IGNORE, it causes problems
197 if(n.getContent() == CONTENT_IGNORE){
199 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
200 <<" while trying to replace \""
201 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
202 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
205 block->setNodeNoCheck(relpos, n);
208 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
209 std::map<v3s16, MapBlock*> &modified_blocks,
210 bool remove_metadata)
212 // Collect old node for rollback
213 RollbackNode rollback_oldnode(this, p, m_gamedef);
215 // This is needed for updating the lighting
216 MapNode oldnode = getNode(p);
218 // Remove node metadata
219 if (remove_metadata) {
220 removeNodeMetadata(p);
223 // Set the node on the map
224 // Ignore light (because calling voxalgo::update_lighting_nodes)
225 n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
226 n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
230 std::vector<std::pair<v3s16, MapNode> > oldnodes;
231 oldnodes.emplace_back(p, oldnode);
232 voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
234 for (auto &modified_block : modified_blocks) {
235 modified_block.second->expireDayNightDiff();
238 // Report for rollback
239 if(m_gamedef->rollback())
241 RollbackNode rollback_newnode(this, p, m_gamedef);
242 RollbackAction action;
243 action.setSetNode(p, rollback_oldnode, rollback_newnode);
244 m_gamedef->rollback()->reportAction(action);
248 Add neighboring liquid nodes and this node to transform queue.
249 (it's vital for the node itself to get updated last, if it was removed.)
252 for (const v3s16 &dir : g_7dirs) {
255 bool is_valid_position;
256 MapNode n2 = getNode(p2, &is_valid_position);
257 if(is_valid_position &&
258 (m_nodedef->get(n2).isLiquid() ||
259 n2.getContent() == CONTENT_AIR))
260 m_transforming_liquid.push_back(p2);
264 void Map::removeNodeAndUpdate(v3s16 p,
265 std::map<v3s16, MapBlock*> &modified_blocks)
267 addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
270 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
273 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
277 bool succeeded = true;
279 std::map<v3s16, MapBlock*> modified_blocks;
280 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
282 // Copy modified_blocks to event
283 for (auto &modified_block : modified_blocks) {
284 event.modified_blocks.insert(modified_block.first);
287 catch(InvalidPositionException &e){
291 dispatchEvent(event);
296 bool Map::removeNodeWithEvent(v3s16 p)
299 event.type = MEET_REMOVENODE;
302 bool succeeded = true;
304 std::map<v3s16, MapBlock*> modified_blocks;
305 removeNodeAndUpdate(p, modified_blocks);
307 // Copy modified_blocks to event
308 for (auto &modified_block : modified_blocks) {
309 event.modified_blocks.insert(modified_block.first);
312 catch(InvalidPositionException &e){
316 dispatchEvent(event);
321 struct TimeOrderedMapBlock {
325 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
330 bool operator<(const TimeOrderedMapBlock &b) const
332 return block->getUsageTimer() < b.block->getUsageTimer();
339 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
340 std::vector<v3s16> *unloaded_blocks)
342 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
344 // Profile modified reasons
345 Profiler modprofiler;
347 std::vector<v2s16> sector_deletion_queue;
348 u32 deleted_blocks_count = 0;
349 u32 saved_blocks_count = 0;
350 u32 block_count_all = 0;
354 // If there is no practical limit, we spare creation of mapblock_queue
355 if (max_loaded_blocks == U32_MAX) {
356 for (auto §or_it : m_sectors) {
357 MapSector *sector = sector_it.second;
359 bool all_blocks_deleted = true;
362 sector->getBlocks(blocks);
364 for (MapBlock *block : blocks) {
365 block->incrementUsageTimer(dtime);
367 if (block->refGet() == 0
368 && block->getUsageTimer() > unload_timeout) {
369 v3s16 p = block->getPos();
372 if (block->getModified() != MOD_STATE_CLEAN
373 && save_before_unloading) {
374 modprofiler.add(block->getModifiedReasonString(), 1);
375 if (!saveBlock(block))
377 saved_blocks_count++;
380 // Delete from memory
381 sector->deleteBlock(block);
384 unloaded_blocks->push_back(p);
386 deleted_blocks_count++;
388 all_blocks_deleted = false;
393 if (all_blocks_deleted) {
394 sector_deletion_queue.push_back(sector_it.first);
398 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
399 for (auto §or_it : m_sectors) {
400 MapSector *sector = sector_it.second;
403 sector->getBlocks(blocks);
405 for (MapBlock *block : blocks) {
406 block->incrementUsageTimer(dtime);
407 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
410 block_count_all = mapblock_queue.size();
411 // Delete old blocks, and blocks over the limit from the memory
412 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
413 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
414 TimeOrderedMapBlock b = mapblock_queue.top();
415 mapblock_queue.pop();
417 MapBlock *block = b.block;
419 if (block->refGet() != 0)
422 v3s16 p = block->getPos();
425 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
426 modprofiler.add(block->getModifiedReasonString(), 1);
427 if (!saveBlock(block))
429 saved_blocks_count++;
432 // Delete from memory
433 b.sect->deleteBlock(block);
436 unloaded_blocks->push_back(p);
438 deleted_blocks_count++;
441 // Delete empty sectors
442 for (auto §or_it : m_sectors) {
443 if (sector_it.second->empty()) {
444 sector_deletion_queue.push_back(sector_it.first);
450 // Finally delete the empty sectors
451 deleteSectors(sector_deletion_queue);
453 if(deleted_blocks_count != 0)
455 PrintInfo(infostream); // ServerMap/ClientMap:
456 infostream<<"Unloaded "<<deleted_blocks_count
457 <<" blocks from memory";
458 if(save_before_unloading)
459 infostream<<", of which "<<saved_blocks_count<<" were written";
460 infostream<<", "<<block_count_all<<" blocks in memory";
461 infostream<<"."<<std::endl;
462 if(saved_blocks_count != 0){
463 PrintInfo(infostream); // ServerMap/ClientMap:
464 infostream<<"Blocks modified by: "<<std::endl;
465 modprofiler.print(infostream);
470 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
472 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
475 void Map::deleteSectors(std::vector<v2s16> §orList)
477 for (v2s16 j : sectorList) {
478 MapSector *sector = m_sectors[j];
479 // If sector is in sector cache, remove it from there
480 if(m_sector_cache == sector)
481 m_sector_cache = NULL;
482 // Remove from map and delete
488 void Map::PrintInfo(std::ostream &out)
493 #define WATER_DROP_BOOST 4
495 const static v3s16 liquid_6dirs[6] = {
496 // order: upper before same level before lower
505 enum NeighborType : u8 {
511 struct NodeNeighbor {
517 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
520 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
527 void Map::transforming_liquid_add(v3s16 p) {
528 m_transforming_liquid.push_back(p);
531 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
532 ServerEnvironment *env)
535 u32 initial_size = m_transforming_liquid.size();
537 /*if(initial_size != 0)
538 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
540 // list of nodes that due to viscosity have not reached their max level height
541 std::deque<v3s16> must_reflow;
543 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
545 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
546 u32 loop_max = liquid_loop_max;
550 /* If liquid_loop_max is not keeping up with the queue size increase
551 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
553 if (m_transforming_liquid.size() > loop_max * 2) {
555 float server_step = g_settings->getFloat("dedicated_server_step");
556 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
557 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
559 m_transforming_liquid_loop_count_multiplier = 1.0;
562 loop_max *= m_transforming_liquid_loop_count_multiplier;
565 while (m_transforming_liquid.size() != 0)
567 // This should be done here so that it is done when continue is used
568 if (loopcount >= initial_size || loopcount >= loop_max)
573 Get a queued transforming liquid node
575 v3s16 p0 = m_transforming_liquid.front();
576 m_transforming_liquid.pop_front();
578 MapNode n0 = getNode(p0);
581 Collect information about current node
583 s8 liquid_level = -1;
584 // The liquid node which will be placed there if
585 // the liquid flows into this node.
586 content_t liquid_kind = CONTENT_IGNORE;
587 // The node which will be placed there if liquid
588 // can't flow into this node.
589 content_t floodable_node = CONTENT_AIR;
590 const ContentFeatures &cf = m_nodedef->get(n0);
591 LiquidType liquid_type = cf.liquid_type;
592 switch (liquid_type) {
594 liquid_level = LIQUID_LEVEL_SOURCE;
595 liquid_kind = cf.liquid_alternative_flowing_id;
598 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
599 liquid_kind = n0.getContent();
602 // if this node is 'floodable', it *could* be transformed
603 // into a liquid, otherwise, continue with the next node.
606 floodable_node = n0.getContent();
607 liquid_kind = CONTENT_AIR;
612 Collect information about the environment
614 NodeNeighbor sources[6]; // surrounding sources
616 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
618 NodeNeighbor airs[6]; // surrounding air
620 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
621 int num_neutrals = 0;
622 bool flowing_down = false;
623 bool ignored_sources = false;
624 for (u16 i = 0; i < 6; i++) {
625 NeighborType nt = NEIGHBOR_SAME_LEVEL;
636 v3s16 npos = p0 + liquid_6dirs[i];
637 NodeNeighbor nb(getNode(npos), nt, npos);
638 const ContentFeatures &cfnb = m_nodedef->get(nb.n);
639 switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
641 if (cfnb.floodable) {
642 airs[num_airs++] = nb;
643 // if the current node is a water source the neighbor
644 // should be enqueded for transformation regardless of whether the
645 // current node changes or not.
646 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
647 m_transforming_liquid.push_back(npos);
648 // if the current node happens to be a flowing node, it will start to flow down here.
649 if (nb.t == NEIGHBOR_LOWER)
652 neutrals[num_neutrals++] = nb;
653 if (nb.n.getContent() == CONTENT_IGNORE) {
654 // If node below is ignore prevent water from
655 // spreading outwards and otherwise prevent from
656 // flowing away as ignore node might be the source
657 if (nb.t == NEIGHBOR_LOWER)
660 ignored_sources = true;
665 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
666 if (liquid_kind == CONTENT_AIR)
667 liquid_kind = cfnb.liquid_alternative_flowing_id;
668 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
669 neutrals[num_neutrals++] = nb;
671 // Do not count bottom source, it will screw things up
672 if(nt != NEIGHBOR_LOWER)
673 sources[num_sources++] = nb;
677 if (nb.t != NEIGHBOR_SAME_LEVEL ||
678 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
679 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
680 // but exclude falling liquids on the same level, they cannot flow here anyway
681 if (liquid_kind == CONTENT_AIR)
682 liquid_kind = cfnb.liquid_alternative_flowing_id;
684 if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
685 neutrals[num_neutrals++] = nb;
687 flows[num_flows++] = nb;
688 if (nb.t == NEIGHBOR_LOWER)
696 decide on the type (and possibly level) of the current node
698 content_t new_node_content;
699 s8 new_node_level = -1;
700 s8 max_node_level = -1;
702 u8 range = m_nodedef->get(liquid_kind).liquid_range;
703 if (range > LIQUID_LEVEL_MAX + 1)
704 range = LIQUID_LEVEL_MAX + 1;
706 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
707 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
708 // or the flowing alternative of the first of the surrounding sources (if it's air), so
709 // it's perfectly safe to use liquid_kind here to determine the new node content.
710 new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
711 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
712 // liquid_kind is set properly, see above
713 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
714 if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
715 new_node_content = liquid_kind;
717 new_node_content = floodable_node;
718 } else if (ignored_sources && liquid_level >= 0) {
719 // Maybe there are neighbouring sources that aren't loaded yet
720 // so prevent flowing away.
721 new_node_level = liquid_level;
722 new_node_content = liquid_kind;
724 // no surrounding sources, so get the maximum level that can flow into this node
725 for (u16 i = 0; i < num_flows; i++) {
726 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
727 switch (flows[i].t) {
729 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
730 max_node_level = LIQUID_LEVEL_MAX;
731 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
732 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
733 } else if (nb_liquid_level > max_node_level) {
734 max_node_level = nb_liquid_level;
739 case NEIGHBOR_SAME_LEVEL:
740 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
741 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
742 max_node_level = nb_liquid_level - 1;
747 u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
748 if (viscosity > 1 && max_node_level != liquid_level) {
749 // amount to gain, limited by viscosity
750 // must be at least 1 in absolute value
751 s8 level_inc = max_node_level - liquid_level;
752 if (level_inc < -viscosity || level_inc > viscosity)
753 new_node_level = liquid_level + level_inc/viscosity;
754 else if (level_inc < 0)
755 new_node_level = liquid_level - 1;
756 else if (level_inc > 0)
757 new_node_level = liquid_level + 1;
758 if (new_node_level != max_node_level)
759 must_reflow.push_back(p0);
761 new_node_level = max_node_level;
764 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
765 new_node_content = liquid_kind;
767 new_node_content = floodable_node;
772 check if anything has changed. if not, just continue with the next node.
774 if (new_node_content == n0.getContent() &&
775 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
776 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
777 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
783 update the current node
786 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
787 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
788 // set level to last 3 bits, flowing down bit to 4th bit
789 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
791 // set the liquid level and flow bits to 0
792 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
796 n0.setContent(new_node_content);
798 // on_flood() the node
799 if (floodable_node != CONTENT_AIR) {
800 if (env->getScriptIface()->node_on_flood(p0, n00, n0))
804 // Ignore light (because calling voxalgo::update_lighting_nodes)
805 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
806 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
808 // Find out whether there is a suspect for this action
810 if (m_gamedef->rollback())
811 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
813 if (m_gamedef->rollback() && !suspect.empty()) {
815 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
816 // Get old node for rollback
817 RollbackNode rollback_oldnode(this, p0, m_gamedef);
821 RollbackNode rollback_newnode(this, p0, m_gamedef);
822 RollbackAction action;
823 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
824 m_gamedef->rollback()->reportAction(action);
830 v3s16 blockpos = getNodeBlockPos(p0);
831 MapBlock *block = getBlockNoCreateNoEx(blockpos);
833 modified_blocks[blockpos] = block;
834 changed_nodes.emplace_back(p0, n00);
838 enqueue neighbors for update if neccessary
840 switch (m_nodedef->get(n0.getContent()).liquid_type) {
843 // make sure source flows into all neighboring nodes
844 for (u16 i = 0; i < num_flows; i++)
845 if (flows[i].t != NEIGHBOR_UPPER)
846 m_transforming_liquid.push_back(flows[i].p);
847 for (u16 i = 0; i < num_airs; i++)
848 if (airs[i].t != NEIGHBOR_UPPER)
849 m_transforming_liquid.push_back(airs[i].p);
852 // this flow has turned to air; neighboring flows might need to do the same
853 for (u16 i = 0; i < num_flows; i++)
854 m_transforming_liquid.push_back(flows[i].p);
858 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
860 for (auto &iter : must_reflow)
861 m_transforming_liquid.push_back(iter);
863 voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
866 /* ----------------------------------------------------------------------
867 * Manage the queue so that it does not grow indefinately
869 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
871 if (time_until_purge == 0)
872 return; // Feature disabled
874 time_until_purge *= 1000; // seconds -> milliseconds
876 u64 curr_time = porting::getTimeMs();
877 u32 prev_unprocessed = m_unprocessed_count;
878 m_unprocessed_count = m_transforming_liquid.size();
880 // if unprocessed block count is decreasing or stable
881 if (m_unprocessed_count <= prev_unprocessed) {
882 m_queue_size_timer_started = false;
884 if (!m_queue_size_timer_started)
885 m_inc_trending_up_start_time = curr_time;
886 m_queue_size_timer_started = true;
889 // Account for curr_time overflowing
890 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
891 m_queue_size_timer_started = false;
893 /* If the queue has been growing for more than liquid_queue_purge_time seconds
894 * and the number of unprocessed blocks is still > liquid_loop_max then we
895 * cannot keep up; dump the oldest blocks from the queue so that the queue
896 * has liquid_loop_max items in it
898 if (m_queue_size_timer_started
899 && curr_time - m_inc_trending_up_start_time > time_until_purge
900 && m_unprocessed_count > liquid_loop_max) {
902 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
904 infostream << "transformLiquids(): DUMPING " << dump_qty
905 << " blocks from the queue" << std::endl;
908 m_transforming_liquid.pop_front();
910 m_queue_size_timer_started = false; // optimistically assume we can keep up now
911 m_unprocessed_count = m_transforming_liquid.size();
915 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
917 std::vector<v3s16> positions_with_meta;
919 sortBoxVerticies(p1, p2);
920 v3s16 bpmin = getNodeBlockPos(p1);
921 v3s16 bpmax = getNodeBlockPos(p2);
923 VoxelArea area(p1, p2);
925 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
926 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
927 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
928 v3s16 blockpos(x, y, z);
930 MapBlock *block = getBlockNoCreateNoEx(blockpos);
932 verbosestream << "Map::getNodeMetadata(): Need to emerge "
933 << PP(blockpos) << std::endl;
934 block = emergeBlock(blockpos, false);
937 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
942 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
943 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
944 for (size_t i = 0; i != keys.size(); i++) {
945 v3s16 p(keys[i] + p_base);
946 if (!area.contains(p))
949 positions_with_meta.push_back(p);
953 return positions_with_meta;
956 NodeMetadata *Map::getNodeMetadata(v3s16 p)
958 v3s16 blockpos = getNodeBlockPos(p);
959 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
960 MapBlock *block = getBlockNoCreateNoEx(blockpos);
962 infostream<<"Map::getNodeMetadata(): Need to emerge "
963 <<PP(blockpos)<<std::endl;
964 block = emergeBlock(blockpos, false);
967 warningstream<<"Map::getNodeMetadata(): Block not found"
971 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
975 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
977 v3s16 blockpos = getNodeBlockPos(p);
978 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
979 MapBlock *block = getBlockNoCreateNoEx(blockpos);
981 infostream<<"Map::setNodeMetadata(): Need to emerge "
982 <<PP(blockpos)<<std::endl;
983 block = emergeBlock(blockpos, false);
986 warningstream<<"Map::setNodeMetadata(): Block not found"
990 block->m_node_metadata.set(p_rel, meta);
994 void Map::removeNodeMetadata(v3s16 p)
996 v3s16 blockpos = getNodeBlockPos(p);
997 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
998 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1001 warningstream<<"Map::removeNodeMetadata(): Block not found"
1005 block->m_node_metadata.remove(p_rel);
1008 NodeTimer Map::getNodeTimer(v3s16 p)
1010 v3s16 blockpos = getNodeBlockPos(p);
1011 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1012 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1014 infostream<<"Map::getNodeTimer(): Need to emerge "
1015 <<PP(blockpos)<<std::endl;
1016 block = emergeBlock(blockpos, false);
1019 warningstream<<"Map::getNodeTimer(): Block not found"
1023 NodeTimer t = block->m_node_timers.get(p_rel);
1024 NodeTimer nt(t.timeout, t.elapsed, p);
1028 void Map::setNodeTimer(const NodeTimer &t)
1030 v3s16 p = t.position;
1031 v3s16 blockpos = getNodeBlockPos(p);
1032 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1033 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1035 infostream<<"Map::setNodeTimer(): Need to emerge "
1036 <<PP(blockpos)<<std::endl;
1037 block = emergeBlock(blockpos, false);
1040 warningstream<<"Map::setNodeTimer(): Block not found"
1044 NodeTimer nt(t.timeout, t.elapsed, p_rel);
1045 block->m_node_timers.set(nt);
1048 void Map::removeNodeTimer(v3s16 p)
1050 v3s16 blockpos = getNodeBlockPos(p);
1051 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1052 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1055 warningstream<<"Map::removeNodeTimer(): Block not found"
1059 block->m_node_timers.remove(p_rel);
1062 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1063 const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1066 This functions determines the node inside the target block that is
1067 closest to the camera position. This increases the occlusion culling
1068 accuracy in straight and diagonal corridors.
1069 The returned position will be occlusion checked first in addition to the
1070 others (8 corners + center).
1071 No position is returned if
1072 - the closest node is a corner, corners are checked anyway.
1073 - the camera is inside the target block, it will never be occluded.
1075 #define CLOSEST_EDGE(pos, bounds, axis) \
1076 ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1077 (bounds).MaxEdge.axis
1079 bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1080 (pos_camera.X <= block_bounds.MaxEdge.X);
1081 bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1082 (pos_camera.Y <= block_bounds.MaxEdge.Y);
1083 bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1084 (pos_camera.Z <= block_bounds.MaxEdge.Z);
1086 if (x_inside && y_inside && z_inside)
1087 return false; // Camera inside target mapblock
1090 if (x_inside && y_inside) {
1091 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1092 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1094 } else if (y_inside && z_inside) {
1095 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1096 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1098 } else if (x_inside && z_inside) {
1099 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1100 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1106 check = v3s16(pos_camera.X, 0, 0);
1107 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1108 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1110 } else if (y_inside) {
1111 check = v3s16(0, pos_camera.Y, 0);
1112 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1113 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1115 } else if (z_inside) {
1116 check = v3s16(0, 0, pos_camera.Z);
1117 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1118 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1122 // Closest node would be a corner, none returned
1126 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1127 float step, float stepfac, float offset, float end_offset, u32 needed_count)
1129 v3f direction = intToFloat(pos_target - pos_camera, BS);
1130 float distance = direction.getLength();
1132 // Normalize direction vector
1133 if (distance > 0.0f)
1134 direction /= distance;
1136 v3f pos_origin_f = intToFloat(pos_camera, BS);
1138 bool is_valid_position;
1140 for (; offset < distance + end_offset; offset += step) {
1141 v3f pos_node_f = pos_origin_f + direction * offset;
1142 v3s16 pos_node = floatToInt(pos_node_f, BS);
1144 MapNode node = getNode(pos_node, &is_valid_position);
1146 if (is_valid_position &&
1147 !m_nodedef->get(node).light_propagates) {
1148 // Cannot see through light-blocking nodes --> occluded
1150 if (count >= needed_count)
1158 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1160 // Check occlusion for center and all 8 corners of the mapblock
1161 // Overshoot a little for less flickering
1162 static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1163 static const v3s16 dir9[9] = {
1165 v3s16( 1, 1, 1) * bs2,
1166 v3s16( 1, 1, -1) * bs2,
1167 v3s16( 1, -1, 1) * bs2,
1168 v3s16( 1, -1, -1) * bs2,
1169 v3s16(-1, 1, 1) * bs2,
1170 v3s16(-1, 1, -1) * bs2,
1171 v3s16(-1, -1, 1) * bs2,
1172 v3s16(-1, -1, -1) * bs2,
1175 v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1177 // Starting step size, value between 1m and sqrt(3)m
1178 float step = BS * 1.2f;
1179 // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1180 float stepfac = 1.05f;
1182 float start_offset = BS * 1.0f;
1184 // The occlusion search of 'isOccluded()' must stop short of the target
1185 // point by distance 'end_offset' to not enter the target mapblock.
1186 // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1187 // diagonal of a mapblock, because we must consider all view angles.
1188 // sqrt(1^2 + 1^2 + 1^2) = 1.732
1189 float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1191 // to reduce the likelihood of falsely occluded blocks
1192 // require at least two solid blocks
1193 // this is a HACK, we should think of a more precise algorithm
1194 u32 needed_count = 2;
1196 // Additional occlusion check, see comments in that function
1198 if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1199 // node is always on a side facing the camera, end_offset can be lower
1200 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1201 -1.0f, needed_count))
1205 for (const v3s16 &dir : dir9) {
1206 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1207 start_offset, end_offset, needed_count))
1216 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1217 EmergeManager *emerge, MetricsBackend *mb):
1219 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1222 verbosestream<<FUNCTION_NAME<<std::endl;
1224 // Tell the EmergeManager about our MapSettingsManager
1225 emerge->map_settings_mgr = &settings_mgr;
1228 Try to load map; if not found, create a new one.
1231 // Determine which database backend to use
1232 std::string conf_path = savedir + DIR_DELIM + "world.mt";
1234 bool succeeded = conf.readConfigFile(conf_path.c_str());
1235 if (!succeeded || !conf.exists("backend")) {
1236 // fall back to sqlite3
1237 conf.set("backend", "sqlite3");
1239 std::string backend = conf.get("backend");
1240 dbase = createDatabase(backend, savedir, conf);
1241 if (conf.exists("readonly_backend")) {
1242 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1243 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1245 if (!conf.updateConfigFile(conf_path.c_str()))
1246 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1248 m_savedir = savedir;
1249 m_map_saving_enabled = false;
1251 m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1254 // If directory exists, check contents and load if possible
1255 if (fs::PathExists(m_savedir)) {
1256 // If directory is empty, it is safe to save into it.
1257 if (fs::GetDirListing(m_savedir).empty()) {
1258 infostream<<"ServerMap: Empty save directory is valid."
1260 m_map_saving_enabled = true;
1265 if (settings_mgr.loadMapMeta()) {
1266 infostream << "ServerMap: Metadata loaded from "
1267 << savedir << std::endl;
1269 infostream << "ServerMap: Metadata could not be loaded "
1270 "from " << savedir << ", assuming valid save "
1271 "directory." << std::endl;
1274 m_map_saving_enabled = true;
1275 // Map loaded, not creating new one
1279 // If directory doesn't exist, it is safe to save to it
1281 m_map_saving_enabled = true;
1284 catch(std::exception &e)
1286 warningstream<<"ServerMap: Failed to load map from "<<savedir
1287 <<", exception: "<<e.what()<<std::endl;
1288 infostream<<"Please remove the map or fix it."<<std::endl;
1289 warningstream<<"Map saving will be disabled."<<std::endl;
1293 ServerMap::~ServerMap()
1295 verbosestream<<FUNCTION_NAME<<std::endl;
1299 if (m_map_saving_enabled) {
1300 // Save only changed parts
1301 save(MOD_STATE_WRITE_AT_UNLOAD);
1302 infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1304 infostream << "ServerMap: Map not saved" << std::endl;
1307 catch(std::exception &e)
1309 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1310 <<", exception: "<<e.what()<<std::endl;
1314 Close database if it was opened
1323 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1324 for(; i.atEnd() == false; i++)
1326 MapChunk *chunk = i.getNode()->getValue();
1332 MapgenParams *ServerMap::getMapgenParams()
1334 // getMapgenParams() should only ever be called after Server is initialized
1335 assert(settings_mgr.mapgen_params != NULL);
1336 return settings_mgr.mapgen_params;
1339 u64 ServerMap::getSeed()
1341 return getMapgenParams()->seed;
1344 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1346 const s16 mapgen_limit_bp = rangelim(
1347 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1349 return p.X < -mapgen_limit_bp ||
1350 p.X > mapgen_limit_bp ||
1351 p.Y < -mapgen_limit_bp ||
1352 p.Y > mapgen_limit_bp ||
1353 p.Z < -mapgen_limit_bp ||
1354 p.Z > mapgen_limit_bp;
1357 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1359 s16 csize = getMapgenParams()->chunksize;
1360 v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1361 v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1363 if (!m_chunks_in_progress.insert(bpmin).second)
1366 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1367 EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1369 v3s16 extra_borders(1, 1, 1);
1370 v3s16 full_bpmin = bpmin - extra_borders;
1371 v3s16 full_bpmax = bpmax + extra_borders;
1373 // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1374 if (blockpos_over_mapgen_limit(full_bpmin) ||
1375 blockpos_over_mapgen_limit(full_bpmax))
1378 data->seed = getSeed();
1379 data->blockpos_min = bpmin;
1380 data->blockpos_max = bpmax;
1381 data->nodedef = m_nodedef;
1384 Create the whole area of this and the neighboring blocks
1386 for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1387 for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1388 v2s16 sectorpos(x, z);
1389 // Sector metadata is loaded from disk if not already loaded.
1390 MapSector *sector = createSector(sectorpos);
1391 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1393 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1396 MapBlock *block = emergeBlock(p, false);
1397 if (block == NULL) {
1398 block = createBlock(p);
1400 // Block gets sunlight if this is true.
1401 // Refer to the map generator heuristics.
1402 bool ug = m_emerge->isBlockUnderground(p);
1403 block->setIsUnderground(ug);
1409 Now we have a big empty area.
1411 Make a ManualMapVoxelManipulator that contains this and the
1415 data->vmanip = new MMVManip(this);
1416 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1418 // Note: we may need this again at some point.
1420 // Ensure none of the blocks to be generated were marked as
1421 // containing CONTENT_IGNORE
1422 for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1423 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1424 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1425 core::map<v3s16, u8>::Node *n;
1426 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1429 u8 flags = n->getValue();
1430 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1437 // Data is ready now.
1441 void ServerMap::finishBlockMake(BlockMakeData *data,
1442 std::map<v3s16, MapBlock*> *changed_blocks)
1444 v3s16 bpmin = data->blockpos_min;
1445 v3s16 bpmax = data->blockpos_max;
1447 v3s16 extra_borders(1, 1, 1);
1449 bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1450 EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1453 Blit generated stuff to map
1454 NOTE: blitBackAll adds nearly everything to changed_blocks
1456 data->vmanip->blitBackAll(changed_blocks);
1458 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1459 << changed_blocks->size());
1462 Copy transforming liquid information
1464 while (data->transforming_liquid.size()) {
1465 m_transforming_liquid.push_back(data->transforming_liquid.front());
1466 data->transforming_liquid.pop_front();
1469 for (auto &changed_block : *changed_blocks) {
1470 MapBlock *block = changed_block.second;
1474 Update day/night difference cache of the MapBlocks
1476 block->expireDayNightDiff();
1478 Set block as modified
1480 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1481 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1485 Set central blocks as generated
1487 for (s16 x = bpmin.X; x <= bpmax.X; x++)
1488 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1489 for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1490 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1494 block->setGenerated(true);
1498 Save changed parts of map
1499 NOTE: Will be saved later.
1501 //save(MOD_STATE_WRITE_AT_UNLOAD);
1502 m_chunks_in_progress.erase(bpmin);
1505 MapSector *ServerMap::createSector(v2s16 p2d)
1508 Check if it exists already in memory
1510 MapSector *sector = getSectorNoGenerate(p2d);
1515 Do not create over max mapgen limit
1517 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1518 if (p2d.X < -max_limit_bp ||
1519 p2d.X > max_limit_bp ||
1520 p2d.Y < -max_limit_bp ||
1521 p2d.Y > max_limit_bp)
1522 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1525 Generate blank sector
1528 sector = new MapSector(this, p2d, m_gamedef);
1530 // Sector position on map in nodes
1531 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1536 m_sectors[p2d] = sector;
1543 This is a quick-hand function for calling makeBlock().
1545 MapBlock * ServerMap::generateBlock(
1547 std::map<v3s16, MapBlock*> &modified_blocks
1550 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
1552 TimeTaker timer("generateBlock");
1554 //MapBlock *block = original_dummy;
1556 v2s16 p2d(p.X, p.Z);
1557 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
1560 Do not generate over-limit
1562 if(blockpos_over_limit(p))
1564 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
1565 throw InvalidPositionException("generateBlock(): pos. over limit");
1569 Create block make data
1572 initBlockMake(&data, p);
1578 TimeTaker t("mapgen::make_block()");
1579 mapgen->makeChunk(&data);
1580 //mapgen::make_block(&data);
1582 if(enable_mapgen_debug_info == false)
1583 t.stop(true); // Hide output
1587 Blit data back on map, update lighting, add mobs and whatever this does
1589 finishBlockMake(&data, modified_blocks);
1594 MapBlock *block = getBlockNoCreateNoEx(p);
1602 bool erroneus_content = false;
1603 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1604 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1605 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1608 MapNode n = block->getNode(p);
1609 if(n.getContent() == CONTENT_IGNORE)
1611 infostream<<"CONTENT_IGNORE at "
1612 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1614 erroneus_content = true;
1618 if(erroneus_content)
1627 Generate a completely empty block
1631 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1632 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1634 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1637 n.setContent(CONTENT_AIR);
1638 block->setNode(v3s16(x0,y0,z0), n);
1644 if(enable_mapgen_debug_info == false)
1645 timer.stop(true); // Hide output
1651 MapBlock * ServerMap::createBlock(v3s16 p)
1654 Do not create over max mapgen limit
1656 if (blockpos_over_max_limit(p))
1657 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1659 v2s16 p2d(p.X, p.Z);
1662 This will create or load a sector if not found in memory.
1663 If block exists on disk, it will be loaded.
1665 NOTE: On old save formats, this will be slow, as it generates
1666 lighting on blocks for them.
1670 sector = createSector(p2d);
1671 } catch (InvalidPositionException &e) {
1672 infostream<<"createBlock: createSector() failed"<<std::endl;
1677 Try to get a block from the sector
1680 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1682 if(block->isDummy())
1687 block = sector->createBlankBlock(block_y);
1692 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1695 MapBlock *block = getBlockNoCreateNoEx(p);
1696 if (block && !block->isDummy())
1701 MapBlock *block = loadBlock(p);
1707 MapSector *sector = createSector(v2s16(p.X, p.Z));
1708 MapBlock *block = sector->createBlankBlock(p.Y);
1716 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1718 MapBlock *block = getBlockNoCreateNoEx(p3d);
1720 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1725 // N.B. This requires no synchronization, since data will not be modified unless
1726 // the VoxelManipulator being updated belongs to the same thread.
1727 void ServerMap::updateVManip(v3s16 pos)
1729 Mapgen *mg = m_emerge->getCurrentMapgen();
1733 MMVManip *vm = mg->vm;
1737 if (!vm->m_area.contains(pos))
1740 s32 idx = vm->m_area.index(pos);
1741 vm->m_data[idx] = getNode(pos);
1742 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1744 vm->m_is_dirty = true;
1747 void ServerMap::save(ModifiedState save_level)
1749 if (!m_map_saving_enabled) {
1750 warningstream<<"Not saving map, saving disabled."<<std::endl;
1754 u64 start_time = porting::getTimeNs();
1756 if(save_level == MOD_STATE_CLEAN)
1757 infostream<<"ServerMap: Saving whole map, this can take time."
1760 if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1761 if (settings_mgr.saveMapMeta())
1762 m_map_metadata_changed = false;
1765 // Profile modified reasons
1766 Profiler modprofiler;
1768 u32 block_count = 0;
1769 u32 block_count_all = 0; // Number of blocks in memory
1771 // Don't do anything with sqlite unless something is really saved
1772 bool save_started = false;
1774 for (auto §or_it : m_sectors) {
1775 MapSector *sector = sector_it.second;
1777 MapBlockVect blocks;
1778 sector->getBlocks(blocks);
1780 for (MapBlock *block : blocks) {
1783 if(block->getModified() >= (u32)save_level) {
1787 save_started = true;
1790 modprofiler.add(block->getModifiedReasonString(), 1);
1802 Only print if something happened or saved whole map
1804 if(save_level == MOD_STATE_CLEAN
1805 || block_count != 0) {
1806 infostream << "ServerMap: Written: "
1807 << block_count << " blocks"
1808 << ", " << block_count_all << " blocks in memory."
1810 PrintInfo(infostream); // ServerMap/ClientMap:
1811 infostream<<"Blocks modified by: "<<std::endl;
1812 modprofiler.print(infostream);
1815 auto end_time = porting::getTimeNs();
1816 m_save_time_counter->increment(end_time - start_time);
1819 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1821 dbase->listAllLoadableBlocks(dst);
1823 dbase_ro->listAllLoadableBlocks(dst);
1826 MapDatabase *ServerMap::createDatabase(
1827 const std::string &name,
1828 const std::string &savedir,
1831 if (name == "sqlite3")
1832 return new MapDatabaseSQLite3(savedir);
1833 if (name == "dummy")
1834 return new Database_Dummy();
1836 if (name == "leveldb")
1837 return new Database_LevelDB(savedir);
1840 if (name == "redis")
1841 return new Database_Redis(conf);
1844 if (name == "postgresql") {
1845 std::string connect_string;
1846 conf.getNoEx("pgsql_connection", connect_string);
1847 return new MapDatabasePostgreSQL(connect_string);
1851 throw BaseException(std::string("Database backend ") + name + " not supported.");
1854 void ServerMap::beginSave()
1859 void ServerMap::endSave()
1864 bool ServerMap::saveBlock(MapBlock *block)
1866 return saveBlock(block, dbase);
1869 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
1871 v3s16 p3d = block->getPos();
1873 // Dummy blocks are not written
1874 if (block->isDummy()) {
1875 warningstream << "saveBlock: Not writing dummy block "
1876 << PP(p3d) << std::endl;
1880 // Format used for writing
1881 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1884 [0] u8 serialization version
1887 std::ostringstream o(std::ios_base::binary);
1888 o.write((char*) &version, 1);
1889 block->serialize(o, version, true);
1891 bool ret = db->saveBlock(p3d, o.str());
1893 // We just wrote it to the disk so clear modified flag
1894 block->resetModified();
1899 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1902 std::istringstream is(*blob, std::ios_base::binary);
1904 u8 version = SER_FMT_VER_INVALID;
1905 is.read((char*)&version, 1);
1908 throw SerializationError("ServerMap::loadBlock(): Failed"
1909 " to read MapBlock version");
1911 MapBlock *block = NULL;
1912 bool created_new = false;
1913 block = sector->getBlockNoCreateNoEx(p3d.Y);
1916 block = sector->createBlankBlockNoInsert(p3d.Y);
1921 block->deSerialize(is, version, true);
1923 // If it's a new block, insert it to the map
1925 sector->insertBlock(block);
1926 ReflowScan scanner(this, m_emerge->ndef);
1927 scanner.scan(block, &m_transforming_liquid);
1931 Save blocks loaded in old format in new format
1934 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1935 // Only save if asked to; no need to update version
1939 // We just loaded it from, so it's up-to-date.
1940 block->resetModified();
1942 catch(SerializationError &e)
1944 errorstream<<"Invalid block data in database"
1945 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1946 <<" (SerializationError): "<<e.what()<<std::endl;
1948 // TODO: Block should be marked as invalid in memory so that it is
1949 // not touched but the game can run
1951 if(g_settings->getBool("ignore_world_load_errors")){
1952 errorstream<<"Ignoring block load error. Duck and cover! "
1953 <<"(ignore_world_load_errors)"<<std::endl;
1955 throw SerializationError("Invalid block data in database");
1960 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1962 bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1964 v2s16 p2d(blockpos.X, blockpos.Z);
1967 dbase->loadBlock(blockpos, &ret);
1969 loadBlock(&ret, blockpos, createSector(p2d), false);
1970 } else if (dbase_ro) {
1971 dbase_ro->loadBlock(blockpos, &ret);
1973 loadBlock(&ret, blockpos, createSector(p2d), false);
1979 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1980 if (created_new && (block != NULL)) {
1981 std::map<v3s16, MapBlock*> modified_blocks;
1982 // Fix lighting if necessary
1983 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1984 if (!modified_blocks.empty()) {
1985 //Modified lighting, send event
1987 event.type = MEET_OTHER;
1988 std::map<v3s16, MapBlock *>::iterator it;
1989 for (it = modified_blocks.begin();
1990 it != modified_blocks.end(); ++it)
1991 event.modified_blocks.insert(it->first);
1992 dispatchEvent(event);
1998 bool ServerMap::deleteBlock(v3s16 blockpos)
2000 if (!dbase->deleteBlock(blockpos))
2003 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2005 v2s16 p2d(blockpos.X, blockpos.Z);
2006 MapSector *sector = getSectorNoGenerate(p2d);
2009 sector->deleteBlock(block);
2015 void ServerMap::PrintInfo(std::ostream &out)
2020 bool ServerMap::repairBlockLight(v3s16 blockpos,
2021 std::map<v3s16, MapBlock *> *modified_blocks)
2023 MapBlock *block = emergeBlock(blockpos, false);
2024 if (!block || !block->isGenerated())
2026 voxalgo::repair_block_light(this, block, modified_blocks);
2030 MMVManip::MMVManip(Map *map):
2036 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2037 bool load_if_inexistent)
2039 TimeTaker timer1("initialEmerge", &emerge_time);
2041 // Units of these are MapBlocks
2042 v3s16 p_min = blockpos_min;
2043 v3s16 p_max = blockpos_max;
2045 VoxelArea block_area_nodes
2046 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2048 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2051 infostream<<"initialEmerge: area: ";
2052 block_area_nodes.print(infostream);
2053 infostream<<" ("<<size_MB<<"MB)";
2054 infostream<<std::endl;
2057 addArea(block_area_nodes);
2059 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2060 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2061 for(s32 x=p_min.X; x<=p_max.X; x++)
2066 std::map<v3s16, u8>::iterator n;
2067 n = m_loaded_blocks.find(p);
2068 if(n != m_loaded_blocks.end())
2071 bool block_data_inexistent = false;
2073 TimeTaker timer2("emerge load", &emerge_load_time);
2075 block = m_map->getBlockNoCreateNoEx(p);
2076 if (!block || block->isDummy())
2077 block_data_inexistent = true;
2079 block->copyTo(*this);
2082 if(block_data_inexistent)
2085 if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2086 ServerMap *svrmap = (ServerMap *)m_map;
2087 block = svrmap->emergeBlock(p, false);
2089 block = svrmap->createBlock(p);
2090 block->copyTo(*this);
2092 flags |= VMANIP_BLOCK_DATA_INEXIST;
2095 Mark area inexistent
2097 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2098 // Fill with VOXELFLAG_NO_DATA
2099 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2100 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2102 s32 i = m_area.index(a.MinEdge.X,y,z);
2103 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2107 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2109 // Mark that block was loaded as blank
2110 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2113 m_loaded_blocks[p] = flags;
2119 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2120 bool overwrite_generated)
2122 if(m_area.getExtent() == v3s16(0,0,0))
2126 Copy data of all blocks
2128 for (auto &loaded_block : m_loaded_blocks) {
2129 v3s16 p = loaded_block.first;
2130 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2131 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2132 if (!existed || (block == NULL) ||
2133 (!overwrite_generated && block->isGenerated()))
2136 block->copyFrom(*this);
2137 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2140 (*modified_blocks)[p] = block;