]> git.lizzy.rs Git - minetest.git/blob - src/map.cpp
Add minetest.get_objects_in_area (#10668)
[minetest.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 #if 0
534
535         /* If liquid_loop_max is not keeping up with the queue size increase
536          * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
537          */
538         if (m_transforming_liquid.size() > loop_max * 2) {
539                 // "Burst" mode
540                 float server_step = g_settings->getFloat("dedicated_server_step");
541                 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
542                         m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
543         } else {
544                 m_transforming_liquid_loop_count_multiplier = 1.0;
545         }
546
547         loop_max *= m_transforming_liquid_loop_count_multiplier;
548 #endif
549
550         while (m_transforming_liquid.size() != 0)
551         {
552                 // This should be done here so that it is done when continue is used
553                 if (loopcount >= initial_size || loopcount >= loop_max)
554                         break;
555                 loopcount++;
556
557                 /*
558                         Get a queued transforming liquid node
559                 */
560                 v3s16 p0 = m_transforming_liquid.front();
561                 m_transforming_liquid.pop_front();
562
563                 MapNode n0 = getNode(p0);
564
565                 /*
566                         Collect information about current node
567                  */
568                 s8 liquid_level = -1;
569                 // The liquid node which will be placed there if
570                 // the liquid flows into this node.
571                 content_t liquid_kind = CONTENT_IGNORE;
572                 // The node which will be placed there if liquid
573                 // can't flow into this node.
574                 content_t floodable_node = CONTENT_AIR;
575                 const ContentFeatures &cf = m_nodedef->get(n0);
576                 LiquidType liquid_type = cf.liquid_type;
577                 switch (liquid_type) {
578                         case LIQUID_SOURCE:
579                                 liquid_level = LIQUID_LEVEL_SOURCE;
580                                 liquid_kind = cf.liquid_alternative_flowing_id;
581                                 break;
582                         case LIQUID_FLOWING:
583                                 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
584                                 liquid_kind = n0.getContent();
585                                 break;
586                         case LIQUID_NONE:
587                                 // if this node is 'floodable', it *could* be transformed
588                                 // into a liquid, otherwise, continue with the next node.
589                                 if (!cf.floodable)
590                                         continue;
591                                 floodable_node = n0.getContent();
592                                 liquid_kind = CONTENT_AIR;
593                                 break;
594                 }
595
596                 /*
597                         Collect information about the environment
598                  */
599                 NodeNeighbor sources[6]; // surrounding sources
600                 int num_sources = 0;
601                 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
602                 int num_flows = 0;
603                 NodeNeighbor airs[6]; // surrounding air
604                 int num_airs = 0;
605                 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
606                 int num_neutrals = 0;
607                 bool flowing_down = false;
608                 bool ignored_sources = false;
609                 for (u16 i = 0; i < 6; i++) {
610                         NeighborType nt = NEIGHBOR_SAME_LEVEL;
611                         switch (i) {
612                                 case 0:
613                                         nt = NEIGHBOR_UPPER;
614                                         break;
615                                 case 5:
616                                         nt = NEIGHBOR_LOWER;
617                                         break;
618                                 default:
619                                         break;
620                         }
621                         v3s16 npos = p0 + liquid_6dirs[i];
622                         NodeNeighbor nb(getNode(npos), nt, npos);
623                         const ContentFeatures &cfnb = m_nodedef->get(nb.n);
624                         switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
625                                 case LIQUID_NONE:
626                                         if (cfnb.floodable) {
627                                                 airs[num_airs++] = nb;
628                                                 // if the current node is a water source the neighbor
629                                                 // should be enqueded for transformation regardless of whether the
630                                                 // current node changes or not.
631                                                 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
632                                                         m_transforming_liquid.push_back(npos);
633                                                 // if the current node happens to be a flowing node, it will start to flow down here.
634                                                 if (nb.t == NEIGHBOR_LOWER)
635                                                         flowing_down = true;
636                                         } else {
637                                                 neutrals[num_neutrals++] = nb;
638                                                 if (nb.n.getContent() == CONTENT_IGNORE) {
639                                                         // If node below is ignore prevent water from
640                                                         // spreading outwards and otherwise prevent from
641                                                         // flowing away as ignore node might be the source
642                                                         if (nb.t == NEIGHBOR_LOWER)
643                                                                 flowing_down = true;
644                                                         else
645                                                                 ignored_sources = true;
646                                                 }
647                                         }
648                                         break;
649                                 case LIQUID_SOURCE:
650                                         // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
651                                         if (liquid_kind == CONTENT_AIR)
652                                                 liquid_kind = cfnb.liquid_alternative_flowing_id;
653                                         if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
654                                                 neutrals[num_neutrals++] = nb;
655                                         } else {
656                                                 // Do not count bottom source, it will screw things up
657                                                 if(nt != NEIGHBOR_LOWER)
658                                                         sources[num_sources++] = nb;
659                                         }
660                                         break;
661                                 case LIQUID_FLOWING:
662                                         if (nb.t != NEIGHBOR_SAME_LEVEL ||
663                                                 (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
664                                                 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
665                                                 // but exclude falling liquids on the same level, they cannot flow here anyway
666                                                 if (liquid_kind == CONTENT_AIR)
667                                                         liquid_kind = cfnb.liquid_alternative_flowing_id;
668                                         }
669                                         if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
670                                                 neutrals[num_neutrals++] = nb;
671                                         } else {
672                                                 flows[num_flows++] = nb;
673                                                 if (nb.t == NEIGHBOR_LOWER)
674                                                         flowing_down = true;
675                                         }
676                                         break;
677                         }
678                 }
679
680                 /*
681                         decide on the type (and possibly level) of the current node
682                  */
683                 content_t new_node_content;
684                 s8 new_node_level = -1;
685                 s8 max_node_level = -1;
686
687                 u8 range = m_nodedef->get(liquid_kind).liquid_range;
688                 if (range > LIQUID_LEVEL_MAX + 1)
689                         range = LIQUID_LEVEL_MAX + 1;
690
691                 if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
692                         // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
693                         // or the flowing alternative of the first of the surrounding sources (if it's air), so
694                         // it's perfectly safe to use liquid_kind here to determine the new node content.
695                         new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
696                 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
697                         // liquid_kind is set properly, see above
698                         max_node_level = new_node_level = LIQUID_LEVEL_MAX;
699                         if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
700                                 new_node_content = liquid_kind;
701                         else
702                                 new_node_content = floodable_node;
703                 } else if (ignored_sources && liquid_level >= 0) {
704                         // Maybe there are neighbouring sources that aren't loaded yet
705                         // so prevent flowing away.
706                         new_node_level = liquid_level;
707                         new_node_content = liquid_kind;
708                 } else {
709                         // no surrounding sources, so get the maximum level that can flow into this node
710                         for (u16 i = 0; i < num_flows; i++) {
711                                 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
712                                 switch (flows[i].t) {
713                                         case NEIGHBOR_UPPER:
714                                                 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
715                                                         max_node_level = LIQUID_LEVEL_MAX;
716                                                         if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
717                                                                 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
718                                                 } else if (nb_liquid_level > max_node_level) {
719                                                         max_node_level = nb_liquid_level;
720                                                 }
721                                                 break;
722                                         case NEIGHBOR_LOWER:
723                                                 break;
724                                         case NEIGHBOR_SAME_LEVEL:
725                                                 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
726                                                                 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
727                                                         max_node_level = nb_liquid_level - 1;
728                                                 break;
729                                 }
730                         }
731
732                         u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
733                         if (viscosity > 1 && max_node_level != liquid_level) {
734                                 // amount to gain, limited by viscosity
735                                 // must be at least 1 in absolute value
736                                 s8 level_inc = max_node_level - liquid_level;
737                                 if (level_inc < -viscosity || level_inc > viscosity)
738                                         new_node_level = liquid_level + level_inc/viscosity;
739                                 else if (level_inc < 0)
740                                         new_node_level = liquid_level - 1;
741                                 else if (level_inc > 0)
742                                         new_node_level = liquid_level + 1;
743                                 if (new_node_level != max_node_level)
744                                         must_reflow.push_back(p0);
745                         } else {
746                                 new_node_level = max_node_level;
747                         }
748
749                         if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
750                                 new_node_content = liquid_kind;
751                         else
752                                 new_node_content = floodable_node;
753
754                 }
755
756                 /*
757                         check if anything has changed. if not, just continue with the next node.
758                  */
759                 if (new_node_content == n0.getContent() &&
760                                 (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
761                                 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
762                                 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
763                                 == flowing_down)))
764                         continue;
765
766
767                 /*
768                         update the current node
769                  */
770                 MapNode n00 = n0;
771                 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
772                 if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
773                         // set level to last 3 bits, flowing down bit to 4th bit
774                         n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
775                 } else {
776                         // set the liquid level and flow bits to 0
777                         n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
778                 }
779
780                 // change the node.
781                 n0.setContent(new_node_content);
782
783                 // on_flood() the node
784                 if (floodable_node != CONTENT_AIR) {
785                         if (env->getScriptIface()->node_on_flood(p0, n00, n0))
786                                 continue;
787                 }
788
789                 // Ignore light (because calling voxalgo::update_lighting_nodes)
790                 n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
791                 n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
792
793                 // Find out whether there is a suspect for this action
794                 std::string suspect;
795                 if (m_gamedef->rollback())
796                         suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
797
798                 if (m_gamedef->rollback() && !suspect.empty()) {
799                         // Blame suspect
800                         RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
801                         // Get old node for rollback
802                         RollbackNode rollback_oldnode(this, p0, m_gamedef);
803                         // Set node
804                         setNode(p0, n0);
805                         // Report
806                         RollbackNode rollback_newnode(this, p0, m_gamedef);
807                         RollbackAction action;
808                         action.setSetNode(p0, rollback_oldnode, rollback_newnode);
809                         m_gamedef->rollback()->reportAction(action);
810                 } else {
811                         // Set node
812                         setNode(p0, n0);
813                 }
814
815                 v3s16 blockpos = getNodeBlockPos(p0);
816                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
817                 if (block != NULL) {
818                         modified_blocks[blockpos] =  block;
819                         changed_nodes.emplace_back(p0, n00);
820                 }
821
822                 /*
823                         enqueue neighbors for update if neccessary
824                  */
825                 switch (m_nodedef->get(n0.getContent()).liquid_type) {
826                         case LIQUID_SOURCE:
827                         case LIQUID_FLOWING:
828                                 // make sure source flows into all neighboring nodes
829                                 for (u16 i = 0; i < num_flows; i++)
830                                         if (flows[i].t != NEIGHBOR_UPPER)
831                                                 m_transforming_liquid.push_back(flows[i].p);
832                                 for (u16 i = 0; i < num_airs; i++)
833                                         if (airs[i].t != NEIGHBOR_UPPER)
834                                                 m_transforming_liquid.push_back(airs[i].p);
835                                 break;
836                         case LIQUID_NONE:
837                                 // this flow has turned to air; neighboring flows might need to do the same
838                                 for (u16 i = 0; i < num_flows; i++)
839                                         m_transforming_liquid.push_back(flows[i].p);
840                                 break;
841                 }
842         }
843         //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
844
845         for (auto &iter : must_reflow)
846                 m_transforming_liquid.push_back(iter);
847
848         voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
849
850
851         /* ----------------------------------------------------------------------
852          * Manage the queue so that it does not grow indefinately
853          */
854         u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
855
856         if (time_until_purge == 0)
857                 return; // Feature disabled
858
859         time_until_purge *= 1000;       // seconds -> milliseconds
860
861         u64 curr_time = porting::getTimeMs();
862         u32 prev_unprocessed = m_unprocessed_count;
863         m_unprocessed_count = m_transforming_liquid.size();
864
865         // if unprocessed block count is decreasing or stable
866         if (m_unprocessed_count <= prev_unprocessed) {
867                 m_queue_size_timer_started = false;
868         } else {
869                 if (!m_queue_size_timer_started)
870                         m_inc_trending_up_start_time = curr_time;
871                 m_queue_size_timer_started = true;
872         }
873
874         // Account for curr_time overflowing
875         if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
876                 m_queue_size_timer_started = false;
877
878         /* If the queue has been growing for more than liquid_queue_purge_time seconds
879          * and the number of unprocessed blocks is still > liquid_loop_max then we
880          * cannot keep up; dump the oldest blocks from the queue so that the queue
881          * has liquid_loop_max items in it
882          */
883         if (m_queue_size_timer_started
884                         && curr_time - m_inc_trending_up_start_time > time_until_purge
885                         && m_unprocessed_count > liquid_loop_max) {
886
887                 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
888
889                 infostream << "transformLiquids(): DUMPING " << dump_qty
890                            << " blocks from the queue" << std::endl;
891
892                 while (dump_qty--)
893                         m_transforming_liquid.pop_front();
894
895                 m_queue_size_timer_started = false; // optimistically assume we can keep up now
896                 m_unprocessed_count = m_transforming_liquid.size();
897         }
898 }
899
900 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
901 {
902         std::vector<v3s16> positions_with_meta;
903
904         sortBoxVerticies(p1, p2);
905         v3s16 bpmin = getNodeBlockPos(p1);
906         v3s16 bpmax = getNodeBlockPos(p2);
907
908         VoxelArea area(p1, p2);
909
910         for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
911         for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
912         for (s16 x = bpmin.X; x <= bpmax.X; x++) {
913                 v3s16 blockpos(x, y, z);
914
915                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
916                 if (!block) {
917                         verbosestream << "Map::getNodeMetadata(): Need to emerge "
918                                 << PP(blockpos) << std::endl;
919                         block = emergeBlock(blockpos, false);
920                 }
921                 if (!block) {
922                         infostream << "WARNING: Map::getNodeMetadata(): Block not found"
923                                 << std::endl;
924                         continue;
925                 }
926
927                 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
928                 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
929                 for (size_t i = 0; i != keys.size(); i++) {
930                         v3s16 p(keys[i] + p_base);
931                         if (!area.contains(p))
932                                 continue;
933
934                         positions_with_meta.push_back(p);
935                 }
936         }
937
938         return positions_with_meta;
939 }
940
941 NodeMetadata *Map::getNodeMetadata(v3s16 p)
942 {
943         v3s16 blockpos = getNodeBlockPos(p);
944         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
945         MapBlock *block = getBlockNoCreateNoEx(blockpos);
946         if(!block){
947                 infostream<<"Map::getNodeMetadata(): Need to emerge "
948                                 <<PP(blockpos)<<std::endl;
949                 block = emergeBlock(blockpos, false);
950         }
951         if(!block){
952                 warningstream<<"Map::getNodeMetadata(): Block not found"
953                                 <<std::endl;
954                 return NULL;
955         }
956         NodeMetadata *meta = block->m_node_metadata.get(p_rel);
957         return meta;
958 }
959
960 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
961 {
962         v3s16 blockpos = getNodeBlockPos(p);
963         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
964         MapBlock *block = getBlockNoCreateNoEx(blockpos);
965         if(!block){
966                 infostream<<"Map::setNodeMetadata(): Need to emerge "
967                                 <<PP(blockpos)<<std::endl;
968                 block = emergeBlock(blockpos, false);
969         }
970         if(!block){
971                 warningstream<<"Map::setNodeMetadata(): Block not found"
972                                 <<std::endl;
973                 return false;
974         }
975         block->m_node_metadata.set(p_rel, meta);
976         return true;
977 }
978
979 void Map::removeNodeMetadata(v3s16 p)
980 {
981         v3s16 blockpos = getNodeBlockPos(p);
982         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
983         MapBlock *block = getBlockNoCreateNoEx(blockpos);
984         if(block == NULL)
985         {
986                 warningstream<<"Map::removeNodeMetadata(): Block not found"
987                                 <<std::endl;
988                 return;
989         }
990         block->m_node_metadata.remove(p_rel);
991 }
992
993 NodeTimer Map::getNodeTimer(v3s16 p)
994 {
995         v3s16 blockpos = getNodeBlockPos(p);
996         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
997         MapBlock *block = getBlockNoCreateNoEx(blockpos);
998         if(!block){
999                 infostream<<"Map::getNodeTimer(): Need to emerge "
1000                                 <<PP(blockpos)<<std::endl;
1001                 block = emergeBlock(blockpos, false);
1002         }
1003         if(!block){
1004                 warningstream<<"Map::getNodeTimer(): Block not found"
1005                                 <<std::endl;
1006                 return NodeTimer();
1007         }
1008         NodeTimer t = block->m_node_timers.get(p_rel);
1009         NodeTimer nt(t.timeout, t.elapsed, p);
1010         return nt;
1011 }
1012
1013 void Map::setNodeTimer(const NodeTimer &t)
1014 {
1015         v3s16 p = t.position;
1016         v3s16 blockpos = getNodeBlockPos(p);
1017         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1018         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1019         if(!block){
1020                 infostream<<"Map::setNodeTimer(): Need to emerge "
1021                                 <<PP(blockpos)<<std::endl;
1022                 block = emergeBlock(blockpos, false);
1023         }
1024         if(!block){
1025                 warningstream<<"Map::setNodeTimer(): Block not found"
1026                                 <<std::endl;
1027                 return;
1028         }
1029         NodeTimer nt(t.timeout, t.elapsed, p_rel);
1030         block->m_node_timers.set(nt);
1031 }
1032
1033 void Map::removeNodeTimer(v3s16 p)
1034 {
1035         v3s16 blockpos = getNodeBlockPos(p);
1036         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1037         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1038         if(block == NULL)
1039         {
1040                 warningstream<<"Map::removeNodeTimer(): Block not found"
1041                                 <<std::endl;
1042                 return;
1043         }
1044         block->m_node_timers.remove(p_rel);
1045 }
1046
1047 bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
1048         const core::aabbox3d<s16> &block_bounds, v3s16 &check)
1049 {
1050         /*
1051                 This functions determines the node inside the target block that is
1052                 closest to the camera position. This increases the occlusion culling
1053                 accuracy in straight and diagonal corridors.
1054                 The returned position will be occlusion checked first in addition to the
1055                 others (8 corners + center).
1056                 No position is returned if
1057                 - the closest node is a corner, corners are checked anyway.
1058                 - the camera is inside the target block, it will never be occluded.
1059         */
1060 #define CLOSEST_EDGE(pos, bounds, axis) \
1061         ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
1062         (bounds).MaxEdge.axis
1063
1064         bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
1065                         (pos_camera.X <= block_bounds.MaxEdge.X);
1066         bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
1067                         (pos_camera.Y <= block_bounds.MaxEdge.Y);
1068         bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
1069                         (pos_camera.Z <= block_bounds.MaxEdge.Z);
1070
1071         if (x_inside && y_inside && z_inside)
1072                 return false; // Camera inside target mapblock
1073
1074         // straight
1075         if (x_inside && y_inside) {
1076                 check = v3s16(pos_camera.X, pos_camera.Y, 0);
1077                 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1078                 return true;
1079         } else if (y_inside && z_inside) {
1080                 check = v3s16(0, pos_camera.Y, pos_camera.Z);
1081                 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1082                 return true;
1083         } else if (x_inside && z_inside) {
1084                 check = v3s16(pos_camera.X, 0, pos_camera.Z);
1085                 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1086                 return true;
1087         }
1088
1089         // diagonal
1090         if (x_inside) {
1091                 check = v3s16(pos_camera.X, 0, 0);
1092                 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1093                 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1094                 return true;
1095         } else if (y_inside) {
1096                 check = v3s16(0, pos_camera.Y, 0);
1097                 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1098                 check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
1099                 return true;
1100         } else if (z_inside) {
1101                 check = v3s16(0, 0, pos_camera.Z);
1102                 check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
1103                 check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
1104                 return true;
1105         }
1106
1107         // Closest node would be a corner, none returned
1108         return false;
1109 }
1110
1111 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
1112         float step, float stepfac, float offset, float end_offset, u32 needed_count)
1113 {
1114         v3f direction = intToFloat(pos_target - pos_camera, BS);
1115         float distance = direction.getLength();
1116
1117         // Normalize direction vector
1118         if (distance > 0.0f)
1119                 direction /= distance;
1120
1121         v3f pos_origin_f = intToFloat(pos_camera, BS);
1122         u32 count = 0;
1123         bool is_valid_position;
1124
1125         for (; offset < distance + end_offset; offset += step) {
1126                 v3f pos_node_f = pos_origin_f + direction * offset;
1127                 v3s16 pos_node = floatToInt(pos_node_f, BS);
1128
1129                 MapNode node = getNode(pos_node, &is_valid_position);
1130
1131                 if (is_valid_position &&
1132                                 !m_nodedef->get(node).light_propagates) {
1133                         // Cannot see through light-blocking nodes --> occluded
1134                         count++;
1135                         if (count >= needed_count)
1136                                 return true;
1137                 }
1138                 step *= stepfac;
1139         }
1140         return false;
1141 }
1142
1143 bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
1144 {
1145         // Check occlusion for center and all 8 corners of the mapblock
1146         // Overshoot a little for less flickering
1147         static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
1148         static const v3s16 dir9[9] = {
1149                 v3s16( 0,  0,  0),
1150                 v3s16( 1,  1,  1) * bs2,
1151                 v3s16( 1,  1, -1) * bs2,
1152                 v3s16( 1, -1,  1) * bs2,
1153                 v3s16( 1, -1, -1) * bs2,
1154                 v3s16(-1,  1,  1) * bs2,
1155                 v3s16(-1,  1, -1) * bs2,
1156                 v3s16(-1, -1,  1) * bs2,
1157                 v3s16(-1, -1, -1) * bs2,
1158         };
1159
1160         v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
1161
1162         // Starting step size, value between 1m and sqrt(3)m
1163         float step = BS * 1.2f;
1164         // Multiply step by each iteraction by 'stepfac' to reduce checks in distance
1165         float stepfac = 1.05f;
1166
1167         float start_offset = BS * 1.0f;
1168
1169         // The occlusion search of 'isOccluded()' must stop short of the target
1170         // point by distance 'end_offset' to not enter the target mapblock.
1171         // For the 8 mapblock corners 'end_offset' must therefore be the maximum
1172         // diagonal of a mapblock, because we must consider all view angles.
1173         // sqrt(1^2 + 1^2 + 1^2) = 1.732
1174         float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
1175
1176         // to reduce the likelihood of falsely occluded blocks
1177         // require at least two solid blocks
1178         // this is a HACK, we should think of a more precise algorithm
1179         u32 needed_count = 2;
1180
1181         // Additional occlusion check, see comments in that function
1182         v3s16 check;
1183         if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
1184                 // node is always on a side facing the camera, end_offset can be lower
1185                 if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
1186                                 -1.0f, needed_count))
1187                         return false;
1188         }
1189
1190         for (const v3s16 &dir : dir9) {
1191                 if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
1192                                 start_offset, end_offset, needed_count))
1193                         return false;
1194         }
1195         return true;
1196 }
1197
1198 /*
1199         ServerMap
1200 */
1201 ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
1202                 EmergeManager *emerge, MetricsBackend *mb):
1203         Map(gamedef),
1204         settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
1205         m_emerge(emerge)
1206 {
1207         verbosestream<<FUNCTION_NAME<<std::endl;
1208
1209         // Tell the EmergeManager about our MapSettingsManager
1210         emerge->map_settings_mgr = &settings_mgr;
1211
1212         /*
1213                 Try to load map; if not found, create a new one.
1214         */
1215
1216         // Determine which database backend to use
1217         std::string conf_path = savedir + DIR_DELIM + "world.mt";
1218         Settings conf;
1219         bool succeeded = conf.readConfigFile(conf_path.c_str());
1220         if (!succeeded || !conf.exists("backend")) {
1221                 // fall back to sqlite3
1222                 conf.set("backend", "sqlite3");
1223         }
1224         std::string backend = conf.get("backend");
1225         dbase = createDatabase(backend, savedir, conf);
1226         if (conf.exists("readonly_backend")) {
1227                 std::string readonly_dir = savedir + DIR_DELIM + "readonly";
1228                 dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
1229         }
1230         if (!conf.updateConfigFile(conf_path.c_str()))
1231                 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
1232
1233         m_savedir = savedir;
1234         m_map_saving_enabled = false;
1235
1236         m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
1237
1238         m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
1239
1240         try {
1241                 // If directory exists, check contents and load if possible
1242                 if (fs::PathExists(m_savedir)) {
1243                         // If directory is empty, it is safe to save into it.
1244                         if (fs::GetDirListing(m_savedir).empty()) {
1245                                 infostream<<"ServerMap: Empty save directory is valid."
1246                                                 <<std::endl;
1247                                 m_map_saving_enabled = true;
1248                         }
1249                         else
1250                         {
1251
1252                                 if (settings_mgr.loadMapMeta()) {
1253                                         infostream << "ServerMap: Metadata loaded from "
1254                                                 << savedir << std::endl;
1255                                 } else {
1256                                         infostream << "ServerMap: Metadata could not be loaded "
1257                                                 "from " << savedir << ", assuming valid save "
1258                                                 "directory." << std::endl;
1259                                 }
1260
1261                                 m_map_saving_enabled = true;
1262                                 // Map loaded, not creating new one
1263                                 return;
1264                         }
1265                 }
1266                 // If directory doesn't exist, it is safe to save to it
1267                 else{
1268                         m_map_saving_enabled = true;
1269                 }
1270         }
1271         catch(std::exception &e)
1272         {
1273                 warningstream<<"ServerMap: Failed to load map from "<<savedir
1274                                 <<", exception: "<<e.what()<<std::endl;
1275                 infostream<<"Please remove the map or fix it."<<std::endl;
1276                 warningstream<<"Map saving will be disabled."<<std::endl;
1277         }
1278 }
1279
1280 ServerMap::~ServerMap()
1281 {
1282         verbosestream<<FUNCTION_NAME<<std::endl;
1283
1284         try
1285         {
1286                 if (m_map_saving_enabled) {
1287                         // Save only changed parts
1288                         save(MOD_STATE_WRITE_AT_UNLOAD);
1289                         infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
1290                 } else {
1291                         infostream << "ServerMap: Map not saved" << std::endl;
1292                 }
1293         }
1294         catch(std::exception &e)
1295         {
1296                 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1297                                 <<", exception: "<<e.what()<<std::endl;
1298         }
1299
1300         /*
1301                 Close database if it was opened
1302         */
1303         delete dbase;
1304         delete dbase_ro;
1305
1306 #if 0
1307         /*
1308                 Free all MapChunks
1309         */
1310         core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1311         for(; i.atEnd() == false; i++)
1312         {
1313                 MapChunk *chunk = i.getNode()->getValue();
1314                 delete chunk;
1315         }
1316 #endif
1317 }
1318
1319 MapgenParams *ServerMap::getMapgenParams()
1320 {
1321         // getMapgenParams() should only ever be called after Server is initialized
1322         assert(settings_mgr.mapgen_params != NULL);
1323         return settings_mgr.mapgen_params;
1324 }
1325
1326 u64 ServerMap::getSeed()
1327 {
1328         return getMapgenParams()->seed;
1329 }
1330
1331 bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1332 {
1333         const s16 mapgen_limit_bp = rangelim(
1334                 getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
1335                 MAP_BLOCKSIZE;
1336         return p.X < -mapgen_limit_bp ||
1337                 p.X >  mapgen_limit_bp ||
1338                 p.Y < -mapgen_limit_bp ||
1339                 p.Y >  mapgen_limit_bp ||
1340                 p.Z < -mapgen_limit_bp ||
1341                 p.Z >  mapgen_limit_bp;
1342 }
1343
1344 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
1345 {
1346         s16 csize = getMapgenParams()->chunksize;
1347         v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
1348         v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
1349
1350         if (!m_chunks_in_progress.insert(bpmin).second)
1351                 return false;
1352
1353         bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1354         EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
1355
1356         v3s16 extra_borders(1, 1, 1);
1357         v3s16 full_bpmin = bpmin - extra_borders;
1358         v3s16 full_bpmax = bpmax + extra_borders;
1359
1360         // Do nothing if not inside mapgen limits (+-1 because of neighbors)
1361         if (blockpos_over_mapgen_limit(full_bpmin) ||
1362                         blockpos_over_mapgen_limit(full_bpmax))
1363                 return false;
1364
1365         data->seed = getSeed();
1366         data->blockpos_min = bpmin;
1367         data->blockpos_max = bpmax;
1368         data->nodedef = m_nodedef;
1369
1370         /*
1371                 Create the whole area of this and the neighboring blocks
1372         */
1373         for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
1374         for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
1375                 v2s16 sectorpos(x, z);
1376                 // Sector metadata is loaded from disk if not already loaded.
1377                 MapSector *sector = createSector(sectorpos);
1378                 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
1379
1380                 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
1381                         v3s16 p(x, y, z);
1382
1383                         MapBlock *block = emergeBlock(p, false);
1384                         if (block == NULL) {
1385                                 block = createBlock(p);
1386
1387                                 // Block gets sunlight if this is true.
1388                                 // Refer to the map generator heuristics.
1389                                 bool ug = m_emerge->isBlockUnderground(p);
1390                                 block->setIsUnderground(ug);
1391                         }
1392                 }
1393         }
1394
1395         /*
1396                 Now we have a big empty area.
1397
1398                 Make a ManualMapVoxelManipulator that contains this and the
1399                 neighboring blocks
1400         */
1401
1402         data->vmanip = new MMVManip(this);
1403         data->vmanip->initialEmerge(full_bpmin, full_bpmax);
1404
1405         // Note: we may need this again at some point.
1406 #if 0
1407         // Ensure none of the blocks to be generated were marked as
1408         // containing CONTENT_IGNORE
1409         for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
1410                 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
1411                         for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
1412                                 core::map<v3s16, u8>::Node *n;
1413                                 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
1414                                 if (n == NULL)
1415                                         continue;
1416                                 u8 flags = n->getValue();
1417                                 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
1418                                 n->setValue(flags);
1419                         }
1420                 }
1421         }
1422 #endif
1423
1424         // Data is ready now.
1425         return true;
1426 }
1427
1428 void ServerMap::finishBlockMake(BlockMakeData *data,
1429         std::map<v3s16, MapBlock*> *changed_blocks)
1430 {
1431         v3s16 bpmin = data->blockpos_min;
1432         v3s16 bpmax = data->blockpos_max;
1433
1434         v3s16 extra_borders(1, 1, 1);
1435
1436         bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
1437         EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
1438
1439         /*
1440                 Blit generated stuff to map
1441                 NOTE: blitBackAll adds nearly everything to changed_blocks
1442         */
1443         data->vmanip->blitBackAll(changed_blocks);
1444
1445         EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
1446                 << changed_blocks->size());
1447
1448         /*
1449                 Copy transforming liquid information
1450         */
1451         while (data->transforming_liquid.size()) {
1452                 m_transforming_liquid.push_back(data->transforming_liquid.front());
1453                 data->transforming_liquid.pop_front();
1454         }
1455
1456         for (auto &changed_block : *changed_blocks) {
1457                 MapBlock *block = changed_block.second;
1458                 if (!block)
1459                         continue;
1460                 /*
1461                         Update day/night difference cache of the MapBlocks
1462                 */
1463                 block->expireDayNightDiff();
1464                 /*
1465                         Set block as modified
1466                 */
1467                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1468                         MOD_REASON_EXPIRE_DAYNIGHTDIFF);
1469         }
1470
1471         /*
1472                 Set central blocks as generated
1473         */
1474         for (s16 x = bpmin.X; x <= bpmax.X; x++)
1475         for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1476         for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
1477                 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
1478                 if (!block)
1479                         continue;
1480
1481                 block->setGenerated(true);
1482         }
1483
1484         /*
1485                 Save changed parts of map
1486                 NOTE: Will be saved later.
1487         */
1488         //save(MOD_STATE_WRITE_AT_UNLOAD);
1489         m_chunks_in_progress.erase(bpmin);
1490 }
1491
1492 MapSector *ServerMap::createSector(v2s16 p2d)
1493 {
1494         /*
1495                 Check if it exists already in memory
1496         */
1497         MapSector *sector = getSectorNoGenerate(p2d);
1498         if (sector)
1499                 return sector;
1500
1501         /*
1502                 Do not create over max mapgen limit
1503         */
1504         const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
1505         if (p2d.X < -max_limit_bp ||
1506                         p2d.X >  max_limit_bp ||
1507                         p2d.Y < -max_limit_bp ||
1508                         p2d.Y >  max_limit_bp)
1509                 throw InvalidPositionException("createSector(): pos. over max mapgen limit");
1510
1511         /*
1512                 Generate blank sector
1513         */
1514
1515         sector = new MapSector(this, p2d, m_gamedef);
1516
1517         // Sector position on map in nodes
1518         //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1519
1520         /*
1521                 Insert to container
1522         */
1523         m_sectors[p2d] = sector;
1524
1525         return sector;
1526 }
1527
1528 #if 0
1529 /*
1530         This is a quick-hand function for calling makeBlock().
1531 */
1532 MapBlock * ServerMap::generateBlock(
1533                 v3s16 p,
1534                 std::map<v3s16, MapBlock*> &modified_blocks
1535 )
1536 {
1537         bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
1538
1539         TimeTaker timer("generateBlock");
1540
1541         //MapBlock *block = original_dummy;
1542
1543         v2s16 p2d(p.X, p.Z);
1544         v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
1545
1546         /*
1547                 Do not generate over-limit
1548         */
1549         if(blockpos_over_limit(p))
1550         {
1551                 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
1552                 throw InvalidPositionException("generateBlock(): pos. over limit");
1553         }
1554
1555         /*
1556                 Create block make data
1557         */
1558         BlockMakeData data;
1559         initBlockMake(&data, p);
1560
1561         /*
1562                 Generate block
1563         */
1564         {
1565                 TimeTaker t("mapgen::make_block()");
1566                 mapgen->makeChunk(&data);
1567                 //mapgen::make_block(&data);
1568
1569                 if(enable_mapgen_debug_info == false)
1570                         t.stop(true); // Hide output
1571         }
1572
1573         /*
1574                 Blit data back on map, update lighting, add mobs and whatever this does
1575         */
1576         finishBlockMake(&data, modified_blocks);
1577
1578         /*
1579                 Get central block
1580         */
1581         MapBlock *block = getBlockNoCreateNoEx(p);
1582
1583 #if 0
1584         /*
1585                 Check result
1586         */
1587         if(block)
1588         {
1589                 bool erroneus_content = false;
1590                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1591                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1592                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1593                 {
1594                         v3s16 p(x0,y0,z0);
1595                         MapNode n = block->getNode(p);
1596                         if(n.getContent() == CONTENT_IGNORE)
1597                         {
1598                                 infostream<<"CONTENT_IGNORE at "
1599                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1600                                                 <<std::endl;
1601                                 erroneus_content = true;
1602                                 assert(0);
1603                         }
1604                 }
1605                 if(erroneus_content)
1606                 {
1607                         assert(0);
1608                 }
1609         }
1610 #endif
1611
1612 #if 0
1613         /*
1614                 Generate a completely empty block
1615         */
1616         if(block)
1617         {
1618                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1619                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1620                 {
1621                         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1622                         {
1623                                 MapNode n;
1624                                 n.setContent(CONTENT_AIR);
1625                                 block->setNode(v3s16(x0,y0,z0), n);
1626                         }
1627                 }
1628         }
1629 #endif
1630
1631         if(enable_mapgen_debug_info == false)
1632                 timer.stop(true); // Hide output
1633
1634         return block;
1635 }
1636 #endif
1637
1638 MapBlock * ServerMap::createBlock(v3s16 p)
1639 {
1640         /*
1641                 Do not create over max mapgen limit
1642         */
1643         if (blockpos_over_max_limit(p))
1644                 throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
1645
1646         v2s16 p2d(p.X, p.Z);
1647         s16 block_y = p.Y;
1648         /*
1649                 This will create or load a sector if not found in memory.
1650                 If block exists on disk, it will be loaded.
1651
1652                 NOTE: On old save formats, this will be slow, as it generates
1653                       lighting on blocks for them.
1654         */
1655         MapSector *sector;
1656         try {
1657                 sector = createSector(p2d);
1658         } catch (InvalidPositionException &e) {
1659                 infostream<<"createBlock: createSector() failed"<<std::endl;
1660                 throw e;
1661         }
1662
1663         /*
1664                 Try to get a block from the sector
1665         */
1666
1667         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
1668         if (block) {
1669                 if(block->isDummy())
1670                         block->unDummify();
1671                 return block;
1672         }
1673         // Create blank
1674         block = sector->createBlankBlock(block_y);
1675
1676         return block;
1677 }
1678
1679 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
1680 {
1681         {
1682                 MapBlock *block = getBlockNoCreateNoEx(p);
1683                 if (block && !block->isDummy())
1684                         return block;
1685         }
1686
1687         {
1688                 MapBlock *block = loadBlock(p);
1689                 if(block)
1690                         return block;
1691         }
1692
1693         if (create_blank) {
1694                 MapSector *sector = createSector(v2s16(p.X, p.Z));
1695                 MapBlock *block = sector->createBlankBlock(p.Y);
1696
1697                 return block;
1698         }
1699
1700         return NULL;
1701 }
1702
1703 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
1704 {
1705         MapBlock *block = getBlockNoCreateNoEx(p3d);
1706         if (block == NULL)
1707                 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
1708
1709         return block;
1710 }
1711
1712 // N.B.  This requires no synchronization, since data will not be modified unless
1713 // the VoxelManipulator being updated belongs to the same thread.
1714 void ServerMap::updateVManip(v3s16 pos)
1715 {
1716         Mapgen *mg = m_emerge->getCurrentMapgen();
1717         if (!mg)
1718                 return;
1719
1720         MMVManip *vm = mg->vm;
1721         if (!vm)
1722                 return;
1723
1724         if (!vm->m_area.contains(pos))
1725                 return;
1726
1727         s32 idx = vm->m_area.index(pos);
1728         vm->m_data[idx] = getNode(pos);
1729         vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
1730
1731         vm->m_is_dirty = true;
1732 }
1733
1734 void ServerMap::save(ModifiedState save_level)
1735 {
1736         if (!m_map_saving_enabled) {
1737                 warningstream<<"Not saving map, saving disabled."<<std::endl;
1738                 return;
1739         }
1740
1741         u64 start_time = porting::getTimeNs();
1742
1743         if(save_level == MOD_STATE_CLEAN)
1744                 infostream<<"ServerMap: Saving whole map, this can take time."
1745                                 <<std::endl;
1746
1747         if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
1748                 if (settings_mgr.saveMapMeta())
1749                         m_map_metadata_changed = false;
1750         }
1751
1752         // Profile modified reasons
1753         Profiler modprofiler;
1754
1755         u32 block_count = 0;
1756         u32 block_count_all = 0; // Number of blocks in memory
1757
1758         // Don't do anything with sqlite unless something is really saved
1759         bool save_started = false;
1760
1761         for (auto &sector_it : m_sectors) {
1762                 MapSector *sector = sector_it.second;
1763
1764                 MapBlockVect blocks;
1765                 sector->getBlocks(blocks);
1766
1767                 for (MapBlock *block : blocks) {
1768                         block_count_all++;
1769
1770                         if(block->getModified() >= (u32)save_level) {
1771                                 // Lazy beginSave()
1772                                 if(!save_started) {
1773                                         beginSave();
1774                                         save_started = true;
1775                                 }
1776
1777                                 modprofiler.add(block->getModifiedReasonString(), 1);
1778
1779                                 saveBlock(block);
1780                                 block_count++;
1781                         }
1782                 }
1783         }
1784
1785         if(save_started)
1786                 endSave();
1787
1788         /*
1789                 Only print if something happened or saved whole map
1790         */
1791         if(save_level == MOD_STATE_CLEAN
1792                         || block_count != 0) {
1793                 infostream << "ServerMap: Written: "
1794                                 << block_count << " blocks"
1795                                 << ", " << block_count_all << " blocks in memory."
1796                                 << std::endl;
1797                 PrintInfo(infostream); // ServerMap/ClientMap:
1798                 infostream<<"Blocks modified by: "<<std::endl;
1799                 modprofiler.print(infostream);
1800         }
1801
1802         auto end_time = porting::getTimeNs();
1803         m_save_time_counter->increment(end_time - start_time);
1804 }
1805
1806 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
1807 {
1808         dbase->listAllLoadableBlocks(dst);
1809         if (dbase_ro)
1810                 dbase_ro->listAllLoadableBlocks(dst);
1811 }
1812
1813 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
1814 {
1815         for (auto &sector_it : m_sectors) {
1816                 MapSector *sector = sector_it.second;
1817
1818                 MapBlockVect blocks;
1819                 sector->getBlocks(blocks);
1820
1821                 for (MapBlock *block : blocks) {
1822                         v3s16 p = block->getPos();
1823                         dst.push_back(p);
1824                 }
1825         }
1826 }
1827
1828 MapDatabase *ServerMap::createDatabase(
1829         const std::string &name,
1830         const std::string &savedir,
1831         Settings &conf)
1832 {
1833         if (name == "sqlite3")
1834                 return new MapDatabaseSQLite3(savedir);
1835         if (name == "dummy")
1836                 return new Database_Dummy();
1837         #if USE_LEVELDB
1838         if (name == "leveldb")
1839                 return new Database_LevelDB(savedir);
1840         #endif
1841         #if USE_REDIS
1842         if (name == "redis")
1843                 return new Database_Redis(conf);
1844         #endif
1845         #if USE_POSTGRESQL
1846         if (name == "postgresql") {
1847                 std::string connect_string;
1848                 conf.getNoEx("pgsql_connection", connect_string);
1849                 return new MapDatabasePostgreSQL(connect_string);
1850         }
1851         #endif
1852
1853         throw BaseException(std::string("Database backend ") + name + " not supported.");
1854 }
1855
1856 void ServerMap::beginSave()
1857 {
1858         dbase->beginSave();
1859 }
1860
1861 void ServerMap::endSave()
1862 {
1863         dbase->endSave();
1864 }
1865
1866 bool ServerMap::saveBlock(MapBlock *block)
1867 {
1868         return saveBlock(block, dbase, m_map_compression_level);
1869 }
1870
1871 bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level)
1872 {
1873         v3s16 p3d = block->getPos();
1874
1875         // Dummy blocks are not written
1876         if (block->isDummy()) {
1877                 warningstream << "saveBlock: Not writing dummy block "
1878                         << PP(p3d) << std::endl;
1879                 return true;
1880         }
1881
1882         // Format used for writing
1883         u8 version = SER_FMT_VER_HIGHEST_WRITE;
1884
1885         /*
1886                 [0] u8 serialization version
1887                 [1] data
1888         */
1889         std::ostringstream o(std::ios_base::binary);
1890         o.write((char*) &version, 1);
1891         block->serialize(o, version, true, compression_level);
1892
1893         bool ret = db->saveBlock(p3d, o.str());
1894         if (ret) {
1895                 // We just wrote it to the disk so clear modified flag
1896                 block->resetModified();
1897         }
1898         return ret;
1899 }
1900
1901 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
1902 {
1903         try {
1904                 std::istringstream is(*blob, std::ios_base::binary);
1905
1906                 u8 version = SER_FMT_VER_INVALID;
1907                 is.read((char*)&version, 1);
1908
1909                 if(is.fail())
1910                         throw SerializationError("ServerMap::loadBlock(): Failed"
1911                                         " to read MapBlock version");
1912
1913                 MapBlock *block = NULL;
1914                 bool created_new = false;
1915                 block = sector->getBlockNoCreateNoEx(p3d.Y);
1916                 if(block == NULL)
1917                 {
1918                         block = sector->createBlankBlockNoInsert(p3d.Y);
1919                         created_new = true;
1920                 }
1921
1922                 // Read basic data
1923                 block->deSerialize(is, version, true);
1924
1925                 // If it's a new block, insert it to the map
1926                 if (created_new) {
1927                         sector->insertBlock(block);
1928                         ReflowScan scanner(this, m_emerge->ndef);
1929                         scanner.scan(block, &m_transforming_liquid);
1930                 }
1931
1932                 /*
1933                         Save blocks loaded in old format in new format
1934                 */
1935
1936                 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
1937                 // Only save if asked to; no need to update version
1938                 if(save_after_load)
1939                         saveBlock(block);
1940
1941                 // We just loaded it from, so it's up-to-date.
1942                 block->resetModified();
1943         }
1944         catch(SerializationError &e)
1945         {
1946                 errorstream<<"Invalid block data in database"
1947                                 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
1948                                 <<" (SerializationError): "<<e.what()<<std::endl;
1949
1950                 // TODO: Block should be marked as invalid in memory so that it is
1951                 // not touched but the game can run
1952
1953                 if(g_settings->getBool("ignore_world_load_errors")){
1954                         errorstream<<"Ignoring block load error. Duck and cover! "
1955                                         <<"(ignore_world_load_errors)"<<std::endl;
1956                 } else {
1957                         throw SerializationError("Invalid block data in database");
1958                 }
1959         }
1960 }
1961
1962 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
1963 {
1964         bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
1965
1966         v2s16 p2d(blockpos.X, blockpos.Z);
1967
1968         std::string ret;
1969         dbase->loadBlock(blockpos, &ret);
1970         if (!ret.empty()) {
1971                 loadBlock(&ret, blockpos, createSector(p2d), false);
1972         } else if (dbase_ro) {
1973                 dbase_ro->loadBlock(blockpos, &ret);
1974                 if (!ret.empty()) {
1975                         loadBlock(&ret, blockpos, createSector(p2d), false);
1976                 }
1977         } else {
1978                 return NULL;
1979         }
1980
1981         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1982         if (created_new && (block != NULL)) {
1983                 std::map<v3s16, MapBlock*> modified_blocks;
1984                 // Fix lighting if necessary
1985                 voxalgo::update_block_border_lighting(this, block, modified_blocks);
1986                 if (!modified_blocks.empty()) {
1987                         //Modified lighting, send event
1988                         MapEditEvent event;
1989                         event.type = MEET_OTHER;
1990                         std::map<v3s16, MapBlock *>::iterator it;
1991                         for (it = modified_blocks.begin();
1992                                         it != modified_blocks.end(); ++it)
1993                                 event.modified_blocks.insert(it->first);
1994                         dispatchEvent(event);
1995                 }
1996         }
1997         return block;
1998 }
1999
2000 bool ServerMap::deleteBlock(v3s16 blockpos)
2001 {
2002         if (!dbase->deleteBlock(blockpos))
2003                 return false;
2004
2005         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2006         if (block) {
2007                 v2s16 p2d(blockpos.X, blockpos.Z);
2008                 MapSector *sector = getSectorNoGenerate(p2d);
2009                 if (!sector)
2010                         return false;
2011                 sector->deleteBlock(block);
2012         }
2013
2014         return true;
2015 }
2016
2017 void ServerMap::PrintInfo(std::ostream &out)
2018 {
2019         out<<"ServerMap: ";
2020 }
2021
2022 bool ServerMap::repairBlockLight(v3s16 blockpos,
2023         std::map<v3s16, MapBlock *> *modified_blocks)
2024 {
2025         MapBlock *block = emergeBlock(blockpos, false);
2026         if (!block || !block->isGenerated())
2027                 return false;
2028         voxalgo::repair_block_light(this, block, modified_blocks);
2029         return true;
2030 }
2031
2032 MMVManip::MMVManip(Map *map):
2033                 VoxelManipulator(),
2034                 m_map(map)
2035 {
2036 }
2037
2038 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
2039         bool load_if_inexistent)
2040 {
2041         TimeTaker timer1("initialEmerge", &emerge_time);
2042
2043         // Units of these are MapBlocks
2044         v3s16 p_min = blockpos_min;
2045         v3s16 p_max = blockpos_max;
2046
2047         VoxelArea block_area_nodes
2048                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2049
2050         u32 size_MB = block_area_nodes.getVolume()*4/1000000;
2051         if(size_MB >= 1)
2052         {
2053                 infostream<<"initialEmerge: area: ";
2054                 block_area_nodes.print(infostream);
2055                 infostream<<" ("<<size_MB<<"MB)";
2056                 infostream<<std::endl;
2057         }
2058
2059         addArea(block_area_nodes);
2060
2061         for(s32 z=p_min.Z; z<=p_max.Z; z++)
2062         for(s32 y=p_min.Y; y<=p_max.Y; y++)
2063         for(s32 x=p_min.X; x<=p_max.X; x++)
2064         {
2065                 u8 flags = 0;
2066                 MapBlock *block;
2067                 v3s16 p(x,y,z);
2068                 std::map<v3s16, u8>::iterator n;
2069                 n = m_loaded_blocks.find(p);
2070                 if(n != m_loaded_blocks.end())
2071                         continue;
2072
2073                 bool block_data_inexistent = false;
2074                 {
2075                         TimeTaker timer2("emerge load", &emerge_load_time);
2076
2077                         block = m_map->getBlockNoCreateNoEx(p);
2078                         if (!block || block->isDummy())
2079                                 block_data_inexistent = true;
2080                         else
2081                                 block->copyTo(*this);
2082                 }
2083
2084                 if(block_data_inexistent)
2085                 {
2086
2087                         if (load_if_inexistent && !blockpos_over_max_limit(p)) {
2088                                 ServerMap *svrmap = (ServerMap *)m_map;
2089                                 block = svrmap->emergeBlock(p, false);
2090                                 if (block == NULL)
2091                                         block = svrmap->createBlock(p);
2092                                 block->copyTo(*this);
2093                         } else {
2094                                 flags |= VMANIP_BLOCK_DATA_INEXIST;
2095
2096                                 /*
2097                                         Mark area inexistent
2098                                 */
2099                                 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2100                                 // Fill with VOXELFLAG_NO_DATA
2101                                 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2102                                 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2103                                 {
2104                                         s32 i = m_area.index(a.MinEdge.X,y,z);
2105                                         memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
2106                                 }
2107                         }
2108                 }
2109                 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2110                 {
2111                         // Mark that block was loaded as blank
2112                         flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
2113                 }*/
2114
2115                 m_loaded_blocks[p] = flags;
2116         }
2117
2118         m_is_dirty = false;
2119 }
2120
2121 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
2122         bool overwrite_generated)
2123 {
2124         if(m_area.getExtent() == v3s16(0,0,0))
2125                 return;
2126
2127         /*
2128                 Copy data of all blocks
2129         */
2130         for (auto &loaded_block : m_loaded_blocks) {
2131                 v3s16 p = loaded_block.first;
2132                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
2133                 bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
2134                 if (!existed || (block == NULL) ||
2135                         (!overwrite_generated && block->isGenerated()))
2136                         continue;
2137
2138                 block->copyFrom(*this);
2139                 block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
2140
2141                 if(modified_blocks)
2142                         (*modified_blocks)[p] = block;
2143         }
2144 }
2145
2146 //END