]> git.lizzy.rs Git - dragonfireclient.git/blob - src/map.cpp
20dc44139c3b9ed409e38ab1c191e309cbcc70ca
[dragonfireclient.git] / src / map.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "map.h"
21 #include "mapsector.h"
22 #include "mapblock.h"
23 #include "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.pop_front();
1667
1668                 MapNode n0 = getNodeNoEx(p0);
1669
1670                 /*
1671                         Collect information about current node
1672                  */
1673                 s8 liquid_level = -1;
1674                 content_t liquid_kind = CONTENT_IGNORE;
1675                 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1676                 switch (liquid_type) {
1677                         case LIQUID_SOURCE:
1678                                 liquid_level = LIQUID_LEVEL_SOURCE;
1679                                 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1680                                 break;
1681                         case LIQUID_FLOWING:
1682                                 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1683                                 liquid_kind = n0.getContent();
1684                                 break;
1685                         case LIQUID_NONE:
1686                                 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1687                                 // continue with the next node.
1688                                 if (n0.getContent() != CONTENT_AIR)
1689                                         continue;
1690                                 liquid_kind = CONTENT_AIR;
1691                                 break;
1692                 }
1693
1694                 /*
1695                         Collect information about the environment
1696                  */
1697                 const v3s16 *dirs = g_6dirs;
1698                 NodeNeighbor sources[6]; // surrounding sources
1699                 int num_sources = 0;
1700                 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1701                 int num_flows = 0;
1702                 NodeNeighbor airs[6]; // surrounding air
1703                 int num_airs = 0;
1704                 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1705                 int num_neutrals = 0;
1706                 bool flowing_down = false;
1707                 for (u16 i = 0; i < 6; i++) {
1708                         NeighborType nt = NEIGHBOR_SAME_LEVEL;
1709                         switch (i) {
1710                                 case 1:
1711                                         nt = NEIGHBOR_UPPER;
1712                                         break;
1713                                 case 4:
1714                                         nt = NEIGHBOR_LOWER;
1715                                         break;
1716                         }
1717                         v3s16 npos = p0 + dirs[i];
1718                         NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1719                         switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1720                                 case LIQUID_NONE:
1721                                         if (nb.n.getContent() == CONTENT_AIR) {
1722                                                 airs[num_airs++] = nb;
1723                                                 // if the current node is a water source the neighbor
1724                                                 // should be enqueded for transformation regardless of whether the
1725                                                 // current node changes or not.
1726                                                 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1727                                                         m_transforming_liquid.push_back(npos);
1728                                                 // if the current node happens to be a flowing node, it will start to flow down here.
1729                                                 if (nb.t == NEIGHBOR_LOWER) {
1730                                                         flowing_down = true;
1731                                                 }
1732                                         } else {
1733                                                 neutrals[num_neutrals++] = nb;
1734                                         }
1735                                         break;
1736                                 case LIQUID_SOURCE:
1737                                         // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1738                                         if (liquid_kind == CONTENT_AIR)
1739                                                 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1740                                         if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1741                                                 neutrals[num_neutrals++] = nb;
1742                                         } else {
1743                                                 // Do not count bottom source, it will screw things up
1744                                                 if(dirs[i].Y != -1)
1745                                                         sources[num_sources++] = nb;
1746                                         }
1747                                         break;
1748                                 case LIQUID_FLOWING:
1749                                         // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1750                                         if (liquid_kind == CONTENT_AIR)
1751                                                 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1752                                         if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1753                                                 neutrals[num_neutrals++] = nb;
1754                                         } else {
1755                                                 flows[num_flows++] = nb;
1756                                                 if (nb.t == NEIGHBOR_LOWER)
1757                                                         flowing_down = true;
1758                                         }
1759                                         break;
1760                         }
1761                 }
1762
1763                 /*
1764                         decide on the type (and possibly level) of the current node
1765                  */
1766                 content_t new_node_content;
1767                 s8 new_node_level = -1;
1768                 s8 max_node_level = -1;
1769
1770                 u8 range = nodemgr->get(liquid_kind).liquid_range;
1771                 if (range > LIQUID_LEVEL_MAX+1)
1772                         range = LIQUID_LEVEL_MAX+1;
1773
1774                 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1775                         // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1776                         // or the flowing alternative of the first of the surrounding sources (if it's air), so
1777                         // it's perfectly safe to use liquid_kind here to determine the new node content.
1778                         new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1779                 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1780                         // liquid_kind is set properly, see above
1781                         new_node_content = liquid_kind;
1782                         max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1783                         if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1784                                 new_node_content = CONTENT_AIR;
1785                 } else {
1786                         // no surrounding sources, so get the maximum level that can flow into this node
1787                         for (u16 i = 0; i < num_flows; i++) {
1788                                 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1789                                 switch (flows[i].t) {
1790                                         case NEIGHBOR_UPPER:
1791                                                 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1792                                                         max_node_level = LIQUID_LEVEL_MAX;
1793                                                         if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1794                                                                 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1795                                                 } else if (nb_liquid_level > max_node_level)
1796                                                         max_node_level = nb_liquid_level;
1797                                                 break;
1798                                         case NEIGHBOR_LOWER:
1799                                                 break;
1800                                         case NEIGHBOR_SAME_LEVEL:
1801                                                 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1802                                                         nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1803                                                         max_node_level = nb_liquid_level - 1;
1804                                                 }
1805                                                 break;
1806                                 }
1807                         }
1808
1809                         u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1810                         if (viscosity > 1 && max_node_level != liquid_level) {
1811                                 // amount to gain, limited by viscosity
1812                                 // must be at least 1 in absolute value
1813                                 s8 level_inc = max_node_level - liquid_level;
1814                                 if (level_inc < -viscosity || level_inc > viscosity)
1815                                         new_node_level = liquid_level + level_inc/viscosity;
1816                                 else if (level_inc < 0)
1817                                         new_node_level = liquid_level - 1;
1818                                 else if (level_inc > 0)
1819                                         new_node_level = liquid_level + 1;
1820                                 if (new_node_level != max_node_level)
1821                                         must_reflow.push_back(p0);
1822                         } else
1823                                 new_node_level = max_node_level;
1824
1825                         if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1826                                 new_node_content = liquid_kind;
1827                         else
1828                                 new_node_content = CONTENT_AIR;
1829
1830                 }
1831
1832                 /*
1833                         check if anything has changed. if not, just continue with the next node.
1834                  */
1835                 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1836                                                                                  ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1837                                                                                  ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1838                                                                                  == flowing_down)))
1839                         continue;
1840
1841
1842                 /*
1843                         update the current node
1844                  */
1845                 MapNode n00 = n0;
1846                 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1847                 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1848                         // set level to last 3 bits, flowing down bit to 4th bit
1849                         n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1850                 } else {
1851                         // set the liquid level and flow bit to 0
1852                         n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1853                 }
1854                 n0.setContent(new_node_content);
1855
1856                 // Find out whether there is a suspect for this action
1857                 std::string suspect;
1858                 if(m_gamedef->rollback()){
1859                         suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1860                 }
1861
1862                 if(!suspect.empty()){
1863                         // Blame suspect
1864                         RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1865                         // Get old node for rollback
1866                         RollbackNode rollback_oldnode(this, p0, m_gamedef);
1867                         // Set node
1868                         setNode(p0, n0);
1869                         // Report
1870                         RollbackNode rollback_newnode(this, p0, m_gamedef);
1871                         RollbackAction action;
1872                         action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1873                         m_gamedef->rollback()->reportAction(action);
1874                 } else {
1875                         // Set node
1876                         setNode(p0, n0);
1877                 }
1878
1879                 v3s16 blockpos = getNodeBlockPos(p0);
1880                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1881                 if(block != NULL) {
1882                         modified_blocks[blockpos] =  block;
1883                         // If new or old node emits light, MapBlock requires lighting update
1884                         if(nodemgr->get(n0).light_source != 0 ||
1885                                         nodemgr->get(n00).light_source != 0)
1886                                 lighting_modified_blocks[block->getPos()] = block;
1887                 }
1888
1889                 /*
1890                         enqueue neighbors for update if neccessary
1891                  */
1892                 switch (nodemgr->get(n0.getContent()).liquid_type) {
1893                         case LIQUID_SOURCE:
1894                         case LIQUID_FLOWING:
1895                                 // make sure source flows into all neighboring nodes
1896                                 for (u16 i = 0; i < num_flows; i++)
1897                                         if (flows[i].t != NEIGHBOR_UPPER)
1898                                                 m_transforming_liquid.push_back(flows[i].p);
1899                                 for (u16 i = 0; i < num_airs; i++)
1900                                         if (airs[i].t != NEIGHBOR_UPPER)
1901                                                 m_transforming_liquid.push_back(airs[i].p);
1902                                 break;
1903                         case LIQUID_NONE:
1904                                 // this flow has turned to air; neighboring flows might need to do the same
1905                                 for (u16 i = 0; i < num_flows; i++)
1906                                         m_transforming_liquid.push_back(flows[i].p);
1907                                 break;
1908                 }
1909         }
1910         //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1911         while (must_reflow.size() > 0)
1912                 m_transforming_liquid.push_back(must_reflow.pop_front());
1913         updateLighting(lighting_modified_blocks, modified_blocks);
1914
1915
1916         /* ----------------------------------------------------------------------
1917          * Manage the queue so that it does not grow indefinately
1918          */
1919         u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1920
1921         if (time_until_purge == 0)
1922                 return; // Feature disabled
1923
1924         time_until_purge *= 1000;       // seconds -> milliseconds
1925
1926         u32 curr_time = getTime(PRECISION_MILLI);
1927         u32 prev_unprocessed = m_unprocessed_count;
1928         m_unprocessed_count = m_transforming_liquid.size();
1929
1930         // if unprocessed block count is decreasing or stable
1931         if (m_unprocessed_count <= prev_unprocessed) {
1932                 m_queue_size_timer_started = false;
1933         } else {
1934                 if (!m_queue_size_timer_started)
1935                         m_inc_trending_up_start_time = curr_time;
1936                 m_queue_size_timer_started = true;
1937         }
1938
1939         // Account for curr_time overflowing
1940         if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1941                 m_queue_size_timer_started = false;
1942
1943         /* If the queue has been growing for more than liquid_queue_purge_time seconds
1944          * and the number of unprocessed blocks is still > liquid_loop_max then we
1945          * cannot keep up; dump the oldest blocks from the queue so that the queue
1946          * has liquid_loop_max items in it
1947          */
1948         if (m_queue_size_timer_started
1949                         && curr_time - m_inc_trending_up_start_time > time_until_purge
1950                         && m_unprocessed_count > liquid_loop_max) {
1951
1952                 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1953
1954                 infostream << "transformLiquids(): DUMPING " << dump_qty
1955                            << " blocks from the queue" << std::endl;
1956
1957                 while (dump_qty--)
1958                         m_transforming_liquid.pop_front();
1959
1960                 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1961                 m_unprocessed_count = m_transforming_liquid.size();
1962         }
1963 }
1964
1965 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1966 {
1967         v3s16 blockpos = getNodeBlockPos(p);
1968         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1969         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1970         if(!block){
1971                 infostream<<"Map::getNodeMetadata(): Need to emerge "
1972                                 <<PP(blockpos)<<std::endl;
1973                 block = emergeBlock(blockpos, false);
1974         }
1975         if(!block){
1976                 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1977                                 <<std::endl;
1978                 return NULL;
1979         }
1980         NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1981         return meta;
1982 }
1983
1984 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1985 {
1986         v3s16 blockpos = getNodeBlockPos(p);
1987         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1988         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1989         if(!block){
1990                 infostream<<"Map::setNodeMetadata(): Need to emerge "
1991                                 <<PP(blockpos)<<std::endl;
1992                 block = emergeBlock(blockpos, false);
1993         }
1994         if(!block){
1995                 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1996                                 <<std::endl;
1997                 return false;
1998         }
1999         block->m_node_metadata.set(p_rel, meta);
2000         return true;
2001 }
2002
2003 void Map::removeNodeMetadata(v3s16 p)
2004 {
2005         v3s16 blockpos = getNodeBlockPos(p);
2006         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2007         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2008         if(block == NULL)
2009         {
2010                 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2011                                 <<std::endl;
2012                 return;
2013         }
2014         block->m_node_metadata.remove(p_rel);
2015 }
2016
2017 NodeTimer Map::getNodeTimer(v3s16 p)
2018 {
2019         v3s16 blockpos = getNodeBlockPos(p);
2020         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2021         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2022         if(!block){
2023                 infostream<<"Map::getNodeTimer(): Need to emerge "
2024                                 <<PP(blockpos)<<std::endl;
2025                 block = emergeBlock(blockpos, false);
2026         }
2027         if(!block){
2028                 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2029                                 <<std::endl;
2030                 return NodeTimer();
2031         }
2032         NodeTimer t = block->m_node_timers.get(p_rel);
2033         return t;
2034 }
2035
2036 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2037 {
2038         v3s16 blockpos = getNodeBlockPos(p);
2039         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2040         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2041         if(!block){
2042                 infostream<<"Map::setNodeTimer(): Need to emerge "
2043                                 <<PP(blockpos)<<std::endl;
2044                 block = emergeBlock(blockpos, false);
2045         }
2046         if(!block){
2047                 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2048                                 <<std::endl;
2049                 return;
2050         }
2051         block->m_node_timers.set(p_rel, t);
2052 }
2053
2054 void Map::removeNodeTimer(v3s16 p)
2055 {
2056         v3s16 blockpos = getNodeBlockPos(p);
2057         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2058         MapBlock *block = getBlockNoCreateNoEx(blockpos);
2059         if(block == NULL)
2060         {
2061                 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2062                                 <<std::endl;
2063                 return;
2064         }
2065         block->m_node_timers.remove(p_rel);
2066 }
2067
2068 /*
2069         ServerMap
2070 */
2071 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2072         Map(dout_server, gamedef),
2073         m_emerge(emerge),
2074         m_map_metadata_changed(true)
2075 {
2076         verbosestream<<__FUNCTION_NAME<<std::endl;
2077
2078         /*
2079                 Try to load map; if not found, create a new one.
2080         */
2081
2082         // Determine which database backend to use
2083         std::string conf_path = savedir + DIR_DELIM + "world.mt";
2084         Settings conf;
2085         bool succeeded = conf.readConfigFile(conf_path.c_str());
2086         if (!succeeded || !conf.exists("backend")) {
2087                 // fall back to sqlite3
2088                 dbase = new Database_SQLite3(this, savedir);
2089                 conf.set("backend", "sqlite3");
2090         } else {
2091                 std::string backend = conf.get("backend");
2092                 if (backend == "dummy")
2093                         dbase = new Database_Dummy(this);
2094                 else if (backend == "sqlite3")
2095                         dbase = new Database_SQLite3(this, savedir);
2096                 #if USE_LEVELDB
2097                 else if (backend == "leveldb")
2098                         dbase = new Database_LevelDB(this, savedir);
2099                 #endif
2100                 #if USE_REDIS
2101                 else if (backend == "redis")
2102                         dbase = new Database_Redis(this, savedir);
2103                 #endif
2104                 else
2105                         throw BaseException("Unknown map backend");
2106         }
2107
2108         m_savedir = savedir;
2109         m_map_saving_enabled = false;
2110
2111         try
2112         {
2113                 // If directory exists, check contents and load if possible
2114                 if(fs::PathExists(m_savedir))
2115                 {
2116                         // If directory is empty, it is safe to save into it.
2117                         if(fs::GetDirListing(m_savedir).size() == 0)
2118                         {
2119                                 infostream<<"ServerMap: Empty save directory is valid."
2120                                                 <<std::endl;
2121                                 m_map_saving_enabled = true;
2122                         }
2123                         else
2124                         {
2125                                 try{
2126                                         // Load map metadata (seed, chunksize)
2127                                         loadMapMeta();
2128                                 }
2129                                 catch(SettingNotFoundException &e){
2130                                         infostream<<"ServerMap:  Some metadata not found."
2131                                                           <<" Using default settings."<<std::endl;
2132                                 }
2133                                 catch(FileNotGoodException &e){
2134                                         infostream<<"WARNING: Could not load map metadata"
2135                                                         //<<" Disabling chunk-based generator."
2136                                                         <<std::endl;
2137                                         //m_chunksize = 0;
2138                                 }
2139
2140                                 infostream<<"ServerMap: Successfully loaded map "
2141                                                 <<"metadata from "<<savedir
2142                                                 <<", assuming valid save directory."
2143                                                 <<" seed="<< m_emerge->params.seed <<"."
2144                                                 <<std::endl;
2145
2146                                 m_map_saving_enabled = true;
2147                                 // Map loaded, not creating new one
2148                                 return;
2149                         }
2150                 }
2151                 // If directory doesn't exist, it is safe to save to it
2152                 else{
2153                         m_map_saving_enabled = true;
2154                 }
2155         }
2156         catch(std::exception &e)
2157         {
2158                 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2159                                 <<", exception: "<<e.what()<<std::endl;
2160                 infostream<<"Please remove the map or fix it."<<std::endl;
2161                 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2162         }
2163
2164         infostream<<"Initializing new map."<<std::endl;
2165
2166         // Create zero sector
2167         emergeSector(v2s16(0,0));
2168
2169         // Initially write whole map
2170         save(MOD_STATE_CLEAN);
2171 }
2172
2173 ServerMap::~ServerMap()
2174 {
2175         verbosestream<<__FUNCTION_NAME<<std::endl;
2176
2177         try
2178         {
2179                 if(m_map_saving_enabled)
2180                 {
2181                         // Save only changed parts
2182                         save(MOD_STATE_WRITE_AT_UNLOAD);
2183                         infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2184                 }
2185                 else
2186                 {
2187                         infostream<<"ServerMap: Map not saved"<<std::endl;
2188                 }
2189         }
2190         catch(std::exception &e)
2191         {
2192                 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2193                                 <<", exception: "<<e.what()<<std::endl;
2194         }
2195
2196         /*
2197                 Close database if it was opened
2198         */
2199         delete dbase;
2200
2201 #if 0
2202         /*
2203                 Free all MapChunks
2204         */
2205         core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2206         for(; i.atEnd() == false; i++)
2207         {
2208                 MapChunk *chunk = i.getNode()->getValue();
2209                 delete chunk;
2210         }
2211 #endif
2212 }
2213
2214 u64 ServerMap::getSeed()
2215 {
2216         return m_emerge->params.seed;
2217 }
2218
2219 s16 ServerMap::getWaterLevel()
2220 {
2221         return m_emerge->params.water_level;
2222 }
2223
2224 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2225 {
2226         bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2227         EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2228
2229         s16 chunksize = m_emerge->params.chunksize;
2230         s16 coffset = -chunksize / 2;
2231         v3s16 chunk_offset(coffset, coffset, coffset);
2232         v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2233         v3s16 blockpos_min = blockpos_div * chunksize;
2234         v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2235         blockpos_min += chunk_offset;
2236         blockpos_max += chunk_offset;
2237
2238         v3s16 extra_borders(1,1,1);
2239
2240         // Do nothing if not inside limits (+-1 because of neighbors)
2241         if(blockpos_over_limit(blockpos_min - extra_borders) ||
2242                 blockpos_over_limit(blockpos_max + extra_borders))
2243                 return false;
2244
2245         data->seed = m_emerge->params.seed;
2246         data->blockpos_min = blockpos_min;
2247         data->blockpos_max = blockpos_max;
2248         data->blockpos_requested = blockpos;
2249         data->nodedef = m_gamedef->ndef();
2250
2251         /*
2252                 Create the whole area of this and the neighboring blocks
2253         */
2254         {
2255                 //TimeTaker timer("initBlockMake() create area");
2256
2257                 for(s16 x=blockpos_min.X-extra_borders.X;
2258                                 x<=blockpos_max.X+extra_borders.X; x++)
2259                 for(s16 z=blockpos_min.Z-extra_borders.Z;
2260                                 z<=blockpos_max.Z+extra_borders.Z; z++)
2261                 {
2262                         v2s16 sectorpos(x, z);
2263                         // Sector metadata is loaded from disk if not already loaded.
2264                         ServerMapSector *sector = createSector(sectorpos);
2265                         assert(sector);
2266                         (void) sector;
2267
2268                         for(s16 y=blockpos_min.Y-extra_borders.Y;
2269                                         y<=blockpos_max.Y+extra_borders.Y; y++)
2270                         {
2271                                 v3s16 p(x,y,z);
2272                                 //MapBlock *block = createBlock(p);
2273                                 // 1) get from memory, 2) load from disk
2274                                 MapBlock *block = emergeBlock(p, false);
2275                                 // 3) create a blank one
2276                                 if(block == NULL)
2277                                 {
2278                                         block = createBlock(p);
2279
2280                                         /*
2281                                                 Block gets sunlight if this is true.
2282
2283                                                 Refer to the map generator heuristics.
2284                                         */
2285                                         bool ug = m_emerge->isBlockUnderground(p);
2286                                         block->setIsUnderground(ug);
2287                                 }
2288
2289                                 // Lighting will not be valid after make_chunk is called
2290                                 block->setLightingExpired(true);
2291                                 // Lighting will be calculated
2292                                 //block->setLightingExpired(false);
2293                         }
2294                 }
2295         }
2296
2297         /*
2298                 Now we have a big empty area.
2299
2300                 Make a ManualMapVoxelManipulator that contains this and the
2301                 neighboring blocks
2302         */
2303
2304         // The area that contains this block and it's neighbors
2305         v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2306         v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2307
2308         data->vmanip = new MMVManip(this);
2309         //data->vmanip->setMap(this);
2310
2311         // Add the area
2312         {
2313                 //TimeTaker timer("initBlockMake() initialEmerge");
2314                 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2315         }
2316
2317         // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2318 /*      for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2319                 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2320                         for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2321                                 core::map<v3s16, u8>::Node *n;
2322                                 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2323                                 if (n == NULL)
2324                                         continue;
2325                                 u8 flags = n->getValue();
2326                                 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2327                                 n->setValue(flags);
2328                         }
2329                 }
2330         }*/
2331
2332         // Data is ready now.
2333         return true;
2334 }
2335
2336 void ServerMap::finishBlockMake(BlockMakeData *data,
2337                 std::map<v3s16, MapBlock*> &changed_blocks)
2338 {
2339         v3s16 blockpos_min = data->blockpos_min;
2340         v3s16 blockpos_max = data->blockpos_max;
2341         v3s16 blockpos_requested = data->blockpos_requested;
2342         /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2343                         <<blockpos_requested.Y<<","
2344                         <<blockpos_requested.Z<<")"<<std::endl;*/
2345
2346         v3s16 extra_borders(1,1,1);
2347
2348         bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2349
2350         /*infostream<<"Resulting vmanip:"<<std::endl;
2351         data->vmanip.print(infostream);*/
2352
2353         // Make sure affected blocks are loaded
2354         for(s16 x=blockpos_min.X-extra_borders.X;
2355                         x<=blockpos_max.X+extra_borders.X; x++)
2356         for(s16 z=blockpos_min.Z-extra_borders.Z;
2357                         z<=blockpos_max.Z+extra_borders.Z; z++)
2358         for(s16 y=blockpos_min.Y-extra_borders.Y;
2359                         y<=blockpos_max.Y+extra_borders.Y; y++)
2360         {
2361                 v3s16 p(x, y, z);
2362                 // Load from disk if not already in memory
2363                 emergeBlock(p, false);
2364         }
2365
2366         /*
2367                 Blit generated stuff to map
2368                 NOTE: blitBackAll adds nearly everything to changed_blocks
2369         */
2370         {
2371                 // 70ms @cs=8
2372                 //TimeTaker timer("finishBlockMake() blitBackAll");
2373                 data->vmanip->blitBackAll(&changed_blocks);
2374         }
2375
2376         EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2377
2378         /*
2379                 Copy transforming liquid information
2380         */
2381         while(data->transforming_liquid.size() > 0)
2382         {
2383                 v3s16 p = data->transforming_liquid.pop_front();
2384                 m_transforming_liquid.push_back(p);
2385         }
2386
2387         /*
2388                 Do stuff in central blocks
2389         */
2390
2391         /*
2392                 Update lighting
2393         */
2394         {
2395 #if 0
2396                 TimeTaker t("finishBlockMake lighting update");
2397
2398                 core::map<v3s16, MapBlock*> lighting_update_blocks;
2399
2400                 // Center blocks
2401                 for(s16 x=blockpos_min.X-extra_borders.X;
2402                                 x<=blockpos_max.X+extra_borders.X; x++)
2403                 for(s16 z=blockpos_min.Z-extra_borders.Z;
2404                                 z<=blockpos_max.Z+extra_borders.Z; z++)
2405                 for(s16 y=blockpos_min.Y-extra_borders.Y;
2406                                 y<=blockpos_max.Y+extra_borders.Y; y++)
2407                 {
2408                         v3s16 p(x, y, z);
2409                         MapBlock *block = getBlockNoCreateNoEx(p);
2410                         assert(block);
2411                         lighting_update_blocks.insert(block->getPos(), block);
2412                 }
2413
2414                 updateLighting(lighting_update_blocks, changed_blocks);
2415 #endif
2416
2417                 /*
2418                         Set lighting to non-expired state in all of them.
2419                         This is cheating, but it is not fast enough if all of them
2420                         would actually be updated.
2421                 */
2422                 for(s16 x=blockpos_min.X-extra_borders.X;
2423                                 x<=blockpos_max.X+extra_borders.X; x++)
2424                 for(s16 z=blockpos_min.Z-extra_borders.Z;
2425                                 z<=blockpos_max.Z+extra_borders.Z; z++)
2426                 for(s16 y=blockpos_min.Y-extra_borders.Y;
2427                                 y<=blockpos_max.Y+extra_borders.Y; y++)
2428                 {
2429                         v3s16 p(x, y, z);
2430                         MapBlock * block = getBlockNoCreateNoEx(p);
2431                         if (block != NULL)
2432                                 block->setLightingExpired(false);
2433                 }
2434
2435 #if 0
2436                 if(enable_mapgen_debug_info == false)
2437                         t.stop(true); // Hide output
2438 #endif
2439         }
2440
2441         /*
2442                 Go through changed blocks
2443         */
2444         for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2445                         i != changed_blocks.end(); ++i)
2446         {
2447                 MapBlock *block = i->second;
2448                 if (!block)
2449                         continue;
2450                 /*
2451                         Update day/night difference cache of the MapBlocks
2452                 */
2453                 block->expireDayNightDiff();
2454                 /*
2455                         Set block as modified
2456                 */
2457                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2458                                 "finishBlockMake expireDayNightDiff");
2459         }
2460
2461         /*
2462                 Set central blocks as generated
2463         */
2464         for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2465         for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2466         for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2467         {
2468                 v3s16 p(x, y, z);
2469                 MapBlock *block = getBlockNoCreateNoEx(p);
2470                 if (!block)
2471                         continue;
2472                 block->setGenerated(true);
2473         }
2474
2475         /*
2476                 Save changed parts of map
2477                 NOTE: Will be saved later.
2478         */
2479         //save(MOD_STATE_WRITE_AT_UNLOAD);
2480
2481         /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2482                         <<","<<blockpos_requested.Y<<","
2483                         <<blockpos_requested.Z<<")"<<std::endl;*/
2484
2485
2486 #if 0
2487         if(enable_mapgen_debug_info)
2488         {
2489                 /*
2490                         Analyze resulting blocks
2491                 */
2492                 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2493                 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2494                 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2495                 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2496                 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2497                 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2498                 {
2499                         v3s16 p = v3s16(x,y,z);
2500                         MapBlock *block = getBlockNoCreateNoEx(p);
2501                         char spos[20];
2502                         snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2503                         infostream<<"Generated "<<spos<<": "
2504                                         <<analyze_block(block)<<std::endl;
2505                 }
2506         }
2507 #endif
2508
2509         getBlockNoCreateNoEx(blockpos_requested);
2510 }
2511
2512 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2513 {
2514         DSTACKF("%s: p2d=(%d,%d)",
2515                         __FUNCTION_NAME,
2516                         p2d.X, p2d.Y);
2517
2518         /*
2519                 Check if it exists already in memory
2520         */
2521         ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2522         if(sector != NULL)
2523                 return sector;
2524
2525         /*
2526                 Try to load it from disk (with blocks)
2527         */
2528         //if(loadSectorFull(p2d) == true)
2529
2530         /*
2531                 Try to load metadata from disk
2532         */
2533 #if 0
2534         if(loadSectorMeta(p2d) == true)
2535         {
2536                 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2537                 if(sector == NULL)
2538                 {
2539                         infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2540                         throw InvalidPositionException("");
2541                 }
2542                 return sector;
2543         }
2544 #endif
2545         /*
2546                 Do not create over-limit
2547         */
2548         if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2549         || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2550         || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2551         || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2552                 throw InvalidPositionException("createSector(): pos. over limit");
2553
2554         /*
2555                 Generate blank sector
2556         */
2557
2558         sector = new ServerMapSector(this, p2d, m_gamedef);
2559
2560         // Sector position on map in nodes
2561         //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2562
2563         /*
2564                 Insert to container
2565         */
2566         m_sectors[p2d] = sector;
2567
2568         return sector;
2569 }
2570
2571 #if 0
2572 /*
2573         This is a quick-hand function for calling makeBlock().
2574 */
2575 MapBlock * ServerMap::generateBlock(
2576                 v3s16 p,
2577                 std::map<v3s16, MapBlock*> &modified_blocks
2578 )
2579 {
2580         DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2581
2582         /*infostream<<"generateBlock(): "
2583                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2584                         <<std::endl;*/
2585
2586         bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2587
2588         TimeTaker timer("generateBlock");
2589
2590         //MapBlock *block = original_dummy;
2591
2592         v2s16 p2d(p.X, p.Z);
2593         v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2594
2595         /*
2596                 Do not generate over-limit
2597         */
2598         if(blockpos_over_limit(p))
2599         {
2600                 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2601                 throw InvalidPositionException("generateBlock(): pos. over limit");
2602         }
2603
2604         /*
2605                 Create block make data
2606         */
2607         BlockMakeData data;
2608         initBlockMake(&data, p);
2609
2610         /*
2611                 Generate block
2612         */
2613         {
2614                 TimeTaker t("mapgen::make_block()");
2615                 mapgen->makeChunk(&data);
2616                 //mapgen::make_block(&data);
2617
2618                 if(enable_mapgen_debug_info == false)
2619                         t.stop(true); // Hide output
2620         }
2621
2622         /*
2623                 Blit data back on map, update lighting, add mobs and whatever this does
2624         */
2625         finishBlockMake(&data, modified_blocks);
2626
2627         /*
2628                 Get central block
2629         */
2630         MapBlock *block = getBlockNoCreateNoEx(p);
2631
2632 #if 0
2633         /*
2634                 Check result
2635         */
2636         if(block)
2637         {
2638                 bool erroneus_content = false;
2639                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2640                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2641                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2642                 {
2643                         v3s16 p(x0,y0,z0);
2644                         MapNode n = block->getNode(p);
2645                         if(n.getContent() == CONTENT_IGNORE)
2646                         {
2647                                 infostream<<"CONTENT_IGNORE at "
2648                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2649                                                 <<std::endl;
2650                                 erroneus_content = true;
2651                                 assert(0);
2652                         }
2653                 }
2654                 if(erroneus_content)
2655                 {
2656                         assert(0);
2657                 }
2658         }
2659 #endif
2660
2661 #if 0
2662         /*
2663                 Generate a completely empty block
2664         */
2665         if(block)
2666         {
2667                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2668                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2669                 {
2670                         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2671                         {
2672                                 MapNode n;
2673                                 n.setContent(CONTENT_AIR);
2674                                 block->setNode(v3s16(x0,y0,z0), n);
2675                         }
2676                 }
2677         }
2678 #endif
2679
2680         if(enable_mapgen_debug_info == false)
2681                 timer.stop(true); // Hide output
2682
2683         return block;
2684 }
2685 #endif
2686
2687 MapBlock * ServerMap::createBlock(v3s16 p)
2688 {
2689         DSTACKF("%s: p=(%d,%d,%d)",
2690                         __FUNCTION_NAME, p.X, p.Y, p.Z);
2691
2692         /*
2693                 Do not create over-limit
2694         */
2695         if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2696         || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2697         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2698         || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2699         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2700         || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2701                 throw InvalidPositionException("createBlock(): pos. over limit");
2702
2703         v2s16 p2d(p.X, p.Z);
2704         s16 block_y = p.Y;
2705         /*
2706                 This will create or load a sector if not found in memory.
2707                 If block exists on disk, it will be loaded.
2708
2709                 NOTE: On old save formats, this will be slow, as it generates
2710                       lighting on blocks for them.
2711         */
2712         ServerMapSector *sector;
2713         try{
2714                 sector = (ServerMapSector*)createSector(p2d);
2715                 assert(sector->getId() == MAPSECTOR_SERVER);
2716         }
2717         catch(InvalidPositionException &e)
2718         {
2719                 infostream<<"createBlock: createSector() failed"<<std::endl;
2720                 throw e;
2721         }
2722         /*
2723                 NOTE: This should not be done, or at least the exception
2724                 should not be passed on as std::exception, because it
2725                 won't be catched at all.
2726         */
2727         /*catch(std::exception &e)
2728         {
2729                 infostream<<"createBlock: createSector() failed: "
2730                                 <<e.what()<<std::endl;
2731                 throw e;
2732         }*/
2733
2734         /*
2735                 Try to get a block from the sector
2736         */
2737
2738         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2739         if(block)
2740         {
2741                 if(block->isDummy())
2742                         block->unDummify();
2743                 return block;
2744         }
2745         // Create blank
2746         block = sector->createBlankBlock(block_y);
2747
2748         return block;
2749 }
2750
2751 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2752 {
2753         DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2754                         __FUNCTION_NAME,
2755                         p.X, p.Y, p.Z, create_blank);
2756
2757         {
2758                 MapBlock *block = getBlockNoCreateNoEx(p);
2759                 if(block && block->isDummy() == false)
2760                         return block;
2761         }
2762
2763         {
2764                 MapBlock *block = loadBlock(p);
2765                 if(block)
2766                         return block;
2767         }
2768
2769         if (create_blank) {
2770                 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2771                 MapBlock *block = sector->createBlankBlock(p.Y);
2772
2773                 return block;
2774         }
2775
2776 #if 0
2777         if(allow_generate)
2778         {
2779                 std::map<v3s16, MapBlock*> modified_blocks;
2780                 MapBlock *block = generateBlock(p, modified_blocks);
2781                 if(block)
2782                 {
2783                         MapEditEvent event;
2784                         event.type = MEET_OTHER;
2785                         event.p = p;
2786
2787                         // Copy modified_blocks to event
2788                         for(std::map<v3s16, MapBlock*>::iterator
2789                                         i = modified_blocks.begin();
2790                                         i != modified_blocks.end(); ++i)
2791                         {
2792                                 event.modified_blocks.insert(i->first);
2793                         }
2794
2795                         // Queue event
2796                         dispatchEvent(&event);
2797
2798                         return block;
2799                 }
2800         }
2801 #endif
2802
2803         return NULL;
2804 }
2805
2806 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2807 {
2808         MapBlock *block = getBlockNoCreateNoEx(p3d);
2809         if (block == NULL)
2810                 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2811
2812         return block;
2813 }
2814
2815 void ServerMap::prepareBlock(MapBlock *block) {
2816 }
2817
2818 // N.B.  This requires no synchronization, since data will not be modified unless
2819 // the VoxelManipulator being updated belongs to the same thread.
2820 void ServerMap::updateVManip(v3s16 pos)
2821 {
2822         Mapgen *mg = m_emerge->getCurrentMapgen();
2823         if (!mg)
2824                 return;
2825
2826         MMVManip *vm = mg->vm;
2827         if (!vm)
2828                 return;
2829
2830         if (!vm->m_area.contains(pos))
2831                 return;
2832
2833         s32 idx = vm->m_area.index(pos);
2834         vm->m_data[idx] = getNodeNoEx(pos);
2835         vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2836
2837         vm->m_is_dirty = true;
2838 }
2839
2840 s16 ServerMap::findGroundLevel(v2s16 p2d)
2841 {
2842 #if 0
2843         /*
2844                 Uh, just do something random...
2845         */
2846         // Find existing map from top to down
2847         s16 max=63;
2848         s16 min=-64;
2849         v3s16 p(p2d.X, max, p2d.Y);
2850         for(; p.Y>min; p.Y--)
2851         {
2852                 MapNode n = getNodeNoEx(p);
2853                 if(n.getContent() != CONTENT_IGNORE)
2854                         break;
2855         }
2856         if(p.Y == min)
2857                 goto plan_b;
2858         // If this node is not air, go to plan b
2859         if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2860                 goto plan_b;
2861         // Search existing walkable and return it
2862         for(; p.Y>min; p.Y--)
2863         {
2864                 MapNode n = getNodeNoEx(p);
2865                 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2866                         return p.Y;
2867         }
2868
2869         // Move to plan b
2870 plan_b:
2871 #endif
2872
2873         /*
2874                 Determine from map generator noise functions
2875         */
2876
2877         s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2878         return level;
2879
2880         //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2881         //return (s16)level;
2882 }
2883
2884 bool ServerMap::loadFromFolders() {
2885         if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2886                 return true;
2887         return false;
2888 }
2889
2890 void ServerMap::createDirs(std::string path)
2891 {
2892         if(fs::CreateAllDirs(path) == false)
2893         {
2894                 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2895                                 <<"\""<<path<<"\""<<std::endl;
2896                 throw BaseException("ServerMap failed to create directory");
2897         }
2898 }
2899
2900 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2901 {
2902         char cc[9];
2903         switch(layout)
2904         {
2905                 case 1:
2906                         snprintf(cc, 9, "%.4x%.4x",
2907                                 (unsigned int)pos.X&0xffff,
2908                                 (unsigned int)pos.Y&0xffff);
2909
2910                         return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2911                 case 2:
2912                         snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2913                                 (unsigned int)pos.X&0xfff,
2914                                 (unsigned int)pos.Y&0xfff);
2915
2916                         return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2917                 default:
2918                         assert(false);
2919                         return "";
2920         }
2921 }
2922
2923 v2s16 ServerMap::getSectorPos(std::string dirname)
2924 {
2925         unsigned int x = 0, y = 0;
2926         int r;
2927         std::string component;
2928         fs::RemoveLastPathComponent(dirname, &component, 1);
2929         if(component.size() == 8)
2930         {
2931                 // Old layout
2932                 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2933         }
2934         else if(component.size() == 3)
2935         {
2936                 // New layout
2937                 fs::RemoveLastPathComponent(dirname, &component, 2);
2938                 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2939                 // Sign-extend the 12 bit values up to 16 bits...
2940                 if(x&0x800) x|=0xF000;
2941                 if(y&0x800) y|=0xF000;
2942         }
2943         else
2944         {
2945                 assert(false);
2946         }
2947         assert(r == 2);
2948         v2s16 pos((s16)x, (s16)y);
2949         return pos;
2950 }
2951
2952 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2953 {
2954         v2s16 p2d = getSectorPos(sectordir);
2955
2956         if(blockfile.size() != 4){
2957                 throw InvalidFilenameException("Invalid block filename");
2958         }
2959         unsigned int y;
2960         int r = sscanf(blockfile.c_str(), "%4x", &y);
2961         if(r != 1)
2962                 throw InvalidFilenameException("Invalid block filename");
2963         return v3s16(p2d.X, y, p2d.Y);
2964 }
2965
2966 std::string ServerMap::getBlockFilename(v3s16 p)
2967 {
2968         char cc[5];
2969         snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2970         return cc;
2971 }
2972
2973 void ServerMap::save(ModifiedState save_level)
2974 {
2975         DSTACK(__FUNCTION_NAME);
2976         if(m_map_saving_enabled == false)
2977         {
2978                 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2979                 return;
2980         }
2981
2982         if(save_level == MOD_STATE_CLEAN)
2983                 infostream<<"ServerMap: Saving whole map, this can take time."
2984                                 <<std::endl;
2985
2986         if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2987         {
2988                 saveMapMeta();
2989         }
2990
2991         // Profile modified reasons
2992         Profiler modprofiler;
2993
2994         u32 sector_meta_count = 0;
2995         u32 block_count = 0;
2996         u32 block_count_all = 0; // Number of blocks in memory
2997
2998         // Don't do anything with sqlite unless something is really saved
2999         bool save_started = false;
3000
3001         for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3002                 i != m_sectors.end(); ++i)
3003         {
3004                 ServerMapSector *sector = (ServerMapSector*)i->second;
3005                 assert(sector->getId() == MAPSECTOR_SERVER);
3006
3007                 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3008                 {
3009                         saveSectorMeta(sector);
3010                         sector_meta_count++;
3011                 }
3012                 std::list<MapBlock*> blocks;
3013                 sector->getBlocks(blocks);
3014
3015                 for(std::list<MapBlock*>::iterator j = blocks.begin();
3016                         j != blocks.end(); ++j)
3017                 {
3018                         MapBlock *block = *j;
3019
3020                         block_count_all++;
3021
3022                         if(block->getModified() >= (u32)save_level)
3023                         {
3024                                 // Lazy beginSave()
3025                                 if(!save_started){
3026                                         beginSave();
3027                                         save_started = true;
3028                                 }
3029
3030                                 modprofiler.add(block->getModifiedReason(), 1);
3031
3032                                 saveBlock(block);
3033                                 block_count++;
3034
3035                                 /*infostream<<"ServerMap: Written block ("
3036                                                 <<block->getPos().X<<","
3037                                                 <<block->getPos().Y<<","
3038                                                 <<block->getPos().Z<<")"
3039                                                 <<std::endl;*/
3040                         }
3041                 }
3042         }
3043         if(save_started)
3044                 endSave();
3045
3046         /*
3047                 Only print if something happened or saved whole map
3048         */
3049         if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3050                         || block_count != 0)
3051         {
3052                 infostream<<"ServerMap: Written: "
3053                                 <<sector_meta_count<<" sector metadata files, "
3054                                 <<block_count<<" block files"
3055                                 <<", "<<block_count_all<<" blocks in memory."
3056                                 <<std::endl;
3057                 PrintInfo(infostream); // ServerMap/ClientMap:
3058                 infostream<<"Blocks modified by: "<<std::endl;
3059                 modprofiler.print(infostream);
3060         }
3061 }
3062
3063 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3064 {
3065         if(loadFromFolders()){
3066                 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3067                                 <<"all blocks that are stored in flat files"<<std::endl;
3068         }
3069         dbase->listAllLoadableBlocks(dst);
3070 }
3071
3072 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3073 {
3074         for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3075                 si != m_sectors.end(); ++si)
3076         {
3077                 MapSector *sector = si->second;
3078
3079                 std::list<MapBlock*> blocks;
3080                 sector->getBlocks(blocks);
3081
3082                 for(std::list<MapBlock*>::iterator i = blocks.begin();
3083                                 i != blocks.end(); ++i)
3084                 {
3085                         MapBlock *block = (*i);
3086                         v3s16 p = block->getPos();
3087                         dst.push_back(p);
3088                 }
3089         }
3090 }
3091
3092 void ServerMap::saveMapMeta()
3093 {
3094         DSTACK(__FUNCTION_NAME);
3095
3096         /*infostream<<"ServerMap::saveMapMeta(): "
3097                         <<"seed="<<m_seed
3098                         <<std::endl;*/
3099
3100         createDirs(m_savedir);
3101
3102         std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3103         std::ostringstream ss(std::ios_base::binary);
3104
3105         Settings params;
3106
3107         m_emerge->saveParamsToSettings(&params);
3108         params.writeLines(ss);
3109
3110         ss<<"[end_of_params]\n";
3111
3112         if(!fs::safeWriteToFile(fullpath, ss.str()))
3113         {
3114                 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3115                                 <<"could not write "<<fullpath<<std::endl;
3116                 throw FileNotGoodException("Cannot save chunk metadata");
3117         }
3118
3119         m_map_metadata_changed = false;
3120 }
3121
3122 void ServerMap::loadMapMeta()
3123 {
3124         DSTACK(__FUNCTION_NAME);
3125
3126         std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3127         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3128         if (!is.good()) {
3129                 errorstream << "ServerMap::loadMapMeta(): "
3130                                 << "could not open" << fullpath << std::endl;
3131                 throw FileNotGoodException("Cannot open map metadata");
3132         }
3133
3134         Settings params;
3135
3136         if (!params.parseConfigLines(is, "[end_of_params]")) {
3137                 throw SerializationError("ServerMap::loadMapMeta(): "
3138                                 "[end_of_params] not found!");
3139         }
3140
3141         m_emerge->loadParamsFromSettings(&params);
3142
3143         verbosestream << "ServerMap::loadMapMeta(): seed="
3144                 << m_emerge->params.seed << std::endl;
3145 }
3146
3147 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3148 {
3149         DSTACK(__FUNCTION_NAME);
3150         // Format used for writing
3151         u8 version = SER_FMT_VER_HIGHEST_WRITE;
3152         // Get destination
3153         v2s16 pos = sector->getPos();
3154         std::string dir = getSectorDir(pos);
3155         createDirs(dir);
3156
3157         std::string fullpath = dir + DIR_DELIM + "meta";
3158         std::ostringstream ss(std::ios_base::binary);
3159
3160         sector->serialize(ss, version);
3161
3162         if(!fs::safeWriteToFile(fullpath, ss.str()))
3163                 throw FileNotGoodException("Cannot write sector metafile");
3164
3165         sector->differs_from_disk = false;
3166 }
3167
3168 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3169 {
3170         DSTACK(__FUNCTION_NAME);
3171         // Get destination
3172         v2s16 p2d = getSectorPos(sectordir);
3173
3174         ServerMapSector *sector = NULL;
3175
3176         std::string fullpath = sectordir + DIR_DELIM + "meta";
3177         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3178         if(is.good() == false)
3179         {
3180                 // If the directory exists anyway, it probably is in some old
3181                 // format. Just go ahead and create the sector.
3182                 if(fs::PathExists(sectordir))
3183                 {
3184                         /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3185                                         <<fullpath<<" doesn't exist but directory does."
3186                                         <<" Continuing with a sector with no metadata."
3187                                         <<std::endl;*/
3188                         sector = new ServerMapSector(this, p2d, m_gamedef);
3189                         m_sectors[p2d] = sector;
3190                 }
3191                 else
3192                 {
3193                         throw FileNotGoodException("Cannot open sector metafile");
3194                 }
3195         }
3196         else
3197         {
3198                 sector = ServerMapSector::deSerialize
3199                                 (is, this, p2d, m_sectors, m_gamedef);
3200                 if(save_after_load)
3201                         saveSectorMeta(sector);
3202         }
3203
3204         sector->differs_from_disk = false;
3205
3206         return sector;
3207 }
3208
3209 bool ServerMap::loadSectorMeta(v2s16 p2d)
3210 {
3211         DSTACK(__FUNCTION_NAME);
3212
3213         MapSector *sector = NULL;
3214
3215         // The directory layout we're going to load from.
3216         //  1 - original sectors/xxxxzzzz/
3217         //  2 - new sectors2/xxx/zzz/
3218         //  If we load from anything but the latest structure, we will
3219         //  immediately save to the new one, and remove the old.
3220         int loadlayout = 1;
3221         std::string sectordir1 = getSectorDir(p2d, 1);
3222         std::string sectordir;
3223         if(fs::PathExists(sectordir1))
3224         {
3225                 sectordir = sectordir1;
3226         }
3227         else
3228         {
3229                 loadlayout = 2;
3230                 sectordir = getSectorDir(p2d, 2);
3231         }
3232
3233         try{
3234                 sector = loadSectorMeta(sectordir, loadlayout != 2);
3235         }
3236         catch(InvalidFilenameException &e)
3237         {
3238                 return false;
3239         }
3240         catch(FileNotGoodException &e)
3241         {
3242                 return false;
3243         }
3244         catch(std::exception &e)
3245         {
3246                 return false;
3247         }
3248
3249         return true;
3250 }
3251
3252 #if 0
3253 bool ServerMap::loadSectorFull(v2s16 p2d)
3254 {
3255         DSTACK(__FUNCTION_NAME);
3256
3257         MapSector *sector = NULL;
3258
3259         // The directory layout we're going to load from.
3260         //  1 - original sectors/xxxxzzzz/
3261         //  2 - new sectors2/xxx/zzz/
3262         //  If we load from anything but the latest structure, we will
3263         //  immediately save to the new one, and remove the old.
3264         int loadlayout = 1;
3265         std::string sectordir1 = getSectorDir(p2d, 1);
3266         std::string sectordir;
3267         if(fs::PathExists(sectordir1))
3268         {
3269                 sectordir = sectordir1;
3270         }
3271         else
3272         {
3273                 loadlayout = 2;
3274                 sectordir = getSectorDir(p2d, 2);
3275         }
3276
3277         try{
3278                 sector = loadSectorMeta(sectordir, loadlayout != 2);
3279         }
3280         catch(InvalidFilenameException &e)
3281         {
3282                 return false;
3283         }
3284         catch(FileNotGoodException &e)
3285         {
3286                 return false;
3287         }
3288         catch(std::exception &e)
3289         {
3290                 return false;
3291         }
3292
3293         /*
3294                 Load blocks
3295         */
3296         std::vector<fs::DirListNode> list2 = fs::GetDirListing
3297                         (sectordir);
3298         std::vector<fs::DirListNode>::iterator i2;
3299         for(i2=list2.begin(); i2!=list2.end(); i2++)
3300         {
3301                 // We want files
3302                 if(i2->dir)
3303                         continue;
3304                 try{
3305                         loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3306                 }
3307                 catch(InvalidFilenameException &e)
3308                 {
3309                         // This catches unknown crap in directory
3310                 }
3311         }
3312
3313         if(loadlayout != 2)
3314         {
3315                 infostream<<"Sector converted to new layout - deleting "<<
3316                         sectordir1<<std::endl;
3317                 fs::RecursiveDelete(sectordir1);
3318         }
3319
3320         return true;
3321 }
3322 #endif
3323
3324 void ServerMap::beginSave()
3325 {
3326         dbase->beginSave();
3327 }
3328
3329 void ServerMap::endSave()
3330 {
3331         dbase->endSave();
3332 }
3333
3334 bool ServerMap::saveBlock(MapBlock *block)
3335 {
3336         return saveBlock(block, dbase);
3337 }
3338
3339 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3340 {
3341         v3s16 p3d = block->getPos();
3342
3343         // Dummy blocks are not written
3344         if (block->isDummy()) {
3345                 errorstream << "WARNING: saveBlock: Not writing dummy block "
3346                         << PP(p3d) << std::endl;
3347                 return true;
3348         }
3349
3350         // Format used for writing
3351         u8 version = SER_FMT_VER_HIGHEST_WRITE;
3352
3353         /*
3354                 [0] u8 serialization version
3355                 [1] data
3356         */
3357         std::ostringstream o(std::ios_base::binary);
3358         o.write((char*) &version, 1);
3359         block->serialize(o, version, true);
3360
3361         std::string data = o.str();
3362         bool ret = db->saveBlock(p3d, data);
3363         if(ret) {
3364                 // We just wrote it to the disk so clear modified flag
3365                 block->resetModified();
3366         }
3367         return ret;
3368 }
3369
3370 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3371                 MapSector *sector, bool save_after_load)
3372 {
3373         DSTACK(__FUNCTION_NAME);
3374
3375         std::string fullpath = sectordir+DIR_DELIM+blockfile;
3376         try {
3377
3378                 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3379                 if(is.good() == false)
3380                         throw FileNotGoodException("Cannot open block file");
3381
3382                 v3s16 p3d = getBlockPos(sectordir, blockfile);
3383                 v2s16 p2d(p3d.X, p3d.Z);
3384
3385                 assert(sector->getPos() == p2d);
3386
3387                 u8 version = SER_FMT_VER_INVALID;
3388                 is.read((char*)&version, 1);
3389
3390                 if(is.fail())
3391                         throw SerializationError("ServerMap::loadBlock(): Failed"
3392                                         " to read MapBlock version");
3393
3394                 /*u32 block_size = MapBlock::serializedLength(version);
3395                 SharedBuffer<u8> data(block_size);
3396                 is.read((char*)*data, block_size);*/
3397
3398                 // This will always return a sector because we're the server
3399                 //MapSector *sector = emergeSector(p2d);
3400
3401                 MapBlock *block = NULL;
3402                 bool created_new = false;
3403                 block = sector->getBlockNoCreateNoEx(p3d.Y);
3404                 if(block == NULL)
3405                 {
3406                         block = sector->createBlankBlockNoInsert(p3d.Y);
3407                         created_new = true;
3408                 }
3409
3410                 // Read basic data
3411                 block->deSerialize(is, version, true);
3412
3413                 // If it's a new block, insert it to the map
3414                 if(created_new)
3415                         sector->insertBlock(block);
3416
3417                 /*
3418                         Save blocks loaded in old format in new format
3419                 */
3420
3421                 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3422                 {
3423                         saveBlock(block);
3424
3425                         // Should be in database now, so delete the old file
3426                         fs::RecursiveDelete(fullpath);
3427                 }
3428
3429                 // We just loaded it from the disk, so it's up-to-date.
3430                 block->resetModified();
3431
3432         }
3433         catch(SerializationError &e)
3434         {
3435                 infostream<<"WARNING: Invalid block data on disk "
3436                                 <<"fullpath="<<fullpath
3437                                 <<" (SerializationError). "
3438                                 <<"what()="<<e.what()
3439                                 <<std::endl;
3440                                 // Ignoring. A new one will be generated.
3441                 assert(0);
3442
3443                 // TODO: Backup file; name is in fullpath.
3444         }
3445 }
3446
3447 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3448 {
3449         DSTACK(__FUNCTION_NAME);
3450
3451         try {
3452                 std::istringstream is(*blob, std::ios_base::binary);
3453
3454                 u8 version = SER_FMT_VER_INVALID;
3455                 is.read((char*)&version, 1);
3456
3457                 if(is.fail())
3458                         throw SerializationError("ServerMap::loadBlock(): Failed"
3459                                         " to read MapBlock version");
3460
3461                 /*u32 block_size = MapBlock::serializedLength(version);
3462                 SharedBuffer<u8> data(block_size);
3463                 is.read((char*)*data, block_size);*/
3464
3465                 // This will always return a sector because we're the server
3466                 //MapSector *sector = emergeSector(p2d);
3467
3468                 MapBlock *block = NULL;
3469                 bool created_new = false;
3470                 block = sector->getBlockNoCreateNoEx(p3d.Y);
3471                 if(block == NULL)
3472                 {
3473                         block = sector->createBlankBlockNoInsert(p3d.Y);
3474                         created_new = true;
3475                 }
3476
3477                 // Read basic data
3478                 block->deSerialize(is, version, true);
3479
3480                 // If it's a new block, insert it to the map
3481                 if(created_new)
3482                         sector->insertBlock(block);
3483
3484                 /*
3485                         Save blocks loaded in old format in new format
3486                 */
3487
3488                 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3489                 // Only save if asked to; no need to update version
3490                 if(save_after_load)
3491                         saveBlock(block);
3492
3493                 // We just loaded it from, so it's up-to-date.
3494                 block->resetModified();
3495
3496         }
3497         catch(SerializationError &e)
3498         {
3499                 errorstream<<"Invalid block data in database"
3500                                 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3501                                 <<" (SerializationError): "<<e.what()<<std::endl;
3502
3503                 // TODO: Block should be marked as invalid in memory so that it is
3504                 // not touched but the game can run
3505
3506                 if(g_settings->getBool("ignore_world_load_errors")){
3507                         errorstream<<"Ignoring block load error. Duck and cover! "
3508                                         <<"(ignore_world_load_errors)"<<std::endl;
3509                 } else {
3510                         throw SerializationError("Invalid block data in database");
3511                         //assert(0);
3512                 }
3513         }
3514 }
3515
3516 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3517 {
3518         DSTACK(__FUNCTION_NAME);
3519
3520         v2s16 p2d(blockpos.X, blockpos.Z);
3521
3522         std::string ret;
3523
3524         ret = dbase->loadBlock(blockpos);
3525         if (ret != "") {
3526                 loadBlock(&ret, blockpos, createSector(p2d), false);
3527                 return getBlockNoCreateNoEx(blockpos);
3528         }
3529         // Not found in database, try the files
3530
3531         // The directory layout we're going to load from.
3532         //  1 - original sectors/xxxxzzzz/
3533         //  2 - new sectors2/xxx/zzz/
3534         //  If we load from anything but the latest structure, we will
3535         //  immediately save to the new one, and remove the old.
3536         int loadlayout = 1;
3537         std::string sectordir1 = getSectorDir(p2d, 1);
3538         std::string sectordir;
3539         if(fs::PathExists(sectordir1))
3540         {
3541                 sectordir = sectordir1;
3542         }
3543         else
3544         {
3545                 loadlayout = 2;
3546                 sectordir = getSectorDir(p2d, 2);
3547         }
3548
3549         /*
3550                 Make sure sector is loaded
3551         */
3552         MapSector *sector = getSectorNoGenerateNoEx(p2d);
3553         if(sector == NULL)
3554         {
3555                 try{
3556                         sector = loadSectorMeta(sectordir, loadlayout != 2);
3557                 }
3558                 catch(InvalidFilenameException &e)
3559                 {
3560                         return NULL;
3561                 }
3562                 catch(FileNotGoodException &e)
3563                 {
3564                         return NULL;
3565                 }
3566                 catch(std::exception &e)
3567                 {
3568                         return NULL;
3569                 }
3570         }
3571
3572         /*
3573                 Make sure file exists
3574         */
3575
3576         std::string blockfilename = getBlockFilename(blockpos);
3577         if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3578                 return NULL;
3579
3580         /*
3581                 Load block and save it to the database
3582         */
3583         loadBlock(sectordir, blockfilename, sector, true);
3584         return getBlockNoCreateNoEx(blockpos);
3585 }
3586
3587 void ServerMap::PrintInfo(std::ostream &out)
3588 {
3589         out<<"ServerMap: ";
3590 }
3591
3592 MMVManip::MMVManip(Map *map):
3593                 VoxelManipulator(),
3594                 m_is_dirty(false),
3595                 m_create_area(false),
3596                 m_map(map)
3597 {
3598 }
3599
3600 MMVManip::~MMVManip()
3601 {
3602 }
3603
3604 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3605         bool load_if_inexistent)
3606 {
3607         TimeTaker timer1("initialEmerge", &emerge_time);
3608
3609         // Units of these are MapBlocks
3610         v3s16 p_min = blockpos_min;
3611         v3s16 p_max = blockpos_max;
3612
3613         VoxelArea block_area_nodes
3614                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3615
3616         u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3617         if(size_MB >= 1)
3618         {
3619                 infostream<<"initialEmerge: area: ";
3620                 block_area_nodes.print(infostream);
3621                 infostream<<" ("<<size_MB<<"MB)";
3622                 infostream<<std::endl;
3623         }
3624
3625         addArea(block_area_nodes);
3626
3627         for(s32 z=p_min.Z; z<=p_max.Z; z++)
3628         for(s32 y=p_min.Y; y<=p_max.Y; y++)
3629         for(s32 x=p_min.X; x<=p_max.X; x++)
3630         {
3631                 u8 flags = 0;
3632                 MapBlock *block;
3633                 v3s16 p(x,y,z);
3634                 std::map<v3s16, u8>::iterator n;
3635                 n = m_loaded_blocks.find(p);
3636                 if(n != m_loaded_blocks.end())
3637                         continue;
3638
3639                 bool block_data_inexistent = false;
3640                 try
3641                 {
3642                         TimeTaker timer1("emerge load", &emerge_load_time);
3643
3644                         block = m_map->getBlockNoCreate(p);
3645                         if(block->isDummy())
3646                                 block_data_inexistent = true;
3647                         else
3648                                 block->copyTo(*this);
3649                 }
3650                 catch(InvalidPositionException &e)
3651                 {
3652                         block_data_inexistent = true;
3653                 }
3654
3655                 if(block_data_inexistent)
3656                 {
3657
3658                         if (load_if_inexistent) {
3659                                 ServerMap *svrmap = (ServerMap *)m_map;
3660                                 block = svrmap->emergeBlock(p, false);
3661                                 if (block == NULL)
3662                                         block = svrmap->createBlock(p);
3663                                 else
3664                                         block->copyTo(*this);
3665                         } else {
3666                                 flags |= VMANIP_BLOCK_DATA_INEXIST;
3667
3668                                 /*
3669                                         Mark area inexistent
3670                                 */
3671                                 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3672                                 // Fill with VOXELFLAG_NO_DATA
3673                                 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3674                                 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3675                                 {
3676                                         s32 i = m_area.index(a.MinEdge.X,y,z);
3677                                         memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3678                                 }
3679                         }
3680                 }
3681                 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3682                 {
3683                         // Mark that block was loaded as blank
3684                         flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3685                 }*/
3686
3687                 m_loaded_blocks[p] = flags;
3688         }
3689
3690         m_is_dirty = false;
3691 }
3692
3693 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3694         bool overwrite_generated)
3695 {
3696         if(m_area.getExtent() == v3s16(0,0,0))
3697                 return;
3698
3699         /*
3700                 Copy data of all blocks
3701         */
3702         for(std::map<v3s16, u8>::iterator
3703                         i = m_loaded_blocks.begin();
3704                         i != m_loaded_blocks.end(); ++i)
3705         {
3706                 v3s16 p = i->first;
3707                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3708                 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3709                 if ((existed == false) || (block == NULL) ||
3710                         (overwrite_generated == false && block->isGenerated() == true))
3711                         continue;
3712
3713                 block->copyFrom(*this);
3714
3715                 if(modified_blocks)
3716                         (*modified_blocks)[p] = block;
3717         }
3718 }
3719
3720 //END