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