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