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