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