nlist: make selected list accessible globally
[waspsaliva.git] / src / map.cpp
blob6d53351efeed2b1bc15ae8f773823063fff4686a
1 /*
2 Minetest
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.
20 #include "map.h"
21 #include "mapsector.h"
22 #include "mapblock.h"
23 #include "filesys.h"
24 #include "voxel.h"
25 #include "voxelalgorithms.h"
26 #include "porting.h"
27 #include "serialization.h"
28 #include "nodemetadata.h"
29 #include "settings.h"
30 #include "log.h"
31 #include "profiler.h"
32 #include "nodedef.h"
33 #include "gamedef.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"
39 #include "emerge.h"
40 #include "mapgen/mapgen_v6.h"
41 #include "mapgen/mg_biome.h"
42 #include "config.h"
43 #include "server.h"
44 #include "database/database.h"
45 #include "database/database-dummy.h"
46 #include "database/database-sqlite3.h"
47 #include "script/scripting_server.h"
48 #include <deque>
49 #include <queue>
50 #if USE_LEVELDB
51 #include "database/database-leveldb.h"
52 #endif
53 #if USE_REDIS
54 #include "database/database-redis.h"
55 #endif
56 #if USE_POSTGRESQL
57 #include "database/database-postgresql.h"
58 #endif
62 Map
65 Map::Map(IGameDef *gamedef):
66 m_gamedef(gamedef),
67 m_nodedef(gamedef->ndef())
71 Map::~Map()
74 Free all MapSectors
76 for (auto &sector : m_sectors) {
77 delete sector.second;
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;
102 return sector;
105 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
107 if (n == m_sectors.end())
108 return NULL;
110 MapSector *sector = n->second;
112 // Cache the last result
113 m_sector_cache_p = p;
114 m_sector_cache = sector;
116 return 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);
128 if(sector == NULL)
129 return NULL;
130 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
131 return block;
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
136 MapBlock *block = getBlockNoCreateNoEx(p3d);
137 if(block == NULL)
138 throw InvalidPositionException();
139 return block;
142 void Map::listAllLoadedBlocks(std::vector<v3s16> &dst)
144 for (auto &sector_it : m_sectors) {
145 MapSector *sector = sector_it.second;
147 MapBlockVect blocks;
148 sector->getBlocks(blocks);
150 for (MapBlock *block : blocks) {
151 v3s16 p = block->getPos();
152 dst.push_back(p);
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);
176 if (block == NULL) {
177 if (is_valid_position != NULL)
178 *is_valid_position = false;
179 return {CONTENT_IGNORE};
182 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
183 bool is_valid_p;
184 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
185 if (is_valid_position != NULL)
186 *is_valid_position = is_valid_p;
187 return node;
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){
198 bool temp_bool;
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;
203 return;
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);
227 setNode(p, n);
229 // Update lighting
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) {
253 v3s16 p2 = p + dir;
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)
272 MapEditEvent event;
273 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
274 event.p = p;
275 event.n = n;
277 bool succeeded = true;
278 try{
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){
288 succeeded = false;
291 dispatchEvent(event);
293 return succeeded;
296 bool Map::removeNodeWithEvent(v3s16 p)
298 MapEditEvent event;
299 event.type = MEET_REMOVENODE;
300 event.p = p;
302 bool succeeded = true;
303 try{
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){
313 succeeded = false;
316 dispatchEvent(event);
318 return succeeded;
321 struct TimeOrderedMapBlock {
322 MapSector *sect;
323 MapBlock *block;
325 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
326 sect(sect),
327 block(block)
330 bool operator<(const TimeOrderedMapBlock &b) const
332 return block->getUsageTimer() < b.block->getUsageTimer();
337 Updates usage timers
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;
352 beginSave();
354 // If there is no practical limit, we spare creation of mapblock_queue
355 if (max_loaded_blocks == U32_MAX) {
356 for (auto &sector_it : m_sectors) {
357 MapSector *sector = sector_it.second;
359 bool all_blocks_deleted = true;
361 MapBlockVect blocks;
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();
371 // Save if modified
372 if (block->getModified() != MOD_STATE_CLEAN
373 && save_before_unloading) {
374 modprofiler.add(block->getModifiedReasonString(), 1);
375 if (!saveBlock(block))
376 continue;
377 saved_blocks_count++;
380 // Delete from memory
381 sector->deleteBlock(block);
383 if (unloaded_blocks)
384 unloaded_blocks->push_back(p);
386 deleted_blocks_count++;
387 } else {
388 all_blocks_deleted = false;
389 block_count_all++;
393 if (all_blocks_deleted) {
394 sector_deletion_queue.push_back(sector_it.first);
397 } else {
398 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
399 for (auto &sector_it : m_sectors) {
400 MapSector *sector = sector_it.second;
402 MapBlockVect blocks;
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)
420 continue;
422 v3s16 p = block->getPos();
424 // Save if modified
425 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
426 modprofiler.add(block->getModifiedReasonString(), 1);
427 if (!saveBlock(block))
428 continue;
429 saved_blocks_count++;
432 // Delete from memory
433 b.sect->deleteBlock(block);
435 if (unloaded_blocks)
436 unloaded_blocks->push_back(p);
438 deleted_blocks_count++;
439 block_count_all--;
441 // Delete empty sectors
442 for (auto &sector_it : m_sectors) {
443 if (sector_it.second->empty()) {
444 sector_deletion_queue.push_back(sector_it.first);
448 endSave();
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> &sectorList)
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
483 m_sectors.erase(j);
484 delete sector;
488 void Map::PrintInfo(std::ostream &out)
490 out<<"Map: ";
493 #define WATER_DROP_BOOST 4
495 const static v3s16 liquid_6dirs[6] = {
496 // order: upper before same level before lower
497 v3s16( 0, 1, 0),
498 v3s16( 0, 0, 1),
499 v3s16( 1, 0, 0),
500 v3s16( 0, 0,-1),
501 v3s16(-1, 0, 0),
502 v3s16( 0,-1, 0)
505 enum NeighborType : u8 {
506 NEIGHBOR_UPPER,
507 NEIGHBOR_SAME_LEVEL,
508 NEIGHBOR_LOWER
511 struct NodeNeighbor {
512 MapNode n;
513 NeighborType t;
514 v3s16 p;
516 NodeNeighbor()
517 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
520 NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
521 : n(node),
522 t(n_type),
523 p(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)
534 u32 loopcount = 0;
535 u32 initial_size = m_transforming_liquid.size();
537 /*if(initial_size != 0)
538 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
540 // list of nodes that due to viscosity have not reached their max level height
541 std::deque<v3s16> must_reflow;
543 std::vector<std::pair<v3s16, MapNode> > changed_nodes;
545 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
546 u32 loop_max = liquid_loop_max;
548 #if 0
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) {
554 // "Burst" mode
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;
558 } else {
559 m_transforming_liquid_loop_count_multiplier = 1.0;
562 loop_max *= m_transforming_liquid_loop_count_multiplier;
563 #endif
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)
569 break;
570 loopcount++;
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) {
593 case LIQUID_SOURCE:
594 liquid_level = LIQUID_LEVEL_SOURCE;
595 liquid_kind = cf.liquid_alternative_flowing_id;
596 break;
597 case LIQUID_FLOWING:
598 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
599 liquid_kind = n0.getContent();
600 break;
601 case LIQUID_NONE:
602 // if this node is 'floodable', it *could* be transformed
603 // into a liquid, otherwise, continue with the next node.
604 if (!cf.floodable)
605 continue;
606 floodable_node = n0.getContent();
607 liquid_kind = CONTENT_AIR;
608 break;
612 Collect information about the environment
614 NodeNeighbor sources[6]; // surrounding sources
615 int num_sources = 0;
616 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
617 int num_flows = 0;
618 NodeNeighbor airs[6]; // surrounding air
619 int num_airs = 0;
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;
626 switch (i) {
627 case 0:
628 nt = NEIGHBOR_UPPER;
629 break;
630 case 5:
631 nt = NEIGHBOR_LOWER;
632 break;
633 default:
634 break;
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) {
640 case LIQUID_NONE:
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)
650 flowing_down = true;
651 } else {
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)
658 flowing_down = true;
659 else
660 ignored_sources = true;
663 break;
664 case LIQUID_SOURCE:
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;
670 } else {
671 // Do not count bottom source, it will screw things up
672 if(nt != NEIGHBOR_LOWER)
673 sources[num_sources++] = nb;
675 break;
676 case LIQUID_FLOWING:
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;
686 } else {
687 flows[num_flows++] = nb;
688 if (nb.t == NEIGHBOR_LOWER)
689 flowing_down = true;
691 break;
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;
716 else
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;
723 } else {
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) {
728 case NEIGHBOR_UPPER:
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;
736 break;
737 case NEIGHBOR_LOWER:
738 break;
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;
743 break;
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);
760 } else {
761 new_node_level = max_node_level;
764 if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
765 new_node_content = liquid_kind;
766 else
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)
778 == flowing_down)))
779 continue;
783 update the current node
785 MapNode n00 = n0;
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);
790 } else {
791 // set the liquid level and flow bits to 0
792 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
795 // change the node.
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))
801 continue;
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
809 std::string suspect;
810 if (m_gamedef->rollback())
811 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
813 if (m_gamedef->rollback() && !suspect.empty()) {
814 // Blame suspect
815 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
816 // Get old node for rollback
817 RollbackNode rollback_oldnode(this, p0, m_gamedef);
818 // Set node
819 setNode(p0, n0);
820 // Report
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);
825 } else {
826 // Set node
827 setNode(p0, n0);
830 v3s16 blockpos = getNodeBlockPos(p0);
831 MapBlock *block = getBlockNoCreateNoEx(blockpos);
832 if (block != NULL) {
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) {
841 case LIQUID_SOURCE:
842 case LIQUID_FLOWING:
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);
850 break;
851 case LIQUID_NONE:
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);
855 break;
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;
883 } else {
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;
907 while (dump_qty--)
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);
931 if (!block) {
932 verbosestream << "Map::getNodeMetadata(): Need to emerge "
933 << PP(blockpos) << std::endl;
934 block = emergeBlock(blockpos, false);
936 if (!block) {
937 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
938 << std::endl;
939 continue;
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))
947 continue;
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);
961 if(!block){
962 infostream<<"Map::getNodeMetadata(): Need to emerge "
963 <<PP(blockpos)<<std::endl;
964 block = emergeBlock(blockpos, false);
966 if(!block){
967 warningstream<<"Map::getNodeMetadata(): Block not found"
968 <<std::endl;
969 return NULL;
971 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
972 return meta;
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);
980 if(!block){
981 infostream<<"Map::setNodeMetadata(): Need to emerge "
982 <<PP(blockpos)<<std::endl;
983 block = emergeBlock(blockpos, false);
985 if(!block){
986 warningstream<<"Map::setNodeMetadata(): Block not found"
987 <<std::endl;
988 return false;
990 block->m_node_metadata.set(p_rel, meta);
991 return true;
994 void Map::removeNodeMetadata(v3s16 p)
996 v3s16 blockpos = getNodeBlockPos(p);
997 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
998 MapBlock *block = getBlockNoCreateNoEx(blockpos);
999 if(block == NULL)
1001 warningstream<<"Map::removeNodeMetadata(): Block not found"
1002 <<std::endl;
1003 return;
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);
1013 if(!block){
1014 infostream<<"Map::getNodeTimer(): Need to emerge "
1015 <<PP(blockpos)<<std::endl;
1016 block = emergeBlock(blockpos, false);
1018 if(!block){
1019 warningstream<<"Map::getNodeTimer(): Block not found"
1020 <<std::endl;
1021 return NodeTimer();
1023 NodeTimer t = block->m_node_timers.get(p_rel);
1024 NodeTimer nt(t.timeout, t.elapsed, p);
1025 return nt;
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);
1034 if(!block){
1035 infostream<<"Map::setNodeTimer(): Need to emerge "
1036 <<PP(blockpos)<<std::endl;
1037 block = emergeBlock(blockpos, false);
1039 if(!block){
1040 warningstream<<"Map::setNodeTimer(): Block not found"
1041 <<std::endl;
1042 return;
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);
1053 if(block == NULL)
1055 warningstream<<"Map::removeNodeTimer(): Block not found"
1056 <<std::endl;
1057 return;
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
1089 // straight
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);
1093 return true;
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);
1097 return true;
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);
1101 return true;
1104 // diagonal
1105 if (x_inside) {
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);
1109 return true;
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);
1114 return true;
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);
1119 return true;
1122 // Closest node would be a corner, none returned
1123 return false;
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);
1137 u32 count = 0;
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
1149 count++;
1150 if (count >= needed_count)
1151 return true;
1153 step *= stepfac;
1155 return false;
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] = {
1164 v3s16( 0, 0, 0),
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
1197 v3s16 check;
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))
1202 return false;
1205 for (const v3s16 &dir : dir9) {
1206 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1207 start_offset, end_offset, needed_count))
1208 return false;
1210 return true;
1214 ServerMap
1216 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1217 EmergeManager *emerge, MetricsBackend *mb):
1218 Map(gamedef),
1219 settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1220 m_emerge(emerge)
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";
1233 Settings conf;
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)");
1253 try {
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."
1259 <<std::endl;
1260 m_map_saving_enabled = true;
1262 else
1265 if (settings_mgr.loadMapMeta()) {
1266 infostream << "ServerMap: Metadata loaded from "
1267 << savedir << std::endl;
1268 } else {
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
1276 return;
1279 // If directory doesn't exist, it is safe to save to it
1280 else{
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;
1303 } else {
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
1316 delete dbase;
1317 delete dbase_ro;
1319 #if 0
1321 Free all MapChunks
1323 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1324 for(; i.atEnd() == false; i++)
1326 MapChunk *chunk = i.getNode()->getValue();
1327 delete chunk;
1329 #endif
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) /
1348 MAP_BLOCKSIZE;
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)
1364 return false;
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))
1376 return false;
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++) {
1394 v3s16 p(x, y, z);
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
1412 neighboring blocks
1415 data->vmanip = new MMVManip(this);
1416 data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1418 // Note: we may need this again at some point.
1419 #if 0
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));
1427 if (n == NULL)
1428 continue;
1429 u8 flags = n->getValue();
1430 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1431 n->setValue(flags);
1435 #endif
1437 // Data is ready now.
1438 return true;
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;
1471 if (!block)
1472 continue;
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));
1491 if (!block)
1492 continue;
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);
1511 if (sector)
1512 return sector;
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;
1534 Insert to container
1536 m_sectors[p2d] = sector;
1538 return sector;
1541 #if 0
1543 This is a quick-hand function for calling makeBlock().
1545 MapBlock * ServerMap::generateBlock(
1546 v3s16 p,
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
1571 BlockMakeData data;
1572 initBlockMake(&data, p);
1575 Generate block
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);
1592 Get central block
1594 MapBlock *block = getBlockNoCreateNoEx(p);
1596 #if 0
1598 Check result
1600 if(block)
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++)
1607 v3s16 p(x0,y0,z0);
1608 MapNode n = block->getNode(p);
1609 if(n.getContent() == CONTENT_IGNORE)
1611 infostream<<"CONTENT_IGNORE at "
1612 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1613 <<std::endl;
1614 erroneus_content = true;
1615 assert(0);
1618 if(erroneus_content)
1620 assert(0);
1623 #endif
1625 #if 0
1627 Generate a completely empty block
1629 if(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++)
1636 MapNode n;
1637 n.setContent(CONTENT_AIR);
1638 block->setNode(v3s16(x0,y0,z0), n);
1642 #endif
1644 if(enable_mapgen_debug_info == false)
1645 timer.stop(true); // Hide output
1647 return block;
1649 #endif
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);
1660 s16 block_y = p.Y;
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.
1668 MapSector *sector;
1669 try {
1670 sector = createSector(p2d);
1671 } catch (InvalidPositionException &e) {
1672 infostream<<"createBlock: createSector() failed"<<std::endl;
1673 throw e;
1677 Try to get a block from the sector
1680 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1681 if (block) {
1682 if(block->isDummy())
1683 block->unDummify();
1684 return block;
1686 // Create blank
1687 block = sector->createBlankBlock(block_y);
1689 return block;
1692 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1695 MapBlock *block = getBlockNoCreateNoEx(p);
1696 if (block && !block->isDummy())
1697 return block;
1701 MapBlock *block = loadBlock(p);
1702 if(block)
1703 return block;
1706 if (create_blank) {
1707 MapSector *sector = createSector(v2s16(p.X, p.Z));
1708 MapBlock *block = sector->createBlankBlock(p.Y);
1710 return block;
1713 return NULL;
1716 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1718 MapBlock *block = getBlockNoCreateNoEx(p3d);
1719 if (block == NULL)
1720 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1722 return block;
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();
1730 if (!mg)
1731 return;
1733 MMVManip *vm = mg->vm;
1734 if (!vm)
1735 return;
1737 if (!vm->m_area.contains(pos))
1738 return;
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;
1751 return;
1754 u64 start_time = porting::getTimeNs();
1756 if(save_level == MOD_STATE_CLEAN)
1757 infostream<<"ServerMap: Saving whole map, this can take time."
1758 <<std::endl;
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 &sector_it : m_sectors) {
1775 MapSector *sector = sector_it.second;
1777 MapBlockVect blocks;
1778 sector->getBlocks(blocks);
1780 for (MapBlock *block : blocks) {
1781 block_count_all++;
1783 if(block->getModified() >= (u32)save_level) {
1784 // Lazy beginSave()
1785 if(!save_started) {
1786 beginSave();
1787 save_started = true;
1790 modprofiler.add(block->getModifiedReasonString(), 1);
1792 saveBlock(block);
1793 block_count++;
1798 if(save_started)
1799 endSave();
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."
1809 << std::endl;
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);
1822 if (dbase_ro)
1823 dbase_ro->listAllLoadableBlocks(dst);
1826 MapDatabase *ServerMap::createDatabase(
1827 const std::string &name,
1828 const std::string &savedir,
1829 Settings &conf)
1831 if (name == "sqlite3")
1832 return new MapDatabaseSQLite3(savedir);
1833 if (name == "dummy")
1834 return new Database_Dummy();
1835 #if USE_LEVELDB
1836 if (name == "leveldb")
1837 return new Database_LevelDB(savedir);
1838 #endif
1839 #if USE_REDIS
1840 if (name == "redis")
1841 return new Database_Redis(conf);
1842 #endif
1843 #if USE_POSTGRESQL
1844 if (name == "postgresql") {
1845 std::string connect_string;
1846 conf.getNoEx("pgsql_connection", connect_string);
1847 return new MapDatabasePostgreSQL(connect_string);
1849 #endif
1851 throw BaseException(std::string("Database backend ") + name + " not supported.");
1854 void ServerMap::beginSave()
1856 dbase->beginSave();
1859 void ServerMap::endSave()
1861 dbase->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;
1877 return true;
1880 // Format used for writing
1881 u8 version = SER_FMT_VER_HIGHEST_WRITE;
1884 [0] u8 serialization version
1885 [1] data
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());
1892 if (ret) {
1893 // We just wrote it to the disk so clear modified flag
1894 block->resetModified();
1896 return ret;
1899 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1901 try {
1902 std::istringstream is(*blob, std::ios_base::binary);
1904 u8 version = SER_FMT_VER_INVALID;
1905 is.read((char*)&version, 1);
1907 if(is.fail())
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);
1914 if(block == NULL)
1916 block = sector->createBlankBlockNoInsert(p3d.Y);
1917 created_new = true;
1920 // Read basic data
1921 block->deSerialize(is, version, true);
1923 // If it's a new block, insert it to the map
1924 if (created_new) {
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
1936 if(save_after_load)
1937 saveBlock(block);
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;
1954 } else {
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);
1966 std::string ret;
1967 dbase->loadBlock(blockpos, &ret);
1968 if (!ret.empty()) {
1969 loadBlock(&ret, blockpos, createSector(p2d), false);
1970 } else if (dbase_ro) {
1971 dbase_ro->loadBlock(blockpos, &ret);
1972 if (!ret.empty()) {
1973 loadBlock(&ret, blockpos, createSector(p2d), false);
1975 } else {
1976 return NULL;
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
1986 MapEditEvent 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);
1995 return block;
1998 bool ServerMap::deleteBlock(v3s16 blockpos)
2000 if (!dbase->deleteBlock(blockpos))
2001 return false;
2003 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2004 if (block) {
2005 v2s16 p2d(blockpos.X, blockpos.Z);
2006 MapSector *sector = getSectorNoGenerate(p2d);
2007 if (!sector)
2008 return false;
2009 sector->deleteBlock(block);
2012 return true;
2015 void ServerMap::PrintInfo(std::ostream &out)
2017 out<<"ServerMap: ";
2020 bool ServerMap::repairBlockLight(v3s16 blockpos,
2021 std::map<v3s16, MapBlock *> *modified_blocks)
2023 MapBlock *block = emergeBlock(blockpos, false);
2024 if (!block || !block->isGenerated())
2025 return false;
2026 voxalgo::repair_block_light(this, block, modified_blocks);
2027 return true;
2030 MMVManip::MMVManip(Map *map):
2031 VoxelManipulator(),
2032 m_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;
2049 if(size_MB >= 1)
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++)
2063 u8 flags = 0;
2064 MapBlock *block;
2065 v3s16 p(x,y,z);
2066 std::map<v3s16, u8>::iterator n;
2067 n = m_loaded_blocks.find(p);
2068 if(n != m_loaded_blocks.end())
2069 continue;
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;
2078 else
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);
2088 if (block == NULL)
2089 block = svrmap->createBlock(p);
2090 block->copyTo(*this);
2091 } else {
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;
2116 m_is_dirty = false;
2119 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2120 bool overwrite_generated)
2122 if(m_area.getExtent() == v3s16(0,0,0))
2123 return;
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()))
2134 continue;
2136 block->copyFrom(*this);
2137 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2139 if(modified_blocks)
2140 (*modified_blocks)[p] = block;
2144 //END