3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
23 #include "jmutexautolock.h"
31 #define sleep_ms(x) Sleep(x)
34 #define sleep_ms(x) usleep(x*1000)
37 MapBlockPointerCache::MapBlockPointerCache(Map *map)
40 m_map->m_blockcachelock.cacheCreated();
42 m_from_cache_count = 0;
46 MapBlockPointerCache::~MapBlockPointerCache()
48 m_map->m_blockcachelock.cacheRemoved();
50 /*dstream<<"MapBlockPointerCache:"
51 <<" from_cache_count="<<m_from_cache_count
52 <<" from_map_count="<<m_from_map_count
56 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
58 core::map<v3s16, MapBlock*>::Node *n = NULL;
68 // Throws InvalidPositionException if not found
69 MapBlock *b = m_map->getBlockNoCreate(p);
78 Map::Map(std::ostream &dout):
80 m_camera_position(0,0,0),
81 m_camera_direction(0,0,1),
86 m_sector_mutex.Init();
87 m_camera_mutex.Init();
88 assert(m_sector_mutex.IsInitialized());
89 assert(m_camera_mutex.IsInitialized());
91 // Get this so that the player can stay on it at first
92 //getSector(v2s16(0,0));
100 /*updater.setRun(false);
101 while(updater.IsRunning())
107 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
108 for(; i.atEnd() == false; i++)
110 MapSector *sector = i.getNode()->getValue();
115 /*bool Map::sectorExists(v2s16 p)
117 JMutexAutoLock lock(m_sector_mutex);
118 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
122 MapSector * Map::getSectorNoGenerate(v2s16 p)
124 JMutexAutoLock lock(m_sector_mutex);
126 if(m_sector_cache != NULL && p == m_sector_cache_p){
127 MapSector * sector = m_sector_cache;
128 // Reset inactivity timer
129 sector->usage_timer = 0.0;
133 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
134 // If sector doesn't exist, throw an exception
137 throw InvalidPositionException();
140 MapSector *sector = n->getValue();
142 // Cache the last result
143 m_sector_cache_p = p;
144 m_sector_cache = sector;
146 //MapSector * ref(sector);
148 // Reset inactivity timer
149 sector->usage_timer = 0.0;
153 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
155 v2s16 p2d(p3d.X, p3d.Z);
156 MapSector * sector = getSectorNoGenerate(p2d);
158 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
163 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
165 dstream<<"Map::getBlock() with generate=true called"
167 v2s16 p2d(p3d.X, p3d.Z);
168 //MapSector * sector = getSector(p2d, generate);
169 MapSector * sector = getSectorNoGenerate(p2d);
172 throw InvalidPositionException();
174 return sector->getBlockNoCreate(p3d.Y);
177 f32 Map::getGroundHeight(v2s16 p, bool generate)
180 v2s16 sectorpos = getNodeSectorPos(p);
181 MapSector * sref = getSectorNoGenerate(sectorpos);
182 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
183 f32 y = sref->getGroundHeight(relpos);
186 catch(InvalidPositionException &e)
188 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
192 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
194 /*m_dout<<DTIME<<"Map::setGroundHeight(("
196 <<"), "<<y<<")"<<std::endl;*/
197 v2s16 sectorpos = getNodeSectorPos(p);
198 MapSector * sref = getSectorNoGenerate(sectorpos);
199 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
200 //sref->mutex.Lock();
201 sref->setGroundHeight(relpos, y);
202 //sref->mutex.Unlock();
205 bool Map::isNodeUnderground(v3s16 p)
207 v3s16 blockpos = getNodeBlockPos(p);
209 MapBlock * block = getBlockNoCreate(blockpos);
210 return block->getIsUnderground();
212 catch(InvalidPositionException &e)
219 void Map::interpolate(v3s16 block,
220 core::map<v3s16, MapBlock*> & modified_blocks)
222 const v3s16 dirs[6] = {
223 v3s16(0,0,1), // back
225 v3s16(1,0,0), // right
226 v3s16(0,0,-1), // front
227 v3s16(0,-1,0), // bottom
228 v3s16(-1,0,0), // left
231 if(from_nodes.size() == 0)
234 u32 blockchangecount = 0;
236 core::map<v3s16, bool> lighted_nodes;
237 core::map<v3s16, bool>::Iterator j;
238 j = from_nodes.getIterator();
241 Initialize block cache
244 MapBlock *block = NULL;
245 // Cache this a bit, too
246 bool block_checked_in_modified = false;
248 for(; j.atEnd() == false; j++)
249 //for(; j != from_nodes.end(); j++)
251 v3s16 pos = j.getNode()->getKey();
253 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
254 v3s16 blockpos = getNodeBlockPos(pos);
256 // Only fetch a new block if the block position has changed
258 if(block == NULL || blockpos != blockpos_last){
259 block = getBlockNoCreate(blockpos);
260 blockpos_last = blockpos;
262 block_checked_in_modified = false;
266 catch(InvalidPositionException &e)
274 // Calculate relative position in block
275 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
277 // Get node straight from the block
278 MapNode n = block->getNode(relpos);
280 u8 oldlight = n.getLight();
281 u8 newlight = diminish_light(oldlight);
283 // Loop through 6 neighbors
284 for(u16 i=0; i<6; i++){
285 // Get the position of the neighbor node
286 v3s16 n2pos = pos + dirs[i];
288 // Get the block where the node is located
289 v3s16 blockpos = getNodeBlockPos(n2pos);
293 // Only fetch a new block if the block position has changed
295 if(block == NULL || blockpos != blockpos_last){
296 block = getBlockNoCreate(blockpos);
297 blockpos_last = blockpos;
299 block_checked_in_modified = false;
303 catch(InvalidPositionException &e)
308 // Calculate relative position in block
309 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
310 // Get node straight from the block
311 MapNode n2 = block->getNode(relpos);
313 bool changed = false;
315 If the neighbor is brighter than the current node,
316 add to list (it will light up this node on its turn)
318 if(n2.getLight() > undiminish_light(oldlight))
320 lighted_nodes.insert(n2pos, true);
321 //lighted_nodes.push_back(n2pos);
325 If the neighbor is dimmer than how much light this node
326 would spread on it, add to list
328 if(n2.getLight() < newlight)
330 if(n2.light_propagates())
332 n2.setLight(newlight);
333 block->setNode(relpos, n2);
334 lighted_nodes.insert(n2pos, true);
335 //lighted_nodes.push_back(n2pos);
340 // Add to modified_blocks
341 if(changed == true && block_checked_in_modified == false)
343 // If the block is not found in modified_blocks, add.
344 if(modified_blocks.find(blockpos) == NULL)
346 modified_blocks.insert(blockpos, block);
348 block_checked_in_modified = true;
351 catch(InvalidPositionException &e)
358 /*dstream<<"spreadLight(): Changed block "
359 <<blockchangecount<<" times"
360 <<" for "<<from_nodes.size()<<" nodes"
363 if(lighted_nodes.size() > 0)
364 spreadLight(lighted_nodes, modified_blocks);
369 Goes recursively through the neighbours of the node.
371 Alters only transparent nodes.
373 If the lighting of the neighbour is lower than the lighting of
374 the node was (before changing it to 0 at the step before), the
375 lighting of the neighbour is set to 0 and then the same stuff
376 repeats for the neighbour.
378 The ending nodes of the routine are stored in light_sources.
379 This is useful when a light is removed. In such case, this
380 routine can be called for the light node and then again for
381 light_sources to re-light the area without the removed light.
383 values of from_nodes are lighting values.
385 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
386 core::map<v3s16, bool> & light_sources,
387 core::map<v3s16, MapBlock*> & modified_blocks)
390 v3s16(0,0,1), // back
392 v3s16(1,0,0), // right
393 v3s16(0,0,-1), // front
394 v3s16(0,-1,0), // bottom
395 v3s16(-1,0,0), // left
398 if(from_nodes.size() == 0)
401 u32 blockchangecount = 0;
403 core::map<v3s16, u8> unlighted_nodes;
404 core::map<v3s16, u8>::Iterator j;
405 j = from_nodes.getIterator();
408 Initialize block cache
411 MapBlock *block = NULL;
412 // Cache this a bit, too
413 bool block_checked_in_modified = false;
415 for(; j.atEnd() == false; j++)
417 v3s16 pos = j.getNode()->getKey();
418 v3s16 blockpos = getNodeBlockPos(pos);
420 // Only fetch a new block if the block position has changed
422 if(block == NULL || blockpos != blockpos_last){
423 block = getBlockNoCreate(blockpos);
424 blockpos_last = blockpos;
426 block_checked_in_modified = false;
430 catch(InvalidPositionException &e)
438 // Calculate relative position in block
439 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
441 // Get node straight from the block
442 MapNode n = block->getNode(relpos);
444 u8 oldlight = j.getNode()->getValue();
446 // Loop through 6 neighbors
447 for(u16 i=0; i<6; i++)
449 // Get the position of the neighbor node
450 v3s16 n2pos = pos + dirs[i];
452 // Get the block where the node is located
453 v3s16 blockpos = getNodeBlockPos(n2pos);
457 // Only fetch a new block if the block position has changed
459 if(block == NULL || blockpos != blockpos_last){
460 block = getBlockNoCreate(blockpos);
461 blockpos_last = blockpos;
463 block_checked_in_modified = false;
467 catch(InvalidPositionException &e)
472 // Calculate relative position in block
473 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
474 // Get node straight from the block
475 MapNode n2 = block->getNode(relpos);
477 bool changed = false;
479 //TODO: Optimize output by optimizing light_sources?
482 If the neighbor is dimmer than what was specified
483 as oldlight (the light of the previous node)
485 if(n2.getLight() < oldlight)
488 And the neighbor is transparent and it has some light
490 if(n2.light_propagates() && n2.getLight() != 0)
493 Set light to 0 and add to queue
496 u8 current_light = n2.getLight();
498 block->setNode(relpos, n2);
500 unlighted_nodes.insert(n2pos, current_light);
504 Remove from light_sources if it is there
505 NOTE: This doesn't happen nearly at all
507 /*if(light_sources.find(n2pos))
509 std::cout<<"Removed from light_sources"<<std::endl;
510 light_sources.remove(n2pos);
515 if(light_sources.find(n2pos) != NULL)
516 light_sources.remove(n2pos);*/
519 light_sources.insert(n2pos, true);
522 // Add to modified_blocks
523 if(changed == true && block_checked_in_modified == false)
525 // If the block is not found in modified_blocks, add.
526 if(modified_blocks.find(blockpos) == NULL)
528 modified_blocks.insert(blockpos, block);
530 block_checked_in_modified = true;
533 catch(InvalidPositionException &e)
540 /*dstream<<"unspreadLight(): Changed block "
541 <<blockchangecount<<" times"
542 <<" for "<<from_nodes.size()<<" nodes"
545 if(unlighted_nodes.size() > 0)
546 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
550 A single-node wrapper of the above
552 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
553 core::map<v3s16, bool> & light_sources,
554 core::map<v3s16, MapBlock*> & modified_blocks)
556 core::map<v3s16, u8> from_nodes;
557 from_nodes.insert(pos, lightwas);
559 unspreadLight(from_nodes, light_sources, modified_blocks);
563 Lights neighbors of from_nodes, collects all them and then
566 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
567 core::map<v3s16, MapBlock*> & modified_blocks)
569 const v3s16 dirs[6] = {
570 v3s16(0,0,1), // back
572 v3s16(1,0,0), // right
573 v3s16(0,0,-1), // front
574 v3s16(0,-1,0), // bottom
575 v3s16(-1,0,0), // left
578 if(from_nodes.size() == 0)
581 u32 blockchangecount = 0;
583 core::map<v3s16, bool> lighted_nodes;
584 core::map<v3s16, bool>::Iterator j;
585 j = from_nodes.getIterator();
588 Initialize block cache
591 MapBlock *block = NULL;
592 // Cache this a bit, too
593 bool block_checked_in_modified = false;
595 for(; j.atEnd() == false; j++)
596 //for(; j != from_nodes.end(); j++)
598 v3s16 pos = j.getNode()->getKey();
600 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
601 v3s16 blockpos = getNodeBlockPos(pos);
603 // Only fetch a new block if the block position has changed
605 if(block == NULL || blockpos != blockpos_last){
606 block = getBlockNoCreate(blockpos);
607 blockpos_last = blockpos;
609 block_checked_in_modified = false;
613 catch(InvalidPositionException &e)
621 // Calculate relative position in block
622 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
624 // Get node straight from the block
625 MapNode n = block->getNode(relpos);
627 u8 oldlight = n.getLight();
628 u8 newlight = diminish_light(oldlight);
630 // Loop through 6 neighbors
631 for(u16 i=0; i<6; i++){
632 // Get the position of the neighbor node
633 v3s16 n2pos = pos + dirs[i];
635 // Get the block where the node is located
636 v3s16 blockpos = getNodeBlockPos(n2pos);
640 // Only fetch a new block if the block position has changed
642 if(block == NULL || blockpos != blockpos_last){
643 block = getBlockNoCreate(blockpos);
644 blockpos_last = blockpos;
646 block_checked_in_modified = false;
650 catch(InvalidPositionException &e)
655 // Calculate relative position in block
656 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
657 // Get node straight from the block
658 MapNode n2 = block->getNode(relpos);
660 bool changed = false;
662 If the neighbor is brighter than the current node,
663 add to list (it will light up this node on its turn)
665 if(n2.getLight() > undiminish_light(oldlight))
667 lighted_nodes.insert(n2pos, true);
668 //lighted_nodes.push_back(n2pos);
672 If the neighbor is dimmer than how much light this node
673 would spread on it, add to list
675 if(n2.getLight() < newlight)
677 if(n2.light_propagates())
679 n2.setLight(newlight);
680 block->setNode(relpos, n2);
681 lighted_nodes.insert(n2pos, true);
682 //lighted_nodes.push_back(n2pos);
687 // Add to modified_blocks
688 if(changed == true && block_checked_in_modified == false)
690 // If the block is not found in modified_blocks, add.
691 if(modified_blocks.find(blockpos) == NULL)
693 modified_blocks.insert(blockpos, block);
695 block_checked_in_modified = true;
698 catch(InvalidPositionException &e)
705 /*dstream<<"spreadLight(): Changed block "
706 <<blockchangecount<<" times"
707 <<" for "<<from_nodes.size()<<" nodes"
710 if(lighted_nodes.size() > 0)
711 spreadLight(lighted_nodes, modified_blocks);
715 A single-node source variation of the above.
717 void Map::lightNeighbors(v3s16 pos,
718 core::map<v3s16, MapBlock*> & modified_blocks)
720 core::map<v3s16, bool> from_nodes;
721 from_nodes.insert(pos, true);
722 spreadLight(from_nodes, modified_blocks);
725 v3s16 Map::getBrightestNeighbour(v3s16 p)
728 v3s16(0,0,1), // back
730 v3s16(1,0,0), // right
731 v3s16(0,0,-1), // front
732 v3s16(0,-1,0), // bottom
733 v3s16(-1,0,0), // left
736 u8 brightest_light = 0;
737 v3s16 brightest_pos(0,0,0);
738 bool found_something = false;
740 // Loop through 6 neighbors
741 for(u16 i=0; i<6; i++){
742 // Get the position of the neighbor node
743 v3s16 n2pos = p + dirs[i];
748 catch(InvalidPositionException &e)
752 if(n2.getLight() > brightest_light || found_something == false){
753 brightest_light = n2.getLight();
754 brightest_pos = n2pos;
755 found_something = true;
759 if(found_something == false)
760 throw InvalidPositionException();
762 return brightest_pos;
766 Propagates sunlight down from a node.
767 Starting point gets sunlight.
769 Returns the lowest y value of where the sunlight went.
771 s16 Map::propagateSunlight(v3s16 start,
772 core::map<v3s16, MapBlock*> & modified_blocks)
777 v3s16 pos(start.X, y, start.Z);
779 v3s16 blockpos = getNodeBlockPos(pos);
782 block = getBlockNoCreate(blockpos);
784 catch(InvalidPositionException &e)
789 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
790 MapNode n = block->getNode(relpos);
792 if(n.sunlight_propagates())
794 n.setLight(LIGHT_SUN);
795 block->setNode(relpos, n);
797 modified_blocks.insert(blockpos, block);
806 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
807 core::map<v3s16, MapBlock*> & modified_blocks)
809 /*m_dout<<DTIME<<"Map::updateLighting(): "
810 <<a_blocks.getSize()<<" blocks... ";*/
814 u32 count_was = modified_blocks.size();
816 /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
817 for(; i != a_blocks.end(); i++)
819 MapBlock *block = *i;*/
821 core::map<v3s16, bool> light_sources;
823 core::map<v3s16, u8> unlight_from;
825 core::map<v3s16, MapBlock*>::Iterator i;
826 i = a_blocks.getIterator();
827 for(; i.atEnd() == false; i++)
829 MapBlock *block = i.getNode()->getValue();
833 // Don't bother with dummy blocks.
837 v3s16 pos = block->getPos();
838 modified_blocks.insert(pos, block);
841 Clear all light from block
843 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
844 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
845 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
850 MapNode n = block->getNode(v3s16(x,y,z));
851 u8 oldlight = n.getLight();
853 block->setNode(v3s16(x,y,z), n);
855 // Collect borders for unlighting
856 if(x==0 || x == MAP_BLOCKSIZE-1
857 || y==0 || y == MAP_BLOCKSIZE-1
858 || z==0 || z == MAP_BLOCKSIZE-1)
860 v3s16 p_map = p + v3s16(
863 MAP_BLOCKSIZE*pos.Z);
864 unlight_from.insert(p_map, oldlight);
867 catch(InvalidPositionException &e)
870 This would happen when dealing with a
874 dstream<<"updateLighting(): InvalidPositionException"
879 bool bottom_valid = block->propagateSunlight(light_sources);
881 // If bottom is valid, we're done.
885 /*dstream<<"Bottom for sunlight-propagated block ("
886 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
889 // Else get the block below and loop to it
893 block = getBlockNoCreate(pos);
895 catch(InvalidPositionException &e)
904 //TimeTaker timer("unspreadLight", g_device);
905 unspreadLight(unlight_from, light_sources, modified_blocks);
910 u32 diff = modified_blocks.size() - count_was;
911 count_was = modified_blocks.size();
912 dstream<<"unspreadLight modified "<<diff<<std::endl;
915 // TODO: Spread light from propagated sunlight?
916 // Yes, add it to light_sources... somehow.
917 // It has to be added at somewhere above, in the loop.
919 // NOTE: This actually works quite fine without it
920 // - Find out why it works
923 //TimeTaker timer("spreadLight", g_device);
924 spreadLight(light_sources, modified_blocks);
929 u32 diff = modified_blocks.size() - count_was;
930 count_was = modified_blocks.size();
931 dstream<<"spreadLight modified "<<diff<<std::endl;
934 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
938 This is called after changing a node from transparent to opaque.
939 The lighting value of the node should be left as-is after changing
940 other values. This sets the lighting value to 0.
942 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
943 core::map<v3s16, MapBlock*> &modified_blocks)*/
944 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
945 core::map<v3s16, MapBlock*> &modified_blocks)
948 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
949 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
951 u8 lightwas = getNode(p).getLight();
953 //core::list<v3s16> light_sources;
954 core::map<v3s16, bool> light_sources;
955 //MapNode n = getNode(p);
958 From this node to nodes underneath:
959 If lighting is sunlight (1.0), unlight neighbours and
964 bool node_under_sunlight = true;
966 v3s16 toppos = p + v3s16(0,1,0);
969 If there is a node at top and it doesn't have sunlight,
970 there has not been any sunlight going down.
972 Otherwise there probably is.
975 MapNode topnode = getNode(toppos);
977 if(topnode.getLight() != LIGHT_SUN)
978 node_under_sunlight = false;
980 catch(InvalidPositionException &e)
984 // Add the block of the added node to modified_blocks
985 v3s16 blockpos = getNodeBlockPos(p);
986 MapBlock * block = getBlockNoCreate(blockpos);
987 assert(block != NULL);
988 modified_blocks.insert(blockpos, block);
990 if(isValidPosition(p) == false)
993 // Unlight neighbours of node.
994 // This means setting light of all consequent dimmer nodes
996 // This also collects the nodes at the border which will spread
997 // light again into this.
998 unLightNeighbors(p, lightwas, light_sources, modified_blocks);
1004 If node is under sunlight, take all sunlighted nodes under
1005 it and clear light from them and from where the light has
1008 if(node_under_sunlight)
1012 //m_dout<<DTIME<<"y="<<y<<std::endl;
1013 v3s16 n2pos(p.X, y, p.Z);
1017 n2 = getNode(n2pos);
1019 catch(InvalidPositionException &e)
1024 if(n2.getLight() == LIGHT_SUN)
1026 //m_dout<<DTIME<<"doing"<<std::endl;
1027 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
1037 Spread light from all nodes that might be capable of doing so
1038 TODO: Convert to spreadLight
1040 spreadLight(light_sources, modified_blocks);
1045 void Map::removeNodeAndUpdate(v3s16 p,
1046 core::map<v3s16, MapBlock*> &modified_blocks)
1048 /*PrintInfo(m_dout);
1049 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1050 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1052 bool node_under_sunlight = true;
1054 v3s16 toppos = p + v3s16(0,1,0);
1056 // Node will be replaced with this
1057 u8 replace_material = CONTENT_AIR;
1059 // NOTE: Water is now managed elsewhere
1063 Find out with what material the node will be replaced.
1064 It will be replaced with the mostly seen buildable_to.
1068 v3s16(0,0,1), // back
1069 v3s16(0,1,0), // top
1070 v3s16(1,0,0), // right
1071 v3s16(0,0,-1), // front
1072 v3s16(0,-1,0), // bottom
1073 v3s16(-1,0,0), // left
1076 core::map<u8, u16> neighbor_rankings;
1078 for(u32 i=0; i<sizeof(dirs)/sizeof(dirs[0]); i++)
1081 MapNode n2 = getNode(p + dirs[i]);
1083 if(material_buildable_to(n2.d))
1085 if(neighbor_rankings.find(n2.d) == NULL)
1086 neighbor_rankings[n2.d] = 1;
1088 neighbor_rankings[n2.d]
1089 = neighbor_rankings[n2.d] + 1;
1092 catch(InvalidPositionException &e)
1097 u16 highest_ranking = 0;
1099 for(core::map<u8, u16>::Iterator
1100 i = neighbor_rankings.getIterator();
1101 i.atEnd() == false; i++)
1103 u8 m = i.getNode()->getKey();
1104 u8 c = i.getNode()->getValue();
1106 c > highest_ranking ||
1107 // Prefer something else than air
1108 (c >= highest_ranking && m != CONTENT_AIR)
1112 replace_material = m;
1113 highest_ranking = c;
1121 If there is a node at top and it doesn't have sunlight,
1122 there will be no sunlight going down.
1125 MapNode topnode = getNode(toppos);
1127 if(topnode.getLight() != LIGHT_SUN)
1128 node_under_sunlight = false;
1130 catch(InvalidPositionException &e)
1135 Unlight neighbors (in case the node is a light source)
1137 //core::list<v3s16> light_sources;
1138 core::map<v3s16, bool> light_sources;
1139 unLightNeighbors(p, getNode(p).getLight(),
1140 light_sources, modified_blocks);
1146 n.d = replace_material;
1151 Recalculate lighting
1153 spreadLight(light_sources, modified_blocks);
1155 // Add the block of the removed node to modified_blocks
1156 v3s16 blockpos = getNodeBlockPos(p);
1157 MapBlock * block = getBlockNoCreate(blockpos);
1158 assert(block != NULL);
1159 modified_blocks.insert(blockpos, block);
1162 If the removed node was under sunlight, propagate the
1163 sunlight down from it and then light all neighbors
1164 of the propagated blocks.
1166 if(node_under_sunlight)
1168 s16 ybottom = propagateSunlight(p, modified_blocks);
1169 /*m_dout<<DTIME<<"Node was under sunlight. "
1170 "Propagating sunlight";
1171 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1173 for(; y >= ybottom; y--)
1175 v3s16 p2(p.X, y, p.Z);
1176 /*m_dout<<DTIME<<"lighting neighbors of node ("
1177 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1179 lightNeighbors(p2, modified_blocks);
1184 // Set the lighting of this node to 0
1186 MapNode n = getNode(p);
1190 catch(InvalidPositionException &e)
1196 // Get the brightest neighbour node and propagate light from it
1197 v3s16 n2p = getBrightestNeighbour(p);
1199 MapNode n2 = getNode(n2p);
1200 lightNeighbors(n2p, modified_blocks);
1202 catch(InvalidPositionException &e)
1207 void Map::updateMeshes(v3s16 blockpos)
1209 assert(mapType() == MAPTYPE_CLIENT);
1212 v3s16 p = blockpos + v3s16(0,0,0);
1213 MapBlock *b = getBlockNoCreate(p);
1216 catch(InvalidPositionException &e){}
1218 v3s16 p = blockpos + v3s16(-1,0,0);
1219 MapBlock *b = getBlockNoCreate(p);
1222 catch(InvalidPositionException &e){}
1224 v3s16 p = blockpos + v3s16(0,-1,0);
1225 MapBlock *b = getBlockNoCreate(p);
1228 catch(InvalidPositionException &e){}
1230 v3s16 p = blockpos + v3s16(0,0,-1);
1231 MapBlock *b = getBlockNoCreate(p);
1234 catch(InvalidPositionException &e){}
1238 Updates usage timers
1240 void Map::timerUpdate(float dtime)
1242 JMutexAutoLock lock(m_sector_mutex);
1244 core::map<v2s16, MapSector*>::Iterator si;
1246 si = m_sectors.getIterator();
1247 for(; si.atEnd() == false; si++)
1249 MapSector *sector = si.getNode()->getValue();
1250 sector->usage_timer += dtime;
1254 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1257 Wait for caches to be removed before continuing.
1259 This disables the existence of caches while locked
1261 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1263 core::list<v2s16>::Iterator j;
1264 for(j=list.begin(); j!=list.end(); j++)
1266 MapSector *sector = m_sectors[*j];
1269 sector->deleteBlocks();
1274 If sector is in sector cache, remove it from there
1276 if(m_sector_cache == sector)
1278 m_sector_cache = NULL;
1281 Remove from map and delete
1283 m_sectors.remove(*j);
1289 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1290 core::list<v3s16> *deleted_blocks)
1292 JMutexAutoLock lock(m_sector_mutex);
1294 core::list<v2s16> sector_deletion_queue;
1295 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1296 for(; i.atEnd() == false; i++)
1298 MapSector *sector = i.getNode()->getValue();
1300 Delete sector from memory if it hasn't been used in a long time
1302 if(sector->usage_timer > timeout)
1304 sector_deletion_queue.push_back(i.getNode()->getKey());
1306 if(deleted_blocks != NULL)
1308 // Collect positions of blocks of sector
1309 MapSector *sector = i.getNode()->getValue();
1310 core::list<MapBlock*> blocks;
1311 sector->getBlocks(blocks);
1312 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1313 i != blocks.end(); i++)
1315 deleted_blocks->push_back((*i)->getPos());
1320 deleteSectors(sector_deletion_queue, only_blocks);
1321 return sector_deletion_queue.getSize();
1324 void Map::PrintInfo(std::ostream &out)
1333 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1337 m_savedir = savedir;
1338 m_map_saving_enabled = false;
1342 // If directory exists, check contents and load if possible
1343 if(fs::PathExists(m_savedir))
1345 // If directory is empty, it is safe to save into it.
1346 if(fs::GetDirListing(m_savedir).size() == 0)
1348 dstream<<DTIME<<"Server: Empty save directory is valid."
1350 m_map_saving_enabled = true;
1354 // Load master heightmap
1355 loadMasterHeightmap();
1357 // Load sector (0,0) and throw and exception on fail
1358 if(loadSectorFull(v2s16(0,0)) == false)
1359 throw LoadError("Failed to load sector (0,0)");
1361 dstream<<DTIME<<"Server: Successfully loaded master "
1362 "heightmap and sector (0,0) from "<<savedir<<
1363 ", assuming valid save directory."
1366 m_map_saving_enabled = true;
1367 // Map loaded, not creating new one
1371 // If directory doesn't exist, it is safe to save to it
1373 m_map_saving_enabled = true;
1376 catch(std::exception &e)
1378 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1379 <<", exception: "<<e.what()<<std::endl;
1380 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1381 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1384 dstream<<DTIME<<"Initializing new map."<<std::endl;
1386 // Create master heightmap
1387 ValueGenerator *maxgen =
1388 ValueGenerator::deSerialize(hmp.randmax);
1389 ValueGenerator *factorgen =
1390 ValueGenerator::deSerialize(hmp.randfactor);
1391 ValueGenerator *basegen =
1392 ValueGenerator::deSerialize(hmp.base);
1393 m_heightmap = new UnlimitedHeightmap
1394 (hmp.blocksize, maxgen, factorgen, basegen);
1396 // Set map parameters
1399 // Create zero sector
1400 emergeSector(v2s16(0,0));
1402 // Initially write whole map
1406 ServerMap::~ServerMap()
1410 if(m_map_saving_enabled)
1413 // Save only changed parts
1415 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1419 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1422 catch(std::exception &e)
1424 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1425 <<", exception: "<<e.what()<<std::endl;
1428 if(m_heightmap != NULL)
1432 MapSector * ServerMap::emergeSector(v2s16 p2d)
1434 DSTACK("%s: p2d=(%d,%d)",
1437 // Check that it doesn't exist already
1439 return getSectorNoGenerate(p2d);
1441 catch(InvalidPositionException &e)
1446 Try to load the sector from disk.
1448 if(loadSectorFull(p2d) == true)
1450 return getSectorNoGenerate(p2d);
1454 If there is no master heightmap, throw.
1456 if(m_heightmap == NULL)
1458 throw InvalidPositionException("emergeSector(): no heightmap");
1462 Do not generate over-limit
1464 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1465 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1466 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1467 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1468 throw InvalidPositionException("emergeSector(): pos. over limit");
1471 Generate sector and heightmaps
1474 // Number of heightmaps in sector in each direction
1475 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1477 // Heightmap side width
1478 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1480 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1482 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1483 " heightmaps and objects"<<std::endl;*/
1485 // Loop through sub-heightmaps
1486 for(s16 y=0; y<hm_split; y++)
1487 for(s16 x=0; x<hm_split; x++)
1489 v2s16 p_in_sector = v2s16(x,y);
1490 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1492 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1493 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1494 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1495 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1498 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1499 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1502 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1504 sector->setHeightmap(p_in_sector, hm);
1506 //TODO: Make these values configurable
1507 //hm->generateContinued(0.0, 0.0, corners);
1508 hm->generateContinued(0.5, 0.2, corners);
1509 //hm->generateContinued(1.0, 0.2, corners);
1510 //hm->generateContinued(2.0, 0.2, corners);
1520 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1521 sector->setObjects(objects);
1523 v2s16 mhm_p = p2d * hm_split;
1525 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1526 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1527 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1528 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1531 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1532 float avgslope = 0.0;
1533 avgslope += fabs(avgheight - corners[0]);
1534 avgslope += fabs(avgheight - corners[1]);
1535 avgslope += fabs(avgheight - corners[2]);
1536 avgslope += fabs(avgheight - corners[3]);
1538 avgslope /= MAP_BLOCKSIZE;
1539 //dstream<<"avgslope="<<avgslope<<std::endl;
1541 float pitness = 0.0;
1543 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1546 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1549 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1552 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1556 pitness /= MAP_BLOCKSIZE;
1557 //dstream<<"pitness="<<pitness<<std::endl;
1560 Plant some trees if there is not much slope
1563 // Avgslope is the derivative of a hill
1564 float t = avgslope * avgslope;
1565 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1568 tree_max = a / (t/0.03);
1571 u32 count = (rand()%(tree_max+1));
1572 //u32 count = tree_max;
1573 for(u32 i=0; i<count; i++)
1575 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1576 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1577 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1580 objects->insert(v3s16(x, y, z),
1581 SECTOR_OBJECT_TREE_1);
1585 Plant some bushes if sector is pit-like
1588 // Pitness usually goes at around -0.5...0.5
1590 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1592 bush_max = (pitness*a*4);
1595 u32 count = (rand()%(bush_max+1));
1596 for(u32 i=0; i<count; i++)
1598 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1599 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1600 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1603 objects->insert(v3s16(x, y, z),
1604 SECTOR_OBJECT_BUSH_1);
1608 Add ravine (randomly)
1610 if(m_params.ravines_amount != 0)
1612 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1615 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1616 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1619 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1620 objects->insert(v3s16(x, y, z),
1621 SECTOR_OBJECT_RAVINE);
1628 JMutexAutoLock lock(m_sector_mutex);
1629 m_sectors.insert(p2d, sector);
1634 MapBlock * ServerMap::emergeBlock(
1636 bool only_from_disk,
1637 core::map<v3s16, MapBlock*> &changed_blocks,
1638 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1641 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1643 p.X, p.Y, p.Z, only_from_disk);
1645 /*dstream<<"ServerMap::emergeBlock(): "
1646 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1647 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1648 v2s16 p2d(p.X, p.Z);
1651 This will create or load a sector if not found in memory.
1652 If block exists on disk, it will be loaded.
1654 NOTE: On old save formats, this will be slow, as it generates
1655 lighting on blocks for them.
1657 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1658 assert(sector->getId() == MAPSECTOR_SERVER);
1660 // Try to get a block from the sector
1661 MapBlock *block = NULL;
1662 bool not_on_disk = false;
1664 block = sector->getBlockNoCreate(block_y);
1665 if(block->isDummy() == true)
1670 catch(InvalidPositionException &e)
1676 If block was not found on disk and not going to generate a
1677 new one, make sure there is a dummy block in place.
1679 if(not_on_disk && only_from_disk)
1683 // Create dummy block
1684 block = new MapBlock(this, p, true);
1686 // Add block to sector
1687 sector->insertBlock(block);
1693 //dstream<<"Not found on disk, generating."<<std::endl;
1696 Do not generate over-limit
1698 if(blockpos_over_limit(p))
1699 throw InvalidPositionException("emergeBlock(): pos. over limit");
1704 Go on generating the block.
1706 TODO: If a dungeon gets generated so that it's side gets
1707 revealed to the outside air, the lighting should be
1712 If block doesn't exist, create one.
1713 If it exists, it is a dummy. In that case unDummify() it.
1717 block = sector->createBlankBlockNoInsert(block_y);
1721 // Remove the block so that nobody can get a half-generated one.
1722 sector->removeBlock(block);
1723 // Allocate the block to be a proper one.
1727 // Randomize a bit. This makes dungeons.
1728 /*bool low_block_is_empty = false;
1730 low_block_is_empty = true;*/
1733 //const s32 ued = 8;
1734 bool underground_emptiness[ued*ued*ued];
1735 for(s32 i=0; i<ued*ued*ued; i++)
1737 underground_emptiness[i] = ((rand() % 5) == 0);
1742 This is a messy hack to sort the emptiness a bit
1744 for(s32 j=0; j<2; j++)
1745 for(s32 y0=0; y0<ued; y0++)
1746 for(s32 z0=0; z0<ued; z0++)
1747 for(s32 x0=0; x0<ued; x0++)
1750 bool &e0 = underground_emptiness[
1751 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1752 +ued*(y0*ued/MAP_BLOCKSIZE)
1753 +(x0*ued/MAP_BLOCKSIZE)];
1756 v3s16(0,0,1), // back
1757 v3s16(1,0,0), // right
1758 v3s16(0,0,-1), // front
1759 v3s16(-1,0,0), // left
1760 /*v3s16(0,1,0), // top
1761 v3s16(0,-1,0), // bottom*/
1763 for(s32 i=0; i<4; i++)
1765 v3s16 p1 = p0 + dirs[i];
1766 if(isInArea(p1, ued) == false)
1768 bool &e1 = underground_emptiness[
1769 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1770 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1771 +(p1.X*ued/MAP_BLOCKSIZE)];
1776 v3s16(0,1,0), // top
1777 v3s16(0,-1,0), // bottom
1778 /*v3s16(0,0,1), // back
1779 v3s16(1,0,0), // right
1780 v3s16(0,0,-1), // front
1781 v3s16(-1,0,0), // left*/
1783 for(s32 i=0; i<2; i++)
1785 v3s16 p2 = p1 + dirs[i];
1788 if(isInArea(p2, ued) == false)
1790 bool &e2 = underground_emptiness[
1791 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1792 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1793 +(p2.X*ued/MAP_BLOCKSIZE)];
1808 // This is the basic material of what the visible flat ground
1810 u8 material = CONTENT_GRASS;
1812 u8 water_material = CONTENT_WATER;
1813 if(g_settings.getBool("endless_water"))
1814 water_material = CONTENT_OCEAN;
1816 s32 lowest_ground_y = 32767;
1817 s32 highest_ground_y = -32768;
1820 //sector->printHeightmaps();
1822 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1823 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1825 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1827 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1828 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1829 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1831 dstream<<"WARNING: Surface height not found in sector "
1832 "for block that is being emerged"<<std::endl;
1836 s16 surface_y = surface_y_f;
1837 //avg_ground_y += surface_y;
1838 if(surface_y < lowest_ground_y)
1839 lowest_ground_y = surface_y;
1840 if(surface_y > highest_ground_y)
1841 highest_ground_y = surface_y;
1843 s32 surface_depth = 0;
1845 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1847 //float min_slope = 0.45;
1848 //float max_slope = 0.85;
1849 float min_slope = 0.60;
1850 float max_slope = 1.20;
1851 float min_slope_depth = 5.0;
1852 float max_slope_depth = 0;
1853 if(slope < min_slope)
1854 surface_depth = min_slope_depth;
1855 else if(slope > max_slope)
1856 surface_depth = max_slope_depth;
1858 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1860 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1862 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1867 NOTE: If there are some man-made structures above the
1868 newly created block, they won't be taken into account.
1870 if(real_y > surface_y)
1871 n.setLight(LIGHT_SUN);
1877 // If node is very low
1878 /*if(real_y <= surface_y - 7)
1881 if(underground_emptiness[
1882 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1883 +ued*(y0*ued/MAP_BLOCKSIZE)
1884 +(x0*ued/MAP_BLOCKSIZE)])
1890 n.d = CONTENT_STONE;
1893 // If node is under surface level
1894 else if(real_y <= surface_y - surface_depth)
1895 n.d = CONTENT_STONE;
1897 if(real_y <= surface_y - surface_depth)
1900 if(underground_emptiness[
1901 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1902 +ued*(y0*ued/MAP_BLOCKSIZE)
1903 +(x0*ued/MAP_BLOCKSIZE)])
1909 n.d = CONTENT_STONE;
1912 // If node is at or under heightmap y
1913 else if(real_y <= surface_y)
1915 // If under water level, it's mud
1916 if(real_y < WATER_LEVEL)
1918 // Only the topmost node is grass
1919 else if(real_y <= surface_y - 1)
1921 // Else it's the main material
1925 // If node is over heightmap y
1927 // If under water level, it's water
1928 if(real_y < WATER_LEVEL)
1930 n.d = water_material;
1931 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1937 block->setNode(v3s16(x0,y0,z0), n);
1942 Calculate is_underground
1944 // Probably underground if the highest part of block is under lowest
1946 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1947 block->setIsUnderground(is_underground);
1950 Force lighting update if some part of block is underground
1951 This is needed because of caves.
1954 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1955 if(some_part_underground)
1956 //if(is_underground)
1958 lighting_invalidated_blocks[block->getPos()] = block;
1965 //if(is_underground)
1966 if(some_part_underground)
1968 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1969 for(s16 i=0; i<underground_level*3; i++)
1974 (rand()%(MAP_BLOCKSIZE-2))+1,
1975 (rand()%(MAP_BLOCKSIZE-2))+1,
1976 (rand()%(MAP_BLOCKSIZE-2))+1
1982 //if(is_ground_content(block->getNode(cp).d))
1983 if(block->getNode(cp).d == CONTENT_STONE)
1985 block->setNode(cp, n);
1987 for(u16 i=0; i<26; i++)
1989 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1990 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1992 block->setNode(cp+g_26dirs[i], n);
1999 Create a few rats in empty blocks underground
2003 //for(u16 i=0; i<2; i++)
2006 (rand()%(MAP_BLOCKSIZE-2))+1,
2007 (rand()%(MAP_BLOCKSIZE-2))+1,
2008 (rand()%(MAP_BLOCKSIZE-2))+1
2011 // Check that the place is empty
2012 //if(!is_ground_content(block->getNode(cp).d))
2015 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2016 block->addObject(obj);
2022 Add block to sector.
2024 sector->insertBlock(block);
2027 Do some interpolation for dungeons
2032 TimeTaker timer("interpolation", g_device);
2034 MapVoxelManipulator vmanip(this);
2036 v3s16 relpos = block->getPosRelative();
2038 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
2039 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
2040 /*vmanip.interpolate(VoxelArea(relpos,
2041 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
2043 core::map<v3s16, MapBlock*> modified_blocks;
2044 vmanip.blitBack(modified_blocks);
2045 dstream<<"blitBack modified "<<modified_blocks.size()
2046 <<" blocks"<<std::endl;
2048 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
2049 for(core::map<v3s16, MapBlock*>::Iterator
2050 i = modified_blocks.getIterator();
2051 i.atEnd() == false; i++)
2053 MapBlock *block = i.getNode()->getValue();
2055 changed_blocks.insert(block->getPos(), block);
2056 //lighting_invalidated_blocks.insert(block->getPos(), block);
2066 // An y-wise container of changed blocks
2067 core::map<s16, MapBlock*> changed_blocks_sector;
2070 Check if any sector's objects can be placed now.
2073 core::map<v3s16, u8> *objects = sector->getObjects();
2074 core::list<v3s16> objects_to_remove;
2075 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2076 i.atEnd() == false; i++)
2078 v3s16 p = i.getNode()->getKey();
2080 u8 d = i.getNode()->getValue();
2082 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2087 if(d == SECTOR_OBJECT_TEST)
2089 if(sector->isValidArea(p + v3s16(0,0,0),
2090 p + v3s16(0,0,0), &changed_blocks_sector))
2093 n.d = CONTENT_TORCH;
2094 sector->setNode(p, n);
2095 objects_to_remove.push_back(p);
2098 else if(d == SECTOR_OBJECT_TREE_1)
2100 v3s16 p_min = p + v3s16(-1,0,-1);
2101 v3s16 p_max = p + v3s16(1,4,1);
2102 if(sector->isValidArea(p_min, p_max,
2103 &changed_blocks_sector))
2107 sector->setNode(p+v3s16(0,0,0), n);
2108 sector->setNode(p+v3s16(0,1,0), n);
2109 sector->setNode(p+v3s16(0,2,0), n);
2110 sector->setNode(p+v3s16(0,3,0), n);
2112 n.d = CONTENT_LEAVES;
2114 sector->setNode(p+v3s16(0,4,0), n);
2116 sector->setNode(p+v3s16(-1,4,0), n);
2117 sector->setNode(p+v3s16(1,4,0), n);
2118 sector->setNode(p+v3s16(0,4,-1), n);
2119 sector->setNode(p+v3s16(0,4,1), n);
2120 sector->setNode(p+v3s16(1,4,1), n);
2121 sector->setNode(p+v3s16(-1,4,1), n);
2122 sector->setNode(p+v3s16(-1,4,-1), n);
2123 sector->setNode(p+v3s16(1,4,-1), n);
2125 sector->setNode(p+v3s16(-1,3,0), n);
2126 sector->setNode(p+v3s16(1,3,0), n);
2127 sector->setNode(p+v3s16(0,3,-1), n);
2128 sector->setNode(p+v3s16(0,3,1), n);
2129 sector->setNode(p+v3s16(1,3,1), n);
2130 sector->setNode(p+v3s16(-1,3,1), n);
2131 sector->setNode(p+v3s16(-1,3,-1), n);
2132 sector->setNode(p+v3s16(1,3,-1), n);
2134 objects_to_remove.push_back(p);
2136 // Lighting has to be recalculated for this one.
2137 sector->getBlocksInArea(p_min, p_max,
2138 lighting_invalidated_blocks);
2141 else if(d == SECTOR_OBJECT_BUSH_1)
2143 if(sector->isValidArea(p + v3s16(0,0,0),
2144 p + v3s16(0,0,0), &changed_blocks_sector))
2147 n.d = CONTENT_LEAVES;
2148 sector->setNode(p+v3s16(0,0,0), n);
2150 objects_to_remove.push_back(p);
2153 else if(d == SECTOR_OBJECT_RAVINE)
2156 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2157 v3s16 p_max = p + v3s16(6,6,6);
2158 if(sector->isValidArea(p_min, p_max,
2159 &changed_blocks_sector))
2162 n.d = CONTENT_STONE;
2165 s16 depth = maxdepth + (rand()%10);
2167 s16 minz = -6 - (-2);
2169 for(s16 x=-6; x<=6; x++)
2171 z += -1 + (rand()%3);
2176 for(s16 y=depth+(rand()%2); y<=6; y++)
2178 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2181 v3s16 p2 = p + v3s16(x,y,z-2);
2182 if(is_ground_content(sector->getNode(p2).d))
2183 sector->setNode(p2, n);
2186 v3s16 p2 = p + v3s16(x,y,z-1);
2187 if(is_ground_content(sector->getNode(p2).d))
2188 sector->setNode(p2, n2);
2191 v3s16 p2 = p + v3s16(x,y,z+0);
2192 if(is_ground_content(sector->getNode(p2).d))
2193 sector->setNode(p2, n2);
2196 v3s16 p2 = p + v3s16(x,y,z+1);
2197 if(is_ground_content(sector->getNode(p2).d))
2198 sector->setNode(p2, n);
2201 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2202 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2206 objects_to_remove.push_back(p);
2208 // Lighting has to be recalculated for this one.
2209 sector->getBlocksInArea(p_min, p_max,
2210 lighting_invalidated_blocks);
2215 dstream<<"ServerMap::emergeBlock(): "
2216 "Invalid heightmap object"
2221 catch(InvalidPositionException &e)
2223 dstream<<"WARNING: "<<__FUNCTION_NAME
2224 <<": while inserting object "<<(int)d
2225 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2226 <<" InvalidPositionException.what()="
2227 <<e.what()<<std::endl;
2228 // This is not too fatal and seems to happen sometimes.
2233 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2234 i != objects_to_remove.end(); i++)
2236 objects->remove(*i);
2239 for(core::map<s16, MapBlock*>::Iterator
2240 i = changed_blocks_sector.getIterator();
2241 i.atEnd() == false; i++)
2243 MapBlock *block = i.getNode()->getValue();
2245 changed_blocks.insert(block->getPos(), block);
2251 void ServerMap::createDir(std::string path)
2253 if(fs::CreateDir(path) == false)
2255 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2256 <<"\""<<path<<"\""<<std::endl;
2257 throw BaseException("ServerMap failed to create directory");
2261 std::string ServerMap::getSectorSubDir(v2s16 pos)
2264 snprintf(cc, 9, "%.4x%.4x",
2265 (unsigned int)pos.X&0xffff,
2266 (unsigned int)pos.Y&0xffff);
2268 return std::string(cc);
2271 std::string ServerMap::getSectorDir(v2s16 pos)
2273 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2276 v2s16 ServerMap::getSectorPos(std::string dirname)
2278 if(dirname.size() != 8)
2279 throw InvalidFilenameException("Invalid sector directory name");
2281 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2283 throw InvalidFilenameException("Invalid sector directory name");
2284 v2s16 pos((s16)x, (s16)y);
2288 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2290 v2s16 p2d = getSectorPos(sectordir);
2292 if(blockfile.size() != 4){
2293 throw InvalidFilenameException("Invalid block filename");
2296 int r = sscanf(blockfile.c_str(), "%4x", &y);
2298 throw InvalidFilenameException("Invalid block filename");
2299 return v3s16(p2d.X, y, p2d.Y);
2303 #define ENABLE_SECTOR_SAVING 1
2304 #define ENABLE_SECTOR_LOADING 1
2305 #define ENABLE_BLOCK_SAVING 1
2306 #define ENABLE_BLOCK_LOADING 1
2308 void ServerMap::save(bool only_changed)
2310 DSTACK(__FUNCTION_NAME);
2311 if(m_map_saving_enabled == false)
2313 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2317 if(only_changed == false)
2318 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2321 saveMasterHeightmap();
2323 u32 sector_meta_count = 0;
2324 u32 block_count = 0;
2327 JMutexAutoLock lock(m_sector_mutex);
2329 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2330 for(; i.atEnd() == false; i++)
2332 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2333 assert(sector->getId() == MAPSECTOR_SERVER);
2335 if(ENABLE_SECTOR_SAVING)
2337 if(sector->differs_from_disk || only_changed == false)
2339 saveSectorMeta(sector);
2340 sector_meta_count++;
2343 if(ENABLE_BLOCK_SAVING)
2345 core::list<MapBlock*> blocks;
2346 sector->getBlocks(blocks);
2347 core::list<MapBlock*>::Iterator j;
2348 for(j=blocks.begin(); j!=blocks.end(); j++)
2350 MapBlock *block = *j;
2351 if(block->getChangedFlag() || only_changed == false)
2362 u32 deleted_count = 0;
2363 deleted_count = deleteUnusedSectors
2364 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2367 Only print if something happened or saved whole map
2369 if(only_changed == false || sector_meta_count != 0
2370 || block_count != 0 || deleted_count != 0)
2372 dstream<<DTIME<<"ServerMap: Written: "
2373 <<sector_meta_count<<" sector metadata files, "
2374 <<block_count<<" block files, "
2375 <<deleted_count<<" sectors unloaded from memory."
2380 void ServerMap::loadAll()
2382 DSTACK(__FUNCTION_NAME);
2383 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2385 loadMasterHeightmap();
2387 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2389 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2391 JMutexAutoLock lock(m_sector_mutex);
2394 s32 printed_counter = -100000;
2395 s32 count = list.size();
2397 std::vector<fs::DirListNode>::iterator i;
2398 for(i=list.begin(); i!=list.end(); i++)
2400 if(counter > printed_counter + 10)
2402 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2403 printed_counter = counter;
2407 MapSector *sector = NULL;
2409 // We want directories
2413 sector = loadSectorMeta(i->name);
2415 catch(InvalidFilenameException &e)
2417 // This catches unknown crap in directory
2420 if(ENABLE_BLOCK_LOADING)
2422 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2423 (m_savedir+"/sectors/"+i->name);
2424 std::vector<fs::DirListNode>::iterator i2;
2425 for(i2=list2.begin(); i2!=list2.end(); i2++)
2431 loadBlock(i->name, i2->name, sector);
2433 catch(InvalidFilenameException &e)
2435 // This catches unknown crap in directory
2440 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2443 void ServerMap::saveMasterHeightmap()
2445 DSTACK(__FUNCTION_NAME);
2446 createDir(m_savedir);
2448 std::string fullpath = m_savedir + "/master_heightmap";
2449 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2450 if(o.good() == false)
2451 throw FileNotGoodException("Cannot open master heightmap");
2453 // Format used for writing
2454 u8 version = SER_FMT_VER_HIGHEST;
2457 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2459 [0] u8 serialization version
2460 [1] X master heightmap
2462 u32 fullsize = 1 + hmdata.getSize();
2463 SharedBuffer<u8> data(fullsize);
2466 memcpy(&data[1], *hmdata, hmdata.getSize());
2468 o.write((const char*)*data, fullsize);
2471 m_heightmap->serialize(o, version);
2474 void ServerMap::loadMasterHeightmap()
2476 DSTACK(__FUNCTION_NAME);
2477 std::string fullpath = m_savedir + "/master_heightmap";
2478 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2479 if(is.good() == false)
2480 throw FileNotGoodException("Cannot open master heightmap");
2482 if(m_heightmap != NULL)
2485 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2488 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2490 DSTACK(__FUNCTION_NAME);
2491 // Format used for writing
2492 u8 version = SER_FMT_VER_HIGHEST;
2494 v2s16 pos = sector->getPos();
2495 createDir(m_savedir);
2496 createDir(m_savedir+"/sectors");
2497 std::string dir = getSectorDir(pos);
2500 std::string fullpath = dir + "/heightmap";
2501 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2502 if(o.good() == false)
2503 throw FileNotGoodException("Cannot open master heightmap");
2505 sector->serialize(o, version);
2507 sector->differs_from_disk = false;
2510 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2512 DSTACK(__FUNCTION_NAME);
2514 v2s16 p2d = getSectorPos(dirname);
2515 std::string dir = m_savedir + "/sectors/" + dirname;
2517 std::string fullpath = dir + "/heightmap";
2518 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2519 if(is.good() == false)
2520 throw FileNotGoodException("Cannot open sector heightmap");
2522 ServerMapSector *sector = ServerMapSector::deSerialize
2523 (is, this, p2d, &m_hwrapper, m_sectors);
2525 sector->differs_from_disk = false;
2530 bool ServerMap::loadSectorFull(v2s16 p2d)
2532 DSTACK(__FUNCTION_NAME);
2533 std::string sectorsubdir = getSectorSubDir(p2d);
2535 MapSector *sector = NULL;
2537 JMutexAutoLock lock(m_sector_mutex);
2540 sector = loadSectorMeta(sectorsubdir);
2542 catch(InvalidFilenameException &e)
2546 catch(FileNotGoodException &e)
2550 catch(std::exception &e)
2555 if(ENABLE_BLOCK_LOADING)
2557 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2558 (m_savedir+"/sectors/"+sectorsubdir);
2559 std::vector<fs::DirListNode>::iterator i2;
2560 for(i2=list2.begin(); i2!=list2.end(); i2++)
2566 loadBlock(sectorsubdir, i2->name, sector);
2568 catch(InvalidFilenameException &e)
2570 // This catches unknown crap in directory
2578 bool ServerMap::deFlushSector(v2s16 p2d)
2580 DSTACK(__FUNCTION_NAME);
2581 // See if it already exists in memory
2583 MapSector *sector = getSectorNoGenerate(p2d);
2586 catch(InvalidPositionException &e)
2589 Try to load the sector from disk.
2591 if(loadSectorFull(p2d) == true)
2600 void ServerMap::saveBlock(MapBlock *block)
2602 DSTACK(__FUNCTION_NAME);
2604 Dummy blocks are not written
2606 if(block->isDummy())
2608 /*v3s16 p = block->getPos();
2609 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2610 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2614 // Format used for writing
2615 u8 version = SER_FMT_VER_HIGHEST;
2617 v3s16 p3d = block->getPos();
2618 v2s16 p2d(p3d.X, p3d.Z);
2619 createDir(m_savedir);
2620 createDir(m_savedir+"/sectors");
2621 std::string dir = getSectorDir(p2d);
2624 // Block file is map/sectors/xxxxxxxx/xxxx
2626 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2627 std::string fullpath = dir + "/" + cc;
2628 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2629 if(o.good() == false)
2630 throw FileNotGoodException("Cannot open block data");
2633 [0] u8 serialization version
2636 o.write((char*)&version, 1);
2638 block->serialize(o, version);
2641 Versions up from 9 have block objects.
2645 block->serializeObjects(o, version);
2648 // We just wrote it to the disk
2649 block->resetChangedFlag();
2652 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2654 DSTACK(__FUNCTION_NAME);
2658 // Block file is map/sectors/xxxxxxxx/xxxx
2659 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2660 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2661 if(is.good() == false)
2662 throw FileNotGoodException("Cannot open block file");
2664 v3s16 p3d = getBlockPos(sectordir, blockfile);
2665 v2s16 p2d(p3d.X, p3d.Z);
2667 assert(sector->getPos() == p2d);
2669 u8 version = SER_FMT_VER_INVALID;
2670 is.read((char*)&version, 1);
2672 /*u32 block_size = MapBlock::serializedLength(version);
2673 SharedBuffer<u8> data(block_size);
2674 is.read((char*)*data, block_size);*/
2676 // This will always return a sector because we're the server
2677 //MapSector *sector = emergeSector(p2d);
2679 MapBlock *block = NULL;
2680 bool created_new = false;
2682 block = sector->getBlockNoCreate(p3d.Y);
2684 catch(InvalidPositionException &e)
2686 block = sector->createBlankBlockNoInsert(p3d.Y);
2690 // deserialize block data
2691 block->deSerialize(is, version);
2694 Versions up from 9 have block objects.
2698 block->updateObjects(is, version, NULL);
2702 sector->insertBlock(block);
2705 Convert old formats to new and save
2708 // Save old format blocks in new format
2709 if(version < SER_FMT_VER_HIGHEST)
2714 // We just loaded it from the disk, so it's up-to-date.
2715 block->resetChangedFlag();
2718 catch(SerializationError &e)
2720 dstream<<"WARNING: Invalid block data on disk "
2721 "(SerializationError). Ignoring."
2726 // Gets from master heightmap
2727 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2729 assert(m_heightmap != NULL);
2737 corners[0] = m_heightmap->getGroundHeight
2738 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2739 corners[1] = m_heightmap->getGroundHeight
2740 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2741 corners[2] = m_heightmap->getGroundHeight
2742 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2743 corners[3] = m_heightmap->getGroundHeight
2744 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2747 void ServerMap::PrintInfo(std::ostream &out)
2756 ClientMap::ClientMap(
2758 scene::ISceneNode* parent,
2759 scene::ISceneManager* mgr,
2763 scene::ISceneNode(parent, mgr, id),
2767 /*m_box = core::aabbox3d<f32>(0,0,0,
2768 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2769 /*m_box = core::aabbox3d<f32>(0,0,0,
2770 map->getSizeNodes().X * BS,
2771 map->getSizeNodes().Y * BS,
2772 map->getSizeNodes().Z * BS);*/
2773 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2774 BS*1000000,BS*1000000,BS*1000000);
2779 ClientMap::~ClientMap()
2781 JMutexAutoLock lock(mesh_mutex);
2790 MapSector * ClientMap::emergeSector(v2s16 p2d)
2792 DSTACK(__FUNCTION_NAME);
2793 // Check that it doesn't exist already
2795 return getSectorNoGenerate(p2d);
2797 catch(InvalidPositionException &e)
2801 // Create a sector with no heightmaps
2802 ClientMapSector *sector = new ClientMapSector(this, p2d);
2805 JMutexAutoLock lock(m_sector_mutex);
2806 m_sectors.insert(p2d, sector);
2812 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2814 DSTACK(__FUNCTION_NAME);
2815 ClientMapSector *sector = NULL;
2817 JMutexAutoLock lock(m_sector_mutex);
2819 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2823 sector = (ClientMapSector*)n->getValue();
2824 assert(sector->getId() == MAPSECTOR_CLIENT);
2828 sector = new ClientMapSector(this, p2d);
2830 JMutexAutoLock lock(m_sector_mutex);
2831 m_sectors.insert(p2d, sector);
2835 sector->deSerialize(is);
2838 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2840 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2841 DSTACK(__FUNCTION_NAME);
2843 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2846 Get time for measuring timeout.
2848 Measuring time is very useful for long delays when the
2849 machine is swapping a lot.
2851 int time1 = time(0);
2854 Collect all blocks that are in the view range
2856 Should not optimize more here as we want to auto-update
2857 all changed nodes in viewing range at the next step.
2860 s16 viewing_range_nodes;
2861 bool viewing_range_all;
2863 JMutexAutoLock lock(g_range_mutex);
2864 viewing_range_nodes = g_viewing_range_nodes;
2865 viewing_range_all = g_viewing_range_all;
2868 m_camera_mutex.Lock();
2869 v3f camera_position = m_camera_position;
2870 v3f camera_direction = m_camera_direction;
2871 m_camera_mutex.Unlock();
2874 Get all blocks and draw all visible ones
2877 v3s16 cam_pos_nodes(
2878 camera_position.X / BS,
2879 camera_position.Y / BS,
2880 camera_position.Z / BS);
2882 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2884 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2885 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2887 // Take a fair amount as we will be dropping more out later
2889 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2890 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2891 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2893 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2894 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2895 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2897 u32 vertex_count = 0;
2899 core::map<v2s16, MapSector*>::Iterator si;
2901 //NOTE: The sectors map should be locked but we're not doing it
2902 // because it'd cause too much delays
2904 si = m_sectors.getIterator();
2905 for(; si.atEnd() == false; si++)
2908 static int timecheck_counter = 0;
2909 timecheck_counter++;
2910 if(timecheck_counter > 50)
2912 int time2 = time(0);
2913 if(time2 > time1 + 4)
2915 dstream<<"ClientMap::renderMap(): "
2916 "Rendering takes ages, returning."
2923 MapSector *sector = si.getNode()->getValue();
2924 v2s16 sp = sector->getPos();
2926 if(viewing_range_all == false)
2928 if(sp.X < p_blocks_min.X
2929 || sp.X > p_blocks_max.X
2930 || sp.Y < p_blocks_min.Z
2931 || sp.Y > p_blocks_max.Z)
2935 core::list< MapBlock * > sectorblocks;
2936 sector->getBlocks(sectorblocks);
2942 core::list< MapBlock * >::Iterator i;
2943 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2945 MapBlock *block = *i;
2948 Compare block position to camera position, skip
2949 if not seen on display
2952 v3s16 blockpos_nodes = block->getPosRelative();
2954 // Block center position
2956 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2957 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2958 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2961 // Block position relative to camera
2962 v3f blockpos_relative = blockpos - camera_position;
2964 // Distance in camera direction (+=front, -=back)
2965 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2968 f32 d = blockpos_relative.getLength();
2970 if(viewing_range_all == false)
2972 // If block is far away, don't draw it
2973 if(d > viewing_range_nodes * BS)
2977 // Maximum radius of a block
2978 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2980 // If block is (nearly) touching the camera, don't
2981 // bother validating further (that is, render it anyway)
2982 if(d > block_max_radius * 1.5)
2984 // Cosine of the angle between the camera direction
2985 // and the block direction (camera_direction is an unit vector)
2986 f32 cosangle = dforward / d;
2988 // Compensate for the size of the block
2989 // (as the block has to be shown even if it's a bit off FOV)
2990 // This is an estimate.
2991 cosangle += block_max_radius / dforward;
2993 // If block is not in the field of view, skip it
2994 //if(cosangle < cos(FOV_ANGLE/2))
2995 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3000 Draw the faces of the block
3004 JMutexAutoLock lock(block->mesh_mutex);
3006 // Cancel if block has no mesh
3007 if(block->mesh == NULL)
3010 u32 c = block->mesh->getMeshBufferCount();
3012 for(u32 i=0; i<c; i++)
3014 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
3015 const video::SMaterial& material = buf->getMaterial();
3016 video::IMaterialRenderer* rnd =
3017 driver->getMaterialRenderer(material.MaterialType);
3018 bool transparent = (rnd && rnd->isTransparent());
3019 // Render transparent on transparent pass and likewise.
3020 if(transparent == is_transparent_pass)
3022 driver->setMaterial(buf->getMaterial());
3023 driver->drawMeshBuffer(buf);
3024 vertex_count += buf->getVertexCount();
3028 } // foreach sectorblocks
3031 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3032 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3035 void ClientMap::updateMesh()
3040 void ClientMap::PrintInfo(std::ostream &out)
3050 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3055 MapVoxelManipulator::~MapVoxelManipulator()
3057 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3062 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3064 TimeTaker timer1("emerge", g_device, &emerge_time);
3066 // Units of these are MapBlocks
3067 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3068 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3070 VoxelArea block_area_nodes
3071 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3073 addArea(block_area_nodes);
3075 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3076 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3077 for(s32 x=p_min.X; x<=p_max.X; x++)
3080 core::map<v3s16, bool>::Node *n;
3081 n = m_loaded_blocks.find(p);
3085 bool block_data_inexistent = false;
3088 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3090 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3091 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3094 dstream<<std::endl;*/
3096 MapBlock *block = m_map->getBlockNoCreate(p);
3097 if(block->isDummy())
3098 block_data_inexistent = true;
3100 block->copyTo(*this);
3102 catch(InvalidPositionException &e)
3104 block_data_inexistent = true;
3107 if(block_data_inexistent)
3109 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3110 // Fill with VOXELFLAG_INEXISTENT
3111 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3112 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3114 s32 i = m_area.index(a.MinEdge.X,y,z);
3115 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3119 m_loaded_blocks.insert(p, true);
3122 //dstream<<"emerge done"<<std::endl;
3127 void MapVoxelManipulator::emerge(VoxelArea a)
3129 TimeTaker timer1("emerge", g_device, &emerge_time);
3131 v3s16 size = a.getExtent();
3133 VoxelArea padded = a;
3134 padded.pad(m_area.getExtent() / 4);
3137 for(s16 z=0; z<size.Z; z++)
3138 for(s16 y=0; y<size.Y; y++)
3139 for(s16 x=0; x<size.X; x++)
3142 s32 i = m_area.index(a.MinEdge + p);
3143 // Don't touch nodes that have already been loaded
3144 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3148 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3149 MapNode n = m_map->getNode(a.MinEdge + p);
3153 catch(InvalidPositionException &e)
3155 m_flags[i] = VOXELFLAG_INEXISTENT;
3163 TODO: Add an option to only update eg. water and air nodes.
3164 This will make it interfere less with important stuff if
3167 void MapVoxelManipulator::blitBack
3168 (core::map<v3s16, MapBlock*> & modified_blocks)
3170 if(m_area.getExtent() == v3s16(0,0,0))
3173 //TimeTaker timer1("blitBack", g_device);
3176 Initialize block cache
3178 v3s16 blockpos_last;
3179 MapBlock *block = NULL;
3180 bool block_checked_in_modified = false;
3182 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3183 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3184 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3188 u8 f = m_flags[m_area.index(p)];
3189 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3192 MapNode &n = m_data[m_area.index(p)];
3194 v3s16 blockpos = getNodeBlockPos(p);
3199 if(block == NULL || blockpos != blockpos_last){
3200 block = m_map->getBlockNoCreate(blockpos);
3201 blockpos_last = blockpos;
3202 block_checked_in_modified = false;
3205 // Calculate relative position in block
3206 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3208 // Don't continue if nothing has changed here
3209 if(block->getNode(relpos) == n)
3212 //m_map->setNode(m_area.MinEdge + p, n);
3213 block->setNode(relpos, n);
3216 Make sure block is in modified_blocks
3218 if(block_checked_in_modified == false)
3220 modified_blocks[blockpos] = block;
3221 block_checked_in_modified = true;
3224 catch(InvalidPositionException &e)