]> git.lizzy.rs Git - dragonfireclient.git/blob - src/map.cpp
Make node timers more efficient
[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 "porting.h"
26 #include "serialization.h"
27 #include "nodemetadata.h"
28 #include "settings.h"
29 #include "log.h"
30 #include "profiler.h"
31 #include "nodedef.h"
32 #include "gamedef.h"
33 #include "util/directiontables.h"
34 #include "util/mathconstants.h"
35 #include "rollback_interface.h"
36 #include "environment.h"
37 #include "emerge.h"
38 #include "mapgen_v6.h"
39 #include "mg_biome.h"
40 #include "config.h"
41 #include "server.h"
42 #include "database.h"
43 #include "database-dummy.h"
44 #include "database-sqlite3.h"
45 #include <deque>
46 #include <queue>
47 #if USE_LEVELDB
48 #include "database-leveldb.h"
49 #endif
50 #if USE_REDIS
51 #include "database-redis.h"
52 #endif
53 #if USE_POSTGRESQL
54 #include "database-postgresql.h"
55 #endif
56
57 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
58
59
60 /*
61         Map
62 */
63
64 Map::Map(std::ostream &dout, IGameDef *gamedef):
65         m_dout(dout),
66         m_gamedef(gamedef),
67         m_sector_cache(NULL),
68         m_transforming_liquid_loop_count_multiplier(1.0f),
69         m_unprocessed_count(0),
70         m_inc_trending_up_start_time(0),
71         m_queue_size_timer_started(false)
72 {
73 }
74
75 Map::~Map()
76 {
77         /*
78                 Free all MapSectors
79         */
80         for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
81                 i != m_sectors.end(); ++i)
82         {
83                 delete i->second;
84         }
85 }
86
87 void Map::addEventReceiver(MapEventReceiver *event_receiver)
88 {
89         m_event_receivers.insert(event_receiver);
90 }
91
92 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
93 {
94         m_event_receivers.erase(event_receiver);
95 }
96
97 void Map::dispatchEvent(MapEditEvent *event)
98 {
99         for(std::set<MapEventReceiver*>::iterator
100                         i = m_event_receivers.begin();
101                         i != m_event_receivers.end(); ++i)
102         {
103                 (*i)->onMapEditEvent(event);
104         }
105 }
106
107 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
108 {
109         if(m_sector_cache != NULL && p == m_sector_cache_p){
110                 MapSector * sector = m_sector_cache;
111                 return sector;
112         }
113
114         std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
115
116         if(n == m_sectors.end())
117                 return NULL;
118
119         MapSector *sector = n->second;
120
121         // Cache the last result
122         m_sector_cache_p = p;
123         m_sector_cache = sector;
124
125         return sector;
126 }
127
128 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
129 {
130         return getSectorNoGenerateNoExNoLock(p);
131 }
132
133 MapSector * Map::getSectorNoGenerate(v2s16 p)
134 {
135         MapSector *sector = getSectorNoGenerateNoEx(p);
136         if(sector == NULL)
137                 throw InvalidPositionException();
138
139         return sector;
140 }
141
142 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
143 {
144         v2s16 p2d(p3d.X, p3d.Z);
145         MapSector * sector = getSectorNoGenerateNoEx(p2d);
146         if(sector == NULL)
147                 return NULL;
148         MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
149         return block;
150 }
151
152 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
153 {
154         MapBlock *block = getBlockNoCreateNoEx(p3d);
155         if(block == NULL)
156                 throw InvalidPositionException();
157         return block;
158 }
159
160 bool Map::isNodeUnderground(v3s16 p)
161 {
162         v3s16 blockpos = getNodeBlockPos(p);
163         try{
164                 MapBlock * block = getBlockNoCreate(blockpos);
165                 return block->getIsUnderground();
166         }
167         catch(InvalidPositionException &e)
168         {
169                 return false;
170         }
171 }
172
173 bool Map::isValidPosition(v3s16 p)
174 {
175         v3s16 blockpos = getNodeBlockPos(p);
176         MapBlock *block = getBlockNoCreate(blockpos);
177         return (block != NULL);
178 }
179
180 // Returns a CONTENT_IGNORE node if not found
181 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
182 {
183         v3s16 blockpos = getNodeBlockPos(p);
184         MapBlock *block = getBlockNoCreateNoEx(blockpos);
185         if (block == NULL) {
186                 if (is_valid_position != NULL)
187                         *is_valid_position = false;
188                 return MapNode(CONTENT_IGNORE);
189         }
190
191         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
192         bool is_valid_p;
193         MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
194         if (is_valid_position != NULL)
195                 *is_valid_position = is_valid_p;
196         return node;
197 }
198
199 #if 0
200 // Deprecated
201 // throws InvalidPositionException if not found
202 // TODO: Now this is deprecated, getNodeNoEx should be renamed
203 MapNode Map::getNode(v3s16 p)
204 {
205         v3s16 blockpos = getNodeBlockPos(p);
206         MapBlock *block = getBlockNoCreateNoEx(blockpos);
207         if (block == NULL)
208                 throw InvalidPositionException();
209         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
210         bool is_valid_position;
211         MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
212         if (!is_valid_position)
213                 throw InvalidPositionException();
214         return node;
215 }
216 #endif
217
218 // throws InvalidPositionException if not found
219 void Map::setNode(v3s16 p, MapNode & n)
220 {
221         v3s16 blockpos = getNodeBlockPos(p);
222         MapBlock *block = getBlockNoCreate(blockpos);
223         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
224         // Never allow placing CONTENT_IGNORE, it fucks up stuff
225         if(n.getContent() == CONTENT_IGNORE){
226                 bool temp_bool;
227                 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
228                                 <<" while trying to replace \""
229                                 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
230                                 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
231                 debug_stacks_print_to(infostream);
232                 return;
233         }
234         block->setNodeNoCheck(relpos, n);
235 }
236
237
238 /*
239         Goes recursively through the neighbours of the node.
240
241         Alters only transparent nodes.
242
243         If the lighting of the neighbour is lower than the lighting of
244         the node was (before changing it to 0 at the step before), the
245         lighting of the neighbour is set to 0 and then the same stuff
246         repeats for the neighbour.
247
248         The ending nodes of the routine are stored in light_sources.
249         This is useful when a light is removed. In such case, this
250         routine can be called for the light node and then again for
251         light_sources to re-light the area without the removed light.
252
253         values of from_nodes are lighting values.
254 */
255 void Map::unspreadLight(enum LightBank bank,
256                 std::map<v3s16, u8> & from_nodes,
257                 std::set<v3s16> & light_sources,
258                 std::map<v3s16, MapBlock*>  & modified_blocks)
259 {
260         INodeDefManager *nodemgr = m_gamedef->ndef();
261
262         v3s16 dirs[6] = {
263                 v3s16(0,0,1), // back
264                 v3s16(0,1,0), // top
265                 v3s16(1,0,0), // right
266                 v3s16(0,0,-1), // front
267                 v3s16(0,-1,0), // bottom
268                 v3s16(-1,0,0), // left
269         };
270
271         if(from_nodes.empty())
272                 return;
273
274         u32 blockchangecount = 0;
275
276         std::map<v3s16, u8> unlighted_nodes;
277
278         /*
279                 Initialize block cache
280         */
281         v3s16 blockpos_last;
282         MapBlock *block = NULL;
283         // Cache this a bit, too
284         bool block_checked_in_modified = false;
285
286         for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
287                 j != from_nodes.end(); ++j)
288         {
289                 v3s16 pos = j->first;
290                 v3s16 blockpos = getNodeBlockPos(pos);
291
292                 // Only fetch a new block if the block position has changed
293                 try{
294                         if(block == NULL || blockpos != blockpos_last){
295                                 block = getBlockNoCreate(blockpos);
296                                 blockpos_last = blockpos;
297
298                                 block_checked_in_modified = false;
299                                 blockchangecount++;
300                         }
301                 }
302                 catch(InvalidPositionException &e)
303                 {
304                         continue;
305                 }
306
307                 if(block->isDummy())
308                         continue;
309
310                 // Calculate relative position in block
311                 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
312
313                 // Get node straight from the block
314                 //MapNode n = block->getNode(relpos);
315
316                 u8 oldlight = j->second;
317
318                 // Loop through 6 neighbors
319                 for(u16 i=0; i<6; i++)
320                 {
321                         // Get the position of the neighbor node
322                         v3s16 n2pos = pos + dirs[i];
323
324                         // Get the block where the node is located
325                         v3s16 blockpos, relpos;
326                         getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
327
328                         // Only fetch a new block if the block position has changed
329                         try {
330                                 if(block == NULL || blockpos != blockpos_last){
331                                         block = getBlockNoCreate(blockpos);
332                                         blockpos_last = blockpos;
333
334                                         block_checked_in_modified = false;
335                                         blockchangecount++;
336                                 }
337                         }
338                         catch(InvalidPositionException &e) {
339                                 continue;
340                         }
341
342                         // Get node straight from the block
343                         bool is_valid_position;
344                         MapNode n2 = block->getNode(relpos, &is_valid_position);
345                         if (!is_valid_position)
346                                 continue;
347
348                         bool changed = false;
349
350                         //TODO: Optimize output by optimizing light_sources?
351
352                         /*
353                                 If the neighbor is dimmer than what was specified
354                                 as oldlight (the light of the previous node)
355                         */
356                         if(n2.getLight(bank, nodemgr) < oldlight)
357                         {
358                                 /*
359                                         And the neighbor is transparent and it has some light
360                                 */
361                                 if(nodemgr->get(n2).light_propagates
362                                                 && n2.getLight(bank, nodemgr) != 0)
363                                 {
364                                         /*
365                                                 Set light to 0 and add to queue
366                                         */
367
368                                         u8 current_light = n2.getLight(bank, nodemgr);
369                                         n2.setLight(bank, 0, nodemgr);
370                                         block->setNode(relpos, n2);
371
372                                         unlighted_nodes[n2pos] = current_light;
373                                         changed = true;
374
375                                         /*
376                                                 Remove from light_sources if it is there
377                                                 NOTE: This doesn't happen nearly at all
378                                         */
379                                         /*if(light_sources.find(n2pos))
380                                         {
381                                                 infostream<<"Removed from light_sources"<<std::endl;
382                                                 light_sources.remove(n2pos);
383                                         }*/
384                                 }
385
386                                 /*// DEBUG
387                                 if(light_sources.find(n2pos) != NULL)
388                                         light_sources.remove(n2pos);*/
389                         }
390                         else{
391                                 light_sources.insert(n2pos);
392                         }
393
394                         // Add to modified_blocks
395                         if(changed == true && block_checked_in_modified == false)
396                         {
397                                 // If the block is not found in modified_blocks, add.
398                                 if(modified_blocks.find(blockpos) == modified_blocks.end())
399                                 {
400                                         modified_blocks[blockpos] = block;
401                                 }
402                                 block_checked_in_modified = true;
403                         }
404                 }
405         }
406
407         /*infostream<<"unspreadLight(): Changed block "
408         <<blockchangecount<<" times"
409         <<" for "<<from_nodes.size()<<" nodes"
410         <<std::endl;*/
411
412         if(!unlighted_nodes.empty())
413                 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
414 }
415
416 /*
417         A single-node wrapper of the above
418 */
419 void Map::unLightNeighbors(enum LightBank bank,
420                 v3s16 pos, u8 lightwas,
421                 std::set<v3s16> & light_sources,
422                 std::map<v3s16, MapBlock*>  & modified_blocks)
423 {
424         std::map<v3s16, u8> from_nodes;
425         from_nodes[pos] = lightwas;
426
427         unspreadLight(bank, from_nodes, light_sources, modified_blocks);
428 }
429
430 /*
431         Lights neighbors of from_nodes, collects all them and then
432         goes on recursively.
433 */
434 void Map::spreadLight(enum LightBank bank,
435                 std::set<v3s16> & from_nodes,
436                 std::map<v3s16, MapBlock*> & modified_blocks)
437 {
438         INodeDefManager *nodemgr = m_gamedef->ndef();
439
440         const v3s16 dirs[6] = {
441                 v3s16(0,0,1), // back
442                 v3s16(0,1,0), // top
443                 v3s16(1,0,0), // right
444                 v3s16(0,0,-1), // front
445                 v3s16(0,-1,0), // bottom
446                 v3s16(-1,0,0), // left
447         };
448
449         if(from_nodes.empty())
450                 return;
451
452         u32 blockchangecount = 0;
453
454         std::set<v3s16> lighted_nodes;
455
456         /*
457                 Initialize block cache
458         */
459         v3s16 blockpos_last;
460         MapBlock *block = NULL;
461                 // Cache this a bit, too
462         bool block_checked_in_modified = false;
463
464         for(std::set<v3s16>::iterator j = from_nodes.begin();
465                 j != from_nodes.end(); ++j)
466         {
467                 v3s16 pos = *j;
468                 v3s16 blockpos, relpos;
469
470                 getNodeBlockPosWithOffset(pos, blockpos, relpos);
471
472                 // Only fetch a new block if the block position has changed
473                 try {
474                         if(block == NULL || blockpos != blockpos_last){
475                                 block = getBlockNoCreate(blockpos);
476                                 blockpos_last = blockpos;
477
478                                 block_checked_in_modified = false;
479                                 blockchangecount++;
480                         }
481                 }
482                 catch(InvalidPositionException &e) {
483                         continue;
484                 }
485
486                 if(block->isDummy())
487                         continue;
488
489                 // Get node straight from the block
490                 bool is_valid_position;
491                 MapNode n = block->getNode(relpos, &is_valid_position);
492
493                 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
494                 u8 newlight = diminish_light(oldlight);
495
496                 // Loop through 6 neighbors
497                 for(u16 i=0; i<6; i++){
498                         // Get the position of the neighbor node
499                         v3s16 n2pos = pos + dirs[i];
500
501                         // Get the block where the node is located
502                         v3s16 blockpos, relpos;
503                         getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
504
505                         // Only fetch a new block if the block position has changed
506                         try {
507                                 if(block == NULL || blockpos != blockpos_last){
508                                         block = getBlockNoCreate(blockpos);
509                                         blockpos_last = blockpos;
510
511                                         block_checked_in_modified = false;
512                                         blockchangecount++;
513                                 }
514                         }
515                         catch(InvalidPositionException &e) {
516                                 continue;
517                         }
518
519                         // Get node straight from the block
520                         MapNode n2 = block->getNode(relpos, &is_valid_position);
521                         if (!is_valid_position)
522                                 continue;
523
524                         bool changed = false;
525                         /*
526                                 If the neighbor is brighter than the current node,
527                                 add to list (it will light up this node on its turn)
528                         */
529                         if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
530                         {
531                                 lighted_nodes.insert(n2pos);
532                                 changed = true;
533                         }
534                         /*
535                                 If the neighbor is dimmer than how much light this node
536                                 would spread on it, add to list
537                         */
538                         if(n2.getLight(bank, nodemgr) < newlight)
539                         {
540                                 if(nodemgr->get(n2).light_propagates)
541                                 {
542                                         n2.setLight(bank, newlight, nodemgr);
543                                         block->setNode(relpos, n2);
544                                         lighted_nodes.insert(n2pos);
545                                         changed = true;
546                                 }
547                         }
548
549                         // Add to modified_blocks
550                         if(changed == true && block_checked_in_modified == false)
551                         {
552                                 // If the block is not found in modified_blocks, add.
553                                 if(modified_blocks.find(blockpos) == modified_blocks.end())
554                                 {
555                                         modified_blocks[blockpos] = block;
556                                 }
557                                 block_checked_in_modified = true;
558                         }
559                 }
560         }
561
562         /*infostream<<"spreadLight(): Changed block "
563                         <<blockchangecount<<" times"
564                         <<" for "<<from_nodes.size()<<" nodes"
565                         <<std::endl;*/
566
567         if(!lighted_nodes.empty())
568                 spreadLight(bank, lighted_nodes, modified_blocks);
569 }
570
571 /*
572         A single-node source variation of the above.
573 */
574 void Map::lightNeighbors(enum LightBank bank,
575                 v3s16 pos,
576                 std::map<v3s16, MapBlock*> & modified_blocks)
577 {
578         std::set<v3s16> from_nodes;
579         from_nodes.insert(pos);
580         spreadLight(bank, from_nodes, modified_blocks);
581 }
582
583 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
584 {
585         INodeDefManager *nodemgr = m_gamedef->ndef();
586
587         v3s16 dirs[6] = {
588                 v3s16(0,0,1), // back
589                 v3s16(0,1,0), // top
590                 v3s16(1,0,0), // right
591                 v3s16(0,0,-1), // front
592                 v3s16(0,-1,0), // bottom
593                 v3s16(-1,0,0), // left
594         };
595
596         u8 brightest_light = 0;
597         v3s16 brightest_pos(0,0,0);
598         bool found_something = false;
599
600         // Loop through 6 neighbors
601         for(u16 i=0; i<6; i++){
602                 // Get the position of the neighbor node
603                 v3s16 n2pos = p + dirs[i];
604                 MapNode n2;
605                 bool is_valid_position;
606                 n2 = getNodeNoEx(n2pos, &is_valid_position);
607                 if (!is_valid_position)
608                         continue;
609
610                 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
611                         brightest_light = n2.getLight(bank, nodemgr);
612                         brightest_pos = n2pos;
613                         found_something = true;
614                 }
615         }
616
617         if(found_something == false)
618                 throw InvalidPositionException();
619
620         return brightest_pos;
621 }
622
623 /*
624         Propagates sunlight down from a node.
625         Starting point gets sunlight.
626
627         Returns the lowest y value of where the sunlight went.
628
629         Mud is turned into grass in where the sunlight stops.
630 */
631 s16 Map::propagateSunlight(v3s16 start,
632                 std::map<v3s16, MapBlock*> & modified_blocks)
633 {
634         INodeDefManager *nodemgr = m_gamedef->ndef();
635
636         s16 y = start.Y;
637         for(; ; y--)
638         {
639                 v3s16 pos(start.X, y, start.Z);
640
641                 v3s16 blockpos = getNodeBlockPos(pos);
642                 MapBlock *block;
643                 try{
644                         block = getBlockNoCreate(blockpos);
645                 }
646                 catch(InvalidPositionException &e)
647                 {
648                         break;
649                 }
650
651                 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
652                 bool is_valid_position;
653                 MapNode n = block->getNode(relpos, &is_valid_position);
654                 if (!is_valid_position)
655                         break;
656
657                 if(nodemgr->get(n).sunlight_propagates)
658                 {
659                         n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
660                         block->setNode(relpos, n);
661
662                         modified_blocks[blockpos] = block;
663                 }
664                 else
665                 {
666                         // Sunlight goes no further
667                         break;
668                 }
669         }
670         return y + 1;
671 }
672
673 void Map::updateLighting(enum LightBank bank,
674                 std::map<v3s16, MapBlock*> & a_blocks,
675                 std::map<v3s16, MapBlock*> & modified_blocks)
676 {
677         INodeDefManager *nodemgr = m_gamedef->ndef();
678
679         /*m_dout<<"Map::updateLighting(): "
680                         <<a_blocks.size()<<" blocks."<<std::endl;*/
681
682         //TimeTaker timer("updateLighting");
683
684         // For debugging
685         //bool debug=true;
686         //u32 count_was = modified_blocks.size();
687
688         //std::map<v3s16, MapBlock*> blocks_to_update;
689
690         std::set<v3s16> light_sources;
691
692         std::map<v3s16, u8> unlight_from;
693
694         int num_bottom_invalid = 0;
695
696         {
697         //TimeTaker t("first stuff");
698
699         for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
700                 i != a_blocks.end(); ++i)
701         {
702                 MapBlock *block = i->second;
703
704                 for(;;)
705                 {
706                         // Don't bother with dummy blocks.
707                         if(block->isDummy())
708                                 break;
709
710                         v3s16 pos = block->getPos();
711                         v3s16 posnodes = block->getPosRelative();
712                         modified_blocks[pos] = block;
713                         //blocks_to_update[pos] = block;
714
715                         /*
716                                 Clear all light from block
717                         */
718                         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
719                         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
720                         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
721                         {
722                                 v3s16 p(x,y,z);
723                                 bool is_valid_position;
724                                 MapNode n = block->getNode(p, &is_valid_position);
725                                 if (!is_valid_position) {
726                                         /* This would happen when dealing with a
727                                            dummy block.
728                                         */
729                                         infostream<<"updateLighting(): InvalidPositionException"
730                                                         <<std::endl;
731                                         continue;
732                                 }
733                                 u8 oldlight = n.getLight(bank, nodemgr);
734                                 n.setLight(bank, 0, nodemgr);
735                                 block->setNode(p, n);
736
737                                 // If node sources light, add to list
738                                 u8 source = nodemgr->get(n).light_source;
739                                 if(source != 0)
740                                         light_sources.insert(p + posnodes);
741
742                                 // Collect borders for unlighting
743                                 if((x==0 || x == MAP_BLOCKSIZE-1
744                                                 || y==0 || y == MAP_BLOCKSIZE-1
745                                                 || z==0 || z == MAP_BLOCKSIZE-1)
746                                                 && oldlight != 0)
747                                 {
748                                         v3s16 p_map = p + posnodes;
749                                         unlight_from[p_map] = oldlight;
750                                 }
751
752
753                         }
754
755                         if(bank == LIGHTBANK_DAY)
756                         {
757                                 bool bottom_valid = block->propagateSunlight(light_sources);
758
759                                 if(!bottom_valid)
760                                         num_bottom_invalid++;
761
762                                 // If bottom is valid, we're done.
763                                 if(bottom_valid)
764                                         break;
765                         }
766                         else if(bank == LIGHTBANK_NIGHT)
767                         {
768                                 // For night lighting, sunlight is not propagated
769                                 break;
770                         }
771                         else
772                         {
773                                 assert("Invalid lighting bank" == NULL);
774                         }
775
776                         /*infostream<<"Bottom for sunlight-propagated block ("
777                                         <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
778                                         <<std::endl;*/
779
780                         // Bottom sunlight is not valid; get the block and loop to it
781
782                         pos.Y--;
783                         try{
784                                 block = getBlockNoCreate(pos);
785                         }
786                         catch(InvalidPositionException &e)
787                         {
788                                 FATAL_ERROR("Invalid position");
789                         }
790
791                 }
792         }
793
794         }
795
796         /*
797                 Enable this to disable proper lighting for speeding up map
798                 generation for testing or whatever
799         */
800 #if 0
801         //if(g_settings->get(""))
802         {
803                 core::map<v3s16, MapBlock*>::Iterator i;
804                 i = blocks_to_update.getIterator();
805                 for(; i.atEnd() == false; i++)
806                 {
807                         MapBlock *block = i.getNode()->getValue();
808                         v3s16 p = block->getPos();
809                         block->setLightingExpired(false);
810                 }
811                 return;
812         }
813 #endif
814
815 #if 1
816         {
817                 //TimeTaker timer("unspreadLight");
818                 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
819         }
820
821         /*if(debug)
822         {
823                 u32 diff = modified_blocks.size() - count_was;
824                 count_was = modified_blocks.size();
825                 infostream<<"unspreadLight modified "<<diff<<std::endl;
826         }*/
827
828         {
829                 //TimeTaker timer("spreadLight");
830                 spreadLight(bank, light_sources, modified_blocks);
831         }
832
833         /*if(debug)
834         {
835                 u32 diff = modified_blocks.size() - count_was;
836                 count_was = modified_blocks.size();
837                 infostream<<"spreadLight modified "<<diff<<std::endl;
838         }*/
839 #endif
840
841 #if 0
842         {
843                 //MapVoxelManipulator vmanip(this);
844
845                 // Make a manual voxel manipulator and load all the blocks
846                 // that touch the requested blocks
847                 ManualMapVoxelManipulator vmanip(this);
848
849                 {
850                 //TimeTaker timer("initialEmerge");
851
852                 core::map<v3s16, MapBlock*>::Iterator i;
853                 i = blocks_to_update.getIterator();
854                 for(; i.atEnd() == false; i++)
855                 {
856                         MapBlock *block = i.getNode()->getValue();
857                         v3s16 p = block->getPos();
858
859                         // Add all surrounding blocks
860                         vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
861
862                         /*
863                                 Add all surrounding blocks that have up-to-date lighting
864                                 NOTE: This doesn't quite do the job (not everything
865                                           appropriate is lighted)
866                         */
867                         /*for(s16 z=-1; z<=1; z++)
868                         for(s16 y=-1; y<=1; y++)
869                         for(s16 x=-1; x<=1; x++)
870                         {
871                                 v3s16 p2 = p + v3s16(x,y,z);
872                                 MapBlock *block = getBlockNoCreateNoEx(p2);
873                                 if(block == NULL)
874                                         continue;
875                                 if(block->isDummy())
876                                         continue;
877                                 if(block->getLightingExpired())
878                                         continue;
879                                 vmanip.initialEmerge(p2, p2);
880                         }*/
881
882                         // Lighting of block will be updated completely
883                         block->setLightingExpired(false);
884                 }
885                 }
886
887                 {
888                         //TimeTaker timer("unSpreadLight");
889                         vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
890                 }
891                 {
892                         //TimeTaker timer("spreadLight");
893                         vmanip.spreadLight(bank, light_sources, nodemgr);
894                 }
895                 {
896                         //TimeTaker timer("blitBack");
897                         vmanip.blitBack(modified_blocks);
898                 }
899                 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
900                 emerge_time = 0;*/
901         }
902 #endif
903
904         //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
905 }
906
907 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
908                 std::map<v3s16, MapBlock*> & modified_blocks)
909 {
910         updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
911         updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
912
913         /*
914                 Update information about whether day and night light differ
915         */
916         for(std::map<v3s16, MapBlock*>::iterator
917                         i = modified_blocks.begin();
918                         i != modified_blocks.end(); ++i)
919         {
920                 MapBlock *block = i->second;
921                 block->expireDayNightDiff();
922         }
923 }
924
925 /*
926 */
927 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
928                 std::map<v3s16, MapBlock*> &modified_blocks,
929                 bool remove_metadata)
930 {
931         INodeDefManager *ndef = m_gamedef->ndef();
932
933         /*PrintInfo(m_dout);
934         m_dout<<"Map::addNodeAndUpdate(): p=("
935                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
936
937         /*
938                 From this node to nodes underneath:
939                 If lighting is sunlight (1.0), unlight neighbours and
940                 set lighting to 0.
941                 Else discontinue.
942         */
943
944         v3s16 toppos = p + v3s16(0,1,0);
945         //v3s16 bottompos = p + v3s16(0,-1,0);
946
947         bool node_under_sunlight = true;
948         std::set<v3s16> light_sources;
949
950         /*
951                 Collect old node for rollback
952         */
953         RollbackNode rollback_oldnode(this, p, m_gamedef);
954
955         /*
956                 If there is a node at top and it doesn't have sunlight,
957                 there has not been any sunlight going down.
958
959                 Otherwise there probably is.
960         */
961
962         bool is_valid_position;
963         MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
964
965         if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
966                 node_under_sunlight = false;
967
968         /*
969                 Remove all light that has come out of this node
970         */
971
972         enum LightBank banks[] =
973         {
974                 LIGHTBANK_DAY,
975                 LIGHTBANK_NIGHT
976         };
977         for(s32 i=0; i<2; i++)
978         {
979                 enum LightBank bank = banks[i];
980
981                 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
982
983                 // Add the block of the added node to modified_blocks
984                 v3s16 blockpos = getNodeBlockPos(p);
985                 MapBlock * block = getBlockNoCreate(blockpos);
986                 assert(block != NULL);
987                 modified_blocks[blockpos] = block;
988
989                 assert(isValidPosition(p));
990
991                 // Unlight neighbours of node.
992                 // This means setting light of all consequent dimmer nodes
993                 // to 0.
994                 // This also collects the nodes at the border which will spread
995                 // light again into this.
996                 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
997
998                 n.setLight(bank, 0, ndef);
999         }
1000
1001         /*
1002                 If node lets sunlight through and is under sunlight, it has
1003                 sunlight too.
1004         */
1005         if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1006         {
1007                 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1008         }
1009
1010         /*
1011                 Remove node metadata
1012         */
1013         if (remove_metadata) {
1014                 removeNodeMetadata(p);
1015         }
1016
1017         /*
1018                 Set the node on the map
1019         */
1020
1021         setNode(p, n);
1022
1023         /*
1024                 If node is under sunlight and doesn't let sunlight through,
1025                 take all sunlighted nodes under it and clear light from them
1026                 and from where the light has been spread.
1027                 TODO: This could be optimized by mass-unlighting instead
1028                           of looping
1029         */
1030         if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1031         {
1032                 s16 y = p.Y - 1;
1033                 for(;; y--){
1034                         //m_dout<<"y="<<y<<std::endl;
1035                         v3s16 n2pos(p.X, y, p.Z);
1036
1037                         MapNode n2;
1038
1039                         n2 = getNodeNoEx(n2pos, &is_valid_position);
1040                         if (!is_valid_position)
1041                                 break;
1042
1043                         if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1044                         {
1045                                 unLightNeighbors(LIGHTBANK_DAY,
1046                                                 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1047                                                 light_sources, modified_blocks);
1048                                 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1049                                 setNode(n2pos, n2);
1050                         }
1051                         else
1052                                 break;
1053                 }
1054         }
1055
1056         for(s32 i=0; i<2; i++)
1057         {
1058                 enum LightBank bank = banks[i];
1059
1060                 /*
1061                         Spread light from all nodes that might be capable of doing so
1062                 */
1063                 spreadLight(bank, light_sources, modified_blocks);
1064         }
1065
1066         /*
1067                 Update information about whether day and night light differ
1068         */
1069         for(std::map<v3s16, MapBlock*>::iterator
1070                         i = modified_blocks.begin();
1071                         i != modified_blocks.end(); ++i)
1072         {
1073                 i->second->expireDayNightDiff();
1074         }
1075
1076         /*
1077                 Report for rollback
1078         */
1079         if(m_gamedef->rollback())
1080         {
1081                 RollbackNode rollback_newnode(this, p, m_gamedef);
1082                 RollbackAction action;
1083                 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1084                 m_gamedef->rollback()->reportAction(action);
1085         }
1086
1087         /*
1088                 Add neighboring liquid nodes and the node itself if it is
1089                 liquid (=water node was added) to transform queue.
1090         */
1091         v3s16 dirs[7] = {
1092                 v3s16(0,0,0), // self
1093                 v3s16(0,0,1), // back
1094                 v3s16(0,1,0), // top
1095                 v3s16(1,0,0), // right
1096                 v3s16(0,0,-1), // front
1097                 v3s16(0,-1,0), // bottom
1098                 v3s16(-1,0,0), // left
1099         };
1100         for(u16 i=0; i<7; i++)
1101         {
1102                 v3s16 p2 = p + dirs[i];
1103
1104                 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1105                 if(is_valid_position
1106                                 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1107                 {
1108                         m_transforming_liquid.push_back(p2);
1109                 }
1110         }
1111 }
1112
1113 /*
1114 */
1115 void Map::removeNodeAndUpdate(v3s16 p,
1116                 std::map<v3s16, MapBlock*> &modified_blocks)
1117 {
1118         INodeDefManager *ndef = m_gamedef->ndef();
1119
1120         /*PrintInfo(m_dout);
1121         m_dout<<"Map::removeNodeAndUpdate(): p=("
1122                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1123
1124         bool node_under_sunlight = true;
1125
1126         v3s16 toppos = p + v3s16(0,1,0);
1127
1128         // Node will be replaced with this
1129         content_t replace_material = CONTENT_AIR;
1130
1131         /*
1132                 Collect old node for rollback
1133         */
1134         RollbackNode rollback_oldnode(this, p, m_gamedef);
1135
1136         /*
1137                 If there is a node at top and it doesn't have sunlight,
1138                 there will be no sunlight going down.
1139         */
1140         bool is_valid_position;
1141         MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1142
1143         if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1144                 node_under_sunlight = false;
1145
1146         std::set<v3s16> light_sources;
1147
1148         enum LightBank banks[] =
1149         {
1150                 LIGHTBANK_DAY,
1151                 LIGHTBANK_NIGHT
1152         };
1153         for(s32 i=0; i<2; i++)
1154         {
1155                 enum LightBank bank = banks[i];
1156
1157                 /*
1158                         Unlight neighbors (in case the node is a light source)
1159                 */
1160                 unLightNeighbors(bank, p,
1161                                 getNodeNoEx(p).getLight(bank, ndef),
1162                                 light_sources, modified_blocks);
1163         }
1164
1165         /*
1166                 Remove node metadata
1167         */
1168
1169         removeNodeMetadata(p);
1170
1171         /*
1172                 Remove the node.
1173                 This also clears the lighting.
1174         */
1175
1176         MapNode n(replace_material);
1177         setNode(p, n);
1178
1179         for(s32 i=0; i<2; i++)
1180         {
1181                 enum LightBank bank = banks[i];
1182
1183                 /*
1184                         Recalculate lighting
1185                 */
1186                 spreadLight(bank, light_sources, modified_blocks);
1187         }
1188
1189         // Add the block of the removed node to modified_blocks
1190         v3s16 blockpos = getNodeBlockPos(p);
1191         MapBlock * block = getBlockNoCreate(blockpos);
1192         assert(block != NULL);
1193         modified_blocks[blockpos] = block;
1194
1195         /*
1196                 If the removed node was under sunlight, propagate the
1197                 sunlight down from it and then light all neighbors
1198                 of the propagated blocks.
1199         */
1200         if(node_under_sunlight)
1201         {
1202                 s16 ybottom = propagateSunlight(p, modified_blocks);
1203                 /*m_dout<<"Node was under sunlight. "
1204                                 "Propagating sunlight";
1205                 m_dout<<" -> ybottom="<<ybottom<<std::endl;*/
1206                 s16 y = p.Y;
1207                 for(; y >= ybottom; y--)
1208                 {
1209                         v3s16 p2(p.X, y, p.Z);
1210                         /*m_dout<<"lighting neighbors of node ("
1211                                         <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1212                                         <<std::endl;*/
1213                         lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1214                 }
1215         }
1216         else
1217         {
1218                 // Set the lighting of this node to 0
1219                 // TODO: Is this needed? Lighting is cleared up there already.
1220                 MapNode n = getNodeNoEx(p, &is_valid_position);
1221                 if (is_valid_position) {
1222                         n.setLight(LIGHTBANK_DAY, 0, ndef);
1223                         setNode(p, n);
1224                 } else {
1225                         FATAL_ERROR("Invalid position");
1226                 }
1227         }
1228
1229         for(s32 i=0; i<2; i++)
1230         {
1231                 enum LightBank bank = banks[i];
1232
1233                 // Get the brightest neighbour node and propagate light from it
1234                 v3s16 n2p = getBrightestNeighbour(bank, p);
1235                 try{
1236                         //MapNode n2 = getNode(n2p);
1237                         lightNeighbors(bank, n2p, modified_blocks);
1238                 }
1239                 catch(InvalidPositionException &e)
1240                 {
1241                 }
1242         }
1243
1244         /*
1245                 Update information about whether day and night light differ
1246         */
1247         for(std::map<v3s16, MapBlock*>::iterator
1248                         i = modified_blocks.begin();
1249                         i != modified_blocks.end(); ++i)
1250         {
1251                 i->second->expireDayNightDiff();
1252         }
1253
1254         /*
1255                 Report for rollback
1256         */
1257         if(m_gamedef->rollback())
1258         {
1259                 RollbackNode rollback_newnode(this, p, m_gamedef);
1260                 RollbackAction action;
1261                 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1262                 m_gamedef->rollback()->reportAction(action);
1263         }
1264
1265         /*
1266                 Add neighboring liquid nodes and this node to transform queue.
1267                 (it's vital for the node itself to get updated last.)
1268         */
1269         v3s16 dirs[7] = {
1270                 v3s16(0,0,1), // back
1271                 v3s16(0,1,0), // top
1272                 v3s16(1,0,0), // right
1273                 v3s16(0,0,-1), // front
1274                 v3s16(0,-1,0), // bottom
1275                 v3s16(-1,0,0), // left
1276                 v3s16(0,0,0), // self
1277         };
1278         for(u16 i=0; i<7; i++)
1279         {
1280                 v3s16 p2 = p + dirs[i];
1281
1282                 bool is_position_valid;
1283                 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1284                 if (is_position_valid
1285                                 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1286                 {
1287                         m_transforming_liquid.push_back(p2);
1288                 }
1289         }
1290 }
1291
1292 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1293 {
1294         MapEditEvent event;
1295         event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1296         event.p = p;
1297         event.n = n;
1298
1299         bool succeeded = true;
1300         try{
1301                 std::map<v3s16, MapBlock*> modified_blocks;
1302                 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1303
1304                 // Copy modified_blocks to event
1305                 for(std::map<v3s16, MapBlock*>::iterator
1306                                 i = modified_blocks.begin();
1307                                 i != modified_blocks.end(); ++i)
1308                 {
1309                         event.modified_blocks.insert(i->first);
1310                 }
1311         }
1312         catch(InvalidPositionException &e){
1313                 succeeded = false;
1314         }
1315
1316         dispatchEvent(&event);
1317
1318         return succeeded;
1319 }
1320
1321 bool Map::removeNodeWithEvent(v3s16 p)
1322 {
1323         MapEditEvent event;
1324         event.type = MEET_REMOVENODE;
1325         event.p = p;
1326
1327         bool succeeded = true;
1328         try{
1329                 std::map<v3s16, MapBlock*> modified_blocks;
1330                 removeNodeAndUpdate(p, modified_blocks);
1331
1332                 // Copy modified_blocks to event
1333                 for(std::map<v3s16, MapBlock*>::iterator
1334                                 i = modified_blocks.begin();
1335                                 i != modified_blocks.end(); ++i)
1336                 {
1337                         event.modified_blocks.insert(i->first);
1338                 }
1339         }
1340         catch(InvalidPositionException &e){
1341                 succeeded = false;
1342         }
1343
1344         dispatchEvent(&event);
1345
1346         return succeeded;
1347 }
1348
1349 bool Map::getDayNightDiff(v3s16 blockpos)
1350 {
1351         try{
1352                 v3s16 p = blockpos + v3s16(0,0,0);
1353                 MapBlock *b = getBlockNoCreate(p);
1354                 if(b->getDayNightDiff())
1355                         return true;
1356         }
1357         catch(InvalidPositionException &e){}
1358         // Leading edges
1359         try{
1360                 v3s16 p = blockpos + v3s16(-1,0,0);
1361                 MapBlock *b = getBlockNoCreate(p);
1362                 if(b->getDayNightDiff())
1363                         return true;
1364         }
1365         catch(InvalidPositionException &e){}
1366         try{
1367                 v3s16 p = blockpos + v3s16(0,-1,0);
1368                 MapBlock *b = getBlockNoCreate(p);
1369                 if(b->getDayNightDiff())
1370                         return true;
1371         }
1372         catch(InvalidPositionException &e){}
1373         try{
1374                 v3s16 p = blockpos + v3s16(0,0,-1);
1375                 MapBlock *b = getBlockNoCreate(p);
1376                 if(b->getDayNightDiff())
1377                         return true;
1378         }
1379         catch(InvalidPositionException &e){}
1380         // Trailing edges
1381         try{
1382                 v3s16 p = blockpos + v3s16(1,0,0);
1383                 MapBlock *b = getBlockNoCreate(p);
1384                 if(b->getDayNightDiff())
1385                         return true;
1386         }
1387         catch(InvalidPositionException &e){}
1388         try{
1389                 v3s16 p = blockpos + v3s16(0,1,0);
1390                 MapBlock *b = getBlockNoCreate(p);
1391                 if(b->getDayNightDiff())
1392                         return true;
1393         }
1394         catch(InvalidPositionException &e){}
1395         try{
1396                 v3s16 p = blockpos + v3s16(0,0,1);
1397                 MapBlock *b = getBlockNoCreate(p);
1398                 if(b->getDayNightDiff())
1399                         return true;
1400         }
1401         catch(InvalidPositionException &e){}
1402
1403         return false;
1404 }
1405
1406 struct TimeOrderedMapBlock {
1407         MapSector *sect;
1408         MapBlock *block;
1409
1410         TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
1411                 sect(sect),
1412                 block(block)
1413         {}
1414
1415         bool operator<(const TimeOrderedMapBlock &b) const
1416         {
1417                 return block->getUsageTimer() < b.block->getUsageTimer();
1418         };
1419 };
1420
1421 /*
1422         Updates usage timers
1423 */
1424 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
1425                 std::vector<v3s16> *unloaded_blocks)
1426 {
1427         bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1428
1429         // Profile modified reasons
1430         Profiler modprofiler;
1431
1432         std::vector<v2s16> sector_deletion_queue;
1433         u32 deleted_blocks_count = 0;
1434         u32 saved_blocks_count = 0;
1435         u32 block_count_all = 0;
1436
1437         beginSave();
1438
1439         // If there is no practical limit, we spare creation of mapblock_queue
1440         if (max_loaded_blocks == U32_MAX) {
1441                 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1442                                 si != m_sectors.end(); ++si) {
1443                         MapSector *sector = si->second;
1444
1445                         bool all_blocks_deleted = true;
1446
1447                         MapBlockVect blocks;
1448                         sector->getBlocks(blocks);
1449
1450                         for (MapBlockVect::iterator i = blocks.begin();
1451                                         i != blocks.end(); ++i) {
1452                                 MapBlock *block = (*i);
1453
1454                                 block->incrementUsageTimer(dtime);
1455
1456                                 if (block->refGet() == 0
1457                                                 && block->getUsageTimer() > unload_timeout) {
1458                                         v3s16 p = block->getPos();
1459
1460                                         // Save if modified
1461                                         if (block->getModified() != MOD_STATE_CLEAN
1462                                                         && save_before_unloading) {
1463                                                 modprofiler.add(block->getModifiedReasonString(), 1);
1464                                                 if (!saveBlock(block))
1465                                                         continue;
1466                                                 saved_blocks_count++;
1467                                         }
1468
1469                                         // Delete from memory
1470                                         sector->deleteBlock(block);
1471
1472                                         if (unloaded_blocks)
1473                                                 unloaded_blocks->push_back(p);
1474
1475                                         deleted_blocks_count++;
1476                                 } else {
1477                                         all_blocks_deleted = false;
1478                                         block_count_all++;
1479                                 }
1480                         }
1481
1482                         if (all_blocks_deleted) {
1483                                 sector_deletion_queue.push_back(si->first);
1484                         }
1485                 }
1486         } else {
1487                 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
1488                 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1489                                 si != m_sectors.end(); ++si) {
1490                         MapSector *sector = si->second;
1491
1492                         MapBlockVect blocks;
1493                         sector->getBlocks(blocks);
1494
1495                         for(MapBlockVect::iterator i = blocks.begin();
1496                                         i != blocks.end(); ++i) {
1497                                 MapBlock *block = (*i);
1498
1499                                 block->incrementUsageTimer(dtime);
1500                                 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
1501                         }
1502                 }
1503                 block_count_all = mapblock_queue.size();
1504                 // Delete old blocks, and blocks over the limit from the memory
1505                 while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
1506                                 || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
1507                         TimeOrderedMapBlock b = mapblock_queue.top();
1508                         mapblock_queue.pop();
1509
1510                         MapBlock *block = b.block;
1511
1512                         if (block->refGet() != 0)
1513                                 continue;
1514
1515                         v3s16 p = block->getPos();
1516
1517                         // Save if modified
1518                         if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1519                                 modprofiler.add(block->getModifiedReasonString(), 1);
1520                                 if (!saveBlock(block))
1521                                         continue;
1522                                 saved_blocks_count++;
1523                         }
1524
1525                         // Delete from memory
1526                         b.sect->deleteBlock(block);
1527
1528                         if (unloaded_blocks)
1529                                 unloaded_blocks->push_back(p);
1530
1531                         deleted_blocks_count++;
1532                         block_count_all--;
1533                 }
1534                 // Delete empty sectors
1535                 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1536                         si != m_sectors.end(); ++si) {
1537                         if (si->second->empty()) {
1538                                 sector_deletion_queue.push_back(si->first);
1539                         }
1540                 }
1541         }
1542         endSave();
1543
1544         // Finally delete the empty sectors
1545         deleteSectors(sector_deletion_queue);
1546
1547         if(deleted_blocks_count != 0)
1548         {
1549                 PrintInfo(infostream); // ServerMap/ClientMap:
1550                 infostream<<"Unloaded "<<deleted_blocks_count
1551                                 <<" blocks from memory";
1552                 if(save_before_unloading)
1553                         infostream<<", of which "<<saved_blocks_count<<" were written";
1554                 infostream<<", "<<block_count_all<<" blocks in memory";
1555                 infostream<<"."<<std::endl;
1556                 if(saved_blocks_count != 0){
1557                         PrintInfo(infostream); // ServerMap/ClientMap:
1558                         infostream<<"Blocks modified by: "<<std::endl;
1559                         modprofiler.print(infostream);
1560                 }
1561         }
1562 }
1563
1564 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1565 {
1566         timerUpdate(0.0, -1.0, 0, unloaded_blocks);
1567 }
1568
1569 void Map::deleteSectors(std::vector<v2s16> &sectorList)
1570 {
1571         for(std::vector<v2s16>::iterator j = sectorList.begin();
1572                 j != sectorList.end(); ++j) {
1573                 MapSector *sector = m_sectors[*j];
1574                 // If sector is in sector cache, remove it from there
1575                 if(m_sector_cache == sector)
1576                         m_sector_cache = NULL;
1577                 // Remove from map and delete
1578                 m_sectors.erase(*j);
1579                 delete sector;
1580         }
1581 }
1582
1583 void Map::PrintInfo(std::ostream &out)
1584 {
1585         out<<"Map: ";
1586 }
1587
1588 #define WATER_DROP_BOOST 4
1589
1590 enum NeighborType {
1591         NEIGHBOR_UPPER,
1592         NEIGHBOR_SAME_LEVEL,
1593         NEIGHBOR_LOWER
1594 };
1595 struct NodeNeighbor {
1596         MapNode n;
1597         NeighborType t;
1598         v3s16 p;
1599         bool l; //can liquid
1600
1601         NodeNeighbor()
1602                 : n(CONTENT_AIR)
1603         { }
1604
1605         NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1606                 : n(node),
1607                   t(n_type),
1608                   p(pos)
1609         { }
1610 };
1611
1612 void Map::transforming_liquid_add(v3s16 p) {
1613         m_transforming_liquid.push_back(p);
1614 }
1615
1616 s32 Map::transforming_liquid_size() {
1617         return m_transforming_liquid.size();
1618 }
1619
1620 void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
1621 {
1622
1623         INodeDefManager *nodemgr = m_gamedef->ndef();
1624
1625         DSTACK(FUNCTION_NAME);
1626         //TimeTaker timer("transformLiquids()");
1627
1628         u32 loopcount = 0;
1629         u32 initial_size = m_transforming_liquid.size();
1630
1631         /*if(initial_size != 0)
1632                 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1633
1634         // list of nodes that due to viscosity have not reached their max level height
1635         std::deque<v3s16> must_reflow;
1636
1637         // List of MapBlocks that will require a lighting update (due to lava)
1638         std::map<v3s16, MapBlock *> lighting_modified_blocks;
1639
1640         u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1641         u32 loop_max = liquid_loop_max;
1642
1643 #if 0
1644
1645         /* If liquid_loop_max is not keeping up with the queue size increase
1646          * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1647          */
1648         if (m_transforming_liquid.size() > loop_max * 2) {
1649                 // "Burst" mode
1650                 float server_step = g_settings->getFloat("dedicated_server_step");
1651                 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1652                         m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1653         } else {
1654                 m_transforming_liquid_loop_count_multiplier = 1.0;
1655         }
1656
1657         loop_max *= m_transforming_liquid_loop_count_multiplier;
1658 #endif
1659
1660         while (m_transforming_liquid.size() != 0)
1661         {
1662                 // This should be done here so that it is done when continue is used
1663                 if (loopcount >= initial_size || loopcount >= loop_max)
1664                         break;
1665                 loopcount++;
1666
1667                 /*
1668                         Get a queued transforming liquid node
1669                 */
1670                 v3s16 p0 = m_transforming_liquid.front();
1671                 m_transforming_liquid.pop_front();
1672
1673                 MapNode n0 = getNodeNoEx(p0);
1674
1675                 /*
1676                         Collect information about current node
1677                  */
1678                 s8 liquid_level = -1;
1679                 content_t liquid_kind = CONTENT_IGNORE;
1680                 content_t floodable_node = CONTENT_AIR;
1681                 const ContentFeatures &cf = nodemgr->get(n0);
1682                 LiquidType liquid_type = cf.liquid_type;
1683                 switch (liquid_type) {
1684                         case LIQUID_SOURCE:
1685                                 liquid_level = LIQUID_LEVEL_SOURCE;
1686                                 liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing);
1687                                 break;
1688                         case LIQUID_FLOWING:
1689                                 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1690                                 liquid_kind = n0.getContent();
1691                                 break;
1692                         case LIQUID_NONE:
1693                                 // if this node is 'floodable', it *could* be transformed
1694                                 // into a liquid, otherwise, continue with the next node.
1695                                 if (!cf.floodable)
1696                                         continue;
1697                                 floodable_node = n0.getContent();
1698                                 liquid_kind = CONTENT_AIR;
1699                                 break;
1700                 }
1701
1702                 /*
1703                         Collect information about the environment
1704                  */
1705                 const v3s16 *dirs = g_6dirs;
1706                 NodeNeighbor sources[6]; // surrounding sources
1707                 int num_sources = 0;
1708                 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1709                 int num_flows = 0;
1710                 NodeNeighbor airs[6]; // surrounding air
1711                 int num_airs = 0;
1712                 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1713                 int num_neutrals = 0;
1714                 bool flowing_down = false;
1715                 for (u16 i = 0; i < 6; i++) {
1716                         NeighborType nt = NEIGHBOR_SAME_LEVEL;
1717                         switch (i) {
1718                                 case 1:
1719                                         nt = NEIGHBOR_UPPER;
1720                                         break;
1721                                 case 4:
1722                                         nt = NEIGHBOR_LOWER;
1723                                         break;
1724                         }
1725                         v3s16 npos = p0 + dirs[i];
1726                         NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1727                         const ContentFeatures &cfnb = nodemgr->get(nb.n);
1728                         switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1729                                 case LIQUID_NONE:
1730                                         if (cfnb.floodable) {
1731                                                 airs[num_airs++] = nb;
1732                                                 // if the current node is a water source the neighbor
1733                                                 // should be enqueded for transformation regardless of whether the
1734                                                 // current node changes or not.
1735                                                 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1736                                                         m_transforming_liquid.push_back(npos);
1737                                                 // if the current node happens to be a flowing node, it will start to flow down here.
1738                                                 if (nb.t == NEIGHBOR_LOWER)
1739                                                         flowing_down = true;
1740                                         } else {
1741                                                 neutrals[num_neutrals++] = nb;
1742                                                 // If neutral below is ignore prevent water spreading outwards
1743                                                 if (nb.t == NEIGHBOR_LOWER &&
1744                                                                 nb.n.getContent() == CONTENT_IGNORE)
1745                                                         flowing_down = true;
1746                                         }
1747                                         break;
1748                                 case LIQUID_SOURCE:
1749                                         // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1750                                         if (liquid_kind == CONTENT_AIR)
1751                                                 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1752                                         if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1753                                                 neutrals[num_neutrals++] = nb;
1754                                         } else {
1755                                                 // Do not count bottom source, it will screw things up
1756                                                 if(dirs[i].Y != -1)
1757                                                         sources[num_sources++] = nb;
1758                                         }
1759                                         break;
1760                                 case LIQUID_FLOWING:
1761                                         // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1762                                         if (liquid_kind == CONTENT_AIR)
1763                                                 liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
1764                                         if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
1765                                                 neutrals[num_neutrals++] = nb;
1766                                         } else {
1767                                                 flows[num_flows++] = nb;
1768                                                 if (nb.t == NEIGHBOR_LOWER)
1769                                                         flowing_down = true;
1770                                         }
1771                                         break;
1772                         }
1773                 }
1774
1775                 /*
1776                         decide on the type (and possibly level) of the current node
1777                  */
1778                 content_t new_node_content;
1779                 s8 new_node_level = -1;
1780                 s8 max_node_level = -1;
1781
1782                 u8 range = nodemgr->get(liquid_kind).liquid_range;
1783                 if (range > LIQUID_LEVEL_MAX + 1)
1784                         range = LIQUID_LEVEL_MAX + 1;
1785
1786                 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1787                         // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1788                         // or the flowing alternative of the first of the surrounding sources (if it's air), so
1789                         // it's perfectly safe to use liquid_kind here to determine the new node content.
1790                         new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1791                 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1792                         // liquid_kind is set properly, see above
1793                         max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1794                         if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1795                                 new_node_content = liquid_kind;
1796                         else
1797                                 new_node_content = floodable_node;
1798                 } else {
1799                         // no surrounding sources, so get the maximum level that can flow into this node
1800                         for (u16 i = 0; i < num_flows; i++) {
1801                                 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1802                                 switch (flows[i].t) {
1803                                         case NEIGHBOR_UPPER:
1804                                                 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1805                                                         max_node_level = LIQUID_LEVEL_MAX;
1806                                                         if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1807                                                                 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1808                                                 } else if (nb_liquid_level > max_node_level) {
1809                                                         max_node_level = nb_liquid_level;
1810                                                 }
1811                                                 break;
1812                                         case NEIGHBOR_LOWER:
1813                                                 break;
1814                                         case NEIGHBOR_SAME_LEVEL:
1815                                                 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1816                                                                 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
1817                                                         max_node_level = nb_liquid_level - 1;
1818                                                 break;
1819                                 }
1820                         }
1821
1822                         u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1823                         if (viscosity > 1 && max_node_level != liquid_level) {
1824                                 // amount to gain, limited by viscosity
1825                                 // must be at least 1 in absolute value
1826                                 s8 level_inc = max_node_level - liquid_level;
1827                                 if (level_inc < -viscosity || level_inc > viscosity)
1828                                         new_node_level = liquid_level + level_inc/viscosity;
1829                                 else if (level_inc < 0)
1830                                         new_node_level = liquid_level - 1;
1831                                 else if (level_inc > 0)
1832                                         new_node_level = liquid_level + 1;
1833                                 if (new_node_level != max_node_level)
1834                                         must_reflow.push_back(p0);
1835                         } else {
1836                                 new_node_level = max_node_level;
1837                         }
1838
1839                         if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
1840                                 new_node_content = liquid_kind;
1841                         else
1842                                 new_node_content = floodable_node;
1843
1844                 }
1845
1846                 /*
1847                         check if anything has changed. if not, just continue with the next node.
1848                  */
1849                 if (new_node_content == n0.getContent() &&
1850                                 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1851                                 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1852                                 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1853                                 == flowing_down)))
1854                         continue;
1855
1856
1857                 /*
1858                         update the current node
1859                  */
1860                 MapNode n00 = n0;
1861                 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1862                 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1863                         // set level to last 3 bits, flowing down bit to 4th bit
1864                         n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1865                 } else {
1866                         // set the liquid level and flow bit to 0
1867                         n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1868                 }
1869                 n0.setContent(new_node_content);
1870
1871                 // Find out whether there is a suspect for this action
1872                 std::string suspect;
1873                 if (m_gamedef->rollback())
1874                         suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1875
1876                 if (m_gamedef->rollback() && !suspect.empty()) {
1877                         // Blame suspect
1878                         RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1879                         // Get old node for rollback
1880                         RollbackNode rollback_oldnode(this, p0, m_gamedef);
1881                         // Set node
1882                         setNode(p0, n0);
1883                         // Report
1884                         RollbackNode rollback_newnode(this, p0, m_gamedef);
1885                         RollbackAction action;
1886                         action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1887                         m_gamedef->rollback()->reportAction(action);
1888                 } else {
1889                         // Set node
1890                         setNode(p0, n0);
1891                 }
1892
1893                 v3s16 blockpos = getNodeBlockPos(p0);
1894                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1895                 if (block != NULL) {
1896                         modified_blocks[blockpos] =  block;
1897                         // If new or old node emits light, MapBlock requires lighting update
1898                         if (nodemgr->get(n0).light_source != 0 ||
1899                                         nodemgr->get(n00).light_source != 0)
1900                                 lighting_modified_blocks[block->getPos()] = block;
1901                 }
1902
1903                 /*
1904                         enqueue neighbors for update if neccessary
1905                  */
1906                 switch (nodemgr->get(n0.getContent()).liquid_type) {
1907                         case LIQUID_SOURCE:
1908                         case LIQUID_FLOWING:
1909                                 // make sure source flows into all neighboring nodes
1910                                 for (u16 i = 0; i < num_flows; i++)
1911                                         if (flows[i].t != NEIGHBOR_UPPER)
1912                                                 m_transforming_liquid.push_back(flows[i].p);
1913                                 for (u16 i = 0; i < num_airs; i++)
1914                                         if (airs[i].t != NEIGHBOR_UPPER)
1915                                                 m_transforming_liquid.push_back(airs[i].p);
1916                                 break;
1917                         case LIQUID_NONE:
1918                                 // this flow has turned to air; neighboring flows might need to do the same
1919                                 for (u16 i = 0; i < num_flows; i++)
1920                                         m_transforming_liquid.push_back(flows[i].p);
1921                                 break;
1922                 }
1923         }
1924         //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1925
1926         for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1927                 m_transforming_liquid.push_back(*iter);
1928
1929         updateLighting(lighting_modified_blocks, modified_blocks);
1930
1931
1932         /* ----------------------------------------------------------------------
1933          * Manage the queue so that it does not grow indefinately
1934          */
1935         u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1936
1937         if (time_until_purge == 0)
1938                 return; // Feature disabled
1939
1940         time_until_purge *= 1000;       // seconds -> milliseconds
1941
1942         u32 curr_time = getTime(PRECISION_MILLI);
1943         u32 prev_unprocessed = m_unprocessed_count;
1944         m_unprocessed_count = m_transforming_liquid.size();
1945
1946         // if unprocessed block count is decreasing or stable
1947         if (m_unprocessed_count <= prev_unprocessed) {
1948                 m_queue_size_timer_started = false;
1949         } else {
1950                 if (!m_queue_size_timer_started)
1951                         m_inc_trending_up_start_time = curr_time;
1952                 m_queue_size_timer_started = true;
1953         }
1954
1955         // Account for curr_time overflowing
1956         if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1957                 m_queue_size_timer_started = false;
1958
1959         /* If the queue has been growing for more than liquid_queue_purge_time seconds
1960          * and the number of unprocessed blocks is still > liquid_loop_max then we
1961          * cannot keep up; dump the oldest blocks from the queue so that the queue
1962          * has liquid_loop_max items in it
1963          */
1964         if (m_queue_size_timer_started
1965                         && curr_time - m_inc_trending_up_start_time > time_until_purge
1966                         && m_unprocessed_count > liquid_loop_max) {
1967
1968                 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1969
1970                 infostream << "transformLiquids(): DUMPING " << dump_qty
1971                            << " blocks from the queue" << std::endl;
1972
1973                 while (dump_qty--)
1974                         m_transforming_liquid.pop_front();
1975
1976                 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1977                 m_unprocessed_count = m_transforming_liquid.size();
1978         }
1979 }
1980
1981 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1982 {
1983         std::vector<v3s16> positions_with_meta;
1984
1985         sortBoxVerticies(p1, p2);
1986         v3s16 bpmin = getNodeBlockPos(p1);
1987         v3s16 bpmax = getNodeBlockPos(p2);
1988
1989         VoxelArea area(p1, p2);
1990
1991         for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1992         for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1993         for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1994                 v3s16 blockpos(x, y, z);
1995
1996                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1997                 if (!block) {
1998                         verbosestream << "Map::getNodeMetadata(): Need to emerge "
1999                                 << PP(blockpos) << std::endl;
2000                         block = emergeBlock(blockpos, false);
2001                 }
2002                 if (!block) {
2003                         infostream << "WARNING: Map::getNodeMetadata(): Block not found"
2004                                 << std::endl;
2005                         continue;
2006                 }
2007
2008                 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
2009                 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
2010                 for (size_t i = 0; i != keys.size(); i++) {
2011                         v3s16 p(keys[i] + p_base);
2012                         if (!area.contains(p))
2013                                 continue;
2014
2015                         positions_with_meta.push_back(p);
2016                 }
2017         }
2018
2019         return positions_with_meta;
2020 }
2021
2022 NodeMetadata *Map::getNodeMetadata(v3s16 p)
2023 {
2024         v3s16 blockpos = getNodeBlockPos(p);
2025         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2026         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2027         if(!block){
2028                 infostream<<"Map::getNodeMetadata(): Need to emerge "
2029                                 <<PP(blockpos)<<std::endl;
2030                 block = emergeBlock(blockpos, false);
2031         }
2032         if(!block){
2033                 warningstream<<"Map::getNodeMetadata(): Block not found"
2034                                 <<std::endl;
2035                 return NULL;
2036         }
2037         NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2038         return meta;
2039 }
2040
2041 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2042 {
2043         v3s16 blockpos = getNodeBlockPos(p);
2044         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2045         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2046         if(!block){
2047                 infostream<<"Map::setNodeMetadata(): Need to emerge "
2048                                 <<PP(blockpos)<<std::endl;
2049                 block = emergeBlock(blockpos, false);
2050         }
2051         if(!block){
2052                 warningstream<<"Map::setNodeMetadata(): Block not found"
2053                                 <<std::endl;
2054                 return false;
2055         }
2056         block->m_node_metadata.set(p_rel, meta);
2057         return true;
2058 }
2059
2060 void Map::removeNodeMetadata(v3s16 p)
2061 {
2062         v3s16 blockpos = getNodeBlockPos(p);
2063         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2064         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2065         if(block == NULL)
2066         {
2067                 warningstream<<"Map::removeNodeMetadata(): Block not found"
2068                                 <<std::endl;
2069                 return;
2070         }
2071         block->m_node_metadata.remove(p_rel);
2072 }
2073
2074 NodeTimer Map::getNodeTimer(v3s16 p)
2075 {
2076         v3s16 blockpos = getNodeBlockPos(p);
2077         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2078         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2079         if(!block){
2080                 infostream<<"Map::getNodeTimer(): Need to emerge "
2081                                 <<PP(blockpos)<<std::endl;
2082                 block = emergeBlock(blockpos, false);
2083         }
2084         if(!block){
2085                 warningstream<<"Map::getNodeTimer(): Block not found"
2086                                 <<std::endl;
2087                 return NodeTimer();
2088         }
2089         NodeTimer t = block->m_node_timers.get(p_rel);
2090         NodeTimer nt(t.timeout, t.elapsed, p);
2091         return nt;
2092 }
2093
2094 void Map::setNodeTimer(const NodeTimer &t)
2095 {
2096         v3s16 p = t.position;
2097         v3s16 blockpos = getNodeBlockPos(p);
2098         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2099         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2100         if(!block){
2101                 infostream<<"Map::setNodeTimer(): Need to emerge "
2102                                 <<PP(blockpos)<<std::endl;
2103                 block = emergeBlock(blockpos, false);
2104         }
2105         if(!block){
2106                 warningstream<<"Map::setNodeTimer(): Block not found"
2107                                 <<std::endl;
2108                 return;
2109         }
2110         NodeTimer nt(t.timeout, t.elapsed, p_rel);
2111         block->m_node_timers.set(nt);
2112 }
2113
2114 void Map::removeNodeTimer(v3s16 p)
2115 {
2116         v3s16 blockpos = getNodeBlockPos(p);
2117         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2118         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2119         if(block == NULL)
2120         {
2121                 warningstream<<"Map::removeNodeTimer(): Block not found"
2122                                 <<std::endl;
2123                 return;
2124         }
2125         block->m_node_timers.remove(p_rel);
2126 }
2127
2128 /*
2129         ServerMap
2130 */
2131 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2132         Map(dout_server, gamedef),
2133         m_emerge(emerge),
2134         m_map_metadata_changed(true)
2135 {
2136         verbosestream<<FUNCTION_NAME<<std::endl;
2137
2138         /*
2139                 Try to load map; if not found, create a new one.
2140         */
2141
2142         // Determine which database backend to use
2143         std::string conf_path = savedir + DIR_DELIM + "world.mt";
2144         Settings conf;
2145         bool succeeded = conf.readConfigFile(conf_path.c_str());
2146         if (!succeeded || !conf.exists("backend")) {
2147                 // fall back to sqlite3
2148                 conf.set("backend", "sqlite3");
2149         }
2150         std::string backend = conf.get("backend");
2151         dbase = createDatabase(backend, savedir, conf);
2152
2153         if (!conf.updateConfigFile(conf_path.c_str()))
2154                 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
2155
2156         m_savedir = savedir;
2157         m_map_saving_enabled = false;
2158
2159         try
2160         {
2161                 // If directory exists, check contents and load if possible
2162                 if(fs::PathExists(m_savedir))
2163                 {
2164                         // If directory is empty, it is safe to save into it.
2165                         if(fs::GetDirListing(m_savedir).size() == 0)
2166                         {
2167                                 infostream<<"ServerMap: Empty save directory is valid."
2168                                                 <<std::endl;
2169                                 m_map_saving_enabled = true;
2170                         }
2171                         else
2172                         {
2173                                 try{
2174                                         // Load map metadata (seed, chunksize)
2175                                         loadMapMeta();
2176                                 }
2177                                 catch(SettingNotFoundException &e){
2178                                         infostream<<"ServerMap:  Some metadata not found."
2179                                                           <<" Using default settings."<<std::endl;
2180                                 }
2181                                 catch(FileNotGoodException &e){
2182                                         warningstream<<"Could not load map metadata"
2183                                                         //<<" Disabling chunk-based generator."
2184                                                         <<std::endl;
2185                                         //m_chunksize = 0;
2186                                 }
2187
2188                                 infostream<<"ServerMap: Successfully loaded map "
2189                                                 <<"metadata from "<<savedir
2190                                                 <<", assuming valid save directory."
2191                                                 <<" seed="<< m_emerge->params.seed <<"."
2192                                                 <<std::endl;
2193
2194                                 m_map_saving_enabled = true;
2195                                 // Map loaded, not creating new one
2196                                 return;
2197                         }
2198                 }
2199                 // If directory doesn't exist, it is safe to save to it
2200                 else{
2201                         m_map_saving_enabled = true;
2202                 }
2203         }
2204         catch(std::exception &e)
2205         {
2206                 warningstream<<"ServerMap: Failed to load map from "<<savedir
2207                                 <<", exception: "<<e.what()<<std::endl;
2208                 infostream<<"Please remove the map or fix it."<<std::endl;
2209                 warningstream<<"Map saving will be disabled."<<std::endl;
2210         }
2211
2212         infostream<<"Initializing new map."<<std::endl;
2213
2214         // Create zero sector
2215         emergeSector(v2s16(0,0));
2216
2217         // Initially write whole map
2218         save(MOD_STATE_CLEAN);
2219 }
2220
2221 ServerMap::~ServerMap()
2222 {
2223         verbosestream<<FUNCTION_NAME<<std::endl;
2224
2225         try
2226         {
2227                 if(m_map_saving_enabled)
2228                 {
2229                         // Save only changed parts
2230                         save(MOD_STATE_WRITE_AT_UNLOAD);
2231                         infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2232                 }
2233                 else
2234                 {
2235                         infostream<<"ServerMap: Map not saved"<<std::endl;
2236                 }
2237         }
2238         catch(std::exception &e)
2239         {
2240                 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2241                                 <<", exception: "<<e.what()<<std::endl;
2242         }
2243
2244         /*
2245                 Close database if it was opened
2246         */
2247         delete dbase;
2248
2249 #if 0
2250         /*
2251                 Free all MapChunks
2252         */
2253         core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2254         for(; i.atEnd() == false; i++)
2255         {
2256                 MapChunk *chunk = i.getNode()->getValue();
2257                 delete chunk;
2258         }
2259 #endif
2260 }
2261
2262 u64 ServerMap::getSeed()
2263 {
2264         return m_emerge->params.seed;
2265 }
2266
2267 s16 ServerMap::getWaterLevel()
2268 {
2269         return m_emerge->params.water_level;
2270 }
2271
2272 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
2273 {
2274         s16 csize = m_emerge->params.chunksize;
2275         v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
2276         v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
2277
2278         bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
2279         EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
2280
2281         v3s16 extra_borders(1, 1, 1);
2282         v3s16 full_bpmin = bpmin - extra_borders;
2283         v3s16 full_bpmax = bpmax + extra_borders;
2284
2285         // Do nothing if not inside limits (+-1 because of neighbors)
2286         if (blockpos_over_limit(full_bpmin) ||
2287                 blockpos_over_limit(full_bpmax))
2288                 return false;
2289
2290         data->seed = m_emerge->params.seed;
2291         data->blockpos_min = bpmin;
2292         data->blockpos_max = bpmax;
2293         data->blockpos_requested = blockpos;
2294         data->nodedef = m_gamedef->ndef();
2295
2296         /*
2297                 Create the whole area of this and the neighboring blocks
2298         */
2299         for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
2300         for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
2301                 v2s16 sectorpos(x, z);
2302                 // Sector metadata is loaded from disk if not already loaded.
2303                 ServerMapSector *sector = createSector(sectorpos);
2304                 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
2305
2306                 for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
2307                         v3s16 p(x, y, z);
2308
2309                         MapBlock *block = emergeBlock(p, false);
2310                         if (block == NULL) {
2311                                 block = createBlock(p);
2312
2313                                 // Block gets sunlight if this is true.
2314                                 // Refer to the map generator heuristics.
2315                                 bool ug = m_emerge->isBlockUnderground(p);
2316                                 block->setIsUnderground(ug);
2317                         }
2318                 }
2319         }
2320
2321         /*
2322                 Now we have a big empty area.
2323
2324                 Make a ManualMapVoxelManipulator that contains this and the
2325                 neighboring blocks
2326         */
2327
2328         data->vmanip = new MMVManip(this);
2329         data->vmanip->initialEmerge(full_bpmin, full_bpmax);
2330
2331         // Note: we may need this again at some point.
2332 #if 0
2333         // Ensure none of the blocks to be generated were marked as
2334         // containing CONTENT_IGNORE
2335         for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2336                 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2337                         for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2338                                 core::map<v3s16, u8>::Node *n;
2339                                 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2340                                 if (n == NULL)
2341                                         continue;
2342                                 u8 flags = n->getValue();
2343                                 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2344                                 n->setValue(flags);
2345                         }
2346                 }
2347         }
2348 #endif
2349
2350         // Data is ready now.
2351         return true;
2352 }
2353
2354 void ServerMap::finishBlockMake(BlockMakeData *data,
2355         std::map<v3s16, MapBlock*> *changed_blocks)
2356 {
2357         v3s16 bpmin = data->blockpos_min;
2358         v3s16 bpmax = data->blockpos_max;
2359
2360         v3s16 extra_borders(1, 1, 1);
2361         v3s16 full_bpmin = bpmin - extra_borders;
2362         v3s16 full_bpmax = bpmax + extra_borders;
2363
2364         bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
2365         EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
2366
2367         /*
2368                 Set lighting to non-expired state in all of them.
2369                 This is cheating, but it is not fast enough if all of them
2370                 would actually be updated.
2371         */
2372         for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
2373         for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
2374         for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
2375                 MapBlock *block = emergeBlock(v3s16(x, y, z), false);
2376                 if (!block)
2377                         continue;
2378
2379                 block->setLightingExpired(false);
2380         }
2381
2382         /*
2383                 Blit generated stuff to map
2384                 NOTE: blitBackAll adds nearly everything to changed_blocks
2385         */
2386         data->vmanip->blitBackAll(changed_blocks);
2387
2388         EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
2389                 << changed_blocks->size());
2390
2391         /*
2392                 Copy transforming liquid information
2393         */
2394         while (data->transforming_liquid.size()) {
2395                 m_transforming_liquid.push_back(data->transforming_liquid.front());
2396                 data->transforming_liquid.pop_front();
2397         }
2398
2399         for (std::map<v3s16, MapBlock *>::iterator
2400                         it = changed_blocks->begin();
2401                         it != changed_blocks->end(); ++it) {
2402                 MapBlock *block = it->second;
2403                 if (!block)
2404                         continue;
2405                 /*
2406                         Update day/night difference cache of the MapBlocks
2407                 */
2408                 block->expireDayNightDiff();
2409                 /*
2410                         Set block as modified
2411                 */
2412                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2413                         MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2414         }
2415
2416         /*
2417                 Set central blocks as generated
2418         */
2419         for (s16 x = bpmin.X; x <= bpmax.X; x++)
2420         for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
2421         for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
2422                 MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
2423                 if (!block)
2424                         continue;
2425
2426                 block->setGenerated(true);
2427         }
2428
2429         /*
2430                 Save changed parts of map
2431                 NOTE: Will be saved later.
2432         */
2433         //save(MOD_STATE_WRITE_AT_UNLOAD);
2434 }
2435
2436 ServerMapSector *ServerMap::createSector(v2s16 p2d)
2437 {
2438         DSTACKF("%s: p2d=(%d,%d)",
2439                         FUNCTION_NAME,
2440                         p2d.X, p2d.Y);
2441
2442         /*
2443                 Check if it exists already in memory
2444         */
2445         ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2446         if(sector != NULL)
2447                 return sector;
2448
2449         /*
2450                 Try to load it from disk (with blocks)
2451         */
2452         //if(loadSectorFull(p2d) == true)
2453
2454         /*
2455                 Try to load metadata from disk
2456         */
2457 #if 0
2458         if(loadSectorMeta(p2d) == true)
2459         {
2460                 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2461                 if(sector == NULL)
2462                 {
2463                         infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2464                         throw InvalidPositionException("");
2465                 }
2466                 return sector;
2467         }
2468 #endif
2469         /*
2470                 Do not create over-limit
2471         */
2472         const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2473                 g_settings->getU16("map_generation_limit"));
2474         if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2475                         || p2d.X >  map_gen_limit / MAP_BLOCKSIZE
2476                         || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2477                         || p2d.Y >  map_gen_limit / MAP_BLOCKSIZE)
2478                 throw InvalidPositionException("createSector(): pos. over limit");
2479
2480         /*
2481                 Generate blank sector
2482         */
2483
2484         sector = new ServerMapSector(this, p2d, m_gamedef);
2485
2486         // Sector position on map in nodes
2487         //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2488
2489         /*
2490                 Insert to container
2491         */
2492         m_sectors[p2d] = sector;
2493
2494         return sector;
2495 }
2496
2497 #if 0
2498 /*
2499         This is a quick-hand function for calling makeBlock().
2500 */
2501 MapBlock * ServerMap::generateBlock(
2502                 v3s16 p,
2503                 std::map<v3s16, MapBlock*> &modified_blocks
2504 )
2505 {
2506         DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
2507
2508         /*infostream<<"generateBlock(): "
2509                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2510                         <<std::endl;*/
2511
2512         bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2513
2514         TimeTaker timer("generateBlock");
2515
2516         //MapBlock *block = original_dummy;
2517
2518         v2s16 p2d(p.X, p.Z);
2519         v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2520
2521         /*
2522                 Do not generate over-limit
2523         */
2524         if(blockpos_over_limit(p))
2525         {
2526                 infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
2527                 throw InvalidPositionException("generateBlock(): pos. over limit");
2528         }
2529
2530         /*
2531                 Create block make data
2532         */
2533         BlockMakeData data;
2534         initBlockMake(&data, p);
2535
2536         /*
2537                 Generate block
2538         */
2539         {
2540                 TimeTaker t("mapgen::make_block()");
2541                 mapgen->makeChunk(&data);
2542                 //mapgen::make_block(&data);
2543
2544                 if(enable_mapgen_debug_info == false)
2545                         t.stop(true); // Hide output
2546         }
2547
2548         /*
2549                 Blit data back on map, update lighting, add mobs and whatever this does
2550         */
2551         finishBlockMake(&data, modified_blocks);
2552
2553         /*
2554                 Get central block
2555         */
2556         MapBlock *block = getBlockNoCreateNoEx(p);
2557
2558 #if 0
2559         /*
2560                 Check result
2561         */
2562         if(block)
2563         {
2564                 bool erroneus_content = false;
2565                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2566                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2567                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2568                 {
2569                         v3s16 p(x0,y0,z0);
2570                         MapNode n = block->getNode(p);
2571                         if(n.getContent() == CONTENT_IGNORE)
2572                         {
2573                                 infostream<<"CONTENT_IGNORE at "
2574                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2575                                                 <<std::endl;
2576                                 erroneus_content = true;
2577                                 assert(0);
2578                         }
2579                 }
2580                 if(erroneus_content)
2581                 {
2582                         assert(0);
2583                 }
2584         }
2585 #endif
2586
2587 #if 0
2588         /*
2589                 Generate a completely empty block
2590         */
2591         if(block)
2592         {
2593                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2594                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2595                 {
2596                         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2597                         {
2598                                 MapNode n;
2599                                 n.setContent(CONTENT_AIR);
2600                                 block->setNode(v3s16(x0,y0,z0), n);
2601                         }
2602                 }
2603         }
2604 #endif
2605
2606         if(enable_mapgen_debug_info == false)
2607                 timer.stop(true); // Hide output
2608
2609         return block;
2610 }
2611 #endif
2612
2613 MapBlock * ServerMap::createBlock(v3s16 p)
2614 {
2615         DSTACKF("%s: p=(%d,%d,%d)",
2616                         FUNCTION_NAME, p.X, p.Y, p.Z);
2617
2618         /*
2619                 Do not create over-limit
2620         */
2621         if (blockpos_over_limit(p))
2622                 throw InvalidPositionException("createBlock(): pos. over limit");
2623
2624         v2s16 p2d(p.X, p.Z);
2625         s16 block_y = p.Y;
2626         /*
2627                 This will create or load a sector if not found in memory.
2628                 If block exists on disk, it will be loaded.
2629
2630                 NOTE: On old save formats, this will be slow, as it generates
2631                       lighting on blocks for them.
2632         */
2633         ServerMapSector *sector;
2634         try {
2635                 sector = (ServerMapSector*)createSector(p2d);
2636                 assert(sector->getId() == MAPSECTOR_SERVER);
2637         }
2638         catch(InvalidPositionException &e)
2639         {
2640                 infostream<<"createBlock: createSector() failed"<<std::endl;
2641                 throw e;
2642         }
2643         /*
2644                 NOTE: This should not be done, or at least the exception
2645                 should not be passed on as std::exception, because it
2646                 won't be catched at all.
2647         */
2648         /*catch(std::exception &e)
2649         {
2650                 infostream<<"createBlock: createSector() failed: "
2651                                 <<e.what()<<std::endl;
2652                 throw e;
2653         }*/
2654
2655         /*
2656                 Try to get a block from the sector
2657         */
2658
2659         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2660         if(block)
2661         {
2662                 if(block->isDummy())
2663                         block->unDummify();
2664                 return block;
2665         }
2666         // Create blank
2667         block = sector->createBlankBlock(block_y);
2668
2669         return block;
2670 }
2671
2672 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2673 {
2674         DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2675                         FUNCTION_NAME,
2676                         p.X, p.Y, p.Z, create_blank);
2677
2678         {
2679                 MapBlock *block = getBlockNoCreateNoEx(p);
2680                 if(block && block->isDummy() == false)
2681                         return block;
2682         }
2683
2684         {
2685                 MapBlock *block = loadBlock(p);
2686                 if(block)
2687                         return block;
2688         }
2689
2690         if (create_blank) {
2691                 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2692                 MapBlock *block = sector->createBlankBlock(p.Y);
2693
2694                 return block;
2695         }
2696
2697 #if 0
2698         if(allow_generate)
2699         {
2700                 std::map<v3s16, MapBlock*> modified_blocks;
2701                 MapBlock *block = generateBlock(p, modified_blocks);
2702                 if(block)
2703                 {
2704                         MapEditEvent event;
2705                         event.type = MEET_OTHER;
2706                         event.p = p;
2707
2708                         // Copy modified_blocks to event
2709                         for(std::map<v3s16, MapBlock*>::iterator
2710                                         i = modified_blocks.begin();
2711                                         i != modified_blocks.end(); ++i)
2712                         {
2713                                 event.modified_blocks.insert(i->first);
2714                         }
2715
2716                         // Queue event
2717                         dispatchEvent(&event);
2718
2719                         return block;
2720                 }
2721         }
2722 #endif
2723
2724         return NULL;
2725 }
2726
2727 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2728 {
2729         MapBlock *block = getBlockNoCreateNoEx(p3d);
2730         if (block == NULL)
2731                 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2732
2733         return block;
2734 }
2735
2736 void ServerMap::prepareBlock(MapBlock *block) {
2737 }
2738
2739 // N.B.  This requires no synchronization, since data will not be modified unless
2740 // the VoxelManipulator being updated belongs to the same thread.
2741 void ServerMap::updateVManip(v3s16 pos)
2742 {
2743         Mapgen *mg = m_emerge->getCurrentMapgen();
2744         if (!mg)
2745                 return;
2746
2747         MMVManip *vm = mg->vm;
2748         if (!vm)
2749                 return;
2750
2751         if (!vm->m_area.contains(pos))
2752                 return;
2753
2754         s32 idx = vm->m_area.index(pos);
2755         vm->m_data[idx] = getNodeNoEx(pos);
2756         vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2757
2758         vm->m_is_dirty = true;
2759 }
2760
2761 s16 ServerMap::findGroundLevel(v2s16 p2d)
2762 {
2763 #if 0
2764         /*
2765                 Uh, just do something random...
2766         */
2767         // Find existing map from top to down
2768         s16 max=63;
2769         s16 min=-64;
2770         v3s16 p(p2d.X, max, p2d.Y);
2771         for(; p.Y>min; p.Y--)
2772         {
2773                 MapNode n = getNodeNoEx(p);
2774                 if(n.getContent() != CONTENT_IGNORE)
2775                         break;
2776         }
2777         if(p.Y == min)
2778                 goto plan_b;
2779         // If this node is not air, go to plan b
2780         if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2781                 goto plan_b;
2782         // Search existing walkable and return it
2783         for(; p.Y>min; p.Y--)
2784         {
2785                 MapNode n = getNodeNoEx(p);
2786                 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2787                         return p.Y;
2788         }
2789
2790         // Move to plan b
2791 plan_b:
2792 #endif
2793
2794         /*
2795                 Determine from map generator noise functions
2796         */
2797
2798         s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2799         return level;
2800
2801         //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2802         //return (s16)level;
2803 }
2804
2805 bool ServerMap::loadFromFolders() {
2806         if (!dbase->initialized() &&
2807                         !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2808                 return true;
2809         return false;
2810 }
2811
2812 void ServerMap::createDirs(std::string path)
2813 {
2814         if(fs::CreateAllDirs(path) == false)
2815         {
2816                 m_dout<<"ServerMap: Failed to create directory "
2817                                 <<"\""<<path<<"\""<<std::endl;
2818                 throw BaseException("ServerMap failed to create directory");
2819         }
2820 }
2821
2822 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2823 {
2824         char cc[9];
2825         switch(layout)
2826         {
2827                 case 1:
2828                         snprintf(cc, 9, "%.4x%.4x",
2829                                 (unsigned int) pos.X & 0xffff,
2830                                 (unsigned int) pos.Y & 0xffff);
2831
2832                         return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2833                 case 2:
2834                         snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2835                                 (unsigned int) pos.X & 0xfff,
2836                                 (unsigned int) pos.Y & 0xfff);
2837
2838                         return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2839                 default:
2840                         assert(false);
2841                         return "";
2842         }
2843 }
2844
2845 v2s16 ServerMap::getSectorPos(std::string dirname)
2846 {
2847         unsigned int x = 0, y = 0;
2848         int r;
2849         std::string component;
2850         fs::RemoveLastPathComponent(dirname, &component, 1);
2851         if(component.size() == 8)
2852         {
2853                 // Old layout
2854                 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2855         }
2856         else if(component.size() == 3)
2857         {
2858                 // New layout
2859                 fs::RemoveLastPathComponent(dirname, &component, 2);
2860                 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2861                 // Sign-extend the 12 bit values up to 16 bits...
2862                 if(x & 0x800) x |= 0xF000;
2863                 if(y & 0x800) y |= 0xF000;
2864         }
2865         else
2866         {
2867                 r = -1;
2868         }
2869
2870         FATAL_ERROR_IF(r != 2, "getSectorPos()");
2871         v2s16 pos((s16)x, (s16)y);
2872         return pos;
2873 }
2874
2875 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2876 {
2877         v2s16 p2d = getSectorPos(sectordir);
2878
2879         if(blockfile.size() != 4){
2880                 throw InvalidFilenameException("Invalid block filename");
2881         }
2882         unsigned int y;
2883         int r = sscanf(blockfile.c_str(), "%4x", &y);
2884         if(r != 1)
2885                 throw InvalidFilenameException("Invalid block filename");
2886         return v3s16(p2d.X, y, p2d.Y);
2887 }
2888
2889 std::string ServerMap::getBlockFilename(v3s16 p)
2890 {
2891         char cc[5];
2892         snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2893         return cc;
2894 }
2895
2896 void ServerMap::save(ModifiedState save_level)
2897 {
2898         DSTACK(FUNCTION_NAME);
2899         if(m_map_saving_enabled == false) {
2900                 warningstream<<"Not saving map, saving disabled."<<std::endl;
2901                 return;
2902         }
2903
2904         if(save_level == MOD_STATE_CLEAN)
2905                 infostream<<"ServerMap: Saving whole map, this can take time."
2906                                 <<std::endl;
2907
2908         if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2909                 saveMapMeta();
2910         }
2911
2912         // Profile modified reasons
2913         Profiler modprofiler;
2914
2915         u32 sector_meta_count = 0;
2916         u32 block_count = 0;
2917         u32 block_count_all = 0; // Number of blocks in memory
2918
2919         // Don't do anything with sqlite unless something is really saved
2920         bool save_started = false;
2921
2922         for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2923                 i != m_sectors.end(); ++i) {
2924                 ServerMapSector *sector = (ServerMapSector*)i->second;
2925                 assert(sector->getId() == MAPSECTOR_SERVER);
2926
2927                 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2928                         saveSectorMeta(sector);
2929                         sector_meta_count++;
2930                 }
2931
2932                 MapBlockVect blocks;
2933                 sector->getBlocks(blocks);
2934
2935                 for(MapBlockVect::iterator j = blocks.begin();
2936                         j != blocks.end(); ++j) {
2937                         MapBlock *block = *j;
2938
2939                         block_count_all++;
2940
2941                         if(block->getModified() >= (u32)save_level) {
2942                                 // Lazy beginSave()
2943                                 if(!save_started) {
2944                                         beginSave();
2945                                         save_started = true;
2946                                 }
2947
2948                                 modprofiler.add(block->getModifiedReasonString(), 1);
2949
2950                                 saveBlock(block);
2951                                 block_count++;
2952
2953                                 /*infostream<<"ServerMap: Written block ("
2954                                                 <<block->getPos().X<<","
2955                                                 <<block->getPos().Y<<","
2956                                                 <<block->getPos().Z<<")"
2957                                                 <<std::endl;*/
2958                         }
2959                 }
2960         }
2961
2962         if(save_started)
2963                 endSave();
2964
2965         /*
2966                 Only print if something happened or saved whole map
2967         */
2968         if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2969                         || block_count != 0) {
2970                 infostream<<"ServerMap: Written: "
2971                                 <<sector_meta_count<<" sector metadata files, "
2972                                 <<block_count<<" block files"
2973                                 <<", "<<block_count_all<<" blocks in memory."
2974                                 <<std::endl;
2975                 PrintInfo(infostream); // ServerMap/ClientMap:
2976                 infostream<<"Blocks modified by: "<<std::endl;
2977                 modprofiler.print(infostream);
2978         }
2979 }
2980
2981 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2982 {
2983         if (loadFromFolders()) {
2984                 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2985                                 << "all blocks that are stored in flat files." << std::endl;
2986         }
2987         dbase->listAllLoadableBlocks(dst);
2988 }
2989
2990 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2991 {
2992         for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2993                 si != m_sectors.end(); ++si)
2994         {
2995                 MapSector *sector = si->second;
2996
2997                 MapBlockVect blocks;
2998                 sector->getBlocks(blocks);
2999
3000                 for(MapBlockVect::iterator i = blocks.begin();
3001                                 i != blocks.end(); ++i) {
3002                         v3s16 p = (*i)->getPos();
3003                         dst.push_back(p);
3004                 }
3005         }
3006 }
3007
3008 void ServerMap::saveMapMeta()
3009 {
3010         DSTACK(FUNCTION_NAME);
3011
3012         createDirs(m_savedir);
3013
3014         std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3015         std::ostringstream oss(std::ios_base::binary);
3016         Settings conf;
3017
3018         m_emerge->params.save(conf);
3019         conf.writeLines(oss);
3020
3021         oss << "[end_of_params]\n";
3022
3023         if(!fs::safeWriteToFile(fullpath, oss.str())) {
3024                 errorstream << "ServerMap::saveMapMeta(): "
3025                                 << "could not write " << fullpath << std::endl;
3026                 throw FileNotGoodException("Cannot save chunk metadata");
3027         }
3028
3029         m_map_metadata_changed = false;
3030 }
3031
3032 void ServerMap::loadMapMeta()
3033 {
3034         DSTACK(FUNCTION_NAME);
3035
3036         Settings conf;
3037         std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3038
3039         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3040         if (!is.good()) {
3041                 errorstream << "ServerMap::loadMapMeta(): "
3042                         "could not open " << fullpath << std::endl;
3043                 throw FileNotGoodException("Cannot open map metadata");
3044         }
3045
3046         if (!conf.parseConfigLines(is, "[end_of_params]")) {
3047                 throw SerializationError("ServerMap::loadMapMeta(): "
3048                                 "[end_of_params] not found!");
3049         }
3050
3051         m_emerge->params.load(conf);
3052
3053         verbosestream << "ServerMap::loadMapMeta(): seed="
3054                 << m_emerge->params.seed << std::endl;
3055 }
3056
3057 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3058 {
3059         DSTACK(FUNCTION_NAME);
3060         // Format used for writing
3061         u8 version = SER_FMT_VER_HIGHEST_WRITE;
3062         // Get destination
3063         v2s16 pos = sector->getPos();
3064         std::string dir = getSectorDir(pos);
3065         createDirs(dir);
3066
3067         std::string fullpath = dir + DIR_DELIM + "meta";
3068         std::ostringstream ss(std::ios_base::binary);
3069
3070         sector->serialize(ss, version);
3071
3072         if(!fs::safeWriteToFile(fullpath, ss.str()))
3073                 throw FileNotGoodException("Cannot write sector metafile");
3074
3075         sector->differs_from_disk = false;
3076 }
3077
3078 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3079 {
3080         DSTACK(FUNCTION_NAME);
3081         // Get destination
3082         v2s16 p2d = getSectorPos(sectordir);
3083
3084         ServerMapSector *sector = NULL;
3085
3086         std::string fullpath = sectordir + DIR_DELIM + "meta";
3087         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3088         if(is.good() == false)
3089         {
3090                 // If the directory exists anyway, it probably is in some old
3091                 // format. Just go ahead and create the sector.
3092                 if(fs::PathExists(sectordir))
3093                 {
3094                         /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3095                                         <<fullpath<<" doesn't exist but directory does."
3096                                         <<" Continuing with a sector with no metadata."
3097                                         <<std::endl;*/
3098                         sector = new ServerMapSector(this, p2d, m_gamedef);
3099                         m_sectors[p2d] = sector;
3100                 }
3101                 else
3102                 {
3103                         throw FileNotGoodException("Cannot open sector metafile");
3104                 }
3105         }
3106         else
3107         {
3108                 sector = ServerMapSector::deSerialize
3109                                 (is, this, p2d, m_sectors, m_gamedef);
3110                 if(save_after_load)
3111                         saveSectorMeta(sector);
3112         }
3113
3114         sector->differs_from_disk = false;
3115
3116         return sector;
3117 }
3118
3119 bool ServerMap::loadSectorMeta(v2s16 p2d)
3120 {
3121         DSTACK(FUNCTION_NAME);
3122
3123         // The directory layout we're going to load from.
3124         //  1 - original sectors/xxxxzzzz/
3125         //  2 - new sectors2/xxx/zzz/
3126         //  If we load from anything but the latest structure, we will
3127         //  immediately save to the new one, and remove the old.
3128         int loadlayout = 1;
3129         std::string sectordir1 = getSectorDir(p2d, 1);
3130         std::string sectordir;
3131         if(fs::PathExists(sectordir1))
3132         {
3133                 sectordir = sectordir1;
3134         }
3135         else
3136         {
3137                 loadlayout = 2;
3138                 sectordir = getSectorDir(p2d, 2);
3139         }
3140
3141         try{
3142                 loadSectorMeta(sectordir, loadlayout != 2);
3143         }
3144         catch(InvalidFilenameException &e)
3145         {
3146                 return false;
3147         }
3148         catch(FileNotGoodException &e)
3149         {
3150                 return false;
3151         }
3152         catch(std::exception &e)
3153         {
3154                 return false;
3155         }
3156
3157         return true;
3158 }
3159
3160 #if 0
3161 bool ServerMap::loadSectorFull(v2s16 p2d)
3162 {
3163         DSTACK(FUNCTION_NAME);
3164
3165         MapSector *sector = NULL;
3166
3167         // The directory layout we're going to load from.
3168         //  1 - original sectors/xxxxzzzz/
3169         //  2 - new sectors2/xxx/zzz/
3170         //  If we load from anything but the latest structure, we will
3171         //  immediately save to the new one, and remove the old.
3172         int loadlayout = 1;
3173         std::string sectordir1 = getSectorDir(p2d, 1);
3174         std::string sectordir;
3175         if(fs::PathExists(sectordir1))
3176         {
3177                 sectordir = sectordir1;
3178         }
3179         else
3180         {
3181                 loadlayout = 2;
3182                 sectordir = getSectorDir(p2d, 2);
3183         }
3184
3185         try{
3186                 sector = loadSectorMeta(sectordir, loadlayout != 2);
3187         }
3188         catch(InvalidFilenameException &e)
3189         {
3190                 return false;
3191         }
3192         catch(FileNotGoodException &e)
3193         {
3194                 return false;
3195         }
3196         catch(std::exception &e)
3197         {
3198                 return false;
3199         }
3200
3201         /*
3202                 Load blocks
3203         */
3204         std::vector<fs::DirListNode> list2 = fs::GetDirListing
3205                         (sectordir);
3206         std::vector<fs::DirListNode>::iterator i2;
3207         for(i2=list2.begin(); i2!=list2.end(); i2++)
3208         {
3209                 // We want files
3210                 if(i2->dir)
3211                         continue;
3212                 try{
3213                         loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3214                 }
3215                 catch(InvalidFilenameException &e)
3216                 {
3217                         // This catches unknown crap in directory
3218                 }
3219         }
3220
3221         if(loadlayout != 2)
3222         {
3223                 infostream<<"Sector converted to new layout - deleting "<<
3224                         sectordir1<<std::endl;
3225                 fs::RecursiveDelete(sectordir1);
3226         }
3227
3228         return true;
3229 }
3230 #endif
3231
3232 Database *ServerMap::createDatabase(
3233         const std::string &name,
3234         const std::string &savedir,
3235         Settings &conf)
3236 {
3237         if (name == "sqlite3")
3238                 return new Database_SQLite3(savedir);
3239         if (name == "dummy")
3240                 return new Database_Dummy();
3241         #if USE_LEVELDB
3242         else if (name == "leveldb")
3243                 return new Database_LevelDB(savedir);
3244         #endif
3245         #if USE_REDIS
3246         else if (name == "redis")
3247                 return new Database_Redis(conf);
3248         #endif
3249         #if USE_POSTGRESQL
3250         else if (name == "postgresql")
3251                 return new Database_PostgreSQL(conf);
3252         #endif
3253         else
3254                 throw BaseException(std::string("Database backend ") + name + " not supported.");
3255 }
3256
3257 void ServerMap::beginSave()
3258 {
3259         dbase->beginSave();
3260 }
3261
3262 void ServerMap::endSave()
3263 {
3264         dbase->endSave();
3265 }
3266
3267 bool ServerMap::saveBlock(MapBlock *block)
3268 {
3269         return saveBlock(block, dbase);
3270 }
3271
3272 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3273 {
3274         v3s16 p3d = block->getPos();
3275
3276         // Dummy blocks are not written
3277         if (block->isDummy()) {
3278                 warningstream << "saveBlock: Not writing dummy block "
3279                         << PP(p3d) << std::endl;
3280                 return true;
3281         }
3282
3283         // Format used for writing
3284         u8 version = SER_FMT_VER_HIGHEST_WRITE;
3285
3286         /*
3287                 [0] u8 serialization version
3288                 [1] data
3289         */
3290         std::ostringstream o(std::ios_base::binary);
3291         o.write((char*) &version, 1);
3292         block->serialize(o, version, true);
3293
3294         std::string data = o.str();
3295         bool ret = db->saveBlock(p3d, data);
3296         if (ret) {
3297                 // We just wrote it to the disk so clear modified flag
3298                 block->resetModified();
3299         }
3300         return ret;
3301 }
3302
3303 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3304                 MapSector *sector, bool save_after_load)
3305 {
3306         DSTACK(FUNCTION_NAME);
3307
3308         std::string fullpath = sectordir + DIR_DELIM + blockfile;
3309         try {
3310
3311                 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3312                 if(is.good() == false)
3313                         throw FileNotGoodException("Cannot open block file");
3314
3315                 v3s16 p3d = getBlockPos(sectordir, blockfile);
3316                 v2s16 p2d(p3d.X, p3d.Z);
3317
3318                 assert(sector->getPos() == p2d);
3319
3320                 u8 version = SER_FMT_VER_INVALID;
3321                 is.read((char*)&version, 1);
3322
3323                 if(is.fail())
3324                         throw SerializationError("ServerMap::loadBlock(): Failed"
3325                                         " to read MapBlock version");
3326
3327                 /*u32 block_size = MapBlock::serializedLength(version);
3328                 SharedBuffer<u8> data(block_size);
3329                 is.read((char*)*data, block_size);*/
3330
3331                 // This will always return a sector because we're the server
3332                 //MapSector *sector = emergeSector(p2d);
3333
3334                 MapBlock *block = NULL;
3335                 bool created_new = false;
3336                 block = sector->getBlockNoCreateNoEx(p3d.Y);
3337                 if(block == NULL)
3338                 {
3339                         block = sector->createBlankBlockNoInsert(p3d.Y);
3340                         created_new = true;
3341                 }
3342
3343                 // Read basic data
3344                 block->deSerialize(is, version, true);
3345
3346                 // If it's a new block, insert it to the map
3347                 if(created_new)
3348                         sector->insertBlock(block);
3349
3350                 /*
3351                         Save blocks loaded in old format in new format
3352                 */
3353
3354                 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3355                 {
3356                         saveBlock(block);
3357
3358                         // Should be in database now, so delete the old file
3359                         fs::RecursiveDelete(fullpath);
3360                 }
3361
3362                 // We just loaded it from the disk, so it's up-to-date.
3363                 block->resetModified();
3364
3365         }
3366         catch(SerializationError &e)
3367         {
3368                 warningstream<<"Invalid block data on disk "
3369                                 <<"fullpath="<<fullpath
3370                                 <<" (SerializationError). "
3371                                 <<"what()="<<e.what()
3372                                 <<std::endl;
3373                                 // Ignoring. A new one will be generated.
3374                 abort();
3375
3376                 // TODO: Backup file; name is in fullpath.
3377         }
3378 }
3379
3380 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3381 {
3382         DSTACK(FUNCTION_NAME);
3383
3384         try {
3385                 std::istringstream is(*blob, std::ios_base::binary);
3386
3387                 u8 version = SER_FMT_VER_INVALID;
3388                 is.read((char*)&version, 1);
3389
3390                 if(is.fail())
3391                         throw SerializationError("ServerMap::loadBlock(): Failed"
3392                                         " to read MapBlock version");
3393
3394                 /*u32 block_size = MapBlock::serializedLength(version);
3395                 SharedBuffer<u8> data(block_size);
3396                 is.read((char*)*data, block_size);*/
3397
3398                 // This will always return a sector because we're the server
3399                 //MapSector *sector = emergeSector(p2d);
3400
3401                 MapBlock *block = NULL;
3402                 bool created_new = false;
3403                 block = sector->getBlockNoCreateNoEx(p3d.Y);
3404                 if(block == NULL)
3405                 {
3406                         block = sector->createBlankBlockNoInsert(p3d.Y);
3407                         created_new = true;
3408                 }
3409
3410                 // Read basic data
3411                 block->deSerialize(is, version, true);
3412
3413                 // If it's a new block, insert it to the map
3414                 if(created_new)
3415                         sector->insertBlock(block);
3416
3417                 /*
3418                         Save blocks loaded in old format in new format
3419                 */
3420
3421                 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3422                 // Only save if asked to; no need to update version
3423                 if(save_after_load)
3424                         saveBlock(block);
3425
3426                 // We just loaded it from, so it's up-to-date.
3427                 block->resetModified();
3428
3429         }
3430         catch(SerializationError &e)
3431         {
3432                 errorstream<<"Invalid block data in database"
3433                                 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3434                                 <<" (SerializationError): "<<e.what()<<std::endl;
3435
3436                 // TODO: Block should be marked as invalid in memory so that it is
3437                 // not touched but the game can run
3438
3439                 if(g_settings->getBool("ignore_world_load_errors")){
3440                         errorstream<<"Ignoring block load error. Duck and cover! "
3441                                         <<"(ignore_world_load_errors)"<<std::endl;
3442                 } else {
3443                         throw SerializationError("Invalid block data in database");
3444                 }
3445         }
3446 }
3447
3448 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3449 {
3450         DSTACK(FUNCTION_NAME);
3451
3452         v2s16 p2d(blockpos.X, blockpos.Z);
3453
3454         std::string ret;
3455         dbase->loadBlock(blockpos, &ret);
3456         if (ret != "") {
3457                 loadBlock(&ret, blockpos, createSector(p2d), false);
3458                 return getBlockNoCreateNoEx(blockpos);
3459         }
3460         // Not found in database, try the files
3461
3462         // The directory layout we're going to load from.
3463         //  1 - original sectors/xxxxzzzz/
3464         //  2 - new sectors2/xxx/zzz/
3465         //  If we load from anything but the latest structure, we will
3466         //  immediately save to the new one, and remove the old.
3467         int loadlayout = 1;
3468         std::string sectordir1 = getSectorDir(p2d, 1);
3469         std::string sectordir;
3470         if(fs::PathExists(sectordir1))
3471         {
3472                 sectordir = sectordir1;
3473         }
3474         else
3475         {
3476                 loadlayout = 2;
3477                 sectordir = getSectorDir(p2d, 2);
3478         }
3479
3480         /*
3481                 Make sure sector is loaded
3482         */
3483
3484         MapSector *sector = getSectorNoGenerateNoEx(p2d);
3485         if(sector == NULL)
3486         {
3487                 try{
3488                         sector = loadSectorMeta(sectordir, loadlayout != 2);
3489                 }
3490                 catch(InvalidFilenameException &e)
3491                 {
3492                         return NULL;
3493                 }
3494                 catch(FileNotGoodException &e)
3495                 {
3496                         return NULL;
3497                 }
3498                 catch(std::exception &e)
3499                 {
3500                         return NULL;
3501                 }
3502         }
3503
3504         /*
3505                 Make sure file exists
3506         */
3507
3508         std::string blockfilename = getBlockFilename(blockpos);
3509         if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3510                 return NULL;
3511
3512         /*
3513                 Load block and save it to the database
3514         */
3515         loadBlock(sectordir, blockfilename, sector, true);
3516         return getBlockNoCreateNoEx(blockpos);
3517 }
3518
3519 bool ServerMap::deleteBlock(v3s16 blockpos)
3520 {
3521         if (!dbase->deleteBlock(blockpos))
3522                 return false;
3523
3524         MapBlock *block = getBlockNoCreateNoEx(blockpos);
3525         if (block) {
3526                 v2s16 p2d(blockpos.X, blockpos.Z);
3527                 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3528                 if (!sector)
3529                         return false;
3530                 sector->deleteBlock(block);
3531         }
3532
3533         return true;
3534 }
3535
3536 void ServerMap::PrintInfo(std::ostream &out)
3537 {
3538         out<<"ServerMap: ";
3539 }
3540
3541 MMVManip::MMVManip(Map *map):
3542                 VoxelManipulator(),
3543                 m_is_dirty(false),
3544                 m_create_area(false),
3545                 m_map(map)
3546 {
3547 }
3548
3549 MMVManip::~MMVManip()
3550 {
3551 }
3552
3553 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3554         bool load_if_inexistent)
3555 {
3556         TimeTaker timer1("initialEmerge", &emerge_time);
3557
3558         // Units of these are MapBlocks
3559         v3s16 p_min = blockpos_min;
3560         v3s16 p_max = blockpos_max;
3561
3562         VoxelArea block_area_nodes
3563                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3564
3565         u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3566         if(size_MB >= 1)
3567         {
3568                 infostream<<"initialEmerge: area: ";
3569                 block_area_nodes.print(infostream);
3570                 infostream<<" ("<<size_MB<<"MB)";
3571                 infostream<<std::endl;
3572         }
3573
3574         addArea(block_area_nodes);
3575
3576         for(s32 z=p_min.Z; z<=p_max.Z; z++)
3577         for(s32 y=p_min.Y; y<=p_max.Y; y++)
3578         for(s32 x=p_min.X; x<=p_max.X; x++)
3579         {
3580                 u8 flags = 0;
3581                 MapBlock *block;
3582                 v3s16 p(x,y,z);
3583                 std::map<v3s16, u8>::iterator n;
3584                 n = m_loaded_blocks.find(p);
3585                 if(n != m_loaded_blocks.end())
3586                         continue;
3587
3588                 bool block_data_inexistent = false;
3589                 try
3590                 {
3591                         TimeTaker timer1("emerge load", &emerge_load_time);
3592
3593                         block = m_map->getBlockNoCreate(p);
3594                         if(block->isDummy())
3595                                 block_data_inexistent = true;
3596                         else
3597                                 block->copyTo(*this);
3598                 }
3599                 catch(InvalidPositionException &e)
3600                 {
3601                         block_data_inexistent = true;
3602                 }
3603
3604                 if(block_data_inexistent)
3605                 {
3606
3607                         if (load_if_inexistent) {
3608                                 ServerMap *svrmap = (ServerMap *)m_map;
3609                                 block = svrmap->emergeBlock(p, false);
3610                                 if (block == NULL)
3611                                         block = svrmap->createBlock(p);
3612                                 block->copyTo(*this);
3613                         } else {
3614                                 flags |= VMANIP_BLOCK_DATA_INEXIST;
3615
3616                                 /*
3617                                         Mark area inexistent
3618                                 */
3619                                 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3620                                 // Fill with VOXELFLAG_NO_DATA
3621                                 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3622                                 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3623                                 {
3624                                         s32 i = m_area.index(a.MinEdge.X,y,z);
3625                                         memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3626                                 }
3627                         }
3628                 }
3629                 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3630                 {
3631                         // Mark that block was loaded as blank
3632                         flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3633                 }*/
3634
3635                 m_loaded_blocks[p] = flags;
3636         }
3637
3638         m_is_dirty = false;
3639 }
3640
3641 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3642         bool overwrite_generated)
3643 {
3644         if(m_area.getExtent() == v3s16(0,0,0))
3645                 return;
3646
3647         /*
3648                 Copy data of all blocks
3649         */
3650         for(std::map<v3s16, u8>::iterator
3651                         i = m_loaded_blocks.begin();
3652                         i != m_loaded_blocks.end(); ++i)
3653         {
3654                 v3s16 p = i->first;
3655                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3656                 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3657                 if ((existed == false) || (block == NULL) ||
3658                         (overwrite_generated == false && block->isGenerated() == true))
3659                         continue;
3660
3661                 block->copyFrom(*this);
3662
3663                 if(modified_blocks)
3664                         (*modified_blocks)[p] = block;
3665         }
3666 }
3667
3668 //END