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