Working version before block send priorization update
[oweals/minetest.git] / src / map.cpp
1 /*
2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
3 */
4
5 #include "map.h"
6 //#include "player.h"
7 #include "main.h"
8 #include "jmutexautolock.h"
9 #include "client.h"
10 #include "filesys.h"
11 #include "utility.h"
12
13 #ifdef _WIN32
14         #include <windows.h>
15         #define sleep_ms(x) Sleep(x)
16 #else
17         #include <unistd.h>
18         #define sleep_ms(x) usleep(x*1000)
19 #endif
20
21 Map::Map(std::ostream &dout):
22         m_dout(dout),
23         m_camera_position(0,0,0),
24         m_camera_direction(0,0,1),
25         m_sector_cache(NULL),
26         m_hwrapper(this),
27         drawoffset(0,0,0)
28 {
29         m_sector_mutex.Init();
30         m_camera_mutex.Init();
31         assert(m_sector_mutex.IsInitialized());
32         assert(m_camera_mutex.IsInitialized());
33         
34         // Get this so that the player can stay on it at first
35         //getSector(v2s16(0,0));
36 }
37
38 Map::~Map()
39 {
40         /*
41                 Stop updater thread
42         */
43         /*updater.setRun(false);
44         while(updater.IsRunning())
45                 sleep_s(1);*/
46
47         /*
48                 Free all MapSectors.
49         */
50         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
51         for(; i.atEnd() == false; i++)
52         {
53                 MapSector *sector = i.getNode()->getValue();
54                 delete sector;
55         }
56 }
57
58 /*bool Map::sectorExists(v2s16 p)
59 {
60         JMutexAutoLock lock(m_sector_mutex);
61         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
62         return (n != NULL);
63 }*/
64
65 MapSector * Map::getSectorNoGenerate(v2s16 p)
66 {
67         JMutexAutoLock lock(m_sector_mutex);
68
69         if(m_sector_cache != NULL && p == m_sector_cache_p){
70                 MapSector * sector = m_sector_cache;
71                 // Reset inactivity timer
72                 sector->usage_timer = 0.0;
73                 return sector;
74         }
75         
76         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
77         // If sector doesn't exist, throw an exception
78         if(n == NULL)
79         {
80                 throw InvalidPositionException();
81         }
82         
83         MapSector *sector = n->getValue();
84         
85         // Cache the last result
86         m_sector_cache_p = p;
87         m_sector_cache = sector;
88
89         //MapSector * ref(sector);
90         
91         // Reset inactivity timer
92         sector->usage_timer = 0.0;
93         return sector;
94 }
95
96 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
97 {       
98         v2s16 p2d(p3d.X, p3d.Z);
99         MapSector * sector = getSectorNoGenerate(p2d);
100
101         MapBlock *block = sector->getBlockNoCreate(p3d.Y);
102
103         return block;
104 }
105
106 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
107 {
108         dstream<<"Map::getBlock() with generate=true called"
109                         <<std::endl;
110         v2s16 p2d(p3d.X, p3d.Z);
111         //MapSector * sector = getSector(p2d, generate);
112         MapSector * sector = getSectorNoGenerate(p2d);
113
114         if(sector == NULL)
115                 throw InvalidPositionException();
116
117         return sector->getBlockNoCreate(p3d.Y);
118 }*/
119
120 f32 Map::getGroundHeight(v2s16 p, bool generate)
121 {
122         try{
123                 v2s16 sectorpos = getNodeSectorPos(p);
124                 MapSector * sref = getSectorNoGenerate(sectorpos);
125                 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
126                 f32 y = sref->getGroundHeight(relpos);
127                 return y;
128         }
129         catch(InvalidPositionException &e)
130         {
131                 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
132         }
133 }
134
135 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
136 {
137         /*m_dout<<DTIME<<"Map::setGroundHeight(("
138                         <<p.X<<","<<p.Y
139                         <<"), "<<y<<")"<<std::endl;*/
140         v2s16 sectorpos = getNodeSectorPos(p);
141         MapSector * sref = getSectorNoGenerate(sectorpos);
142         v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
143         //sref->mutex.Lock();
144         sref->setGroundHeight(relpos, y);
145         //sref->mutex.Unlock();
146 }
147
148 bool Map::isNodeUnderground(v3s16 p)
149 {
150         v3s16 blockpos = getNodeBlockPos(p);
151         try{
152                 MapBlock * block = getBlockNoCreate(blockpos);
153                 return block->getIsUnderground();
154         }
155         catch(InvalidPositionException &e)
156         {
157                 return false;
158         }
159 }
160
161 #ifdef LKJnb
162 //TODO: Remove: Not used.
163 /*
164         Goes recursively through the neighbours of the node.
165
166         Alters only transparent nodes.
167
168         If the lighting of the neighbour is lower than the lighting of
169         the node was (before changing it to 0 at the step before), the
170         lighting of the neighbour is set to 0 and then the same stuff
171         repeats for the neighbour.
172
173         Some things are made strangely to make it as fast as possible.
174
175         Usage: (for clearing all possible spreaded light of a lamp)
176         NOTE: This is outdated
177                 core::list<v3s16> light_sources;
178                 core::map<v3s16, MapBlock*> modified_blocks;
179                 u8 oldlight = node_at_pos.light;
180                 node_at_pos.setLight(0);
181                 unLightNeighbors(pos, oldlight, light_sources, modified_blocks);
182 */
183 void Map::unLightNeighbors(v3s16 pos, u8 oldlight,
184                 core::map<v3s16, bool> & light_sources,
185                 core::map<v3s16, MapBlock*>  & modified_blocks)
186 {
187         v3s16 dirs[6] = {
188                 v3s16(0,0,1), // back
189                 v3s16(0,1,0), // top
190                 v3s16(1,0,0), // right
191                 v3s16(0,0,-1), // front
192                 v3s16(0,-1,0), // bottom
193                 v3s16(-1,0,0), // left
194         };
195         
196         /*
197                 Initialize block cache
198         */
199         v3s16 blockpos_last;
200         MapBlock *block = NULL;
201         // Cache this a bit, too
202         bool block_checked_in_modified = false;
203         
204         // Loop through 6 neighbors
205         for(u16 i=0; i<6; i++){
206                 // Get the position of the neighbor node
207                 v3s16 n2pos = pos + dirs[i];
208                 
209                 // Get the block where the node is located
210                 v3s16 blockpos = getNodeBlockPos(n2pos);
211
212                 // Only fetch a new block if the block position has changed
213                 try{
214                         if(block == NULL || blockpos != blockpos_last)
215                         {
216                                 block = getBlockNoCreate(blockpos);
217                                 blockpos_last = blockpos;
218                                 
219                                 block_checked_in_modified = false;
220                                 //blockchangecount++;
221                         }
222                 }
223                 catch(InvalidPositionException &e)
224                 {
225                         continue;
226                 }
227
228                 if(block->isDummy())
229                         continue;
230                 
231                 // Calculate relative position in block
232                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
233                 // Get node straight from the block
234                 MapNode n2 = block->getNode(relpos);
235                 
236                 /*
237                         If the neighbor is dimmer than what was specified
238                         as oldlight (the light of the previous node)
239                 */
240                 if(n2.getLight() < oldlight)
241                 {
242                         /*
243                                 And the neighbor is transparent and it has some light
244                         */
245                         if(n2.light_propagates() && n2.getLight() != 0)
246                         {
247                                 /*
248                                         Set light to 0 and recurse.
249                                 */
250                                 u8 current_light = n2.getLight();
251                                 n2.setLight(0);
252                                 block->setNode(relpos, n2);
253                                 unLightNeighbors(n2pos, current_light,
254                                                 light_sources, modified_blocks);
255                                 
256                                 if(block_checked_in_modified == false)
257                                 {
258                                         // If the block is not found in modified_blocks, add.
259                                         if(modified_blocks.find(blockpos) == NULL)
260                                         {
261                                                 modified_blocks.insert(blockpos, block);
262                                         }
263                                         block_checked_in_modified = true;
264                                 }
265                         }
266                 }
267                 else{
268                         //light_sources.push_back(n2pos);
269                         light_sources.insert(n2pos, true);
270                 }
271         }
272 }
273 #endif
274
275 /*
276         Goes recursively through the neighbours of the node.
277
278         Alters only transparent nodes.
279
280         If the lighting of the neighbour is lower than the lighting of
281         the node was (before changing it to 0 at the step before), the
282         lighting of the neighbour is set to 0 and then the same stuff
283         repeats for the neighbour.
284
285         The ending nodes of the routine are stored in light_sources.
286         This is useful when a light is removed. In such case, this
287         routine can be called for the light node and then again for
288         light_sources to re-light the area without the removed light.
289
290         values of from_nodes are lighting values.
291 */
292 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
293                 core::map<v3s16, bool> & light_sources,
294                 core::map<v3s16, MapBlock*>  & modified_blocks)
295 {
296         v3s16 dirs[6] = {
297                 v3s16(0,0,1), // back
298                 v3s16(0,1,0), // top
299                 v3s16(1,0,0), // right
300                 v3s16(0,0,-1), // front
301                 v3s16(0,-1,0), // bottom
302                 v3s16(-1,0,0), // left
303         };
304         
305         if(from_nodes.size() == 0)
306                 return;
307         
308         u32 blockchangecount = 0;
309
310         core::map<v3s16, u8> unlighted_nodes;
311         core::map<v3s16, u8>::Iterator j;
312         j = from_nodes.getIterator();
313
314         /*
315                 Initialize block cache
316         */
317         v3s16 blockpos_last;
318         MapBlock *block = NULL;
319         // Cache this a bit, too
320         bool block_checked_in_modified = false;
321         
322         for(; j.atEnd() == false; j++)
323         {
324                 v3s16 pos = j.getNode()->getKey();
325                 v3s16 blockpos = getNodeBlockPos(pos);
326                 
327                 // Only fetch a new block if the block position has changed
328                 try{
329                         if(block == NULL || blockpos != blockpos_last){
330                                 block = getBlockNoCreate(blockpos);
331                                 blockpos_last = blockpos;
332
333                                 block_checked_in_modified = false;
334                                 blockchangecount++;
335                         }
336                 }
337                 catch(InvalidPositionException &e)
338                 {
339                         continue;
340                 }
341
342                 if(block->isDummy())
343                         continue;
344
345                 // Calculate relative position in block
346                 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
347
348                 // Get node straight from the block
349                 MapNode n = block->getNode(relpos);
350                 
351                 u8 oldlight = j.getNode()->getValue();
352                 
353                 // Loop through 6 neighbors
354                 for(u16 i=0; i<6; i++)
355                 {
356                         // Get the position of the neighbor node
357                         v3s16 n2pos = pos + dirs[i];
358                         
359                         // Get the block where the node is located
360                         v3s16 blockpos = getNodeBlockPos(n2pos);
361
362                         try
363                         {
364                                 // Only fetch a new block if the block position has changed
365                                 try{
366                                         if(block == NULL || blockpos != blockpos_last){
367                                                 block = getBlockNoCreate(blockpos);
368                                                 blockpos_last = blockpos;
369
370                                                 block_checked_in_modified = false;
371                                                 blockchangecount++;
372                                         }
373                                 }
374                                 catch(InvalidPositionException &e)
375                                 {
376                                         continue;
377                                 }
378                                 
379                                 // Calculate relative position in block
380                                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
381                                 // Get node straight from the block
382                                 MapNode n2 = block->getNode(relpos);
383                                 
384                                 bool changed = false;
385
386                                 //TODO: Optimize output by optimizing light_sources?
387
388                                 /*
389                                         If the neighbor is dimmer than what was specified
390                                         as oldlight (the light of the previous node)
391                                 */
392                                 if(n2.getLight() < oldlight)
393                                 {
394                                         /*
395                                                 And the neighbor is transparent and it has some light
396                                         */
397                                         if(n2.light_propagates() && n2.getLight() != 0)
398                                         {
399                                                 /*
400                                                         Set light to 0 and add to queue
401                                                 */
402
403                                                 u8 current_light = n2.getLight();
404                                                 n2.setLight(0);
405                                                 block->setNode(relpos, n2);
406
407                                                 unlighted_nodes.insert(n2pos, current_light);
408                                                 changed = true;
409
410                                                 /*
411                                                         Remove from light_sources if it is there
412                                                         NOTE: This doesn't happen nearly at all
413                                                 */
414                                                 /*if(light_sources.find(n2pos))
415                                                 {
416                                                         std::cout<<"Removed from light_sources"<<std::endl;
417                                                         light_sources.remove(n2pos);
418                                                 }*/
419                                         }
420                                 }
421                                 else{
422                                         light_sources.insert(n2pos, true);
423                                 }
424
425                                 // Add to modified_blocks
426                                 if(changed == true && block_checked_in_modified == false)
427                                 {
428                                         // If the block is not found in modified_blocks, add.
429                                         if(modified_blocks.find(blockpos) == NULL)
430                                         {
431                                                 modified_blocks.insert(blockpos, block);
432                                         }
433                                         block_checked_in_modified = true;
434                                 }
435                         }
436                         catch(InvalidPositionException &e)
437                         {
438                                 continue;
439                         }
440                 }
441         }
442
443         /*dstream<<"unspreadLight(): Changed block "
444                         <<blockchangecount<<" times"
445                         <<" for "<<from_nodes.size()<<" nodes"
446                         <<std::endl;*/
447         
448         if(unlighted_nodes.size() > 0)
449                 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
450 }
451
452 /*
453         A single-node wrapper of the above
454 */
455 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
456                 core::map<v3s16, bool> & light_sources,
457                 core::map<v3s16, MapBlock*>  & modified_blocks)
458 {
459         core::map<v3s16, u8> from_nodes;
460         from_nodes.insert(pos, lightwas);
461
462         unspreadLight(from_nodes, light_sources, modified_blocks);
463 }
464
465 /*
466         Lights neighbors of from_nodes, collects all them and then
467         goes on recursively.
468 */
469 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
470                 core::map<v3s16, MapBlock*> & modified_blocks)
471 {
472         const v3s16 dirs[6] = {
473                 v3s16(0,0,1), // back
474                 v3s16(0,1,0), // top
475                 v3s16(1,0,0), // right
476                 v3s16(0,0,-1), // front
477                 v3s16(0,-1,0), // bottom
478                 v3s16(-1,0,0), // left
479         };
480
481         if(from_nodes.size() == 0)
482                 return;
483         
484         u32 blockchangecount = 0;
485
486         core::map<v3s16, bool> lighted_nodes;
487         core::map<v3s16, bool>::Iterator j;
488         j = from_nodes.getIterator();
489
490         /*
491                 Initialize block cache
492         */
493         v3s16 blockpos_last;
494         MapBlock *block = NULL;
495         // Cache this a bit, too
496         bool block_checked_in_modified = false;
497         
498         for(; j.atEnd() == false; j++)
499         //for(; j != from_nodes.end(); j++)
500         {
501                 v3s16 pos = j.getNode()->getKey();
502                 //v3s16 pos = *j;
503                 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
504                 v3s16 blockpos = getNodeBlockPos(pos);
505                 
506                 // Only fetch a new block if the block position has changed
507                 try{
508                         if(block == NULL || blockpos != blockpos_last){
509                                 block = getBlockNoCreate(blockpos);
510                                 blockpos_last = blockpos;
511
512                                 block_checked_in_modified = false;
513                                 blockchangecount++;
514                         }
515                 }
516                 catch(InvalidPositionException &e)
517                 {
518                         continue;
519                 }
520
521                 if(block->isDummy())
522                         continue;
523
524                 // Calculate relative position in block
525                 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
526
527                 // Get node straight from the block
528                 MapNode n = block->getNode(relpos);
529
530                 u8 oldlight = n.getLight();
531                 u8 newlight = diminish_light(oldlight);
532
533                 // Loop through 6 neighbors
534                 for(u16 i=0; i<6; i++){
535                         // Get the position of the neighbor node
536                         v3s16 n2pos = pos + dirs[i];
537                         
538                         // Get the block where the node is located
539                         v3s16 blockpos = getNodeBlockPos(n2pos);
540
541                         try
542                         {
543                                 // Only fetch a new block if the block position has changed
544                                 try{
545                                         if(block == NULL || blockpos != blockpos_last){
546                                                 block = getBlockNoCreate(blockpos);
547                                                 blockpos_last = blockpos;
548
549                                                 block_checked_in_modified = false;
550                                                 blockchangecount++;
551                                         }
552                                 }
553                                 catch(InvalidPositionException &e)
554                                 {
555                                         continue;
556                                 }
557                                 
558                                 // Calculate relative position in block
559                                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
560                                 // Get node straight from the block
561                                 MapNode n2 = block->getNode(relpos);
562                                 
563                                 bool changed = false;
564                                 /*
565                                         If the neighbor is brighter than the current node,
566                                         add to list (it will light up this node on its turn)
567                                 */
568                                 if(n2.getLight() > undiminish_light(oldlight))
569                                 {
570                                         lighted_nodes.insert(n2pos, true);
571                                         //lighted_nodes.push_back(n2pos);
572                                         changed = true;
573                                 }
574                                 /*
575                                         If the neighbor is dimmer than how much light this node
576                                         would spread on it, add to list
577                                 */
578                                 if(n2.getLight() < newlight)
579                                 {
580                                         if(n2.light_propagates())
581                                         {
582                                                 n2.setLight(newlight);
583                                                 block->setNode(relpos, n2);
584                                                 lighted_nodes.insert(n2pos, true);
585                                                 //lighted_nodes.push_back(n2pos);
586                                                 changed = true;
587                                         }
588                                 }
589
590                                 // Add to modified_blocks
591                                 if(changed == true && block_checked_in_modified == false)
592                                 {
593                                         // If the block is not found in modified_blocks, add.
594                                         if(modified_blocks.find(blockpos) == NULL)
595                                         {
596                                                 modified_blocks.insert(blockpos, block);
597                                         }
598                                         block_checked_in_modified = true;
599                                 }
600                         }
601                         catch(InvalidPositionException &e)
602                         {
603                                 continue;
604                         }
605                 }
606         }
607
608         /*dstream<<"spreadLight(): Changed block "
609                         <<blockchangecount<<" times"
610                         <<" for "<<from_nodes.size()<<" nodes"
611                         <<std::endl;*/
612         
613         if(lighted_nodes.size() > 0)
614                 spreadLight(lighted_nodes, modified_blocks);
615 }
616
617 /*
618         A single-node source variation of the above.
619 */
620 void Map::lightNeighbors(v3s16 pos,
621                 core::map<v3s16, MapBlock*> & modified_blocks)
622 {
623         core::map<v3s16, bool> from_nodes;
624         from_nodes.insert(pos, true);
625         spreadLight(from_nodes, modified_blocks);
626 }
627
628 v3s16 Map::getBrightestNeighbour(v3s16 p)
629 {
630         v3s16 dirs[6] = {
631                 v3s16(0,0,1), // back
632                 v3s16(0,1,0), // top
633                 v3s16(1,0,0), // right
634                 v3s16(0,0,-1), // front
635                 v3s16(0,-1,0), // bottom
636                 v3s16(-1,0,0), // left
637         };
638         
639         u8 brightest_light = 0;
640         v3s16 brightest_pos(0,0,0);
641         bool found_something = false;
642
643         // Loop through 6 neighbors
644         for(u16 i=0; i<6; i++){
645                 // Get the position of the neighbor node
646                 v3s16 n2pos = p + dirs[i];
647                 MapNode n2;
648                 try{
649                         n2 = getNode(n2pos);
650                 }
651                 catch(InvalidPositionException &e)
652                 {
653                         continue;
654                 }
655                 if(n2.getLight() > brightest_light || found_something == false){
656                         brightest_light = n2.getLight();
657                         brightest_pos = n2pos;
658                         found_something = true;
659                 }
660         }
661
662         if(found_something == false)
663                 throw InvalidPositionException();
664                 
665         return brightest_pos;
666 }
667
668 /*
669         Propagates sunlight down from a node.
670         Starting point gets sunlight.
671
672         Returns the lowest y value of where the sunlight went.
673 */
674 s16 Map::propagateSunlight(v3s16 start,
675                 core::map<v3s16, MapBlock*> & modified_blocks)
676 {
677         s16 y = start.Y;
678         for(; ; y--)
679         {
680                 v3s16 pos(start.X, y, start.Z);
681                 
682                 v3s16 blockpos = getNodeBlockPos(pos);
683                 MapBlock *block;
684                 try{
685                         block = getBlockNoCreate(blockpos);
686                 }
687                 catch(InvalidPositionException &e)
688                 {
689                         break;
690                 }
691
692                 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
693                 MapNode n = block->getNode(relpos);
694
695                 if(n.sunlight_propagates())
696                 {
697                         n.setLight(LIGHT_SUN);
698                         block->setNode(relpos, n);
699
700                         modified_blocks.insert(blockpos, block);
701                 }
702                 else{
703                         break;
704                 }
705         }
706         return y + 1;
707 }
708
709 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
710                 core::map<v3s16, MapBlock*> & modified_blocks)
711 {
712         /*m_dout<<DTIME<<"Map::updateLighting(): "
713                         <<a_blocks.getSize()<<" blocks... ";*/
714         
715         // For debugging
716         bool debug=false;
717         u32 count_was = modified_blocks.size();
718
719         /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
720         for(; i != a_blocks.end(); i++)
721         {
722                 MapBlock *block = *i;*/
723
724         core::map<v3s16, bool> light_sources;
725         
726         core::map<v3s16, u8> unlight_from;
727                 
728         core::map<v3s16, MapBlock*>::Iterator i;
729         i = a_blocks.getIterator();
730         for(; i.atEnd() == false; i++)
731         {
732                 MapBlock *block = i.getNode()->getValue();
733                 
734                 for(;;)
735                 {
736                         // Don't bother with dummy blocks.
737                         if(block->isDummy())
738                                 break;
739                 
740                         v3s16 pos = block->getPos();
741                         modified_blocks.insert(pos, block);
742
743                         /*
744                                 Clear all light from block
745                         */
746                         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
747                         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
748                         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
749                         {
750                                 
751                                 try{
752                                         v3s16 p(x,y,z);
753                                         MapNode n = block->getNode(v3s16(x,y,z));
754                                         u8 oldlight = n.getLight();
755                                         n.setLight(0);
756                                         block->setNode(v3s16(x,y,z), n);
757                                         
758                                         // Collect borders for unlighting
759                                         if(x==0 || x == MAP_BLOCKSIZE-1
760                                                         || y==0 || y == MAP_BLOCKSIZE-1
761                                                         || z==0 || z == MAP_BLOCKSIZE-1)
762                                         {
763                                                 v3s16 p_map = p + v3s16(
764                                                                 MAP_BLOCKSIZE*pos.X,
765                                                                 MAP_BLOCKSIZE*pos.Y,
766                                                                 MAP_BLOCKSIZE*pos.Z);
767                                                 unlight_from.insert(p_map, oldlight);
768                                         }
769                                 }
770                                 catch(InvalidPositionException &e)
771                                 {
772                                         /*
773                                                 This would happen when dealing with a
774                                                 dummy block.
775                                         */
776                                         //assert(0);
777                                         dstream<<"updateLighting(): InvalidPositionException"
778                                                         <<std::endl;
779                                 }
780                         }
781                         
782                         bool bottom_valid = block->propagateSunlight(light_sources);
783
784                         // If bottom is valid, we're done.
785                         if(bottom_valid)
786                                 break;
787                                 
788                         /*dstream<<"Bottom for sunlight-propagated block ("
789                                         <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
790                                         <<std::endl;*/
791
792                         // Else get the block below and loop to it
793
794                         pos.Y--;
795                         try{
796                                 block = getBlockNoCreate(pos);
797                         }
798                         catch(InvalidPositionException &e)
799                         {
800                                 assert(0);
801                         }
802                         
803                 }
804         }
805         
806         {
807                 //TimeTaker timer("unspreadLight", g_device);
808                 unspreadLight(unlight_from, light_sources, modified_blocks);
809         }
810         
811         if(debug)
812         {
813                 u32 diff = modified_blocks.size() - count_was;
814                 count_was = modified_blocks.size();
815                 dstream<<"unspreadLight modified "<<diff<<std::endl;
816         }
817
818         // TODO: Spread light from propagated sunlight?
819         // Yes, add it to light_sources... somehow.
820         // It has to be added at somewhere above, in the loop.
821         // TODO
822
823         {
824                 //TimeTaker timer("spreadLight", g_device);
825                 spreadLight(light_sources, modified_blocks);
826         }
827         
828         if(debug)
829         {
830                 u32 diff = modified_blocks.size() - count_was;
831                 count_was = modified_blocks.size();
832                 dstream<<"spreadLight modified "<<diff<<std::endl;
833         }
834
835         //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
836 }
837
838 /*
839         This is called after changing a node from transparent to opaque.
840         The lighting value of the node should be left as-is after changing
841         other values. This sets the lighting value to 0.
842 */
843 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
844                 core::map<v3s16, MapBlock*> &modified_blocks)*/
845 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
846                 core::map<v3s16, MapBlock*> &modified_blocks)
847 {
848         /*PrintInfo(m_dout);
849         m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
850                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
851
852         u8 lightwas = getNode(p).getLight();
853
854         //core::list<v3s16> light_sources;
855         core::map<v3s16, bool> light_sources;
856         //MapNode n = getNode(p);
857
858         /*
859                 From this node to nodes underneath:
860                 If lighting is sunlight (1.0), unlight neighbours and
861                 set lighting to 0.
862                 Else discontinue.
863         */
864
865         bool node_under_sunlight = true;
866         
867         v3s16 toppos = p + v3s16(0,1,0);
868
869         /*
870                 If there is a node at top and it doesn't have sunlight,
871                 there has not been any sunlight going down.
872
873                 Otherwise there probably is.
874         */
875         try{
876                 MapNode topnode = getNode(toppos);
877
878                 if(topnode.getLight() != LIGHT_SUN)
879                         node_under_sunlight = false;
880         }
881         catch(InvalidPositionException &e)
882         {
883         }
884
885         // Add the block of the added node to modified_blocks
886         v3s16 blockpos = getNodeBlockPos(p);
887         MapBlock * block = getBlockNoCreate(blockpos);
888         assert(block != NULL);
889         modified_blocks.insert(blockpos, block);
890         
891         if(isValidPosition(p) == false)
892                 throw;
893                 
894         // Unlight neighbours of node.
895         // This means setting light of all consequent dimmer nodes
896         // to 0.
897         // This also collects the nodes at the border which will spread
898         // light again into this.
899         unLightNeighbors(p, lightwas, light_sources, modified_blocks);
900
901         n.setLight(0);
902         setNode(p, n);
903         
904         /*
905                 If node is under sunlight, take all sunlighted nodes under
906                 it and clear light from them and from where the light has
907                 been spread.
908         */
909         if(node_under_sunlight)
910         {
911                 s16 y = p.Y - 1;
912                 for(;; y--){
913                         //m_dout<<DTIME<<"y="<<y<<std::endl;
914                         v3s16 n2pos(p.X, y, p.Z);
915                         
916                         MapNode n2;
917                         try{
918                                 n2 = getNode(n2pos);
919                         }
920                         catch(InvalidPositionException &e)
921                         {
922                                 break;
923                         }
924
925                         if(n2.getLight() == LIGHT_SUN)
926                         {
927                                 //m_dout<<DTIME<<"doing"<<std::endl;
928                                 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
929                                 n2.setLight(0);
930                                 setNode(n2pos, n2);
931                         }
932                         else
933                                 break;
934                 }
935         }
936         
937         /*
938                 Spread light from all nodes that might be capable of doing so
939                 TODO: Convert to spreadLight
940         */
941         spreadLight(light_sources, modified_blocks);
942 }
943
944 /*
945 */
946 void Map::removeNodeAndUpdate(v3s16 p,
947                 core::map<v3s16, MapBlock*> &modified_blocks)
948 {
949         /*PrintInfo(m_dout);
950         m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
951                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
952         
953         bool node_under_sunlight = true;
954         
955         v3s16 toppos = p + v3s16(0,1,0);
956         
957         /*
958                 If there is a node at top and it doesn't have sunlight,
959                 there will be no sunlight going down.
960         */
961         try{
962                 MapNode topnode = getNode(toppos);
963
964                 if(topnode.getLight() != LIGHT_SUN)
965                         node_under_sunlight = false;
966         }
967         catch(InvalidPositionException &e)
968         {
969         }
970
971         /*
972                 Unlight neighbors (in case the node is a light source)
973         */
974         //core::list<v3s16> light_sources;
975         core::map<v3s16, bool> light_sources;
976         unLightNeighbors(p, getNode(p).getLight(),
977                         light_sources, modified_blocks);
978
979         /*
980                 Remove the node
981         */
982         MapNode n;
983         n.d = MATERIAL_AIR;
984         n.setLight(0);
985         setNode(p, n);
986         
987         /*
988                 Recalculate lighting
989         */
990         spreadLight(light_sources, modified_blocks);
991
992         // Add the block of the removed node to modified_blocks
993         v3s16 blockpos = getNodeBlockPos(p);
994         MapBlock * block = getBlockNoCreate(blockpos);
995         assert(block != NULL);
996         modified_blocks.insert(blockpos, block);
997
998         /*
999                 If the removed node was under sunlight, propagate the
1000                 sunlight down from it and then light all neighbors
1001                 of the propagated blocks.
1002         */
1003         if(node_under_sunlight)
1004         {
1005                 s16 ybottom = propagateSunlight(p, modified_blocks);
1006                 /*m_dout<<DTIME<<"Node was under sunlight. "
1007                                 "Propagating sunlight";
1008                 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1009                 s16 y = p.Y;
1010                 for(; y >= ybottom; y--)
1011                 {
1012                         v3s16 p2(p.X, y, p.Z);
1013                         /*m_dout<<DTIME<<"lighting neighbors of node ("
1014                                         <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1015                                         <<std::endl;*/
1016                         lightNeighbors(p2, modified_blocks);
1017                 }
1018         }
1019         else
1020         {
1021                 // Set the lighting of this node to 0
1022                 try{
1023                         MapNode n = getNode(p);
1024                         n.setLight(0);
1025                         setNode(p, n);
1026                 }
1027                 catch(InvalidPositionException &e)
1028                 {
1029                         throw;
1030                 }
1031         }
1032
1033         // Get the brightest neighbour node and propagate light from it
1034         v3s16 n2p = getBrightestNeighbour(p);
1035         try{
1036                 MapNode n2 = getNode(n2p);
1037                 lightNeighbors(n2p, modified_blocks);
1038         }
1039         catch(InvalidPositionException &e)
1040         {
1041         }
1042 }
1043
1044 void Map::updateMeshes(v3s16 blockpos)
1045 {
1046         assert(mapType() == MAPTYPE_CLIENT);
1047
1048         try{
1049                 v3s16 p = blockpos + v3s16(0,0,0);
1050                 MapBlock *b = getBlockNoCreate(p);
1051                 b->updateMesh();
1052         }
1053         catch(InvalidPositionException &e){}
1054         try{
1055                 v3s16 p = blockpos + v3s16(-1,0,0);
1056                 MapBlock *b = getBlockNoCreate(p);
1057                 b->updateMesh();
1058         }
1059         catch(InvalidPositionException &e){}
1060         try{
1061                 v3s16 p = blockpos + v3s16(0,-1,0);
1062                 MapBlock *b = getBlockNoCreate(p);
1063                 b->updateMesh();
1064         }
1065         catch(InvalidPositionException &e){}
1066         try{
1067                 v3s16 p = blockpos + v3s16(0,0,-1);
1068                 MapBlock *b = getBlockNoCreate(p);
1069                 b->updateMesh();
1070         }
1071         catch(InvalidPositionException &e){}
1072 }
1073
1074 /*
1075         Updates usage timers
1076 */
1077 void Map::timerUpdate(float dtime)
1078 {
1079         JMutexAutoLock lock(m_sector_mutex);
1080
1081         core::map<v2s16, MapSector*>::Iterator si;
1082
1083         si = m_sectors.getIterator();
1084         for(; si.atEnd() == false; si++)
1085         {
1086                 MapSector *sector = si.getNode()->getValue();
1087                 sector->usage_timer += dtime;
1088         }
1089 }
1090
1091 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1092 {
1093         core::list<v2s16>::Iterator j;
1094         for(j=list.begin(); j!=list.end(); j++)
1095         {
1096                 MapSector *sector = m_sectors[*j];
1097                 if(only_blocks)
1098                 {
1099                         sector->deleteBlocks();
1100                 }
1101                 else
1102                 {
1103                         /*
1104                                 If sector is in sector cache, remove it from there
1105                         */
1106                         if(m_sector_cache == sector)
1107                         {
1108                                 m_sector_cache = NULL;
1109                         }
1110                         /*
1111                                 Remove from map and delete
1112                         */
1113                         m_sectors.remove(*j);
1114                         delete sector;
1115                 }
1116         }
1117 }
1118
1119 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1120                 core::list<v3s16> *deleted_blocks)
1121 {
1122         JMutexAutoLock lock(m_sector_mutex);
1123
1124         core::list<v2s16> sector_deletion_queue;
1125         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1126         for(; i.atEnd() == false; i++)
1127         {
1128                 MapSector *sector = i.getNode()->getValue();
1129                 /*
1130                         Delete sector from memory if it hasn't been used in a long time
1131                 */
1132                 if(sector->usage_timer > timeout)
1133                 {
1134                         sector_deletion_queue.push_back(i.getNode()->getKey());
1135                         
1136                         if(deleted_blocks != NULL)
1137                         {
1138                                 // Collect positions of blocks of sector
1139                                 MapSector *sector = i.getNode()->getValue();
1140                                 core::list<MapBlock*> blocks;
1141                                 sector->getBlocks(blocks);
1142                                 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1143                                                 i != blocks.end(); i++)
1144                                 {
1145                                         deleted_blocks->push_back((*i)->getPos());
1146                                 }
1147                         }
1148                 }
1149         }
1150         deleteSectors(sector_deletion_queue, only_blocks);
1151         return sector_deletion_queue.getSize();
1152 }
1153
1154 void Map::PrintInfo(std::ostream &out)
1155 {
1156         out<<"Map: ";
1157 }
1158
1159 /*
1160         ServerMap
1161 */
1162
1163 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1164         Map(dout_server),
1165         m_heightmap(NULL)
1166 {
1167         m_savedir = savedir;
1168         m_map_saving_enabled = false;
1169         
1170         try
1171         {
1172                 // If directory exists, check contents and load if possible
1173                 if(fs::PathExists(m_savedir))
1174                 {
1175                         // If directory is empty, it is safe to save into it.
1176                         if(fs::GetDirListing(m_savedir).size() == 0)
1177                         {
1178                                 dstream<<DTIME<<"Server: Empty save directory is valid."
1179                                                 <<std::endl;
1180                                 m_map_saving_enabled = true;
1181                         }
1182                         else
1183                         {
1184                                 // Load master heightmap
1185                                 loadMasterHeightmap();
1186                                 
1187                                 // Load sector (0,0) and throw and exception on fail
1188                                 if(loadSectorFull(v2s16(0,0)) == false)
1189                                         throw LoadError("Failed to load sector (0,0)");
1190
1191                                 dstream<<DTIME<<"Server: Successfully loaded master "
1192                                                 "heightmap and sector (0,0) from "<<savedir<<
1193                                                 ", assuming valid save directory."
1194                                                 <<std::endl;
1195
1196                                 m_map_saving_enabled = true;
1197                                 // Map loaded, not creating new one
1198                                 return;
1199                         }
1200                 }
1201                 // If directory doesn't exist, it is safe to save to it
1202                 else{
1203                         m_map_saving_enabled = true;
1204                 }
1205         }
1206         catch(std::exception &e)
1207         {
1208                 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1209                                 <<", exception: "<<e.what()<<std::endl;
1210                 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1211                 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1212         }
1213
1214         dstream<<DTIME<<"Initializing new map."<<std::endl;
1215         
1216         // Create master heightmap
1217         ValueGenerator *maxgen =
1218                         ValueGenerator::deSerialize(hmp.height_randmax);
1219         ValueGenerator *factorgen =
1220                         ValueGenerator::deSerialize(hmp.height_randfactor);
1221         ValueGenerator *basegen =
1222                         ValueGenerator::deSerialize(hmp.height_base);
1223         m_heightmap = new UnlimitedHeightmap
1224                         (hmp.heightmap_blocksize, maxgen, factorgen, basegen);
1225         
1226         // Set map parameters
1227         m_params = mp;
1228         
1229         // Create zero sector
1230         emergeSector(v2s16(0,0));
1231
1232         // Initially write whole map
1233         save(false);
1234 }
1235
1236 ServerMap::~ServerMap()
1237 {
1238         try
1239         {
1240                 if(m_map_saving_enabled)
1241                 {
1242                         //save(false);
1243                         // Save only changed parts
1244                         save(true);
1245                         dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1246                 }
1247                 else
1248                 {
1249                         dstream<<DTIME<<"Server: map not saved"<<std::endl;
1250                 }
1251         }
1252         catch(std::exception &e)
1253         {
1254                 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1255                                 <<", exception: "<<e.what()<<std::endl;
1256         }
1257         
1258         if(m_heightmap != NULL)
1259                 delete m_heightmap;
1260 }
1261
1262 MapSector * ServerMap::emergeSector(v2s16 p2d)
1263 {
1264         DSTACK("%s: p2d=(%d,%d)",
1265                         __FUNCTION_NAME,
1266                         p2d.X, p2d.Y);
1267         // Check that it doesn't exist already
1268         try{
1269                 return getSectorNoGenerate(p2d);
1270         }
1271         catch(InvalidPositionException &e)
1272         {
1273         }
1274         
1275         /*
1276                 Try to load the sector from disk.
1277         */
1278         if(loadSectorFull(p2d) == true)
1279         {
1280                 return getSectorNoGenerate(p2d);
1281         }
1282
1283         /*
1284                 If there is no master heightmap, throw.
1285         */
1286         if(m_heightmap == NULL)
1287         {
1288                 throw InvalidPositionException("emergeSector(): no heightmap");
1289         }
1290
1291         /*
1292                 Do not generate over-limit
1293         */
1294         if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1295         || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1296         || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1297         || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1298                 throw InvalidPositionException("emergeSector(): pos. over limit");
1299
1300         /*
1301                 Generate sector and heightmaps
1302         */
1303         
1304         // Number of heightmaps in sector in each direction
1305         u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1306
1307         // Heightmap side width
1308         s16 hm_d = MAP_BLOCKSIZE / hm_split;
1309
1310         ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1311
1312         /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1313                         " heightmaps and objects"<<std::endl;*/
1314         
1315         // Loop through sub-heightmaps
1316         for(s16 y=0; y<hm_split; y++)
1317         for(s16 x=0; x<hm_split; x++)
1318         {
1319                 v2s16 p_in_sector = v2s16(x,y);
1320                 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1321                 f32 corners[4] = {
1322                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1323                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1324                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1325                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1326                 };
1327
1328                 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1329                                 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1330                                 <<std::endl;*/
1331
1332                 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1333                                 mhm_p, hm_d);
1334                 sector->setHeightmap(p_in_sector, hm);
1335
1336                 //TODO: Make these values configurable
1337                 hm->generateContinued(1.0, 0.2, corners);
1338                 //hm->generateContinued(2.0, 0.2, corners);
1339
1340                 //hm->print();
1341                 
1342         }
1343
1344         /*
1345                 Generate objects
1346         */
1347         
1348         core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1349         sector->setObjects(objects);
1350         
1351         v2s16 mhm_p = p2d * hm_split;
1352         f32 corners[4] = {
1353                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1354                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1355                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1356                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1357         };
1358         
1359         float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1360         float avgslope = 0.0;
1361         avgslope += fabs(avgheight - corners[0]);
1362         avgslope += fabs(avgheight - corners[1]);
1363         avgslope += fabs(avgheight - corners[2]);
1364         avgslope += fabs(avgheight - corners[3]);
1365         avgslope /= 4.0;
1366         avgslope /= MAP_BLOCKSIZE;
1367         //dstream<<"avgslope="<<avgslope<<std::endl;
1368
1369         float pitness = 0.0;
1370         v2f32 a;
1371         a = m_heightmap->getSlope(p2d+v2s16(0,0));
1372         pitness += -a.X;
1373         pitness += -a.Y;
1374         a = m_heightmap->getSlope(p2d+v2s16(0,1));
1375         pitness += -a.X;
1376         pitness += a.Y;
1377         a = m_heightmap->getSlope(p2d+v2s16(1,1));
1378         pitness += a.X;
1379         pitness += a.Y;
1380         a = m_heightmap->getSlope(p2d+v2s16(1,0));
1381         pitness += a.X;
1382         pitness += -a.Y;
1383         pitness /= 4.0;
1384         pitness /= MAP_BLOCKSIZE;
1385         //dstream<<"pitness="<<pitness<<std::endl;
1386         
1387         /*
1388                 Plant some trees if there is not much slope
1389         */
1390         {
1391                 // Avgslope is the derivative of a hill
1392                 float t = avgslope * avgslope;
1393                 float a = MAP_BLOCKSIZE * 2 * m_params.plants_amount;
1394                 u32 tree_max;
1395                 if(t > 0.03)
1396                         tree_max = a / (t/0.03);
1397                 else
1398                         tree_max = a;
1399                 u32 count = (rand()%(tree_max+1));
1400                 //u32 count = tree_max;
1401                 for(u32 i=0; i<count; i++)
1402                 {
1403                         s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1404                         s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1405                         s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1406                         if(y < WATER_LEVEL)
1407                                 continue;
1408                         objects->insert(v3s16(x, y, z),
1409                                         SECTOR_OBJECT_TREE_1);
1410                 }
1411         }
1412         {
1413                 // Pitness usually goes at around -0.5...0.5
1414                 u32 bush_max = 0;
1415                 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1416                 if(pitness > 0)
1417                         bush_max = (pitness*a*4);
1418                 if(bush_max > a)
1419                         bush_max = a;
1420                 u32 count = (rand()%(bush_max+1));
1421                 for(u32 i=0; i<count; i++)
1422                 {
1423                         s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1424                         s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1425                         s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1426                         if(y < WATER_LEVEL)
1427                                 continue;
1428                         objects->insert(v3s16(x, y, z),
1429                                         SECTOR_OBJECT_BUSH_1);
1430                 }
1431         }
1432
1433         /*
1434                 Insert to container
1435         */
1436         JMutexAutoLock lock(m_sector_mutex);
1437         m_sectors.insert(p2d, sector);
1438         
1439         return sector;
1440 }
1441
1442 MapBlock * ServerMap::emergeBlock(
1443                 v3s16 p,
1444                 bool only_from_disk,
1445                 core::map<v3s16, MapBlock*> &changed_blocks,
1446                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1447 )
1448 {
1449         DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1450                         __FUNCTION_NAME,
1451                         p.X, p.Y, p.Z, only_from_disk);
1452                         
1453         /*dstream<<"ServerMap::emergeBlock(): "
1454                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1455                         <<", only_from_disk="<<only_from_disk<<std::endl;*/
1456         v2s16 p2d(p.X, p.Z);
1457         s16 block_y = p.Y;
1458         /*
1459                 This will create or load a sector if not found in memory.
1460                 If block exists on disk, it will be loaded.
1461
1462                 NOTE: On old save formats, this will be slow, as it generates
1463                       lighting on blocks for them.
1464         */
1465         ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1466         assert(sector->getId() == MAPSECTOR_SERVER);
1467
1468         // Try to get a block from the sector
1469         MapBlock *block = NULL;
1470         bool not_on_disk = false;
1471         try{
1472                 block = sector->getBlockNoCreate(block_y);
1473                 if(block->isDummy() == true)
1474                         not_on_disk = true;
1475                 else
1476                         return block;
1477         }
1478         catch(InvalidPositionException &e)
1479         {
1480                 not_on_disk = true;
1481         }
1482         
1483         /*
1484                 If block was not found on disk and not going to generate a
1485                 new one, make sure there is a dummy block in place.
1486         */
1487         if(not_on_disk && only_from_disk)
1488         {
1489                 if(block == NULL)
1490                 {
1491                         // Create dummy block
1492                         block = new MapBlock(this, p, true);
1493
1494                         // Add block to sector
1495                         sector->insertBlock(block);
1496                 }
1497                 // Done.
1498                 return block;
1499         }
1500
1501         //dstream<<"Not found on disk, generating."<<std::endl;
1502
1503         /*
1504                 Do not generate over-limit
1505         */
1506         if(blockpos_over_limit(p))
1507                 throw InvalidPositionException("emergeBlock(): pos. over limit");
1508
1509         /*
1510                 OK; Not found.
1511
1512                 Go on generating the block.
1513
1514                 TODO: If a dungeon gets generated so that it's side gets
1515                       revealed to the outside air, the lighting should be
1516                           recalculated.
1517         */
1518         
1519         /*
1520                 If block doesn't exist, create one.
1521                 If it exists, it is a dummy. In that case unDummify() it.
1522         */
1523         if(block == NULL)
1524         {
1525                 block = sector->createBlankBlockNoInsert(block_y);
1526         }
1527         else
1528         {
1529                 // Remove the block so that nobody can get a half-generated one.
1530                 sector->removeBlock(block);
1531                 // Allocate the block to be a proper one.
1532                 block->unDummify();
1533         }
1534
1535         // Randomize a bit. This makes dungeons.
1536         bool low_block_is_empty = false;
1537         if(rand() % 4 == 0)
1538                 low_block_is_empty = true;
1539         
1540         // This is the basic material of what the visible flat ground
1541         // will consist of
1542         u8 material = MATERIAL_GRASS;
1543         
1544         s32 lowest_ground_y = 32767;
1545         
1546         // DEBUG
1547         //sector->printHeightmaps();
1548
1549         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1550         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1551         {
1552                 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1553                 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1554
1555                 assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1556                 
1557                 s16 surface_y = surface_y_f;
1558                 //avg_ground_y += surface_y;
1559                 if(surface_y < lowest_ground_y)
1560                         lowest_ground_y = surface_y;
1561
1562                 s32 surface_depth = 0;
1563                 
1564                 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1565                 
1566                 float min_slope = 0.45;
1567                 float max_slope = 0.85;
1568                 float min_slope_depth = 5.0;
1569                 float max_slope_depth = 0;
1570                 if(slope < min_slope)
1571                         surface_depth = min_slope_depth;
1572                 else if(slope > max_slope)
1573                         surface_depth = max_slope_depth;
1574                 else
1575                         surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1576
1577                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++){
1578                         s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1579                         MapNode n;
1580                         /*
1581                                 Calculate lighting
1582                                 
1583                                 FIXME: If there are some man-made structures above the
1584                                 newly created block, they won't be taken into account.
1585                         */
1586                         if(real_y > surface_y)
1587                                 n.setLight(LIGHT_SUN);
1588                         /*
1589                                 Calculate material
1590                         */
1591                         // If node is very low
1592                         if(real_y <= surface_y - 10){
1593                                 // Create dungeons
1594                                 if(low_block_is_empty){
1595                                         n.d = MATERIAL_AIR;
1596                                 }
1597                                 else{
1598                                         n.d = MATERIAL_STONE;
1599                                 }
1600                         }
1601                         // If node is under surface level
1602                         else if(real_y <= surface_y - surface_depth)
1603                                 n.d = MATERIAL_STONE;
1604                         // If node is at or under heightmap y
1605                         else if(real_y <= surface_y)
1606                                 n.d = material;
1607                         // If node is over heightmap y
1608                         else{
1609                                 // If under water level, it's water
1610                                 if(real_y < WATER_LEVEL)
1611                                 {
1612                                         n.d = MATERIAL_WATER;
1613                                         n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1614                                 }
1615                                 // else air
1616                                 else
1617                                         n.d = MATERIAL_AIR;
1618                         }
1619                         block->setNode(v3s16(x0,y0,z0), n);
1620                 }
1621         }
1622
1623         /*
1624                 Calculate is_underground
1625         */
1626         // Probably underground if the highest part of block is under lowest
1627         // ground height
1628         bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1629         block->setIsUnderground(is_underground);
1630
1631         /*
1632                 Add some minerals
1633         */
1634
1635         if(is_underground && low_block_is_empty == false)
1636         {
1637                 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1638                 for(s16 i=0; i<underground_level*3; i++)
1639                 {
1640                         if(rand()%2 == 0)
1641                         {
1642                                 v3s16 cp(
1643                                         /*(rand()%(MAP_BLOCKSIZE-4))+2,
1644                                         (rand()%(MAP_BLOCKSIZE-4))+2,
1645                                         (rand()%(MAP_BLOCKSIZE-4))+2*/
1646                                         (rand()%(MAP_BLOCKSIZE-2))+1,
1647                                         (rand()%(MAP_BLOCKSIZE-2))+1,
1648                                         (rand()%(MAP_BLOCKSIZE-2))+1
1649                                 );
1650
1651                                 MapNode n;
1652                                 n.d = MATERIAL_MESE;
1653                                 
1654                                 if(rand()%8 == 0)
1655                                         block->setNode(cp, n);
1656
1657                                 for(u16 i=0; i<26; i++)
1658                                 {
1659                                         if(rand()%8 == 0)
1660                                                 block->setNode(cp+g_26dirs[i], n);
1661                                 }
1662                         }
1663                 }
1664         }
1665         
1666         /*
1667                 Create a few rats in empty blocks underground
1668         */
1669         if(is_underground && low_block_is_empty == true)
1670         {
1671                 //for(u16 i=0; i<2; i++)
1672                 {
1673                         v3s16 pos(8, 1, 8);
1674                         RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1675                         block->addObject(obj);
1676                 }
1677         }
1678         
1679         /*
1680                 TODO: REMOVE
1681                 DEBUG
1682                 Add some objects to the block for testing.
1683         */
1684         /*if(p == v3s16(0,0,0))
1685         {
1686                 //TestObject *obj = new TestObject(NULL, -1, v3f(BS*8,BS*8,BS*8));
1687                 Test2Object *obj = new Test2Object(NULL, -1, v3f(BS*8,BS*15,BS*8));
1688                 block->addObject(obj);
1689         }*/
1690         
1691         /*
1692         {
1693                 v3s16 pos(8, 11, 8);
1694                 SignObject *obj = new SignObject(NULL, -1, intToFloat(pos));
1695                 obj->setText("Moicka");
1696                 obj->setYaw(45);
1697                 block->addObject(obj);
1698         }
1699
1700         {
1701                 v3s16 pos(8, 11, 8);
1702                 RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1703                 block->addObject(obj);
1704         }
1705         */
1706
1707         /*
1708                 Add block to sector.
1709         */
1710         sector->insertBlock(block);
1711         
1712         // An y-wise container if changed blocks
1713         core::map<s16, MapBlock*> changed_blocks_sector;
1714
1715         /*
1716                 Check if any sector's objects can be placed now.
1717                 If so, place them.
1718         */
1719         core::map<v3s16, u8> *objects = sector->getObjects();
1720         core::list<v3s16> objects_to_remove;
1721         for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1722                         i.atEnd() == false; i++)
1723         {
1724                 v3s16 p = i.getNode()->getKey();
1725                 u8 d = i.getNode()->getValue();
1726
1727                 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1728                 
1729                 try
1730                 {
1731
1732                 if(d == SECTOR_OBJECT_TEST)
1733                 {
1734                         if(sector->isValidArea(p + v3s16(0,0,0),
1735                                         p + v3s16(0,0,0), &changed_blocks_sector))
1736                         {
1737                                 MapNode n;
1738                                 n.d = MATERIAL_LIGHT;
1739                                 sector->setNode(p, n);
1740                                 objects_to_remove.push_back(p);
1741                         }
1742                 }
1743                 else if(d == SECTOR_OBJECT_TREE_1)
1744                 {
1745                         v3s16 p_min = p + v3s16(-1,0,-1);
1746                         v3s16 p_max = p + v3s16(1,4,1);
1747                         if(sector->isValidArea(p_min, p_max,
1748                                         &changed_blocks_sector))
1749                         {
1750                                 MapNode n;
1751                                 n.d = MATERIAL_TREE;
1752                                 sector->setNode(p+v3s16(0,0,0), n);
1753                                 sector->setNode(p+v3s16(0,1,0), n);
1754                                 sector->setNode(p+v3s16(0,2,0), n);
1755                                 sector->setNode(p+v3s16(0,3,0), n);
1756
1757                                 n.d = MATERIAL_LEAVES;
1758
1759                                 sector->setNode(p+v3s16(0,4,0), n);
1760                                 
1761                                 sector->setNode(p+v3s16(-1,4,0), n);
1762                                 sector->setNode(p+v3s16(1,4,0), n);
1763                                 sector->setNode(p+v3s16(0,4,-1), n);
1764                                 sector->setNode(p+v3s16(0,4,1), n);
1765                                 sector->setNode(p+v3s16(1,4,1), n);
1766                                 sector->setNode(p+v3s16(-1,4,1), n);
1767                                 sector->setNode(p+v3s16(-1,4,-1), n);
1768                                 sector->setNode(p+v3s16(1,4,-1), n);
1769
1770                                 sector->setNode(p+v3s16(-1,3,0), n);
1771                                 sector->setNode(p+v3s16(1,3,0), n);
1772                                 sector->setNode(p+v3s16(0,3,-1), n);
1773                                 sector->setNode(p+v3s16(0,3,1), n);
1774                                 sector->setNode(p+v3s16(1,3,1), n);
1775                                 sector->setNode(p+v3s16(-1,3,1), n);
1776                                 sector->setNode(p+v3s16(-1,3,-1), n);
1777                                 sector->setNode(p+v3s16(1,3,-1), n);
1778                                 
1779                                 objects_to_remove.push_back(p);
1780                                 
1781                                 // Lighting has to be recalculated for this one.
1782                                 sector->getBlocksInArea(p_min, p_max, 
1783                                                 lighting_invalidated_blocks);
1784                         }
1785                 }
1786                 else if(d == SECTOR_OBJECT_BUSH_1)
1787                 {
1788                         if(sector->isValidArea(p + v3s16(0,0,0),
1789                                         p + v3s16(0,0,0), &changed_blocks_sector))
1790                         {
1791                                 MapNode n;
1792                                 n.d = MATERIAL_LEAVES;
1793                                 sector->setNode(p+v3s16(0,0,0), n);
1794                                 
1795                                 objects_to_remove.push_back(p);
1796                         }
1797                 }
1798                 else
1799                 {
1800                         dstream<<"ServerMap::emergeBlock(): "
1801                                         "Invalid heightmap object"
1802                                         <<std::endl;
1803                 }
1804
1805                 }//try
1806                 catch(InvalidPositionException &e)
1807                 {
1808                         dstream<<"WARNING: "<<__FUNCTION_NAME
1809                                         <<": while inserting object "<<(int)d
1810                                         <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1811                                         <<" InvalidPositionException.what()="
1812                                         <<e.what()<<std::endl;
1813                         // This is not too fatal and seems to happen sometimes.
1814                         assert(0);
1815                 }
1816         }
1817
1818         for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
1819                         i != objects_to_remove.end(); i++)
1820         {
1821                 objects->remove(*i);
1822         }
1823
1824         for(core::map<s16, MapBlock*>::Iterator
1825                         i = changed_blocks_sector.getIterator();
1826                         i.atEnd() == false; i++)
1827         {
1828                 MapBlock *block = i.getNode()->getValue();
1829
1830                 changed_blocks.insert(block->getPos(), block);
1831         }
1832
1833         return block;
1834 }
1835
1836 void ServerMap::createDir(std::string path)
1837 {
1838         if(fs::CreateDir(path) == false)
1839         {
1840                 m_dout<<DTIME<<"ServerMap: Failed to create directory "
1841                                 <<"\""<<path<<"\""<<std::endl;
1842                 throw BaseException("ServerMap failed to create directory");
1843         }
1844 }
1845
1846 std::string ServerMap::getSectorSubDir(v2s16 pos)
1847 {
1848         char cc[9];
1849         snprintf(cc, 9, "%.4x%.4x",
1850                         (unsigned int)pos.X&0xffff,
1851                         (unsigned int)pos.Y&0xffff);
1852
1853         return std::string(cc);
1854 }
1855
1856 std::string ServerMap::getSectorDir(v2s16 pos)
1857 {
1858         return m_savedir + "/sectors/" + getSectorSubDir(pos);
1859 }
1860
1861 v2s16 ServerMap::getSectorPos(std::string dirname)
1862 {
1863         if(dirname.size() != 8)
1864                 throw InvalidFilenameException("Invalid sector directory name");
1865         unsigned int x, y;
1866         int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
1867         if(r != 2)
1868                 throw InvalidFilenameException("Invalid sector directory name");
1869         v2s16 pos((s16)x, (s16)y);
1870         return pos;
1871 }
1872
1873 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
1874 {
1875         v2s16 p2d = getSectorPos(sectordir);
1876
1877         if(blockfile.size() != 4){
1878                 throw InvalidFilenameException("Invalid block filename");
1879         }
1880         unsigned int y;
1881         int r = sscanf(blockfile.c_str(), "%4x", &y);
1882         if(r != 1)
1883                 throw InvalidFilenameException("Invalid block filename");
1884         return v3s16(p2d.X, y, p2d.Y);
1885 }
1886
1887 // Debug helpers
1888 #define ENABLE_SECTOR_SAVING 1
1889 #define ENABLE_SECTOR_LOADING 1
1890 #define ENABLE_BLOCK_SAVING 1
1891 #define ENABLE_BLOCK_LOADING 1
1892
1893 void ServerMap::save(bool only_changed)
1894 {
1895         DSTACK(__FUNCTION_NAME);
1896         if(m_map_saving_enabled == false)
1897         {
1898                 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
1899                 return;
1900         }
1901         
1902         if(only_changed == false)
1903                 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
1904                                 <<std::endl;
1905         
1906         saveMasterHeightmap();
1907         
1908         u32 sector_meta_count = 0;
1909         u32 block_count = 0;
1910         
1911         { //sectorlock
1912         JMutexAutoLock lock(m_sector_mutex);
1913         
1914         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1915         for(; i.atEnd() == false; i++)
1916         {
1917                 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
1918                 assert(sector->getId() == MAPSECTOR_SERVER);
1919                 
1920                 if(ENABLE_SECTOR_SAVING)
1921                 {
1922                         if(sector->differs_from_disk || only_changed == false)
1923                         {
1924                                 saveSectorMeta(sector);
1925                                 sector_meta_count++;
1926                         }
1927                 }
1928                 if(ENABLE_BLOCK_SAVING)
1929                 {
1930                         core::list<MapBlock*> blocks;
1931                         sector->getBlocks(blocks);
1932                         core::list<MapBlock*>::Iterator j;
1933                         for(j=blocks.begin(); j!=blocks.end(); j++)
1934                         {
1935                                 MapBlock *block = *j;
1936                                 if(block->getChangedFlag() || only_changed == false)
1937                                 {
1938                                         saveBlock(block);
1939                                         block_count++;
1940                                 }
1941                         }
1942                 }
1943         }
1944
1945         }//sectorlock
1946         
1947         u32 deleted_count = 0;
1948         deleted_count = deleteUnusedSectors
1949                         (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
1950         
1951         /*
1952                 Only print if something happened or saved whole map
1953         */
1954         if(only_changed == false || sector_meta_count != 0
1955                         || block_count != 0 || deleted_count != 0)
1956         {
1957                 dstream<<DTIME<<"ServerMap: Written: "
1958                                 <<sector_meta_count<<" sector metadata files, "
1959                                 <<block_count<<" block files, "
1960                                 <<deleted_count<<" sectors unloaded from memory."
1961                                 <<std::endl;
1962         }
1963 }
1964
1965 void ServerMap::loadAll()
1966 {
1967         DSTACK(__FUNCTION_NAME);
1968         dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
1969
1970         loadMasterHeightmap();
1971
1972         std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
1973
1974         dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
1975         
1976         JMutexAutoLock lock(m_sector_mutex);
1977         
1978         s32 counter = 0;
1979         s32 printed_counter = -100000;
1980         s32 count = list.size();
1981
1982         std::vector<fs::DirListNode>::iterator i;
1983         for(i=list.begin(); i!=list.end(); i++)
1984         {
1985                 if(counter > printed_counter + 10)
1986                 {
1987                         dstream<<DTIME<<counter<<"/"<<count<<std::endl;
1988                         printed_counter = counter;
1989                 }
1990                 counter++;
1991
1992                 MapSector *sector = NULL;
1993
1994                 // We want directories
1995                 if(i->dir == false)
1996                         continue;
1997                 try{
1998                         sector = loadSectorMeta(i->name);
1999                 }
2000                 catch(InvalidFilenameException &e)
2001                 {
2002                         // This catches unknown crap in directory
2003                 }
2004                 
2005                 if(ENABLE_BLOCK_LOADING)
2006                 {
2007                         std::vector<fs::DirListNode> list2 = fs::GetDirListing
2008                                         (m_savedir+"/sectors/"+i->name);
2009                         std::vector<fs::DirListNode>::iterator i2;
2010                         for(i2=list2.begin(); i2!=list2.end(); i2++)
2011                         {
2012                                 // We want files
2013                                 if(i2->dir)
2014                                         continue;
2015                                 try{
2016                                         loadBlock(i->name, i2->name, sector);
2017                                 }
2018                                 catch(InvalidFilenameException &e)
2019                                 {
2020                                         // This catches unknown crap in directory
2021                                 }
2022                         }
2023                 }
2024         }
2025         dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2026 }
2027
2028 void ServerMap::saveMasterHeightmap()
2029 {
2030         DSTACK(__FUNCTION_NAME);
2031         createDir(m_savedir);
2032         
2033         std::string fullpath = m_savedir + "/master_heightmap";
2034         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2035         if(o.good() == false)
2036                 throw FileNotGoodException("Cannot open master heightmap");
2037         
2038         // Format used for writing
2039         u8 version = SER_FMT_VER_HIGHEST;
2040
2041 #if 0
2042         SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2043         /*
2044                 [0] u8 serialization version
2045                 [1] X master heightmap
2046         */
2047         u32 fullsize = 1 + hmdata.getSize();
2048         SharedBuffer<u8> data(fullsize);
2049
2050         data[0] = version;
2051         memcpy(&data[1], *hmdata, hmdata.getSize());
2052
2053         o.write((const char*)*data, fullsize);
2054 #endif
2055         
2056         m_heightmap->serialize(o, version);
2057 }
2058
2059 void ServerMap::loadMasterHeightmap()
2060 {
2061         DSTACK(__FUNCTION_NAME);
2062         std::string fullpath = m_savedir + "/master_heightmap";
2063         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2064         if(is.good() == false)
2065                 throw FileNotGoodException("Cannot open master heightmap");
2066         
2067         if(m_heightmap != NULL)
2068                 delete m_heightmap;
2069                 
2070         m_heightmap = UnlimitedHeightmap::deSerialize(is);
2071 }
2072
2073 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2074 {
2075         DSTACK(__FUNCTION_NAME);
2076         // Format used for writing
2077         u8 version = SER_FMT_VER_HIGHEST;
2078         // Get destination
2079         v2s16 pos = sector->getPos();
2080         createDir(m_savedir);
2081         createDir(m_savedir+"/sectors");
2082         std::string dir = getSectorDir(pos);
2083         createDir(dir);
2084         
2085         std::string fullpath = dir + "/heightmap";
2086         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2087         if(o.good() == false)
2088                 throw FileNotGoodException("Cannot open master heightmap");
2089
2090         sector->serialize(o, version);
2091         
2092         sector->differs_from_disk = false;
2093 }
2094
2095 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2096 {
2097         DSTACK(__FUNCTION_NAME);
2098         // Get destination
2099         v2s16 p2d = getSectorPos(dirname);
2100         std::string dir = m_savedir + "/sectors/" + dirname;
2101         
2102         std::string fullpath = dir + "/heightmap";
2103         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2104         if(is.good() == false)
2105                 throw FileNotGoodException("Cannot open sector heightmap");
2106
2107         ServerMapSector *sector = ServerMapSector::deSerialize
2108                         (is, this, p2d, &m_hwrapper, m_sectors);
2109         
2110         sector->differs_from_disk = false;
2111
2112         return sector;
2113 }
2114
2115 bool ServerMap::loadSectorFull(v2s16 p2d)
2116 {
2117         DSTACK(__FUNCTION_NAME);
2118         std::string sectorsubdir = getSectorSubDir(p2d);
2119
2120         MapSector *sector = NULL;
2121
2122         JMutexAutoLock lock(m_sector_mutex);
2123
2124         try{
2125                 sector = loadSectorMeta(sectorsubdir);
2126         }
2127         catch(InvalidFilenameException &e)
2128         {
2129                 return false;
2130         }
2131         catch(FileNotGoodException &e)
2132         {
2133                 return false;
2134         }
2135         catch(std::exception &e)
2136         {
2137                 return false;
2138         }
2139
2140         if(ENABLE_BLOCK_LOADING)
2141         {
2142                 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2143                                 (m_savedir+"/sectors/"+sectorsubdir);
2144                 std::vector<fs::DirListNode>::iterator i2;
2145                 for(i2=list2.begin(); i2!=list2.end(); i2++)
2146                 {
2147                         // We want files
2148                         if(i2->dir)
2149                                 continue;
2150                         try{
2151                                 loadBlock(sectorsubdir, i2->name, sector);
2152                         }
2153                         catch(InvalidFilenameException &e)
2154                         {
2155                                 // This catches unknown crap in directory
2156                         }
2157                 }
2158         }
2159         return true;
2160 }
2161
2162 #if 0
2163 bool ServerMap::deFlushSector(v2s16 p2d)
2164 {
2165         DSTACK(__FUNCTION_NAME);
2166         // See if it already exists in memory
2167         try{
2168                 MapSector *sector = getSectorNoGenerate(p2d);
2169                 return true;
2170         }
2171         catch(InvalidPositionException &e)
2172         {
2173                 /*
2174                         Try to load the sector from disk.
2175                 */
2176                 if(loadSectorFull(p2d) == true)
2177                 {
2178                         return true;
2179                 }
2180         }
2181         return false;
2182 }
2183 #endif
2184
2185 void ServerMap::saveBlock(MapBlock *block)
2186 {
2187         DSTACK(__FUNCTION_NAME);
2188         /*
2189                 Dummy blocks are not written
2190         */
2191         if(block->isDummy())
2192         {
2193                 /*v3s16 p = block->getPos();
2194                 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2195                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2196                 return;
2197         }
2198
2199         // Format used for writing
2200         u8 version = SER_FMT_VER_HIGHEST;
2201         // Get destination
2202         v3s16 p3d = block->getPos();
2203         v2s16 p2d(p3d.X, p3d.Z);
2204         createDir(m_savedir);
2205         createDir(m_savedir+"/sectors");
2206         std::string dir = getSectorDir(p2d);
2207         createDir(dir);
2208         
2209         // Block file is map/sectors/xxxxxxxx/xxxx
2210         char cc[5];
2211         snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2212         std::string fullpath = dir + "/" + cc;
2213         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2214         if(o.good() == false)
2215                 throw FileNotGoodException("Cannot open block data");
2216
2217         /*
2218                 [0] u8 serialization version
2219                 [1] data
2220         */
2221         o.write((char*)&version, 1);
2222         
2223         block->serialize(o, version);
2224
2225         /*
2226                 Versions up from 9 have block objects.
2227         */
2228         if(version >= 9)
2229         {
2230                 block->serializeObjects(o, version);
2231         }
2232         
2233         // We just wrote it to the disk
2234         block->resetChangedFlag();
2235 }
2236
2237 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2238 {
2239         DSTACK(__FUNCTION_NAME);
2240         // Block file is map/sectors/xxxxxxxx/xxxx
2241         std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2242         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2243         if(is.good() == false)
2244                 throw FileNotGoodException("Cannot open block file");
2245
2246         v3s16 p3d = getBlockPos(sectordir, blockfile);
2247         v2s16 p2d(p3d.X, p3d.Z);
2248         
2249         assert(sector->getPos() == p2d);
2250         
2251         u8 version = SER_FMT_VER_INVALID;
2252         is.read((char*)&version, 1);
2253
2254         /*u32 block_size = MapBlock::serializedLength(version);
2255         SharedBuffer<u8> data(block_size);
2256         is.read((char*)*data, block_size);*/
2257
2258         // This will always return a sector because we're the server
2259         //MapSector *sector = emergeSector(p2d);
2260
2261         MapBlock *block = NULL;
2262         bool created_new = false;
2263         try{
2264                 block = sector->getBlockNoCreate(p3d.Y);
2265         }
2266         catch(InvalidPositionException &e)
2267         {
2268                 block = sector->createBlankBlockNoInsert(p3d.Y);
2269                 created_new = true;
2270         }
2271
2272         block->deSerialize(is, version);
2273         
2274         /*
2275                 Versions up from 9 have block objects.
2276         */
2277         if(version >= 9)
2278         {
2279                 block->updateObjects(is, version, NULL);
2280         }
2281
2282         if(created_new)
2283                 sector->insertBlock(block);
2284         
2285         /*
2286                 Convert old formats to new and save
2287         */
2288
2289         if(version == 0 || version == 1)
2290         {
2291                 dstream<<"Block ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2292                                 " is in old format. Updating lighting and saving"
2293                                 " modified blocks in new format."<<std::endl;
2294
2295                 // Old version has zero lighting, update it
2296                 core::map<v3s16, MapBlock*> blocks_changed;
2297                 blocks_changed.insert(block->getPos(), block);
2298                 core::map<v3s16, MapBlock*> modified_blocks;
2299                 updateLighting(blocks_changed, modified_blocks);
2300                 
2301                 // Close input file
2302                 is.close();
2303                 
2304                 // Save modified blocks
2305                 core::map<v3s16, MapBlock * >::Iterator i = modified_blocks.getIterator();
2306                 for(; i.atEnd() == false; i++)
2307                 {
2308                         MapBlock *b2 = i.getNode()->getValue();
2309                         saveBlock(b2);
2310                 }
2311         }
2312         // Save blocks in new format
2313         else if(version < SER_FMT_VER_HIGHEST)
2314         {
2315                 saveBlock(block);
2316         }
2317         
2318         // We just loaded it from the disk, so it's up-to-date.
2319         block->resetChangedFlag();
2320 }
2321
2322 // Gets from master heightmap
2323 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2324 {
2325         assert(m_heightmap != NULL);
2326         /*
2327                 Corner definition:
2328                 v2s16(0,0),
2329                 v2s16(1,0),
2330                 v2s16(1,1),
2331                 v2s16(0,1),
2332         */
2333         corners[0] = m_heightmap->getGroundHeight
2334                         ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2335         corners[1] = m_heightmap->getGroundHeight
2336                         ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2337         corners[2] = m_heightmap->getGroundHeight
2338                         ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2339         corners[3] = m_heightmap->getGroundHeight
2340                         ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2341 }
2342
2343 void ServerMap::PrintInfo(std::ostream &out)
2344 {
2345         out<<"ServerMap: ";
2346 }
2347
2348 /*
2349         ClientMap
2350 */
2351
2352 ClientMap::ClientMap(
2353                 Client *client,
2354                 video::SMaterial *materials,
2355                 scene::ISceneNode* parent,
2356                 scene::ISceneManager* mgr,
2357                 s32 id
2358 ):
2359         Map(dout_client),
2360         scene::ISceneNode(parent, mgr, id),
2361         m_client(client),
2362         m_materials(materials),
2363         mesh(NULL)
2364 {
2365         /*m_box = core::aabbox3d<f32>(0,0,0,
2366                         map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2367         /*m_box = core::aabbox3d<f32>(0,0,0,
2368                         map->getSizeNodes().X * BS,
2369                         map->getSizeNodes().Y * BS,
2370                         map->getSizeNodes().Z * BS);*/
2371         m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2372                         BS*1000000,BS*1000000,BS*1000000);
2373
2374         mesh_mutex.Init();
2375 }
2376
2377 ClientMap::~ClientMap()
2378 {
2379         JMutexAutoLock lock(mesh_mutex);
2380         
2381         if(mesh != NULL)
2382         {
2383                 mesh->drop();
2384                 mesh = NULL;
2385         }
2386 }
2387
2388 MapSector * ClientMap::emergeSector(v2s16 p2d)
2389 {
2390         DSTACK(__FUNCTION_NAME);
2391         // Check that it doesn't exist already
2392         try{
2393                 return getSectorNoGenerate(p2d);
2394         }
2395         catch(InvalidPositionException &e)
2396         {
2397         }
2398         
2399         // Create a sector with no heightmaps
2400         ClientMapSector *sector = new ClientMapSector(this, p2d);
2401         
2402         {
2403                 JMutexAutoLock lock(m_sector_mutex);
2404                 m_sectors.insert(p2d, sector);
2405         }
2406         
2407         return sector;
2408 }
2409
2410 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2411 {
2412         DSTACK(__FUNCTION_NAME);
2413         ClientMapSector *sector = NULL;
2414
2415         JMutexAutoLock lock(m_sector_mutex);
2416         
2417         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2418
2419         if(n != NULL)
2420         {
2421                 sector = (ClientMapSector*)n->getValue();
2422                 assert(sector->getId() == MAPSECTOR_CLIENT);
2423         }
2424         else
2425         {
2426                 sector = new ClientMapSector(this, p2d);
2427                 {
2428                         JMutexAutoLock lock(m_sector_mutex);
2429                         m_sectors.insert(p2d, sector);
2430                 }
2431         }
2432
2433         sector->deSerialize(is);
2434 }
2435
2436 void ClientMap::renderMap(video::IVideoDriver* driver,
2437         video::SMaterial *materials, s32 pass)
2438 {
2439         //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2440         DSTACK(__FUNCTION_NAME);
2441
2442         bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2443 #if 0
2444         /*
2445                 Draw master heightmap mesh
2446         */
2447         
2448         {
2449                 JMutexAutoLock lock(mesh_mutex);
2450                 if(mesh != NULL)
2451                 {
2452                         u32 c = mesh->getMeshBufferCount();
2453
2454                         for(u32 i=0; i<c; i++)
2455                         {
2456                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2457                                 const video::SMaterial& material = buf->getMaterial();
2458                                 video::IMaterialRenderer* rnd =
2459                                                 driver->getMaterialRenderer(material.MaterialType);
2460                                 bool transparent = (rnd && rnd->isTransparent());
2461                                 // Render transparent on transparent pass and likewise.
2462                                 if(transparent == is_transparent_pass)
2463                                 {
2464                                         driver->setMaterial(buf->getMaterial());
2465                                         driver->drawMeshBuffer(buf);
2466                                 }
2467                         }
2468                 }
2469         }
2470 #endif
2471
2472         /*
2473                 Get time for measuring timeout.
2474                 
2475                 Measuring time is very useful for long delays when the
2476                 machine is swapping a lot.
2477         */
2478         int time1 = time(0);
2479
2480         /*
2481                 Collect all blocks that are in the view range
2482
2483                 Should not optimize more here as we want to auto-update
2484                 all changed nodes in viewing range at the next step.
2485         */
2486
2487         s16 viewing_range_nodes;
2488         bool viewing_range_all;
2489         {
2490                 JMutexAutoLock lock(g_range_mutex);
2491                 viewing_range_nodes = g_viewing_range_nodes;
2492                 viewing_range_all = g_viewing_range_all;
2493         }
2494
2495         m_camera_mutex.Lock();
2496         v3f camera_position = m_camera_position;
2497         v3f camera_direction = m_camera_direction;
2498         m_camera_mutex.Unlock();
2499
2500         /*
2501                 Get all blocks and draw all visible ones
2502         */
2503
2504         v3s16 cam_pos_nodes(
2505                         camera_position.X / BS,
2506                         camera_position.Y / BS,
2507                         camera_position.Z / BS);
2508
2509         v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2510
2511         v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2512         v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2513
2514         // Take a fair amount as we will be dropping more out later
2515         v3s16 p_blocks_min(
2516                         p_nodes_min.X / MAP_BLOCKSIZE - 1,
2517                         p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2518                         p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2519         v3s16 p_blocks_max(
2520                         p_nodes_max.X / MAP_BLOCKSIZE + 1,
2521                         p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2522                         p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2523         
2524         u32 vertex_count = 0;
2525         
2526         core::map<v2s16, MapSector*>::Iterator si;
2527
2528         //NOTE: The sectors map should be locked but we're not doing it
2529         // because it'd cause too much delays
2530
2531         si = m_sectors.getIterator();
2532         for(; si.atEnd() == false; si++)
2533         {
2534                 {
2535                         static int timecheck_counter = 0;
2536                         timecheck_counter++;
2537                         if(timecheck_counter > 50)
2538                         {
2539                                 int time2 = time(0);
2540                                 if(time2 > time1 + 4)
2541                                 {
2542                                         dstream<<"ClientMap::renderMap(): "
2543                                                 "Rendering takes ages, returning."
2544                                                 <<std::endl;
2545                                         return;
2546                                 }
2547                         }
2548                 }
2549
2550                 MapSector *sector = si.getNode()->getValue();
2551                 v2s16 sp = sector->getPos();
2552                 
2553                 if(viewing_range_all == false)
2554                 {
2555                         if(sp.X < p_blocks_min.X
2556                         || sp.X > p_blocks_max.X
2557                         || sp.Y < p_blocks_min.Z
2558                         || sp.Y > p_blocks_max.Z)
2559                                 continue;
2560                 }
2561
2562                 core::list< MapBlock * > sectorblocks;
2563                 sector->getBlocks(sectorblocks);
2564                 
2565                 /*
2566                         Draw blocks
2567                 */
2568
2569                 core::list< MapBlock * >::Iterator i;
2570                 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2571                 {
2572                         MapBlock *block = *i;
2573
2574                         /*
2575                                 Compare block position to camera position, skip
2576                                 if not seen on display
2577                         */
2578                         
2579                         v3s16 blockpos_nodes = block->getPosRelative();
2580                         
2581                         // Block center position
2582                         v3f blockpos(
2583                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2584                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2585                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2586                         );
2587
2588                         // Block position relative to camera
2589                         v3f blockpos_relative = blockpos - camera_position;
2590
2591                         // Distance in camera direction (+=front, -=back)
2592                         f32 dforward = blockpos_relative.dotProduct(camera_direction);
2593
2594                         // Total distance
2595                         f32 d = blockpos_relative.getLength();
2596                         
2597                         if(viewing_range_all == false)
2598                         {
2599                                 // If block is far away, don't draw it
2600                                 if(d > viewing_range_nodes * BS)
2601                                         continue;
2602                         }
2603                         
2604                         // Maximum radius of a block
2605                         f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2606                         
2607                         // If block is (nearly) touching the camera, don't
2608                         // bother validating further (that is, render it anyway)
2609                         if(d > block_max_radius * 1.5)
2610                         {
2611                                 // Cosine of the angle between the camera direction
2612                                 // and the block direction (camera_direction is an unit vector)
2613                                 f32 cosangle = dforward / d;
2614                                 
2615                                 // Compensate for the size of the block
2616                                 // (as the block has to be shown even if it's a bit off FOV)
2617                                 // This is an estimate.
2618                                 cosangle += block_max_radius / dforward;
2619
2620                                 // If block is not in the field of view, skip it
2621                                 //if(cosangle < cos(FOV_ANGLE/2))
2622                                 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2623                                         continue;
2624                         }
2625                         
2626                         /*
2627                                 Draw the faces of the block
2628                         */
2629                         
2630                         {
2631                                 JMutexAutoLock lock(block->mesh_mutex);
2632
2633                                 // Cancel if block has no mesh
2634                                 if(block->mesh == NULL)
2635                                         continue;
2636
2637                                 u32 c = block->mesh->getMeshBufferCount();
2638
2639                                 for(u32 i=0; i<c; i++)
2640                                 {
2641                                         scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2642                                         const video::SMaterial& material = buf->getMaterial();
2643                                         video::IMaterialRenderer* rnd =
2644                                                         driver->getMaterialRenderer(material.MaterialType);
2645                                         bool transparent = (rnd && rnd->isTransparent());
2646                                         // Render transparent on transparent pass and likewise.
2647                                         if(transparent == is_transparent_pass)
2648                                         {
2649                                                 driver->setMaterial(buf->getMaterial());
2650                                                 driver->drawMeshBuffer(buf);
2651                                                 vertex_count += buf->getVertexCount();
2652                                         }
2653                                 }
2654                         }
2655                 } // foreach sectorblocks
2656         }
2657
2658         /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2659                         <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2660 }
2661
2662 void ClientMap::updateMesh()
2663 {
2664 #if 0
2665         DSTACK(__FUNCTION_NAME);
2666         //TODO
2667         /*
2668                 Check what sectors don't draw anything useful at ground level
2669                 and create a mesh of the rough heightmap at those positions.
2670         */
2671
2672         m_camera_mutex.Lock();
2673         v3f camera_position = m_camera_position;
2674         v3f camera_direction = m_camera_direction;
2675         m_camera_mutex.Unlock();
2676
2677         v3s16 cam_pos_nodes(
2678                         camera_position.X / BS,
2679                         camera_position.Y / BS,
2680                         camera_position.Z / BS);
2681
2682         v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2683
2684         v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2685         v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2686
2687         // Take a fair amount as we will be dropping more out later
2688         v3s16 p_blocks_min(
2689                         p_nodes_min.X / MAP_BLOCKSIZE - 1,
2690                         p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2691                         p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2692         v3s16 p_blocks_max(
2693                         p_nodes_max.X / MAP_BLOCKSIZE + 1,
2694                         p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2695                         p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2696         
2697         /*
2698                 Initialize new mesh
2699         */
2700         
2701         scene::SMesh *mesh_new = new scene::SMesh();
2702         //scene::IMeshBuffer *buf = NULL;
2703         scene::SMeshBuffer *buf = NULL;
2704
2705         u8 material_in_use = 0;
2706
2707         /*
2708                 Loop through sectors
2709         */
2710         
2711         for(core::map<v2s16, MapSector*>::Iterator
2712                         si = m_sectors.getIterator();
2713                         si.atEnd() == false; si++)
2714         {
2715                 MapSector *sector = si.getNode()->getValue();
2716                 
2717                 if(sector->getId() != MAPSECTOR_CLIENT)
2718                 {
2719                         dstream<<"WARNING: Client has a non-client sector"
2720                                         <<std::endl;
2721                         continue;
2722                 }
2723                 
2724                 ClientMapSector *cs = (ClientMapSector*)sector;
2725
2726                 v2s16 sp = sector->getPos();
2727
2728                 if(sp.X < p_blocks_min.X
2729                 || sp.X > p_blocks_max.X
2730                 || sp.Y < p_blocks_min.Z
2731                 || sp.Y > p_blocks_max.Z)
2732                         continue;
2733                 
2734                 /*
2735                         Get some ground level info
2736                 */
2737                 
2738                 s16 a = -5;
2739
2740                 s16 cn[4] = 
2741                 {
2742                         cs->getCorner(0)+a,
2743                         cs->getCorner(1)+a,
2744                         cs->getCorner(2)+a,
2745                         cs->getCorner(3)+a,
2746                 };
2747                 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
2748                 s16 cn_min = 32767;
2749                 s16 cn_max = -32768;
2750                 for(s16 i=0; i<4; i++)
2751                 {
2752                         if(cn[i] < cn_min)
2753                                 cn_min = cn[i];
2754                         if(cn[i] > cn_max)
2755                                 cn_max = cn[i];
2756                 }
2757                 s16 cn_slope = cn_max - cn_min;
2758                 
2759                 /*
2760                         Generate this part of the heightmap mesh
2761                 */
2762
2763                 u8 material;
2764                 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
2765                         material = 0;
2766                 else if(cn_slope <= MAP_BLOCKSIZE)
2767                         material = 1;
2768                 else
2769                         material = 2;
2770
2771                 if(material != material_in_use || buf == NULL)
2772                 {
2773                         // Try to get a meshbuffer associated with the material
2774                         buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
2775                                         (g_mesh_materials[material]);
2776                         // If not found, create one
2777                         if(buf == NULL)
2778                         {
2779                                 // This is a "Standard MeshBuffer",
2780                                 // it's a typedeffed CMeshBuffer<video::S3DVertex>
2781                                 buf = new scene::SMeshBuffer();
2782
2783                                 // Set material
2784                                 buf->Material = g_mesh_materials[material];
2785                                 // Use VBO
2786                                 //buf->setHardwareMappingHint(scene::EHM_STATIC);
2787                                 // Add to mesh
2788                                 mesh_new->addMeshBuffer(buf);
2789                                 // Mesh grabbed it
2790                                 buf->drop();
2791                         }
2792                         material_in_use = material;
2793                 }
2794
2795                 // Sector side width in floating-point units
2796                 f32 sd = BS * MAP_BLOCKSIZE;
2797                 // Sector position in global floating-point units
2798                 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
2799
2800                 //video::SColor c(255,255,255,255);
2801                 u8 cc = 180;
2802                 video::SColor c(255,cc,cc,cc);
2803                 
2804                 video::S3DVertex vertices[4] =
2805                 {
2806                         video::S3DVertex(spf.X,   (f32)BS*cn[0],spf.Z,   0,0,0, c, 0,1),
2807                         video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z,   0,0,0, c, 1,1),
2808                         video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
2809                         video::S3DVertex(spf.X,   (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
2810                 };
2811                 u16 indices[] = {0,1,2,2,3,0};
2812                 
2813                 buf->append(vertices, 4, indices, 6);
2814         }
2815         
2816         // Set VBO on
2817         //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
2818
2819         /*
2820                 Replace the mesh
2821         */
2822
2823         mesh_mutex.Lock();
2824
2825         scene::SMesh *mesh_old = mesh;
2826
2827         //DEBUG
2828         /*mesh = NULL;
2829         mesh_new->drop();*/
2830         mesh = mesh_new;
2831         
2832         mesh_mutex.Unlock();
2833
2834         if(mesh_old != NULL)
2835         {
2836                 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
2837                                 <<std::endl;
2838                 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
2839                                 (g_materials[MATERIAL_GRASS]);
2840                 if(buf != NULL)
2841                         dstream<<"grass buf refcount="<<buf->getReferenceCount()
2842                                         <<std::endl;*/
2843
2844                 mesh_old->drop();
2845         }
2846         else
2847         {
2848                 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
2849         }
2850 #endif
2851 }
2852
2853 void ClientMap::PrintInfo(std::ostream &out)
2854 {
2855         out<<"ClientMap: ";
2856 }
2857
2858