]> git.lizzy.rs Git - dragonfireclient.git/blob - src/map.cpp
Remove dead code (#10845)
[dragonfireclient.git] / src / map.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
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
59
60
61 /*
62         Map
63 */
64
65 Map::Map(IGameDef *gamedef):
66         m_gamedef(gamedef),
67         m_nodedef(gamedef->ndef())
68 {
69 }
70
71 Map::~Map()
72 {
73         /*
74                 Free all MapSectors
75         */
76         for (auto &sector : m_sectors) {
77                 delete sector.second;
78         }
79 }
80
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
82 {
83         m_event_receivers.insert(event_receiver);
84 }
85
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
87 {
88         m_event_receivers.erase(event_receiver);
89 }
90
91 void Map::dispatchEvent(const MapEditEvent &event)
92 {
93         for (MapEventReceiver *event_receiver : m_event_receivers) {
94                 event_receiver->onMapEditEvent(event);
95         }
96 }
97
98 MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
99 {
100         if(m_sector_cache != NULL && p == m_sector_cache_p){
101                 MapSector * sector = m_sector_cache;
102                 return sector;
103         }
104
105         std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
106
107         if (n == m_sectors.end())
108                 return NULL;
109
110         MapSector *sector = n->second;
111
112         // Cache the last result
113         m_sector_cache_p = p;
114         m_sector_cache = sector;
115
116         return sector;
117 }
118
119 MapSector * Map::getSectorNoGenerate(v2s16 p)
120 {
121         return getSectorNoGenerateNoLock(p);
122 }
123
124 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 {
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;
132 }
133
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
135 {
136         MapBlock *block = getBlockNoCreateNoEx(p3d);
137         if(block == NULL)
138                 throw InvalidPositionException();
139         return block;
140 }
141
142 bool Map::isNodeUnderground(v3s16 p)
143 {
144         v3s16 blockpos = getNodeBlockPos(p);
145         MapBlock *block = getBlockNoCreateNoEx(blockpos);
146         return block && block->getIsUnderground();
147 }
148
149 bool Map::isValidPosition(v3s16 p)
150 {
151         v3s16 blockpos = getNodeBlockPos(p);
152         MapBlock *block = getBlockNoCreateNoEx(blockpos);
153         return (block != NULL);
154 }
155
156 // Returns a CONTENT_IGNORE node if not found
157 MapNode Map::getNode(v3s16 p, bool *is_valid_position)
158 {
159         v3s16 blockpos = getNodeBlockPos(p);
160         MapBlock *block = getBlockNoCreateNoEx(blockpos);
161         if (block == NULL) {
162                 if (is_valid_position != NULL)
163                         *is_valid_position = false;
164                 return {CONTENT_IGNORE};
165         }
166
167         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
168         bool is_valid_p;
169         MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
170         if (is_valid_position != NULL)
171                 *is_valid_position = is_valid_p;
172         return node;
173 }
174
175 // throws InvalidPositionException if not found
176 void Map::setNode(v3s16 p, MapNode & n)
177 {
178         v3s16 blockpos = getNodeBlockPos(p);
179         MapBlock *block = getBlockNoCreate(blockpos);
180         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
181         // Never allow placing CONTENT_IGNORE, it causes problems
182         if(n.getContent() == CONTENT_IGNORE){
183                 bool temp_bool;
184                 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
185                                 <<" while trying to replace \""
186                                 <<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
187                                 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
188                 return;
189         }
190         block->setNodeNoCheck(relpos, n);
191 }
192
193 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
194                 std::map<v3s16, MapBlock*> &modified_blocks,
195                 bool remove_metadata)
196 {
197         // Collect old node for rollback
198         RollbackNode rollback_oldnode(this, p, m_gamedef);
199
200         // This is needed for updating the lighting
201         MapNode oldnode = getNode(p);
202
203         // Remove node metadata
204         if (remove_metadata) {
205                 removeNodeMetadata(p);
206         }
207
208         // Set the node on the map
209         // Ignore light (because calling voxalgo::update_lighting_nodes)
210         n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
211         n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
212         setNode(p, n);
213
214         // Update lighting
215         std::vector<std::pair<v3s16, MapNode> > oldnodes;
216         oldnodes.emplace_back(p, oldnode);
217         voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
218
219         for (auto &modified_block : modified_blocks) {
220                 modified_block.second->expireDayNightDiff();
221         }
222
223         // Report for rollback
224         if(m_gamedef->rollback())
225         {
226                 RollbackNode rollback_newnode(this, p, m_gamedef);
227                 RollbackAction action;
228                 action.setSetNode(p, rollback_oldnode, rollback_newnode);
229                 m_gamedef->rollback()->reportAction(action);
230         }
231
232         /*
233                 Add neighboring liquid nodes and this node to transform queue.
234                 (it's vital for the node itself to get updated last, if it was removed.)
235          */
236
237         for (const v3s16 &dir : g_7dirs) {
238                 v3s16 p2 = p + dir;
239
240                 bool is_valid_position;
241                 MapNode n2 = getNode(p2, &is_valid_position);
242                 if(is_valid_position &&
243                                 (m_nodedef->get(n2).isLiquid() ||
244                                 n2.getContent() == CONTENT_AIR))
245                         m_transforming_liquid.push_back(p2);
246         }
247 }
248
249 void Map::removeNodeAndUpdate(v3s16 p,
250                 std::map<v3s16, MapBlock*> &modified_blocks)
251 {
252         addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
253 }
254
255 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
256 {
257         MapEditEvent event;
258         event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
259         event.p = p;
260         event.n = n;
261
262         bool succeeded = true;
263         try{
264                 std::map<v3s16, MapBlock*> modified_blocks;
265                 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
266
267                 // Copy modified_blocks to event
268                 for (auto &modified_block : modified_blocks) {
269                         event.modified_blocks.insert(modified_block.first);
270                 }
271         }
272         catch(InvalidPositionException &e){
273                 succeeded = false;
274         }
275
276         dispatchEvent(event);
277
278         return succeeded;
279 }
280
281 bool Map::removeNodeWithEvent(v3s16 p)
282 {
283         MapEditEvent event;
284         event.type = MEET_REMOVENODE;
285         event.p = p;
286
287         bool succeeded = true;
288         try{
289                 std::map<v3s16, MapBlock*> modified_blocks;
290                 removeNodeAndUpdate(p, modified_blocks);
291
292                 // Copy modified_blocks to event
293                 for (auto &modified_block : modified_blocks) {
294                         event.modified_blocks.insert(modified_block.first);
295                 }
296         }
297         catch(InvalidPositionException &e){
298                 succeeded = false;
299         }
300
301         dispatchEvent(event);
302
303         return succeeded;
304 }
305
306 struct TimeOrderedMapBlock {
307         MapSector *sect;
308         MapBlock *block;
309
310         TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
311                 sect(sect),
312                 block(block)
313         {}
314
315         bool operator<(const TimeOrderedMapBlock &b) const
316         {
317                 return block->getUsageTimer() < b.block->getUsageTimer();
318         };
319 };
320
321 /*
322         Updates usage timers
323 */
324 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
325                 std::vector<v3s16> *unloaded_blocks)
326 {
327         bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
328
329         // Profile modified reasons
330         Profiler modprofiler;
331
332         std::vector<v2s16> sector_deletion_queue;
333         u32 deleted_blocks_count = 0;
334         u32 saved_blocks_count = 0;
335         u32 block_count_all = 0;
336
337         beginSave();
338
339         // If there is no practical limit, we spare creation of mapblock_queue
340         if (max_loaded_blocks == U32_MAX) {
341                 for (auto &sector_it : m_sectors) {
342                         MapSector *sector = sector_it.second;
343
344                         bool all_blocks_deleted = true;
345
346                         MapBlockVect blocks;
347                         sector->getBlocks(blocks);
348
349                         for (MapBlock *block : blocks) {
350                                 block->incrementUsageTimer(dtime);
351
352                                 if (block->refGet() == 0
353                                                 && block->getUsageTimer() > unload_timeout) {
354                                         v3s16 p = block->getPos();
355
356                                         // Save if modified
357                                         if (block->getModified() != MOD_STATE_CLEAN
358                                                         && save_before_unloading) {
359                                                 modprofiler.add(block->getModifiedReasonString(), 1);
360                                                 if (!saveBlock(block))
361                                                         continue;
362                                                 saved_blocks_count++;
363                                         }
364
365                                         // Delete from memory
366                                         sector->deleteBlock(block);
367
368                                         if (unloaded_blocks)
369                                                 unloaded_blocks->push_back(p);
370
371                                         deleted_blocks_count++;
372                                 } else {
373                                         all_blocks_deleted = false;
374                                         block_count_all++;
375                                 }
376                         }
377
378                         if (all_blocks_deleted) {
379                                 sector_deletion_queue.push_back(sector_it.first);
380                         }
381                 }
382         } else {
383                 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
384                 for (auto &sector_it : m_sectors) {
385                         MapSector *sector = sector_it.second;
386
387                         MapBlockVect blocks;
388                         sector->getBlocks(blocks);
389
390                         for (MapBlock *block : blocks) {
391                                 block->incrementUsageTimer(dtime);
392                                 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
393                         }
394                 }
395                 block_count_all = mapblock_queue.size();
396                 // Delete old blocks, and blocks over the limit from the memory
397                 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
398                                 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
399                         TimeOrderedMapBlock b = mapblock_queue.top();
400                         mapblock_queue.pop();
401
402                         MapBlock *block = b.block;
403
404                         if (block->refGet() != 0)
405                                 continue;
406
407                         v3s16 p = block->getPos();
408
409                         // Save if modified
410                         if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
411                                 modprofiler.add(block->getModifiedReasonString(), 1);
412                                 if (!saveBlock(block))
413                                         continue;
414                                 saved_blocks_count++;
415                         }
416
417                         // Delete from memory
418                         b.sect->deleteBlock(block);
419
420                         if (unloaded_blocks)
421                                 unloaded_blocks->push_back(p);
422
423                         deleted_blocks_count++;
424                         block_count_all--;
425                 }
426                 // Delete empty sectors
427                 for (auto &sector_it : m_sectors) {
428                         if (sector_it.second->empty()) {
429                                 sector_deletion_queue.push_back(sector_it.first);
430                         }
431                 }
432         }
433         endSave();
434
435         // Finally delete the empty sectors
436         deleteSectors(sector_deletion_queue);
437
438         if(deleted_blocks_count != 0)
439         {
440                 PrintInfo(infostream); // ServerMap/ClientMap:
441                 infostream<<"Unloaded "<<deleted_blocks_count
442                                 <<" blocks from memory";
443                 if(save_before_unloading)
444                         infostream<<", of which "<<saved_blocks_count<<" were written";
445                 infostream<<", "<<block_count_all<<" blocks in memory";
446                 infostream<<"."<<std::endl;
447                 if(saved_blocks_count != 0){
448                         PrintInfo(infostream); // ServerMap/ClientMap:
449                         infostream<<"Blocks modified by: "<<std::endl;
450                         modprofiler.print(infostream);
451                 }
452         }
453 }
454
455 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
456 {
457         timerUpdate(0.0, -1.0, 0, unloaded_blocks);
458 }
459
460 void Map::deleteSectors(std::vector<v2s16> &sectorList)
461 {
462         for (v2s16 j : sectorList) {
463                 MapSector *sector = m_sectors[j];
464                 // If sector is in sector cache, remove it from there
465                 if(m_sector_cache == sector)
466                         m_sector_cache = NULL;
467                 // Remove from map and delete
468                 m_sectors.erase(j);
469                 delete sector;
470         }
471 }
472
473 void Map::PrintInfo(std::ostream &out)
474 {
475         out<<"Map: ";
476 }
477
478 #define WATER_DROP_BOOST 4
479
480 const static v3s16 liquid_6dirs[6] = {
481         // order: upper before same level before lower
482         v3s16( 0, 1, 0),
483         v3s16( 0, 0, 1),
484         v3s16( 1, 0, 0),
485         v3s16( 0, 0,-1),
486         v3s16(-1, 0, 0),
487         v3s16( 0,-1, 0)
488 };
489
490 enum NeighborType : u8 {
491         NEIGHBOR_UPPER,
492         NEIGHBOR_SAME_LEVEL,
493         NEIGHBOR_LOWER
494 };
495
496 struct NodeNeighbor {
497         MapNode n;
498         NeighborType t;
499         v3s16 p;
500
501         NodeNeighbor()
502                 : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
503         { }
504
505         NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
506                 : n(node),
507                   t(n_type),
508                   p(pos)
509         { }
510 };
511
512 void Map::transforming_liquid_add(v3s16 p) {
513         m_transforming_liquid.push_back(p);
514 }
515
516 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
517                 ServerEnvironment *env)
518 {
519         u32 loopcount = 0;
520         u32 initial_size = m_transforming_liquid.size();
521
522         /*if(initial_size != 0)
523                 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
524
525         // list of nodes that due to viscosity have not reached their max level height
526         std::deque<v3s16> must_reflow;
527
528         std::vector<std::pair<v3s16, MapNode> > changed_nodes;
529
530         u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
531         u32 loop_max = liquid_loop_max;
532
533         while (m_transforming_liquid.size() != 0)
534         {
535                 // This should be done here so that it is done when continue is used
536                 if (loopcount >= initial_size || loopcount >= loop_max)
537                         break;
538                 loopcount++;
539
540                 /*
541                         Get a queued transforming liquid node
542                 */
543                 v3s16 p0 = m_transforming_liquid.front();
544                 m_transforming_liquid.pop_front();
545
546                 MapNode n0 = getNode(p0);
547
548                 /*
549                         Collect information about current node
550                  */
551                 s8 liquid_level = -1;
552                 // The liquid node which will be placed there if
553                 // the liquid flows into this node.
554                 content_t liquid_kind = CONTENT_IGNORE;
555                 // The node which will be placed there if liquid
556                 // can't flow into this node.
557                 content_t floodable_node = CONTENT_AIR;
558                 const ContentFeatures &cf = m_nodedef->get(n0);
559                 LiquidType liquid_type = cf.liquid_type;
560                 switch (liquid_type) {
561                         case LIQUID_SOURCE:
562                                 liquid_level = LIQUID_LEVEL_SOURCE;
563                                 liquid_kind = cf.liquid_alternative_flowing_id;
564                                 break;
565                         case LIQUID_FLOWING:
566                                 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
567                                 liquid_kind = n0.getContent();
568                                 break;
569                         case LIQUID_NONE:
570                                 // if this node is 'floodable', it *could* be transformed
571                                 // into a liquid, otherwise, continue with the next node.
572                                 if (!cf.floodable)
573                                         continue;
574                                 floodable_node = n0.getContent();
575                                 liquid_kind = CONTENT_AIR;
576                                 break;
577                 }
578
579                 /*
580                         Collect information about the environment
581                  */
582                 NodeNeighbor sources[6]; // surrounding sources
583                 int num_sources = 0;
584                 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
585                 int num_flows = 0;
586                 NodeNeighbor airs[6]; // surrounding air
587                 int num_airs = 0;
588                 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
589                 int num_neutrals = 0;
590                 bool flowing_down = false;
591                 bool ignored_sources = false;
592                 for (u16 i = 0; i < 6; i++) {
593                         NeighborType nt = NEIGHBOR_SAME_LEVEL;
594                         switch (i) {
595                                 case 0:
596                                         nt = NEIGHBOR_UPPER;
597                                         break;
598                                 case 5:
599                                         nt = NEIGHBOR_LOWER;
600                                         break;
601                                 default:
602                                         break;
603                         }
604                         v3s16 npos = p0 + liquid_6dirs[i];
605                         NodeNeighbor nb(getNode(npos), nt, npos);
606                         const ContentFeatures &cfnb = m_nodedef->get(nb.n);
607                         switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
608                                 case LIQUID_NONE:
609                                         if (cfnb.floodable) {
610                                                 airs[num_airs++] = nb;
611                                                 // if the current node is a water source the neighbor
612                                                 // should be enqueded for transformation regardless of whether the
613                                                 // current node changes or not.
614                                                 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
615                                                         m_transforming_liquid.push_back(npos);
616                                                 // if the current node happens to be a flowing node, it will start to flow down here.
617                                                 if (nb.t == NEIGHBOR_LOWER)
618                                                         flowing_down = true;
619                                         } else {
620                                                 neutrals[num_neutrals++] = nb;
621                                                 if (nb.n.getContent() == CONTENT_IGNORE) {
622                                                         // If node below is ignore prevent water from
623                                                         // spreading outwards and otherwise prevent from
624                                                         // flowing away as ignore node might be the source
625                                                         if (nb.t == NEIGHBOR_LOWER)
626                                                                 flowing_down = true;
627                                                         else
628                                                                 ignored_sources = true;
629                                                 }
630                                         }
631                                         break;
632                                 case LIQUID_SOURCE:
633                                         // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
634                                         if (liquid_kind == CONTENT_AIR)
635                                                 liquid_kind = cfnb.liquid_alternative_flowing_id;
636                                         if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
637                                                 neutrals[num_neutrals++] = nb;
638                                         } else {
639                                                 // Do not count bottom source, it will screw things up
640                                                 if(nt != NEIGHBOR_LOWER)
641                                                         sources[num_sources++] = nb;
642                                         }
643                                         break;
644                                 case LIQUID_FLOWING:
645                                         if (nb.t != NEIGHBOR_SAME_LEVEL ||
646                                                 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
647                                                 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
648                                                 // but exclude falling liquids on the same level, they cannot flow here anyway
649                                                 if (liquid_kind == CONTENT_AIR)
650                                                         liquid_kind = cfnb.liquid_alternative_flowing_id;
651                                         }
652                                         if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
653                                                 neutrals[num_neutrals++] = nb;
654                                         } else {
655                                                 flows[num_flows++] = nb;
656                                                 if (nb.t == NEIGHBOR_LOWER)
657                                                         flowing_down = true;
658                                         }
659                                         break;
660                         }
661                 }
662
663                 /*
664                         decide on the type (and possibly level) of the current node
665                  */
666                 content_t new_node_content;
667                 s8 new_node_level = -1;
668                 s8 max_node_level = -1;
669
670                 u8 range = m_nodedef->get(liquid_kind).liquid_range;
671                 if (range > LIQUID_LEVEL_MAX + 1)
672                         range = LIQUID_LEVEL_MAX + 1;
673
674                 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
675                         // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
676                         // or the flowing alternative of the first of the surrounding sources (if it's air), so
677                         // it's perfectly safe to use liquid_kind here to determine the new node content.
678                         new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
679                 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
680                         // liquid_kind is set properly, see above
681                         max_node_level = new_node_level = LIQUID_LEVEL_MAX;
682                         if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
683                                 new_node_content = liquid_kind;
684                         else
685                                 new_node_content = floodable_node;
686                 } else if (ignored_sources && liquid_level >= 0) {
687                         // Maybe there are neighbouring sources that aren't loaded yet
688                         // so prevent flowing away.
689                         new_node_level = liquid_level;
690                         new_node_content = liquid_kind;
691                 } else {
692                         // no surrounding sources, so get the maximum level that can flow into this node
693                         for (u16 i = 0; i < num_flows; i++) {
694                                 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
695                                 switch (flows[i].t) {
696                                         case NEIGHBOR_UPPER:
697                                                 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
698                                                         max_node_level = LIQUID_LEVEL_MAX;
699                                                         if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
700                                                                 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
701                                                 } else if (nb_liquid_level > max_node_level) {
702                                                         max_node_level = nb_liquid_level;
703                                                 }
704                                                 break;
705                                         case NEIGHBOR_LOWER:
706                                                 break;
707                                         case NEIGHBOR_SAME_LEVEL:
708                                                 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
709                                                                 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
710                                                         max_node_level = nb_liquid_level - 1;
711                                                 break;
712                                 }
713                         }
714
715                         u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
716                         if (viscosity > 1 && max_node_level != liquid_level) {
717                                 // amount to gain, limited by viscosity
718                                 // must be at least 1 in absolute value
719                                 s8 level_inc = max_node_level - liquid_level;
720                                 if (level_inc < -viscosity || level_inc > viscosity)
721                                         new_node_level = liquid_level + level_inc/viscosity;
722                                 else if (level_inc < 0)
723                                         new_node_level = liquid_level - 1;
724                                 else if (level_inc > 0)
725                                         new_node_level = liquid_level + 1;
726                                 if (new_node_level != max_node_level)
727                                         must_reflow.push_back(p0);
728                         } else {
729                                 new_node_level = max_node_level;
730                         }
731
732                         if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
733                                 new_node_content = liquid_kind;
734                         else
735                                 new_node_content = floodable_node;
736
737                 }
738
739                 /*
740                         check if anything has changed. if not, just continue with the next node.
741                  */
742                 if (new_node_content == n0.getContent() &&
743                                 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
744                                 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
745                                 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
746                                 == flowing_down)))
747                         continue;
748
749
750                 /*
751                         update the current node
752                  */
753                 MapNode n00 = n0;
754                 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
755                 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
756                         // set level to last 3 bits, flowing down bit to 4th bit
757                         n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
758                 } else {
759                         // set the liquid level and flow bits to 0
760                         n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
761                 }
762
763                 // change the node.
764                 n0.setContent(new_node_content);
765
766                 // on_flood() the node
767                 if (floodable_node != CONTENT_AIR) {
768                         if (env->getScriptIface()->node_on_flood(p0, n00, n0))
769                                 continue;
770                 }
771
772                 // Ignore light (because calling voxalgo::update_lighting_nodes)
773                 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
774                 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
775
776                 // Find out whether there is a suspect for this action
777                 std::string suspect;
778                 if (m_gamedef->rollback())
779                         suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
780
781                 if (m_gamedef->rollback() && !suspect.empty()) {
782                         // Blame suspect
783                         RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
784                         // Get old node for rollback
785                         RollbackNode rollback_oldnode(this, p0, m_gamedef);
786                         // Set node
787                         setNode(p0, n0);
788                         // Report
789                         RollbackNode rollback_newnode(this, p0, m_gamedef);
790                         RollbackAction action;
791                         action.setSetNode(p0, rollback_oldnode, rollback_newnode);
792                         m_gamedef->rollback()->reportAction(action);
793                 } else {
794                         // Set node
795                         setNode(p0, n0);
796                 }
797
798                 v3s16 blockpos = getNodeBlockPos(p0);
799                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
800                 if (block != NULL) {
801                         modified_blocks[blockpos] =  block;
802                         changed_nodes.emplace_back(p0, n00);
803                 }
804
805                 /*
806                         enqueue neighbors for update if neccessary
807                  */
808                 switch (m_nodedef->get(n0.getContent()).liquid_type) {
809                         case LIQUID_SOURCE:
810                         case LIQUID_FLOWING:
811                                 // make sure source flows into all neighboring nodes
812                                 for (u16 i = 0; i < num_flows; i++)
813                                         if (flows[i].t != NEIGHBOR_UPPER)
814                                                 m_transforming_liquid.push_back(flows[i].p);
815                                 for (u16 i = 0; i < num_airs; i++)
816                                         if (airs[i].t != NEIGHBOR_UPPER)
817                                                 m_transforming_liquid.push_back(airs[i].p);
818                                 break;
819                         case LIQUID_NONE:
820                                 // this flow has turned to air; neighboring flows might need to do the same
821                                 for (u16 i = 0; i < num_flows; i++)
822                                         m_transforming_liquid.push_back(flows[i].p);
823                                 break;
824                 }
825         }
826         //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
827
828         for (auto &iter : must_reflow)
829                 m_transforming_liquid.push_back(iter);
830
831         voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
832
833
834         /* ----------------------------------------------------------------------
835          * Manage the queue so that it does not grow indefinately
836          */
837         u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
838
839         if (time_until_purge == 0)
840                 return; // Feature disabled
841
842         time_until_purge *= 1000;       // seconds -> milliseconds
843
844         u64 curr_time = porting::getTimeMs();
845         u32 prev_unprocessed = m_unprocessed_count;
846         m_unprocessed_count = m_transforming_liquid.size();
847
848         // if unprocessed block count is decreasing or stable
849         if (m_unprocessed_count <= prev_unprocessed) {
850                 m_queue_size_timer_started = false;
851         } else {
852                 if (!m_queue_size_timer_started)
853                         m_inc_trending_up_start_time = curr_time;
854                 m_queue_size_timer_started = true;
855         }
856
857         // Account for curr_time overflowing
858         if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
859                 m_queue_size_timer_started = false;
860
861         /* If the queue has been growing for more than liquid_queue_purge_time seconds
862          * and the number of unprocessed blocks is still > liquid_loop_max then we
863          * cannot keep up; dump the oldest blocks from the queue so that the queue
864          * has liquid_loop_max items in it
865          */
866         if (m_queue_size_timer_started
867                         && curr_time - m_inc_trending_up_start_time > time_until_purge
868                         && m_unprocessed_count > liquid_loop_max) {
869
870                 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
871
872                 infostream << "transformLiquids(): DUMPING " << dump_qty
873                            << " blocks from the queue" << std::endl;
874
875                 while (dump_qty--)
876                         m_transforming_liquid.pop_front();
877
878                 m_queue_size_timer_started = false; // optimistically assume we can keep up now
879                 m_unprocessed_count = m_transforming_liquid.size();
880         }
881 }
882
883 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
884 {
885         std::vector<v3s16> positions_with_meta;
886
887         sortBoxVerticies(p1, p2);
888         v3s16 bpmin = getNodeBlockPos(p1);
889         v3s16 bpmax = getNodeBlockPos(p2);
890
891         VoxelArea area(p1, p2);
892
893         for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
894         for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
895         for (s16 x = bpmin.X; x <= bpmax.X; x++) {
896                 v3s16 blockpos(x, y, z);
897
898                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
899                 if (!block) {
900                         verbosestream << "Map::getNodeMetadata(): Need to emerge "
901                                 << PP(blockpos) << std::endl;
902                         block = emergeBlock(blockpos, false);
903                 }
904                 if (!block) {
905                         infostream << "WARNING: Map::getNodeMetadata(): Block not found"
906                                 << std::endl;
907                         continue;
908                 }
909
910                 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
911                 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
912                 for (size_t i = 0; i != keys.size(); i++) {
913                         v3s16 p(keys[i] + p_base);
914                         if (!area.contains(p))
915                                 continue;
916
917                         positions_with_meta.push_back(p);
918                 }
919         }
920
921         return positions_with_meta;
922 }
923
924 NodeMetadata *Map::getNodeMetadata(v3s16 p)
925 {
926         v3s16 blockpos = getNodeBlockPos(p);
927         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
928         MapBlock *block = getBlockNoCreateNoEx(blockpos);
929         if(!block){
930                 infostream<<"Map::getNodeMetadata(): Need to emerge "
931                                 <<PP(blockpos)<<std::endl;
932                 block = emergeBlock(blockpos, false);
933         }
934         if(!block){
935                 warningstream<<"Map::getNodeMetadata(): Block not found"
936                                 <<std::endl;
937                 return NULL;
938         }
939         NodeMetadata *meta = block->m_node_metadata.get(p_rel);
940         return meta;
941 }
942
943 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
944 {
945         v3s16 blockpos = getNodeBlockPos(p);
946         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
947         MapBlock *block = getBlockNoCreateNoEx(blockpos);
948         if(!block){
949                 infostream<<"Map::setNodeMetadata(): Need to emerge "
950                                 <<PP(blockpos)<<std::endl;
951                 block = emergeBlock(blockpos, false);
952         }
953         if(!block){
954                 warningstream<<"Map::setNodeMetadata(): Block not found"
955                                 <<std::endl;
956                 return false;
957         }
958         block->m_node_metadata.set(p_rel, meta);
959         return true;
960 }
961
962 void Map::removeNodeMetadata(v3s16 p)
963 {
964         v3s16 blockpos = getNodeBlockPos(p);
965         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
966         MapBlock *block = getBlockNoCreateNoEx(blockpos);
967         if(block == NULL)
968         {
969                 warningstream<<"Map::removeNodeMetadata(): Block not found"
970                                 <<std::endl;
971                 return;
972         }
973         block->m_node_metadata.remove(p_rel);
974 }
975
976 NodeTimer Map::getNodeTimer(v3s16 p)
977 {
978         v3s16 blockpos = getNodeBlockPos(p);
979         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
980         MapBlock *block = getBlockNoCreateNoEx(blockpos);
981         if(!block){
982                 infostream<<"Map::getNodeTimer(): Need to emerge "
983                                 <<PP(blockpos)<<std::endl;
984                 block = emergeBlock(blockpos, false);
985         }
986         if(!block){
987                 warningstream<<"Map::getNodeTimer(): Block not found"
988                                 <<std::endl;
989                 return NodeTimer();
990         }
991         NodeTimer t = block->m_node_timers.get(p_rel);
992         NodeTimer nt(t.timeout, t.elapsed, p);
993         return nt;
994 }
995
996 void Map::setNodeTimer(const NodeTimer &t)
997 {
998         v3s16 p = t.position;
999         v3s16 blockpos = getNodeBlockPos(p);
1000         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1001         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1002         if(!block){
1003                 infostream<<"Map::setNodeTimer(): Need to emerge "
1004                                 <<PP(blockpos)<<std::endl;
1005                 block = emergeBlock(blockpos, false);
1006         }
1007         if(!block){
1008                 warningstream<<"Map::setNodeTimer(): Block not found"
1009                                 <<std::endl;
1010                 return;
1011         }
1012         NodeTimer nt(t.timeout, t.elapsed, p_rel);
1013         block->m_node_timers.set(nt);
1014 }
1015
1016 void Map::removeNodeTimer(v3s16 p)
1017 {
1018         v3s16 blockpos = getNodeBlockPos(p);
1019         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1020         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1021         if(block == NULL)
1022         {
1023                 warningstream<<"Map::removeNodeTimer(): Block not found"
1024                                 <<std::endl;
1025                 return;
1026         }
1027         block->m_node_timers.remove(p_rel);
1028 }
1029
1030 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1031         const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1032 {
1033         /*
1034                 This functions determines the node inside the target block that is
1035                 closest to the camera position. This increases the occlusion culling
1036                 accuracy in straight and diagonal corridors.
1037                 The returned position will be occlusion checked first in addition to the
1038                 others (8 corners + center).
1039                 No position is returned if
1040                 - the closest node is a corner, corners are checked anyway.
1041                 - the camera is inside the target block, it will never be occluded.
1042         */
1043 #define CLOSEST_EDGE(pos, bounds, axis) \
1044         ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1045         (bounds).MaxEdge.axis
1046
1047         bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1048                         (pos_camera.X <= block_bounds.MaxEdge.X);
1049         bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1050                         (pos_camera.Y <= block_bounds.MaxEdge.Y);
1051         bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1052                         (pos_camera.Z <= block_bounds.MaxEdge.Z);
1053
1054         if (x_inside && y_inside && z_inside)
1055                 return false; // Camera inside target mapblock
1056
1057         // straight
1058         if (x_inside && y_inside) {
1059                 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1060                 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1061                 return true;
1062         } else if (y_inside && z_inside) {
1063                 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1064                 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1065                 return true;
1066         } else if (x_inside && z_inside) {
1067                 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1068                 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1069                 return true;
1070         }
1071
1072         // diagonal
1073         if (x_inside) {
1074                 check = v3s16(pos_camera.X, 0, 0);
1075                 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1076                 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1077                 return true;
1078         } else if (y_inside) {
1079                 check = v3s16(0, pos_camera.Y, 0);
1080                 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1081                 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1082                 return true;
1083         } else if (z_inside) {
1084                 check = v3s16(0, 0, pos_camera.Z);
1085                 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1086                 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1087                 return true;
1088         }
1089
1090         // Closest node would be a corner, none returned
1091         return false;
1092 }
1093
1094 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1095         float step, float stepfac, float offset, float end_offset, u32 needed_count)
1096 {
1097         v3f direction = intToFloat(pos_target - pos_camera, BS);
1098         float distance = direction.getLength();
1099
1100         // Normalize direction vector
1101         if (distance > 0.0f)
1102                 direction /= distance;
1103
1104         v3f pos_origin_f = intToFloat(pos_camera, BS);
1105         u32 count = 0;
1106         bool is_valid_position;
1107
1108         for (; offset < distance + end_offset; offset += step) {
1109                 v3f pos_node_f = pos_origin_f + direction * offset;
1110                 v3s16 pos_node = floatToInt(pos_node_f, BS);
1111
1112                 MapNode node = getNode(pos_node, &is_valid_position);
1113
1114                 if (is_valid_position &&
1115                                 !m_nodedef->get(node).light_propagates) {
1116                         // Cannot see through light-blocking nodes --> occluded
1117                         count++;
1118                         if (count >= needed_count)
1119                                 return true;
1120                 }
1121                 step *= stepfac;
1122         }
1123         return false;
1124 }
1125
1126 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1127 {
1128         // Check occlusion for center and all 8 corners of the mapblock
1129         // Overshoot a little for less flickering
1130         static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1131         static const v3s16 dir9[9] = {
1132                 v3s16( 0,  0,  0),
1133                 v3s16( 1,  1,  1) * bs2,
1134                 v3s16( 1,  1, -1) * bs2,
1135                 v3s16( 1, -1,  1) * bs2,
1136                 v3s16( 1, -1, -1) * bs2,
1137                 v3s16(-1,  1,  1) * bs2,
1138                 v3s16(-1,  1, -1) * bs2,
1139                 v3s16(-1, -1,  1) * bs2,
1140                 v3s16(-1, -1, -1) * bs2,
1141         };
1142
1143         v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1144
1145         // Starting step size, value between 1m and sqrt(3)m
1146         float step = BS * 1.2f;
1147         // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1148         float stepfac = 1.05f;
1149
1150         float start_offset = BS * 1.0f;
1151
1152         // The occlusion search of 'isOccluded()' must stop short of the target
1153         // point by distance 'end_offset' to not enter the target mapblock.
1154         // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1155         // diagonal of a mapblock, because we must consider all view angles.
1156         // sqrt(1^2 + 1^2 + 1^2) = 1.732
1157         float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1158
1159         // to reduce the likelihood of falsely occluded blocks
1160         // require at least two solid blocks
1161         // this is a HACK, we should think of a more precise algorithm
1162         u32 needed_count = 2;
1163
1164         // Additional occlusion check, see comments in that function
1165         v3s16 check;
1166         if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1167                 // node is always on a side facing the camera, end_offset can be lower
1168                 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1169                                 -1.0f, needed_count))
1170                         return false;
1171         }
1172
1173         for (const v3s16 &dir : dir9) {
1174                 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1175                                 start_offset, end_offset, needed_count))
1176                         return false;
1177         }
1178         return true;
1179 }
1180
1181 /*
1182         ServerMap
1183 */
1184 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1185                 EmergeManager *emerge, MetricsBackend *mb):
1186         Map(gamedef),
1187         settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1188         m_emerge(emerge)
1189 {
1190         verbosestream<<FUNCTION_NAME<<std::endl;
1191
1192         // Tell the EmergeManager about our MapSettingsManager
1193         emerge->map_settings_mgr = &settings_mgr;
1194
1195         /*
1196                 Try to load map; if not found, create a new one.
1197         */
1198
1199         // Determine which database backend to use
1200         std::string conf_path = savedir + DIR_DELIM + "world.mt";
1201         Settings conf;
1202         bool succeeded = conf.readConfigFile(conf_path.c_str());
1203         if (!succeeded || !conf.exists("backend")) {
1204                 // fall back to sqlite3
1205                 conf.set("backend", "sqlite3");
1206         }
1207         std::string backend = conf.get("backend");
1208         dbase = createDatabase(backend, savedir, conf);
1209         if (conf.exists("readonly_backend")) {
1210                 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1211                 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1212         }
1213         if (!conf.updateConfigFile(conf_path.c_str()))
1214                 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1215
1216         m_savedir = savedir;
1217         m_map_saving_enabled = false;
1218
1219         m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1220
1221         m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1222
1223         try {
1224                 // If directory exists, check contents and load if possible
1225                 if (fs::PathExists(m_savedir)) {
1226                         // If directory is empty, it is safe to save into it.
1227                         if (fs::GetDirListing(m_savedir).empty()) {
1228                                 infostream<<"ServerMap: Empty save directory is valid."
1229                                                 <<std::endl;
1230                                 m_map_saving_enabled = true;
1231                         }
1232                         else
1233                         {
1234
1235                                 if (settings_mgr.loadMapMeta()) {
1236                                         infostream << "ServerMap: Metadata loaded from "
1237                                                 << savedir << std::endl;
1238                                 } else {
1239                                         infostream << "ServerMap: Metadata could not be loaded "
1240                                                 "from " << savedir << ", assuming valid save "
1241                                                 "directory." << std::endl;
1242                                 }
1243
1244                                 m_map_saving_enabled = true;
1245                                 // Map loaded, not creating new one
1246                                 return;
1247                         }
1248                 }
1249                 // If directory doesn't exist, it is safe to save to it
1250                 else{
1251                         m_map_saving_enabled = true;
1252                 }
1253         }
1254         catch(std::exception &e)
1255         {
1256                 warningstream<<"ServerMap: Failed to load map from "<<savedir
1257                                 <<", exception: "<<e.what()<<std::endl;
1258                 infostream<<"Please remove the map or fix it."<<std::endl;
1259                 warningstream<<"Map saving will be disabled."<<std::endl;
1260         }
1261 }
1262
1263 ServerMap::~ServerMap()
1264 {
1265         verbosestream<<FUNCTION_NAME<<std::endl;
1266
1267         try
1268         {
1269                 if (m_map_saving_enabled) {
1270                         // Save only changed parts
1271                         save(MOD_STATE_WRITE_AT_UNLOAD);
1272                         infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1273                 } else {
1274                         infostream << "ServerMap: Map not saved" << std::endl;
1275                 }
1276         }
1277         catch(std::exception &e)
1278         {
1279                 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1280                                 <<", exception: "<<e.what()<<std::endl;
1281         }
1282
1283         /*
1284                 Close database if it was opened
1285         */
1286         delete dbase;
1287         delete dbase_ro;
1288 }
1289
1290 MapgenParams *ServerMap::getMapgenParams()
1291 {
1292         // getMapgenParams() should only ever be called after Server is initialized
1293         assert(settings_mgr.mapgen_params != NULL);
1294         return settings_mgr.mapgen_params;
1295 }
1296
1297 u64 ServerMap::getSeed()
1298 {
1299         return getMapgenParams()->seed;
1300 }
1301
1302 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1303 {
1304         const s16 mapgen_limit_bp = rangelim(
1305                 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1306                 MAP_BLOCKSIZE;
1307         return p.X < -mapgen_limit_bp ||
1308                 p.X >  mapgen_limit_bp ||
1309                 p.Y < -mapgen_limit_bp ||
1310                 p.Y >  mapgen_limit_bp ||
1311                 p.Z < -mapgen_limit_bp ||
1312                 p.Z >  mapgen_limit_bp;
1313 }
1314
1315 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1316 {
1317         s16 csize = getMapgenParams()->chunksize;
1318         v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1319         v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1320
1321         if (!m_chunks_in_progress.insert(bpmin).second)
1322                 return false;
1323
1324         bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1325         EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1326
1327         v3s16 extra_borders(1, 1, 1);
1328         v3s16 full_bpmin = bpmin - extra_borders;
1329         v3s16 full_bpmax = bpmax + extra_borders;
1330
1331         // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1332         if (blockpos_over_mapgen_limit(full_bpmin) ||
1333                         blockpos_over_mapgen_limit(full_bpmax))
1334                 return false;
1335
1336         data->seed = getSeed();
1337         data->blockpos_min = bpmin;
1338         data->blockpos_max = bpmax;
1339         data->nodedef = m_nodedef;
1340
1341         /*
1342                 Create the whole area of this and the neighboring blocks
1343         */
1344         for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1345         for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1346                 v2s16 sectorpos(x, z);
1347                 // Sector metadata is loaded from disk if not already loaded.
1348                 MapSector *sector = createSector(sectorpos);
1349                 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1350
1351                 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1352                         v3s16 p(x, y, z);
1353
1354                         MapBlock *block = emergeBlock(p, false);
1355                         if (block == NULL) {
1356                                 block = createBlock(p);
1357
1358                                 // Block gets sunlight if this is true.
1359                                 // Refer to the map generator heuristics.
1360                                 bool ug = m_emerge->isBlockUnderground(p);
1361                                 block->setIsUnderground(ug);
1362                         }
1363                 }
1364         }
1365
1366         /*
1367                 Now we have a big empty area.
1368
1369                 Make a ManualMapVoxelManipulator that contains this and the
1370                 neighboring blocks
1371         */
1372
1373         data->vmanip = new MMVManip(this);
1374         data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1375
1376         // Data is ready now.
1377         return true;
1378 }
1379
1380 void ServerMap::finishBlockMake(BlockMakeData *data,
1381         std::map<v3s16, MapBlock*> *changed_blocks)
1382 {
1383         v3s16 bpmin = data->blockpos_min;
1384         v3s16 bpmax = data->blockpos_max;
1385
1386         bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1387         EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1388
1389         /*
1390                 Blit generated stuff to map
1391                 NOTE: blitBackAll adds nearly everything to changed_blocks
1392         */
1393         data->vmanip->blitBackAll(changed_blocks);
1394
1395         EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1396                 << changed_blocks->size());
1397
1398         /*
1399                 Copy transforming liquid information
1400         */
1401         while (data->transforming_liquid.size()) {
1402                 m_transforming_liquid.push_back(data->transforming_liquid.front());
1403                 data->transforming_liquid.pop_front();
1404         }
1405
1406         for (auto &changed_block : *changed_blocks) {
1407                 MapBlock *block = changed_block.second;
1408                 if (!block)
1409                         continue;
1410                 /*
1411                         Update day/night difference cache of the MapBlocks
1412                 */
1413                 block->expireDayNightDiff();
1414                 /*
1415                         Set block as modified
1416                 */
1417                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1418                         MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1419         }
1420
1421         /*
1422                 Set central blocks as generated
1423         */
1424         for (s16 x = bpmin.X; x <= bpmax.X; x++)
1425         for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1426         for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1427                 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1428                 if (!block)
1429                         continue;
1430
1431                 block->setGenerated(true);
1432         }
1433
1434         /*
1435                 Save changed parts of map
1436                 NOTE: Will be saved later.
1437         */
1438         //save(MOD_STATE_WRITE_AT_UNLOAD);
1439         m_chunks_in_progress.erase(bpmin);
1440 }
1441
1442 MapSector *ServerMap::createSector(v2s16 p2d)
1443 {
1444         /*
1445                 Check if it exists already in memory
1446         */
1447         MapSector *sector = getSectorNoGenerate(p2d);
1448         if (sector)
1449                 return sector;
1450
1451         /*
1452                 Do not create over max mapgen limit
1453         */
1454         const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1455         if (p2d.X < -max_limit_bp ||
1456                         p2d.X >  max_limit_bp ||
1457                         p2d.Y < -max_limit_bp ||
1458                         p2d.Y >  max_limit_bp)
1459                 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1460
1461         /*
1462                 Generate blank sector
1463         */
1464
1465         sector = new MapSector(this, p2d, m_gamedef);
1466
1467         // Sector position on map in nodes
1468         //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1469
1470         /*
1471                 Insert to container
1472         */
1473         m_sectors[p2d] = sector;
1474
1475         return sector;
1476 }
1477
1478 MapBlock * ServerMap::createBlock(v3s16 p)
1479 {
1480         /*
1481                 Do not create over max mapgen limit
1482         */
1483         if (blockpos_over_max_limit(p))
1484                 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1485
1486         v2s16 p2d(p.X, p.Z);
1487         s16 block_y = p.Y;
1488         /*
1489                 This will create or load a sector if not found in memory.
1490                 If block exists on disk, it will be loaded.
1491
1492                 NOTE: On old save formats, this will be slow, as it generates
1493                       lighting on blocks for them.
1494         */
1495         MapSector *sector;
1496         try {
1497                 sector = createSector(p2d);
1498         } catch (InvalidPositionException &e) {
1499                 infostream<<"createBlock: createSector() failed"<<std::endl;
1500                 throw e;
1501         }
1502
1503         /*
1504                 Try to get a block from the sector
1505         */
1506
1507         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1508         if (block) {
1509                 if(block->isDummy())
1510                         block->unDummify();
1511                 return block;
1512         }
1513         // Create blank
1514         block = sector->createBlankBlock(block_y);
1515
1516         return block;
1517 }
1518
1519 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1520 {
1521         {
1522                 MapBlock *block = getBlockNoCreateNoEx(p);
1523                 if (block && !block->isDummy())
1524                         return block;
1525         }
1526
1527         {
1528                 MapBlock *block = loadBlock(p);
1529                 if(block)
1530                         return block;
1531         }
1532
1533         if (create_blank) {
1534                 MapSector *sector = createSector(v2s16(p.X, p.Z));
1535                 MapBlock *block = sector->createBlankBlock(p.Y);
1536
1537                 return block;
1538         }
1539
1540         return NULL;
1541 }
1542
1543 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1544 {
1545         MapBlock *block = getBlockNoCreateNoEx(p3d);
1546         if (block == NULL)
1547                 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1548
1549         return block;
1550 }
1551
1552 // N.B.  This requires no synchronization, since data will not be modified unless
1553 // the VoxelManipulator being updated belongs to the same thread.
1554 void ServerMap::updateVManip(v3s16 pos)
1555 {
1556         Mapgen *mg = m_emerge->getCurrentMapgen();
1557         if (!mg)
1558                 return;
1559
1560         MMVManip *vm = mg->vm;
1561         if (!vm)
1562                 return;
1563
1564         if (!vm->m_area.contains(pos))
1565                 return;
1566
1567         s32 idx = vm->m_area.index(pos);
1568         vm->m_data[idx] = getNode(pos);
1569         vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1570
1571         vm->m_is_dirty = true;
1572 }
1573
1574 void ServerMap::save(ModifiedState save_level)
1575 {
1576         if (!m_map_saving_enabled) {
1577                 warningstream<<"Not saving map, saving disabled."<<std::endl;
1578                 return;
1579         }
1580
1581         u64 start_time = porting::getTimeNs();
1582
1583         if(save_level == MOD_STATE_CLEAN)
1584                 infostream<<"ServerMap: Saving whole map, this can take time."
1585                                 <<std::endl;
1586
1587         if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1588                 if (settings_mgr.saveMapMeta())
1589                         m_map_metadata_changed = false;
1590         }
1591
1592         // Profile modified reasons
1593         Profiler modprofiler;
1594
1595         u32 block_count = 0;
1596         u32 block_count_all = 0; // Number of blocks in memory
1597
1598         // Don't do anything with sqlite unless something is really saved
1599         bool save_started = false;
1600
1601         for (auto &sector_it : m_sectors) {
1602                 MapSector *sector = sector_it.second;
1603
1604                 MapBlockVect blocks;
1605                 sector->getBlocks(blocks);
1606
1607                 for (MapBlock *block : blocks) {
1608                         block_count_all++;
1609
1610                         if(block->getModified() >= (u32)save_level) {
1611                                 // Lazy beginSave()
1612                                 if(!save_started) {
1613                                         beginSave();
1614                                         save_started = true;
1615                                 }
1616
1617                                 modprofiler.add(block->getModifiedReasonString(), 1);
1618
1619                                 saveBlock(block);
1620                                 block_count++;
1621                         }
1622                 }
1623         }
1624
1625         if(save_started)
1626                 endSave();
1627
1628         /*
1629                 Only print if something happened or saved whole map
1630         */
1631         if(save_level == MOD_STATE_CLEAN
1632                         || block_count != 0) {
1633                 infostream << "ServerMap: Written: "
1634                                 << block_count << " blocks"
1635                                 << ", " << block_count_all << " blocks in memory."
1636                                 << std::endl;
1637                 PrintInfo(infostream); // ServerMap/ClientMap:
1638                 infostream<<"Blocks modified by: "<<std::endl;
1639                 modprofiler.print(infostream);
1640         }
1641
1642         auto end_time = porting::getTimeNs();
1643         m_save_time_counter->increment(end_time - start_time);
1644 }
1645
1646 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1647 {
1648         dbase->listAllLoadableBlocks(dst);
1649         if (dbase_ro)
1650                 dbase_ro->listAllLoadableBlocks(dst);
1651 }
1652
1653 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1654 {
1655         for (auto &sector_it : m_sectors) {
1656                 MapSector *sector = sector_it.second;
1657
1658                 MapBlockVect blocks;
1659                 sector->getBlocks(blocks);
1660
1661                 for (MapBlock *block : blocks) {
1662                         v3s16 p = block->getPos();
1663                         dst.push_back(p);
1664                 }
1665         }
1666 }
1667
1668 MapDatabase *ServerMap::createDatabase(
1669         const std::string &name,
1670         const std::string &savedir,
1671         Settings &conf)
1672 {
1673         if (name == "sqlite3")
1674                 return new MapDatabaseSQLite3(savedir);
1675         if (name == "dummy")
1676                 return new Database_Dummy();
1677         #if USE_LEVELDB
1678         if (name == "leveldb")
1679                 return new Database_LevelDB(savedir);
1680         #endif
1681         #if USE_REDIS
1682         if (name == "redis")
1683                 return new Database_Redis(conf);
1684         #endif
1685         #if USE_POSTGRESQL
1686         if (name == "postgresql") {
1687                 std::string connect_string;
1688                 conf.getNoEx("pgsql_connection", connect_string);
1689                 return new MapDatabasePostgreSQL(connect_string);
1690         }
1691         #endif
1692
1693         throw BaseException(std::string("Database backend ") + name + " not supported.");
1694 }
1695
1696 void ServerMap::beginSave()
1697 {
1698         dbase->beginSave();
1699 }
1700
1701 void ServerMap::endSave()
1702 {
1703         dbase->endSave();
1704 }
1705
1706 bool ServerMap::saveBlock(MapBlock *block)
1707 {
1708         return saveBlock(block, dbase, m_map_compression_level);
1709 }
1710
1711 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1712 {
1713         v3s16 p3d = block->getPos();
1714
1715         // Dummy blocks are not written
1716         if (block->isDummy()) {
1717                 warningstream << "saveBlock: Not writing dummy block "
1718                         << PP(p3d) << std::endl;
1719                 return true;
1720         }
1721
1722         // Format used for writing
1723         u8 version = SER_FMT_VER_HIGHEST_WRITE;
1724
1725         /*
1726                 [0] u8 serialization version
1727                 [1] data
1728         */
1729         std::ostringstream o(std::ios_base::binary);
1730         o.write((char*) &version, 1);
1731         block->serialize(o, version, true, compression_level);
1732
1733         bool ret = db->saveBlock(p3d, o.str());
1734         if (ret) {
1735                 // We just wrote it to the disk so clear modified flag
1736                 block->resetModified();
1737         }
1738         return ret;
1739 }
1740
1741 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1742 {
1743         try {
1744                 std::istringstream is(*blob, std::ios_base::binary);
1745
1746                 u8 version = SER_FMT_VER_INVALID;
1747                 is.read((char*)&version, 1);
1748
1749                 if(is.fail())
1750                         throw SerializationError("ServerMap::loadBlock(): Failed"
1751                                         " to read MapBlock version");
1752
1753                 MapBlock *block = NULL;
1754                 bool created_new = false;
1755                 block = sector->getBlockNoCreateNoEx(p3d.Y);
1756                 if(block == NULL)
1757                 {
1758                         block = sector->createBlankBlockNoInsert(p3d.Y);
1759                         created_new = true;
1760                 }
1761
1762                 // Read basic data
1763                 block->deSerialize(is, version, true);
1764
1765                 // If it's a new block, insert it to the map
1766                 if (created_new) {
1767                         sector->insertBlock(block);
1768                         ReflowScan scanner(this, m_emerge->ndef);
1769                         scanner.scan(block, &m_transforming_liquid);
1770                 }
1771
1772                 /*
1773                         Save blocks loaded in old format in new format
1774                 */
1775
1776                 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1777                 // Only save if asked to; no need to update version
1778                 if(save_after_load)
1779                         saveBlock(block);
1780
1781                 // We just loaded it from, so it's up-to-date.
1782                 block->resetModified();
1783         }
1784         catch(SerializationError &e)
1785         {
1786                 errorstream<<"Invalid block data in database"
1787                                 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1788                                 <<" (SerializationError): "<<e.what()<<std::endl;
1789
1790                 // TODO: Block should be marked as invalid in memory so that it is
1791                 // not touched but the game can run
1792
1793                 if(g_settings->getBool("ignore_world_load_errors")){
1794                         errorstream<<"Ignoring block load error. Duck and cover! "
1795                                         <<"(ignore_world_load_errors)"<<std::endl;
1796                 } else {
1797                         throw SerializationError("Invalid block data in database");
1798                 }
1799         }
1800 }
1801
1802 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1803 {
1804         bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1805
1806         v2s16 p2d(blockpos.X, blockpos.Z);
1807
1808         std::string ret;
1809         dbase->loadBlock(blockpos, &ret);
1810         if (!ret.empty()) {
1811                 loadBlock(&ret, blockpos, createSector(p2d), false);
1812         } else if (dbase_ro) {
1813                 dbase_ro->loadBlock(blockpos, &ret);
1814                 if (!ret.empty()) {
1815                         loadBlock(&ret, blockpos, createSector(p2d), false);
1816                 }
1817         } else {
1818                 return NULL;
1819         }
1820
1821         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1822         if (created_new && (block != NULL)) {
1823                 std::map<v3s16, MapBlock*> modified_blocks;
1824                 // Fix lighting if necessary
1825                 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1826                 if (!modified_blocks.empty()) {
1827                         //Modified lighting, send event
1828                         MapEditEvent event;
1829                         event.type = MEET_OTHER;
1830                         std::map<v3s16, MapBlock *>::iterator it;
1831                         for (it = modified_blocks.begin();
1832                                         it != modified_blocks.end(); ++it)
1833                                 event.modified_blocks.insert(it->first);
1834                         dispatchEvent(event);
1835                 }
1836         }
1837         return block;
1838 }
1839
1840 bool ServerMap::deleteBlock(v3s16 blockpos)
1841 {
1842         if (!dbase->deleteBlock(blockpos))
1843                 return false;
1844
1845         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1846         if (block) {
1847                 v2s16 p2d(blockpos.X, blockpos.Z);
1848                 MapSector *sector = getSectorNoGenerate(p2d);
1849                 if (!sector)
1850                         return false;
1851                 sector->deleteBlock(block);
1852         }
1853
1854         return true;
1855 }
1856
1857 void ServerMap::PrintInfo(std::ostream &out)
1858 {
1859         out<<"ServerMap: ";
1860 }
1861
1862 bool ServerMap::repairBlockLight(v3s16 blockpos,
1863         std::map<v3s16, MapBlock *> *modified_blocks)
1864 {
1865         MapBlock *block = emergeBlock(blockpos, false);
1866         if (!block || !block->isGenerated())
1867                 return false;
1868         voxalgo::repair_block_light(this, block, modified_blocks);
1869         return true;
1870 }
1871
1872 MMVManip::MMVManip(Map *map):
1873                 VoxelManipulator(),
1874                 m_map(map)
1875 {
1876 }
1877
1878 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
1879         bool load_if_inexistent)
1880 {
1881         TimeTaker timer1("initialEmerge", &emerge_time);
1882
1883         // Units of these are MapBlocks
1884         v3s16 p_min = blockpos_min;
1885         v3s16 p_max = blockpos_max;
1886
1887         VoxelArea block_area_nodes
1888                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1889
1890         u32 size_MB = block_area_nodes.getVolume()*4/1000000;
1891         if(size_MB >= 1)
1892         {
1893                 infostream<<"initialEmerge: area: ";
1894                 block_area_nodes.print(infostream);
1895                 infostream<<" ("<<size_MB<<"MB)";
1896                 infostream<<std::endl;
1897         }
1898
1899         addArea(block_area_nodes);
1900
1901         for(s32 z=p_min.Z; z<=p_max.Z; z++)
1902         for(s32 y=p_min.Y; y<=p_max.Y; y++)
1903         for(s32 x=p_min.X; x<=p_max.X; x++)
1904         {
1905                 u8 flags = 0;
1906                 MapBlock *block;
1907                 v3s16 p(x,y,z);
1908                 std::map<v3s16, u8>::iterator n;
1909                 n = m_loaded_blocks.find(p);
1910                 if(n != m_loaded_blocks.end())
1911                         continue;
1912
1913                 bool block_data_inexistent = false;
1914                 {
1915                         TimeTaker timer2("emerge load", &emerge_load_time);
1916
1917                         block = m_map->getBlockNoCreateNoEx(p);
1918                         if (!block || block->isDummy())
1919                                 block_data_inexistent = true;
1920                         else
1921                                 block->copyTo(*this);
1922                 }
1923
1924                 if(block_data_inexistent)
1925                 {
1926
1927                         if (load_if_inexistent && !blockpos_over_max_limit(p)) {
1928                                 ServerMap *svrmap = (ServerMap *)m_map;
1929                                 block = svrmap->emergeBlock(p, false);
1930                                 if (block == NULL)
1931                                         block = svrmap->createBlock(p);
1932                                 block->copyTo(*this);
1933                         } else {
1934                                 flags |= VMANIP_BLOCK_DATA_INEXIST;
1935
1936                                 /*
1937                                         Mark area inexistent
1938                                 */
1939                                 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
1940                                 // Fill with VOXELFLAG_NO_DATA
1941                                 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
1942                                 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
1943                                 {
1944                                         s32 i = m_area.index(a.MinEdge.X,y,z);
1945                                         memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
1946                                 }
1947                         }
1948                 }
1949                 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
1950                 {
1951                         // Mark that block was loaded as blank
1952                         flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
1953                 }*/
1954
1955                 m_loaded_blocks[p] = flags;
1956         }
1957
1958         m_is_dirty = false;
1959 }
1960
1961 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
1962         bool overwrite_generated)
1963 {
1964         if(m_area.getExtent() == v3s16(0,0,0))
1965                 return;
1966
1967         /*
1968                 Copy data of all blocks
1969         */
1970         for (auto &loaded_block : m_loaded_blocks) {
1971                 v3s16 p = loaded_block.first;
1972                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1973                 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
1974                 if (!existed || (block == NULL) ||
1975                         (!overwrite_generated && block->isGenerated()))
1976                         continue;
1977
1978                 block->copyFrom(*this);
1979                 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
1980
1981                 if(modified_blocks)
1982                         (*modified_blocks)[p] = block;
1983         }
1984 }
1985
1986 //END