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