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