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.70;
1850 float max_slope = 1.20;
1851 float min_slope_depth = 4.0;
1852 //float min_slope_depth = 5.0;
1853 float max_slope_depth = 0;
1854 if(slope < min_slope)
1855 surface_depth = min_slope_depth;
1856 else if(slope > max_slope)
1857 surface_depth = max_slope_depth;
1859 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1861 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1863 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1868 NOTE: If there are some man-made structures above the
1869 newly created block, they won't be taken into account.
1871 if(real_y > surface_y)
1872 n.setLight(LIGHT_SUN);
1878 // If node is very low
1879 /*if(real_y <= surface_y - 7)
1882 if(underground_emptiness[
1883 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1884 +ued*(y0*ued/MAP_BLOCKSIZE)
1885 +(x0*ued/MAP_BLOCKSIZE)])
1891 n.d = CONTENT_STONE;
1894 // If node is under surface level
1895 else if(real_y <= surface_y - surface_depth)
1896 n.d = CONTENT_STONE;
1898 if(real_y <= surface_y - surface_depth)
1901 if(underground_emptiness[
1902 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1903 +ued*(y0*ued/MAP_BLOCKSIZE)
1904 +(x0*ued/MAP_BLOCKSIZE)])
1910 n.d = CONTENT_STONE;
1913 // If node is at or under heightmap y
1914 else if(real_y <= surface_y)
1916 // If under water level, it's mud
1917 if(real_y < WATER_LEVEL)
1919 // Only the topmost node is grass
1920 else if(real_y <= surface_y - 1)
1922 // Else it's the main material
1926 // If node is over heightmap y
1928 // If under water level, it's water
1929 if(real_y < WATER_LEVEL)
1931 n.d = water_material;
1932 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1938 block->setNode(v3s16(x0,y0,z0), n);
1943 Calculate is_underground
1945 // Probably underground if the highest part of block is under lowest
1947 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1948 block->setIsUnderground(is_underground);
1951 Force lighting update if some part of block is underground
1952 This is needed because of caves.
1955 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1956 if(some_part_underground)
1957 //if(is_underground)
1959 lighting_invalidated_blocks[block->getPos()] = block;
1966 //if(is_underground)
1967 if(some_part_underground)
1969 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1970 for(s16 i=0; i<underground_level*3; i++)
1975 (rand()%(MAP_BLOCKSIZE-2))+1,
1976 (rand()%(MAP_BLOCKSIZE-2))+1,
1977 (rand()%(MAP_BLOCKSIZE-2))+1
1983 //if(is_ground_content(block->getNode(cp).d))
1984 if(block->getNode(cp).d == CONTENT_STONE)
1986 block->setNode(cp, n);
1988 for(u16 i=0; i<26; i++)
1990 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1991 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1993 block->setNode(cp+g_26dirs[i], n);
2000 Create a few rats in empty blocks underground
2004 //for(u16 i=0; i<2; i++)
2007 (rand()%(MAP_BLOCKSIZE-2))+1,
2008 (rand()%(MAP_BLOCKSIZE-2))+1,
2009 (rand()%(MAP_BLOCKSIZE-2))+1
2012 // Check that the place is empty
2013 //if(!is_ground_content(block->getNode(cp).d))
2016 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2017 block->addObject(obj);
2023 Add block to sector.
2025 sector->insertBlock(block);
2028 Do some interpolation for dungeons
2033 TimeTaker timer("interpolation", g_device);
2035 MapVoxelManipulator vmanip(this);
2037 v3s16 relpos = block->getPosRelative();
2039 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
2040 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
2041 /*vmanip.interpolate(VoxelArea(relpos,
2042 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
2044 core::map<v3s16, MapBlock*> modified_blocks;
2045 vmanip.blitBack(modified_blocks);
2046 dstream<<"blitBack modified "<<modified_blocks.size()
2047 <<" blocks"<<std::endl;
2049 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
2050 for(core::map<v3s16, MapBlock*>::Iterator
2051 i = modified_blocks.getIterator();
2052 i.atEnd() == false; i++)
2054 MapBlock *block = i.getNode()->getValue();
2056 changed_blocks.insert(block->getPos(), block);
2057 //lighting_invalidated_blocks.insert(block->getPos(), block);
2067 // An y-wise container of changed blocks
2068 core::map<s16, MapBlock*> changed_blocks_sector;
2071 Check if any sector's objects can be placed now.
2074 core::map<v3s16, u8> *objects = sector->getObjects();
2075 core::list<v3s16> objects_to_remove;
2076 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2077 i.atEnd() == false; i++)
2079 v3s16 p = i.getNode()->getKey();
2081 u8 d = i.getNode()->getValue();
2083 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2088 if(d == SECTOR_OBJECT_TEST)
2090 if(sector->isValidArea(p + v3s16(0,0,0),
2091 p + v3s16(0,0,0), &changed_blocks_sector))
2094 n.d = CONTENT_LIGHT;
2095 sector->setNode(p, n);
2096 objects_to_remove.push_back(p);
2099 else if(d == SECTOR_OBJECT_TREE_1)
2101 v3s16 p_min = p + v3s16(-1,0,-1);
2102 v3s16 p_max = p + v3s16(1,4,1);
2103 if(sector->isValidArea(p_min, p_max,
2104 &changed_blocks_sector))
2108 sector->setNode(p+v3s16(0,0,0), n);
2109 sector->setNode(p+v3s16(0,1,0), n);
2110 sector->setNode(p+v3s16(0,2,0), n);
2111 sector->setNode(p+v3s16(0,3,0), n);
2113 n.d = CONTENT_LEAVES;
2115 sector->setNode(p+v3s16(0,4,0), n);
2117 sector->setNode(p+v3s16(-1,4,0), n);
2118 sector->setNode(p+v3s16(1,4,0), n);
2119 sector->setNode(p+v3s16(0,4,-1), n);
2120 sector->setNode(p+v3s16(0,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);
2124 sector->setNode(p+v3s16(1,4,-1), n);
2126 sector->setNode(p+v3s16(-1,3,0), n);
2127 sector->setNode(p+v3s16(1,3,0), n);
2128 sector->setNode(p+v3s16(0,3,-1), n);
2129 sector->setNode(p+v3s16(0,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);
2133 sector->setNode(p+v3s16(1,3,-1), n);
2135 objects_to_remove.push_back(p);
2137 // Lighting has to be recalculated for this one.
2138 sector->getBlocksInArea(p_min, p_max,
2139 lighting_invalidated_blocks);
2142 else if(d == SECTOR_OBJECT_BUSH_1)
2144 if(sector->isValidArea(p + v3s16(0,0,0),
2145 p + v3s16(0,0,0), &changed_blocks_sector))
2148 n.d = CONTENT_LEAVES;
2149 sector->setNode(p+v3s16(0,0,0), n);
2151 objects_to_remove.push_back(p);
2154 else if(d == SECTOR_OBJECT_RAVINE)
2157 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2158 v3s16 p_max = p + v3s16(6,6,6);
2159 if(sector->isValidArea(p_min, p_max,
2160 &changed_blocks_sector))
2163 n.d = CONTENT_STONE;
2166 s16 depth = maxdepth + (rand()%10);
2168 s16 minz = -6 - (-2);
2170 for(s16 x=-6; x<=6; x++)
2172 z += -1 + (rand()%3);
2177 for(s16 y=depth+(rand()%2); y<=6; y++)
2179 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2182 v3s16 p2 = p + v3s16(x,y,z-2);
2183 if(is_ground_content(sector->getNode(p2).d))
2184 sector->setNode(p2, n);
2187 v3s16 p2 = p + v3s16(x,y,z-1);
2188 if(is_ground_content(sector->getNode(p2).d))
2189 sector->setNode(p2, n2);
2192 v3s16 p2 = p + v3s16(x,y,z+0);
2193 if(is_ground_content(sector->getNode(p2).d))
2194 sector->setNode(p2, n2);
2197 v3s16 p2 = p + v3s16(x,y,z+1);
2198 if(is_ground_content(sector->getNode(p2).d))
2199 sector->setNode(p2, n);
2202 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2203 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2207 objects_to_remove.push_back(p);
2209 // Lighting has to be recalculated for this one.
2210 sector->getBlocksInArea(p_min, p_max,
2211 lighting_invalidated_blocks);
2216 dstream<<"ServerMap::emergeBlock(): "
2217 "Invalid heightmap object"
2222 catch(InvalidPositionException &e)
2224 dstream<<"WARNING: "<<__FUNCTION_NAME
2225 <<": while inserting object "<<(int)d
2226 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2227 <<" InvalidPositionException.what()="
2228 <<e.what()<<std::endl;
2229 // This is not too fatal and seems to happen sometimes.
2234 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2235 i != objects_to_remove.end(); i++)
2237 objects->remove(*i);
2240 for(core::map<s16, MapBlock*>::Iterator
2241 i = changed_blocks_sector.getIterator();
2242 i.atEnd() == false; i++)
2244 MapBlock *block = i.getNode()->getValue();
2246 changed_blocks.insert(block->getPos(), block);
2252 void ServerMap::createDir(std::string path)
2254 if(fs::CreateDir(path) == false)
2256 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2257 <<"\""<<path<<"\""<<std::endl;
2258 throw BaseException("ServerMap failed to create directory");
2262 std::string ServerMap::getSectorSubDir(v2s16 pos)
2265 snprintf(cc, 9, "%.4x%.4x",
2266 (unsigned int)pos.X&0xffff,
2267 (unsigned int)pos.Y&0xffff);
2269 return std::string(cc);
2272 std::string ServerMap::getSectorDir(v2s16 pos)
2274 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2277 v2s16 ServerMap::getSectorPos(std::string dirname)
2279 if(dirname.size() != 8)
2280 throw InvalidFilenameException("Invalid sector directory name");
2282 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2284 throw InvalidFilenameException("Invalid sector directory name");
2285 v2s16 pos((s16)x, (s16)y);
2289 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2291 v2s16 p2d = getSectorPos(sectordir);
2293 if(blockfile.size() != 4){
2294 throw InvalidFilenameException("Invalid block filename");
2297 int r = sscanf(blockfile.c_str(), "%4x", &y);
2299 throw InvalidFilenameException("Invalid block filename");
2300 return v3s16(p2d.X, y, p2d.Y);
2304 #define ENABLE_SECTOR_SAVING 1
2305 #define ENABLE_SECTOR_LOADING 1
2306 #define ENABLE_BLOCK_SAVING 1
2307 #define ENABLE_BLOCK_LOADING 1
2309 void ServerMap::save(bool only_changed)
2311 DSTACK(__FUNCTION_NAME);
2312 if(m_map_saving_enabled == false)
2314 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2318 if(only_changed == false)
2319 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2322 saveMasterHeightmap();
2324 u32 sector_meta_count = 0;
2325 u32 block_count = 0;
2328 JMutexAutoLock lock(m_sector_mutex);
2330 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2331 for(; i.atEnd() == false; i++)
2333 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2334 assert(sector->getId() == MAPSECTOR_SERVER);
2336 if(ENABLE_SECTOR_SAVING)
2338 if(sector->differs_from_disk || only_changed == false)
2340 saveSectorMeta(sector);
2341 sector_meta_count++;
2344 if(ENABLE_BLOCK_SAVING)
2346 core::list<MapBlock*> blocks;
2347 sector->getBlocks(blocks);
2348 core::list<MapBlock*>::Iterator j;
2349 for(j=blocks.begin(); j!=blocks.end(); j++)
2351 MapBlock *block = *j;
2352 if(block->getChangedFlag() || only_changed == false)
2363 u32 deleted_count = 0;
2364 deleted_count = deleteUnusedSectors
2365 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2368 Only print if something happened or saved whole map
2370 if(only_changed == false || sector_meta_count != 0
2371 || block_count != 0 || deleted_count != 0)
2373 dstream<<DTIME<<"ServerMap: Written: "
2374 <<sector_meta_count<<" sector metadata files, "
2375 <<block_count<<" block files, "
2376 <<deleted_count<<" sectors unloaded from memory."
2381 void ServerMap::loadAll()
2383 DSTACK(__FUNCTION_NAME);
2384 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2386 loadMasterHeightmap();
2388 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2390 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2392 JMutexAutoLock lock(m_sector_mutex);
2395 s32 printed_counter = -100000;
2396 s32 count = list.size();
2398 std::vector<fs::DirListNode>::iterator i;
2399 for(i=list.begin(); i!=list.end(); i++)
2401 if(counter > printed_counter + 10)
2403 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2404 printed_counter = counter;
2408 MapSector *sector = NULL;
2410 // We want directories
2414 sector = loadSectorMeta(i->name);
2416 catch(InvalidFilenameException &e)
2418 // This catches unknown crap in directory
2421 if(ENABLE_BLOCK_LOADING)
2423 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2424 (m_savedir+"/sectors/"+i->name);
2425 std::vector<fs::DirListNode>::iterator i2;
2426 for(i2=list2.begin(); i2!=list2.end(); i2++)
2432 loadBlock(i->name, i2->name, sector);
2434 catch(InvalidFilenameException &e)
2436 // This catches unknown crap in directory
2441 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2444 void ServerMap::saveMasterHeightmap()
2446 DSTACK(__FUNCTION_NAME);
2447 createDir(m_savedir);
2449 std::string fullpath = m_savedir + "/master_heightmap";
2450 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2451 if(o.good() == false)
2452 throw FileNotGoodException("Cannot open master heightmap");
2454 // Format used for writing
2455 u8 version = SER_FMT_VER_HIGHEST;
2458 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2460 [0] u8 serialization version
2461 [1] X master heightmap
2463 u32 fullsize = 1 + hmdata.getSize();
2464 SharedBuffer<u8> data(fullsize);
2467 memcpy(&data[1], *hmdata, hmdata.getSize());
2469 o.write((const char*)*data, fullsize);
2472 m_heightmap->serialize(o, version);
2475 void ServerMap::loadMasterHeightmap()
2477 DSTACK(__FUNCTION_NAME);
2478 std::string fullpath = m_savedir + "/master_heightmap";
2479 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2480 if(is.good() == false)
2481 throw FileNotGoodException("Cannot open master heightmap");
2483 if(m_heightmap != NULL)
2486 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2489 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2491 DSTACK(__FUNCTION_NAME);
2492 // Format used for writing
2493 u8 version = SER_FMT_VER_HIGHEST;
2495 v2s16 pos = sector->getPos();
2496 createDir(m_savedir);
2497 createDir(m_savedir+"/sectors");
2498 std::string dir = getSectorDir(pos);
2501 std::string fullpath = dir + "/heightmap";
2502 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2503 if(o.good() == false)
2504 throw FileNotGoodException("Cannot open master heightmap");
2506 sector->serialize(o, version);
2508 sector->differs_from_disk = false;
2511 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2513 DSTACK(__FUNCTION_NAME);
2515 v2s16 p2d = getSectorPos(dirname);
2516 std::string dir = m_savedir + "/sectors/" + dirname;
2518 std::string fullpath = dir + "/heightmap";
2519 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2520 if(is.good() == false)
2521 throw FileNotGoodException("Cannot open sector heightmap");
2523 ServerMapSector *sector = ServerMapSector::deSerialize
2524 (is, this, p2d, &m_hwrapper, m_sectors);
2526 sector->differs_from_disk = false;
2531 bool ServerMap::loadSectorFull(v2s16 p2d)
2533 DSTACK(__FUNCTION_NAME);
2534 std::string sectorsubdir = getSectorSubDir(p2d);
2536 MapSector *sector = NULL;
2538 JMutexAutoLock lock(m_sector_mutex);
2541 sector = loadSectorMeta(sectorsubdir);
2543 catch(InvalidFilenameException &e)
2547 catch(FileNotGoodException &e)
2551 catch(std::exception &e)
2556 if(ENABLE_BLOCK_LOADING)
2558 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2559 (m_savedir+"/sectors/"+sectorsubdir);
2560 std::vector<fs::DirListNode>::iterator i2;
2561 for(i2=list2.begin(); i2!=list2.end(); i2++)
2567 loadBlock(sectorsubdir, i2->name, sector);
2569 catch(InvalidFilenameException &e)
2571 // This catches unknown crap in directory
2579 bool ServerMap::deFlushSector(v2s16 p2d)
2581 DSTACK(__FUNCTION_NAME);
2582 // See if it already exists in memory
2584 MapSector *sector = getSectorNoGenerate(p2d);
2587 catch(InvalidPositionException &e)
2590 Try to load the sector from disk.
2592 if(loadSectorFull(p2d) == true)
2601 void ServerMap::saveBlock(MapBlock *block)
2603 DSTACK(__FUNCTION_NAME);
2605 Dummy blocks are not written
2607 if(block->isDummy())
2609 /*v3s16 p = block->getPos();
2610 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2611 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2615 // Format used for writing
2616 u8 version = SER_FMT_VER_HIGHEST;
2618 v3s16 p3d = block->getPos();
2619 v2s16 p2d(p3d.X, p3d.Z);
2620 createDir(m_savedir);
2621 createDir(m_savedir+"/sectors");
2622 std::string dir = getSectorDir(p2d);
2625 // Block file is map/sectors/xxxxxxxx/xxxx
2627 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2628 std::string fullpath = dir + "/" + cc;
2629 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2630 if(o.good() == false)
2631 throw FileNotGoodException("Cannot open block data");
2634 [0] u8 serialization version
2637 o.write((char*)&version, 1);
2639 block->serialize(o, version);
2642 Versions up from 9 have block objects.
2646 block->serializeObjects(o, version);
2649 // We just wrote it to the disk
2650 block->resetChangedFlag();
2653 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2655 DSTACK(__FUNCTION_NAME);
2659 // Block file is map/sectors/xxxxxxxx/xxxx
2660 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2661 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2662 if(is.good() == false)
2663 throw FileNotGoodException("Cannot open block file");
2665 v3s16 p3d = getBlockPos(sectordir, blockfile);
2666 v2s16 p2d(p3d.X, p3d.Z);
2668 assert(sector->getPos() == p2d);
2670 u8 version = SER_FMT_VER_INVALID;
2671 is.read((char*)&version, 1);
2673 /*u32 block_size = MapBlock::serializedLength(version);
2674 SharedBuffer<u8> data(block_size);
2675 is.read((char*)*data, block_size);*/
2677 // This will always return a sector because we're the server
2678 //MapSector *sector = emergeSector(p2d);
2680 MapBlock *block = NULL;
2681 bool created_new = false;
2683 block = sector->getBlockNoCreate(p3d.Y);
2685 catch(InvalidPositionException &e)
2687 block = sector->createBlankBlockNoInsert(p3d.Y);
2691 // deserialize block data
2692 block->deSerialize(is, version);
2695 Versions up from 9 have block objects.
2699 block->updateObjects(is, version, NULL);
2703 sector->insertBlock(block);
2706 Convert old formats to new and save
2709 // Save old format blocks in new format
2710 if(version < SER_FMT_VER_HIGHEST)
2715 // We just loaded it from the disk, so it's up-to-date.
2716 block->resetChangedFlag();
2719 catch(SerializationError &e)
2721 dstream<<"WARNING: Invalid block data on disk "
2722 "(SerializationError). Ignoring."
2727 // Gets from master heightmap
2728 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2730 assert(m_heightmap != NULL);
2738 corners[0] = m_heightmap->getGroundHeight
2739 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2740 corners[1] = m_heightmap->getGroundHeight
2741 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2742 corners[2] = m_heightmap->getGroundHeight
2743 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2744 corners[3] = m_heightmap->getGroundHeight
2745 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2748 void ServerMap::PrintInfo(std::ostream &out)
2757 ClientMap::ClientMap(
2759 scene::ISceneNode* parent,
2760 scene::ISceneManager* mgr,
2764 scene::ISceneNode(parent, mgr, id),
2768 /*m_box = core::aabbox3d<f32>(0,0,0,
2769 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2770 /*m_box = core::aabbox3d<f32>(0,0,0,
2771 map->getSizeNodes().X * BS,
2772 map->getSizeNodes().Y * BS,
2773 map->getSizeNodes().Z * BS);*/
2774 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2775 BS*1000000,BS*1000000,BS*1000000);
2780 ClientMap::~ClientMap()
2782 JMutexAutoLock lock(mesh_mutex);
2791 MapSector * ClientMap::emergeSector(v2s16 p2d)
2793 DSTACK(__FUNCTION_NAME);
2794 // Check that it doesn't exist already
2796 return getSectorNoGenerate(p2d);
2798 catch(InvalidPositionException &e)
2802 // Create a sector with no heightmaps
2803 ClientMapSector *sector = new ClientMapSector(this, p2d);
2806 JMutexAutoLock lock(m_sector_mutex);
2807 m_sectors.insert(p2d, sector);
2813 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2815 DSTACK(__FUNCTION_NAME);
2816 ClientMapSector *sector = NULL;
2818 JMutexAutoLock lock(m_sector_mutex);
2820 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2824 sector = (ClientMapSector*)n->getValue();
2825 assert(sector->getId() == MAPSECTOR_CLIENT);
2829 sector = new ClientMapSector(this, p2d);
2831 JMutexAutoLock lock(m_sector_mutex);
2832 m_sectors.insert(p2d, sector);
2836 sector->deSerialize(is);
2839 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2841 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2842 DSTACK(__FUNCTION_NAME);
2844 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2847 Get time for measuring timeout.
2849 Measuring time is very useful for long delays when the
2850 machine is swapping a lot.
2852 int time1 = time(0);
2855 Collect all blocks that are in the view range
2857 Should not optimize more here as we want to auto-update
2858 all changed nodes in viewing range at the next step.
2861 s16 viewing_range_nodes;
2862 bool viewing_range_all;
2864 JMutexAutoLock lock(g_range_mutex);
2865 viewing_range_nodes = g_viewing_range_nodes;
2866 viewing_range_all = g_viewing_range_all;
2869 m_camera_mutex.Lock();
2870 v3f camera_position = m_camera_position;
2871 v3f camera_direction = m_camera_direction;
2872 m_camera_mutex.Unlock();
2875 Get all blocks and draw all visible ones
2878 v3s16 cam_pos_nodes(
2879 camera_position.X / BS,
2880 camera_position.Y / BS,
2881 camera_position.Z / BS);
2883 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2885 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2886 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2888 // Take a fair amount as we will be dropping more out later
2890 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2891 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2892 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2894 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2895 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2896 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2898 u32 vertex_count = 0;
2900 core::map<v2s16, MapSector*>::Iterator si;
2902 //NOTE: The sectors map should be locked but we're not doing it
2903 // because it'd cause too much delays
2905 si = m_sectors.getIterator();
2906 for(; si.atEnd() == false; si++)
2909 static int timecheck_counter = 0;
2910 timecheck_counter++;
2911 if(timecheck_counter > 50)
2913 int time2 = time(0);
2914 if(time2 > time1 + 4)
2916 dstream<<"ClientMap::renderMap(): "
2917 "Rendering takes ages, returning."
2924 MapSector *sector = si.getNode()->getValue();
2925 v2s16 sp = sector->getPos();
2927 if(viewing_range_all == false)
2929 if(sp.X < p_blocks_min.X
2930 || sp.X > p_blocks_max.X
2931 || sp.Y < p_blocks_min.Z
2932 || sp.Y > p_blocks_max.Z)
2936 core::list< MapBlock * > sectorblocks;
2937 sector->getBlocks(sectorblocks);
2943 core::list< MapBlock * >::Iterator i;
2944 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2946 MapBlock *block = *i;
2949 Compare block position to camera position, skip
2950 if not seen on display
2953 v3s16 blockpos_nodes = block->getPosRelative();
2955 // Block center position
2957 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2958 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2959 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2962 // Block position relative to camera
2963 v3f blockpos_relative = blockpos - camera_position;
2965 // Distance in camera direction (+=front, -=back)
2966 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2969 f32 d = blockpos_relative.getLength();
2971 if(viewing_range_all == false)
2973 // If block is far away, don't draw it
2974 if(d > viewing_range_nodes * BS)
2978 // Maximum radius of a block
2979 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2981 // If block is (nearly) touching the camera, don't
2982 // bother validating further (that is, render it anyway)
2983 if(d > block_max_radius * 1.5)
2985 // Cosine of the angle between the camera direction
2986 // and the block direction (camera_direction is an unit vector)
2987 f32 cosangle = dforward / d;
2989 // Compensate for the size of the block
2990 // (as the block has to be shown even if it's a bit off FOV)
2991 // This is an estimate.
2992 cosangle += block_max_radius / dforward;
2994 // If block is not in the field of view, skip it
2995 //if(cosangle < cos(FOV_ANGLE/2))
2996 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3001 Draw the faces of the block
3005 JMutexAutoLock lock(block->mesh_mutex);
3007 // Cancel if block has no mesh
3008 if(block->mesh == NULL)
3011 u32 c = block->mesh->getMeshBufferCount();
3013 for(u32 i=0; i<c; i++)
3015 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
3016 const video::SMaterial& material = buf->getMaterial();
3017 video::IMaterialRenderer* rnd =
3018 driver->getMaterialRenderer(material.MaterialType);
3019 bool transparent = (rnd && rnd->isTransparent());
3020 // Render transparent on transparent pass and likewise.
3021 if(transparent == is_transparent_pass)
3023 driver->setMaterial(buf->getMaterial());
3024 driver->drawMeshBuffer(buf);
3025 vertex_count += buf->getVertexCount();
3029 } // foreach sectorblocks
3032 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3033 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3036 void ClientMap::updateMesh()
3041 void ClientMap::PrintInfo(std::ostream &out)
3051 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3056 MapVoxelManipulator::~MapVoxelManipulator()
3058 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3063 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3065 TimeTaker timer1("emerge", g_device, &emerge_time);
3067 // Units of these are MapBlocks
3068 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3069 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3071 VoxelArea block_area_nodes
3072 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3074 addArea(block_area_nodes);
3076 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3077 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3078 for(s32 x=p_min.X; x<=p_max.X; x++)
3081 core::map<v3s16, bool>::Node *n;
3082 n = m_loaded_blocks.find(p);
3086 bool block_data_inexistent = false;
3089 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3091 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3092 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3095 dstream<<std::endl;*/
3097 MapBlock *block = m_map->getBlockNoCreate(p);
3098 if(block->isDummy())
3099 block_data_inexistent = true;
3101 block->copyTo(*this);
3103 catch(InvalidPositionException &e)
3105 block_data_inexistent = true;
3108 if(block_data_inexistent)
3110 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3111 // Fill with VOXELFLAG_INEXISTENT
3112 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3113 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3115 s32 i = m_area.index(a.MinEdge.X,y,z);
3116 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3120 m_loaded_blocks.insert(p, true);
3123 //dstream<<"emerge done"<<std::endl;
3128 void MapVoxelManipulator::emerge(VoxelArea a)
3130 TimeTaker timer1("emerge", g_device, &emerge_time);
3132 v3s16 size = a.getExtent();
3134 VoxelArea padded = a;
3135 padded.pad(m_area.getExtent() / 4);
3138 for(s16 z=0; z<size.Z; z++)
3139 for(s16 y=0; y<size.Y; y++)
3140 for(s16 x=0; x<size.X; x++)
3143 s32 i = m_area.index(a.MinEdge + p);
3144 // Don't touch nodes that have already been loaded
3145 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3149 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3150 MapNode n = m_map->getNode(a.MinEdge + p);
3154 catch(InvalidPositionException &e)
3156 m_flags[i] = VOXELFLAG_INEXISTENT;
3164 TODO: Add an option to only update eg. water and air nodes.
3165 This will make it interfere less with important stuff if
3168 void MapVoxelManipulator::blitBack
3169 (core::map<v3s16, MapBlock*> & modified_blocks)
3171 if(m_area.getExtent() == v3s16(0,0,0))
3174 //TimeTaker timer1("blitBack", g_device);
3177 Initialize block cache
3179 v3s16 blockpos_last;
3180 MapBlock *block = NULL;
3181 bool block_checked_in_modified = false;
3183 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3184 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3185 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3189 u8 f = m_flags[m_area.index(p)];
3190 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3193 MapNode &n = m_data[m_area.index(p)];
3195 v3s16 blockpos = getNodeBlockPos(p);
3200 if(block == NULL || blockpos != blockpos_last){
3201 block = m_map->getBlockNoCreate(blockpos);
3202 blockpos_last = blockpos;
3203 block_checked_in_modified = false;
3206 // Calculate relative position in block
3207 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3209 // Don't continue if nothing has changed here
3210 if(block->getNode(relpos) == n)
3213 //m_map->setNode(m_area.MinEdge + p, n);
3214 block->setNode(relpos, n);
3217 Make sure block is in modified_blocks
3219 if(block_checked_in_modified == false)
3221 modified_blocks[blockpos] = block;
3222 block_checked_in_modified = true;
3225 catch(InvalidPositionException &e)