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 light_sources.insert(n2pos, true);
518 // Add to modified_blocks
519 if(changed == true && block_checked_in_modified == false)
521 // If the block is not found in modified_blocks, add.
522 if(modified_blocks.find(blockpos) == NULL)
524 modified_blocks.insert(blockpos, block);
526 block_checked_in_modified = true;
529 catch(InvalidPositionException &e)
536 /*dstream<<"unspreadLight(): Changed block "
537 <<blockchangecount<<" times"
538 <<" for "<<from_nodes.size()<<" nodes"
541 if(unlighted_nodes.size() > 0)
542 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
546 A single-node wrapper of the above
548 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
549 core::map<v3s16, bool> & light_sources,
550 core::map<v3s16, MapBlock*> & modified_blocks)
552 core::map<v3s16, u8> from_nodes;
553 from_nodes.insert(pos, lightwas);
555 unspreadLight(from_nodes, light_sources, modified_blocks);
559 Lights neighbors of from_nodes, collects all them and then
562 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
563 core::map<v3s16, MapBlock*> & modified_blocks)
565 const v3s16 dirs[6] = {
566 v3s16(0,0,1), // back
568 v3s16(1,0,0), // right
569 v3s16(0,0,-1), // front
570 v3s16(0,-1,0), // bottom
571 v3s16(-1,0,0), // left
574 if(from_nodes.size() == 0)
577 u32 blockchangecount = 0;
579 core::map<v3s16, bool> lighted_nodes;
580 core::map<v3s16, bool>::Iterator j;
581 j = from_nodes.getIterator();
584 Initialize block cache
587 MapBlock *block = NULL;
588 // Cache this a bit, too
589 bool block_checked_in_modified = false;
591 for(; j.atEnd() == false; j++)
592 //for(; j != from_nodes.end(); j++)
594 v3s16 pos = j.getNode()->getKey();
596 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
597 v3s16 blockpos = getNodeBlockPos(pos);
599 // Only fetch a new block if the block position has changed
601 if(block == NULL || blockpos != blockpos_last){
602 block = getBlockNoCreate(blockpos);
603 blockpos_last = blockpos;
605 block_checked_in_modified = false;
609 catch(InvalidPositionException &e)
617 // Calculate relative position in block
618 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
620 // Get node straight from the block
621 MapNode n = block->getNode(relpos);
623 u8 oldlight = n.getLight();
624 u8 newlight = diminish_light(oldlight);
626 // Loop through 6 neighbors
627 for(u16 i=0; i<6; i++){
628 // Get the position of the neighbor node
629 v3s16 n2pos = pos + dirs[i];
631 // Get the block where the node is located
632 v3s16 blockpos = getNodeBlockPos(n2pos);
636 // Only fetch a new block if the block position has changed
638 if(block == NULL || blockpos != blockpos_last){
639 block = getBlockNoCreate(blockpos);
640 blockpos_last = blockpos;
642 block_checked_in_modified = false;
646 catch(InvalidPositionException &e)
651 // Calculate relative position in block
652 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
653 // Get node straight from the block
654 MapNode n2 = block->getNode(relpos);
656 bool changed = false;
658 If the neighbor is brighter than the current node,
659 add to list (it will light up this node on its turn)
661 if(n2.getLight() > undiminish_light(oldlight))
663 lighted_nodes.insert(n2pos, true);
664 //lighted_nodes.push_back(n2pos);
668 If the neighbor is dimmer than how much light this node
669 would spread on it, add to list
671 if(n2.getLight() < newlight)
673 if(n2.light_propagates())
675 n2.setLight(newlight);
676 block->setNode(relpos, n2);
677 lighted_nodes.insert(n2pos, true);
678 //lighted_nodes.push_back(n2pos);
683 // Add to modified_blocks
684 if(changed == true && block_checked_in_modified == false)
686 // If the block is not found in modified_blocks, add.
687 if(modified_blocks.find(blockpos) == NULL)
689 modified_blocks.insert(blockpos, block);
691 block_checked_in_modified = true;
694 catch(InvalidPositionException &e)
701 /*dstream<<"spreadLight(): Changed block "
702 <<blockchangecount<<" times"
703 <<" for "<<from_nodes.size()<<" nodes"
706 if(lighted_nodes.size() > 0)
707 spreadLight(lighted_nodes, modified_blocks);
711 A single-node source variation of the above.
713 void Map::lightNeighbors(v3s16 pos,
714 core::map<v3s16, MapBlock*> & modified_blocks)
716 core::map<v3s16, bool> from_nodes;
717 from_nodes.insert(pos, true);
718 spreadLight(from_nodes, modified_blocks);
721 v3s16 Map::getBrightestNeighbour(v3s16 p)
724 v3s16(0,0,1), // back
726 v3s16(1,0,0), // right
727 v3s16(0,0,-1), // front
728 v3s16(0,-1,0), // bottom
729 v3s16(-1,0,0), // left
732 u8 brightest_light = 0;
733 v3s16 brightest_pos(0,0,0);
734 bool found_something = false;
736 // Loop through 6 neighbors
737 for(u16 i=0; i<6; i++){
738 // Get the position of the neighbor node
739 v3s16 n2pos = p + dirs[i];
744 catch(InvalidPositionException &e)
748 if(n2.getLight() > brightest_light || found_something == false){
749 brightest_light = n2.getLight();
750 brightest_pos = n2pos;
751 found_something = true;
755 if(found_something == false)
756 throw InvalidPositionException();
758 return brightest_pos;
762 Propagates sunlight down from a node.
763 Starting point gets sunlight.
765 Returns the lowest y value of where the sunlight went.
767 s16 Map::propagateSunlight(v3s16 start,
768 core::map<v3s16, MapBlock*> & modified_blocks)
773 v3s16 pos(start.X, y, start.Z);
775 v3s16 blockpos = getNodeBlockPos(pos);
778 block = getBlockNoCreate(blockpos);
780 catch(InvalidPositionException &e)
785 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
786 MapNode n = block->getNode(relpos);
788 if(n.sunlight_propagates())
790 n.setLight(LIGHT_SUN);
791 block->setNode(relpos, n);
793 modified_blocks.insert(blockpos, block);
802 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
803 core::map<v3s16, MapBlock*> & modified_blocks)
805 /*m_dout<<DTIME<<"Map::updateLighting(): "
806 <<a_blocks.getSize()<<" blocks... ";*/
810 u32 count_was = modified_blocks.size();
812 /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
813 for(; i != a_blocks.end(); i++)
815 MapBlock *block = *i;*/
817 core::map<v3s16, bool> light_sources;
819 core::map<v3s16, u8> unlight_from;
821 core::map<v3s16, MapBlock*>::Iterator i;
822 i = a_blocks.getIterator();
823 for(; i.atEnd() == false; i++)
825 MapBlock *block = i.getNode()->getValue();
829 // Don't bother with dummy blocks.
833 v3s16 pos = block->getPos();
834 modified_blocks.insert(pos, block);
837 Clear all light from block
839 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
840 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
841 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
846 MapNode n = block->getNode(v3s16(x,y,z));
847 u8 oldlight = n.getLight();
849 block->setNode(v3s16(x,y,z), n);
851 // Collect borders for unlighting
852 if(x==0 || x == MAP_BLOCKSIZE-1
853 || y==0 || y == MAP_BLOCKSIZE-1
854 || z==0 || z == MAP_BLOCKSIZE-1)
856 v3s16 p_map = p + v3s16(
859 MAP_BLOCKSIZE*pos.Z);
860 unlight_from.insert(p_map, oldlight);
863 catch(InvalidPositionException &e)
866 This would happen when dealing with a
870 dstream<<"updateLighting(): InvalidPositionException"
875 bool bottom_valid = block->propagateSunlight(light_sources);
877 // If bottom is valid, we're done.
881 /*dstream<<"Bottom for sunlight-propagated block ("
882 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
885 // Else get the block below and loop to it
889 block = getBlockNoCreate(pos);
891 catch(InvalidPositionException &e)
900 //TimeTaker timer("unspreadLight", g_device);
901 unspreadLight(unlight_from, light_sources, modified_blocks);
906 u32 diff = modified_blocks.size() - count_was;
907 count_was = modified_blocks.size();
908 dstream<<"unspreadLight modified "<<diff<<std::endl;
911 // TODO: Spread light from propagated sunlight?
912 // Yes, add it to light_sources... somehow.
913 // It has to be added at somewhere above, in the loop.
917 //TimeTaker timer("spreadLight", g_device);
918 spreadLight(light_sources, modified_blocks);
923 u32 diff = modified_blocks.size() - count_was;
924 count_was = modified_blocks.size();
925 dstream<<"spreadLight modified "<<diff<<std::endl;
928 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
932 This is called after changing a node from transparent to opaque.
933 The lighting value of the node should be left as-is after changing
934 other values. This sets the lighting value to 0.
936 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
937 core::map<v3s16, MapBlock*> &modified_blocks)*/
938 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
939 core::map<v3s16, MapBlock*> &modified_blocks)
942 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
943 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
945 u8 lightwas = getNode(p).getLight();
947 //core::list<v3s16> light_sources;
948 core::map<v3s16, bool> light_sources;
949 //MapNode n = getNode(p);
952 From this node to nodes underneath:
953 If lighting is sunlight (1.0), unlight neighbours and
958 bool node_under_sunlight = true;
960 v3s16 toppos = p + v3s16(0,1,0);
963 If there is a node at top and it doesn't have sunlight,
964 there has not been any sunlight going down.
966 Otherwise there probably is.
969 MapNode topnode = getNode(toppos);
971 if(topnode.getLight() != LIGHT_SUN)
972 node_under_sunlight = false;
974 catch(InvalidPositionException &e)
978 // Add the block of the added node to modified_blocks
979 v3s16 blockpos = getNodeBlockPos(p);
980 MapBlock * block = getBlockNoCreate(blockpos);
981 assert(block != NULL);
982 modified_blocks.insert(blockpos, block);
984 if(isValidPosition(p) == false)
987 // Unlight neighbours of node.
988 // This means setting light of all consequent dimmer nodes
990 // This also collects the nodes at the border which will spread
991 // light again into this.
992 unLightNeighbors(p, lightwas, light_sources, modified_blocks);
998 If node is under sunlight, take all sunlighted nodes under
999 it and clear light from them and from where the light has
1002 if(node_under_sunlight)
1006 //m_dout<<DTIME<<"y="<<y<<std::endl;
1007 v3s16 n2pos(p.X, y, p.Z);
1011 n2 = getNode(n2pos);
1013 catch(InvalidPositionException &e)
1018 if(n2.getLight() == LIGHT_SUN)
1020 //m_dout<<DTIME<<"doing"<<std::endl;
1021 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
1031 Spread light from all nodes that might be capable of doing so
1032 TODO: Convert to spreadLight
1034 spreadLight(light_sources, modified_blocks);
1039 void Map::removeNodeAndUpdate(v3s16 p,
1040 core::map<v3s16, MapBlock*> &modified_blocks)
1042 /*PrintInfo(m_dout);
1043 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1044 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1046 bool node_under_sunlight = true;
1048 v3s16 toppos = p + v3s16(0,1,0);
1050 // Node will be replaced with this
1051 u8 replace_material = MATERIAL_AIR;
1053 // NOTE: Water is now managed elsewhere
1057 Find out with what material the node will be replaced.
1058 It will be replaced with the mostly seen buildable_to.
1062 v3s16(0,0,1), // back
1063 v3s16(0,1,0), // top
1064 v3s16(1,0,0), // right
1065 v3s16(0,0,-1), // front
1066 v3s16(0,-1,0), // bottom
1067 v3s16(-1,0,0), // left
1070 core::map<u8, u16> neighbor_rankings;
1072 for(u32 i=0; i<sizeof(dirs)/sizeof(dirs[0]); i++)
1075 MapNode n2 = getNode(p + dirs[i]);
1077 if(material_buildable_to(n2.d))
1079 if(neighbor_rankings.find(n2.d) == NULL)
1080 neighbor_rankings[n2.d] = 1;
1082 neighbor_rankings[n2.d]
1083 = neighbor_rankings[n2.d] + 1;
1086 catch(InvalidPositionException &e)
1091 u16 highest_ranking = 0;
1093 for(core::map<u8, u16>::Iterator
1094 i = neighbor_rankings.getIterator();
1095 i.atEnd() == false; i++)
1097 u8 m = i.getNode()->getKey();
1098 u8 c = i.getNode()->getValue();
1100 c > highest_ranking ||
1101 // Prefer something else than air
1102 (c >= highest_ranking && m != MATERIAL_AIR)
1106 replace_material = m;
1107 highest_ranking = c;
1115 If there is a node at top and it doesn't have sunlight,
1116 there will be no sunlight going down.
1119 MapNode topnode = getNode(toppos);
1121 if(topnode.getLight() != LIGHT_SUN)
1122 node_under_sunlight = false;
1124 catch(InvalidPositionException &e)
1129 Unlight neighbors (in case the node is a light source)
1131 //core::list<v3s16> light_sources;
1132 core::map<v3s16, bool> light_sources;
1133 unLightNeighbors(p, getNode(p).getLight(),
1134 light_sources, modified_blocks);
1140 n.d = replace_material;
1145 Recalculate lighting
1147 spreadLight(light_sources, modified_blocks);
1149 // Add the block of the removed node to modified_blocks
1150 v3s16 blockpos = getNodeBlockPos(p);
1151 MapBlock * block = getBlockNoCreate(blockpos);
1152 assert(block != NULL);
1153 modified_blocks.insert(blockpos, block);
1156 If the removed node was under sunlight, propagate the
1157 sunlight down from it and then light all neighbors
1158 of the propagated blocks.
1160 if(node_under_sunlight)
1162 s16 ybottom = propagateSunlight(p, modified_blocks);
1163 /*m_dout<<DTIME<<"Node was under sunlight. "
1164 "Propagating sunlight";
1165 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1167 for(; y >= ybottom; y--)
1169 v3s16 p2(p.X, y, p.Z);
1170 /*m_dout<<DTIME<<"lighting neighbors of node ("
1171 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1173 lightNeighbors(p2, modified_blocks);
1178 // Set the lighting of this node to 0
1180 MapNode n = getNode(p);
1184 catch(InvalidPositionException &e)
1190 // Get the brightest neighbour node and propagate light from it
1191 v3s16 n2p = getBrightestNeighbour(p);
1193 MapNode n2 = getNode(n2p);
1194 lightNeighbors(n2p, modified_blocks);
1196 catch(InvalidPositionException &e)
1201 void Map::updateMeshes(v3s16 blockpos)
1203 assert(mapType() == MAPTYPE_CLIENT);
1206 v3s16 p = blockpos + v3s16(0,0,0);
1207 MapBlock *b = getBlockNoCreate(p);
1210 catch(InvalidPositionException &e){}
1212 v3s16 p = blockpos + v3s16(-1,0,0);
1213 MapBlock *b = getBlockNoCreate(p);
1216 catch(InvalidPositionException &e){}
1218 v3s16 p = blockpos + v3s16(0,-1,0);
1219 MapBlock *b = getBlockNoCreate(p);
1222 catch(InvalidPositionException &e){}
1224 v3s16 p = blockpos + v3s16(0,0,-1);
1225 MapBlock *b = getBlockNoCreate(p);
1228 catch(InvalidPositionException &e){}
1232 Updates usage timers
1234 void Map::timerUpdate(float dtime)
1236 JMutexAutoLock lock(m_sector_mutex);
1238 core::map<v2s16, MapSector*>::Iterator si;
1240 si = m_sectors.getIterator();
1241 for(; si.atEnd() == false; si++)
1243 MapSector *sector = si.getNode()->getValue();
1244 sector->usage_timer += dtime;
1248 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1251 Wait for caches to be removed before continuing.
1253 This disables the existence of caches while locked
1255 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1257 core::list<v2s16>::Iterator j;
1258 for(j=list.begin(); j!=list.end(); j++)
1260 MapSector *sector = m_sectors[*j];
1263 sector->deleteBlocks();
1268 If sector is in sector cache, remove it from there
1270 if(m_sector_cache == sector)
1272 m_sector_cache = NULL;
1275 Remove from map and delete
1277 m_sectors.remove(*j);
1283 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1284 core::list<v3s16> *deleted_blocks)
1286 JMutexAutoLock lock(m_sector_mutex);
1288 core::list<v2s16> sector_deletion_queue;
1289 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1290 for(; i.atEnd() == false; i++)
1292 MapSector *sector = i.getNode()->getValue();
1294 Delete sector from memory if it hasn't been used in a long time
1296 if(sector->usage_timer > timeout)
1298 sector_deletion_queue.push_back(i.getNode()->getKey());
1300 if(deleted_blocks != NULL)
1302 // Collect positions of blocks of sector
1303 MapSector *sector = i.getNode()->getValue();
1304 core::list<MapBlock*> blocks;
1305 sector->getBlocks(blocks);
1306 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1307 i != blocks.end(); i++)
1309 deleted_blocks->push_back((*i)->getPos());
1314 deleteSectors(sector_deletion_queue, only_blocks);
1315 return sector_deletion_queue.getSize();
1318 void Map::PrintInfo(std::ostream &out)
1327 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1331 m_savedir = savedir;
1332 m_map_saving_enabled = false;
1336 // If directory exists, check contents and load if possible
1337 if(fs::PathExists(m_savedir))
1339 // If directory is empty, it is safe to save into it.
1340 if(fs::GetDirListing(m_savedir).size() == 0)
1342 dstream<<DTIME<<"Server: Empty save directory is valid."
1344 m_map_saving_enabled = true;
1348 // Load master heightmap
1349 loadMasterHeightmap();
1351 // Load sector (0,0) and throw and exception on fail
1352 if(loadSectorFull(v2s16(0,0)) == false)
1353 throw LoadError("Failed to load sector (0,0)");
1355 dstream<<DTIME<<"Server: Successfully loaded master "
1356 "heightmap and sector (0,0) from "<<savedir<<
1357 ", assuming valid save directory."
1360 m_map_saving_enabled = true;
1361 // Map loaded, not creating new one
1365 // If directory doesn't exist, it is safe to save to it
1367 m_map_saving_enabled = true;
1370 catch(std::exception &e)
1372 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1373 <<", exception: "<<e.what()<<std::endl;
1374 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1375 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1378 dstream<<DTIME<<"Initializing new map."<<std::endl;
1380 // Create master heightmap
1381 ValueGenerator *maxgen =
1382 ValueGenerator::deSerialize(hmp.randmax);
1383 ValueGenerator *factorgen =
1384 ValueGenerator::deSerialize(hmp.randfactor);
1385 ValueGenerator *basegen =
1386 ValueGenerator::deSerialize(hmp.base);
1387 m_heightmap = new UnlimitedHeightmap
1388 (hmp.blocksize, maxgen, factorgen, basegen);
1390 // Set map parameters
1393 // Create zero sector
1394 emergeSector(v2s16(0,0));
1396 // Initially write whole map
1400 ServerMap::~ServerMap()
1404 if(m_map_saving_enabled)
1407 // Save only changed parts
1409 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1413 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1416 catch(std::exception &e)
1418 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1419 <<", exception: "<<e.what()<<std::endl;
1422 if(m_heightmap != NULL)
1426 MapSector * ServerMap::emergeSector(v2s16 p2d)
1428 DSTACK("%s: p2d=(%d,%d)",
1431 // Check that it doesn't exist already
1433 return getSectorNoGenerate(p2d);
1435 catch(InvalidPositionException &e)
1440 Try to load the sector from disk.
1442 if(loadSectorFull(p2d) == true)
1444 return getSectorNoGenerate(p2d);
1448 If there is no master heightmap, throw.
1450 if(m_heightmap == NULL)
1452 throw InvalidPositionException("emergeSector(): no heightmap");
1456 Do not generate over-limit
1458 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1459 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1460 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1461 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1462 throw InvalidPositionException("emergeSector(): pos. over limit");
1465 Generate sector and heightmaps
1468 // Number of heightmaps in sector in each direction
1469 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1471 // Heightmap side width
1472 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1474 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1476 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1477 " heightmaps and objects"<<std::endl;*/
1479 // Loop through sub-heightmaps
1480 for(s16 y=0; y<hm_split; y++)
1481 for(s16 x=0; x<hm_split; x++)
1483 v2s16 p_in_sector = v2s16(x,y);
1484 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1486 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1487 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1488 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1489 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1492 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1493 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1496 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1498 sector->setHeightmap(p_in_sector, hm);
1500 //TODO: Make these values configurable
1501 hm->generateContinued(1.0, 0.2, corners);
1502 //hm->generateContinued(2.0, 0.2, corners);
1512 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1513 sector->setObjects(objects);
1515 v2s16 mhm_p = p2d * hm_split;
1517 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1518 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1519 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1520 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1523 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1524 float avgslope = 0.0;
1525 avgslope += fabs(avgheight - corners[0]);
1526 avgslope += fabs(avgheight - corners[1]);
1527 avgslope += fabs(avgheight - corners[2]);
1528 avgslope += fabs(avgheight - corners[3]);
1530 avgslope /= MAP_BLOCKSIZE;
1531 //dstream<<"avgslope="<<avgslope<<std::endl;
1533 float pitness = 0.0;
1535 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1538 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1541 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1544 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1548 pitness /= MAP_BLOCKSIZE;
1549 //dstream<<"pitness="<<pitness<<std::endl;
1552 Plant some trees if there is not much slope
1555 // Avgslope is the derivative of a hill
1556 float t = avgslope * avgslope;
1557 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1560 tree_max = a / (t/0.03);
1563 u32 count = (rand()%(tree_max+1));
1564 //u32 count = tree_max;
1565 for(u32 i=0; i<count; i++)
1567 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1568 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1569 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1572 objects->insert(v3s16(x, y, z),
1573 SECTOR_OBJECT_TREE_1);
1577 Plant some bushes if sector is pit-like
1580 // Pitness usually goes at around -0.5...0.5
1582 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1584 bush_max = (pitness*a*4);
1587 u32 count = (rand()%(bush_max+1));
1588 for(u32 i=0; i<count; i++)
1590 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1591 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1592 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1595 objects->insert(v3s16(x, y, z),
1596 SECTOR_OBJECT_BUSH_1);
1600 Add ravine (randomly)
1602 if(m_params.ravines_amount != 0)
1604 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1607 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1608 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1611 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1612 objects->insert(v3s16(x, y, z),
1613 SECTOR_OBJECT_RAVINE);
1620 JMutexAutoLock lock(m_sector_mutex);
1621 m_sectors.insert(p2d, sector);
1626 MapBlock * ServerMap::emergeBlock(
1628 bool only_from_disk,
1629 core::map<v3s16, MapBlock*> &changed_blocks,
1630 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1633 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1635 p.X, p.Y, p.Z, only_from_disk);
1637 /*dstream<<"ServerMap::emergeBlock(): "
1638 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1639 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1640 v2s16 p2d(p.X, p.Z);
1643 This will create or load a sector if not found in memory.
1644 If block exists on disk, it will be loaded.
1646 NOTE: On old save formats, this will be slow, as it generates
1647 lighting on blocks for them.
1649 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1650 assert(sector->getId() == MAPSECTOR_SERVER);
1652 // Try to get a block from the sector
1653 MapBlock *block = NULL;
1654 bool not_on_disk = false;
1656 block = sector->getBlockNoCreate(block_y);
1657 if(block->isDummy() == true)
1662 catch(InvalidPositionException &e)
1668 If block was not found on disk and not going to generate a
1669 new one, make sure there is a dummy block in place.
1671 if(not_on_disk && only_from_disk)
1675 // Create dummy block
1676 block = new MapBlock(this, p, true);
1678 // Add block to sector
1679 sector->insertBlock(block);
1685 //dstream<<"Not found on disk, generating."<<std::endl;
1688 Do not generate over-limit
1690 if(blockpos_over_limit(p))
1691 throw InvalidPositionException("emergeBlock(): pos. over limit");
1696 Go on generating the block.
1698 TODO: If a dungeon gets generated so that it's side gets
1699 revealed to the outside air, the lighting should be
1704 If block doesn't exist, create one.
1705 If it exists, it is a dummy. In that case unDummify() it.
1709 block = sector->createBlankBlockNoInsert(block_y);
1713 // Remove the block so that nobody can get a half-generated one.
1714 sector->removeBlock(block);
1715 // Allocate the block to be a proper one.
1719 // Randomize a bit. This makes dungeons.
1720 /*bool low_block_is_empty = false;
1722 low_block_is_empty = true;*/
1725 bool underground_emptiness[ued*ued*ued];
1726 for(s32 i=0; i<ued*ued*ued; i++)
1728 underground_emptiness[i] = ((rand() % 4) == 0);
1731 // This is the basic material of what the visible flat ground
1733 u8 material = MATERIAL_GRASS;
1735 s32 lowest_ground_y = 32767;
1738 //sector->printHeightmaps();
1740 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1741 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1743 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1745 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1746 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1747 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1749 dstream<<"WARNING: Surface height not found in sector "
1750 "for block that is being emerged"<<std::endl;
1754 s16 surface_y = surface_y_f;
1755 //avg_ground_y += surface_y;
1756 if(surface_y < lowest_ground_y)
1757 lowest_ground_y = surface_y;
1759 s32 surface_depth = 0;
1761 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1763 float min_slope = 0.45;
1764 float max_slope = 0.85;
1765 float min_slope_depth = 5.0;
1766 float max_slope_depth = 0;
1767 if(slope < min_slope)
1768 surface_depth = min_slope_depth;
1769 else if(slope > max_slope)
1770 surface_depth = max_slope_depth;
1772 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1774 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1776 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1781 NOTE: If there are some man-made structures above the
1782 newly created block, they won't be taken into account.
1784 if(real_y > surface_y)
1785 n.setLight(LIGHT_SUN);
1789 // If node is very low
1790 if(real_y <= surface_y - 7){
1792 if(underground_emptiness[
1793 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1794 +ued*(y0*ued/MAP_BLOCKSIZE)
1795 +(x0*ued/MAP_BLOCKSIZE)])
1801 n.d = MATERIAL_STONE;
1804 // If node is under surface level
1805 else if(real_y <= surface_y - surface_depth)
1806 n.d = MATERIAL_STONE;
1807 // If node is at or under heightmap y
1808 else if(real_y <= surface_y)
1810 // If under water level, it's mud
1811 if(real_y < WATER_LEVEL)
1813 // Else it's the main material
1817 // If node is over heightmap y
1819 // If under water level, it's water
1820 if(real_y < WATER_LEVEL)
1822 n.d = MATERIAL_WATER;
1823 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1829 block->setNode(v3s16(x0,y0,z0), n);
1834 Calculate is_underground
1836 // Probably underground if the highest part of block is under lowest
1838 bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1839 block->setIsUnderground(is_underground);
1842 Force lighting update if underground.
1843 This is needed because of ravines.
1848 lighting_invalidated_blocks[block->getPos()] = block;
1857 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1858 for(s16 i=0; i<underground_level*3; i++)
1863 (rand()%(MAP_BLOCKSIZE-2))+1,
1864 (rand()%(MAP_BLOCKSIZE-2))+1,
1865 (rand()%(MAP_BLOCKSIZE-2))+1
1869 n.d = MATERIAL_MESE;
1871 if(is_ground_material(block->getNode(cp).d))
1873 block->setNode(cp, n);
1875 for(u16 i=0; i<26; i++)
1877 if(is_ground_material(block->getNode(cp+g_26dirs[i]).d))
1879 block->setNode(cp+g_26dirs[i], n);
1886 Create a few rats in empty blocks underground
1890 //for(u16 i=0; i<2; i++)
1893 (rand()%(MAP_BLOCKSIZE-2))+1,
1894 (rand()%(MAP_BLOCKSIZE-2))+1,
1895 (rand()%(MAP_BLOCKSIZE-2))+1
1898 // Check that the place is empty
1899 //if(!is_ground_material(block->getNode(cp).d))
1902 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1903 block->addObject(obj);
1909 Add block to sector.
1911 sector->insertBlock(block);
1914 Do some interpolation for dungeons
1919 TimeTaker timer("interpolation", g_device);
1921 MapVoxelManipulator vmanip(this);
1923 v3s16 relpos = block->getPosRelative();
1925 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1926 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1927 /*vmanip.interpolate(VoxelArea(relpos,
1928 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1930 core::map<v3s16, MapBlock*> modified_blocks;
1931 vmanip.blitBack(modified_blocks);
1932 dstream<<"blitBack modified "<<modified_blocks.size()
1933 <<" blocks"<<std::endl;
1935 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1936 for(core::map<v3s16, MapBlock*>::Iterator
1937 i = modified_blocks.getIterator();
1938 i.atEnd() == false; i++)
1940 MapBlock *block = i.getNode()->getValue();
1942 changed_blocks.insert(block->getPos(), block);
1943 //lighting_invalidated_blocks.insert(block->getPos(), block);
1953 // An y-wise container of changed blocks
1954 core::map<s16, MapBlock*> changed_blocks_sector;
1957 Check if any sector's objects can be placed now.
1960 core::map<v3s16, u8> *objects = sector->getObjects();
1961 core::list<v3s16> objects_to_remove;
1962 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1963 i.atEnd() == false; i++)
1965 v3s16 p = i.getNode()->getKey();
1967 u8 d = i.getNode()->getValue();
1969 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1974 if(d == SECTOR_OBJECT_TEST)
1976 if(sector->isValidArea(p + v3s16(0,0,0),
1977 p + v3s16(0,0,0), &changed_blocks_sector))
1980 n.d = MATERIAL_LIGHT;
1981 sector->setNode(p, n);
1982 objects_to_remove.push_back(p);
1985 else if(d == SECTOR_OBJECT_TREE_1)
1987 v3s16 p_min = p + v3s16(-1,0,-1);
1988 v3s16 p_max = p + v3s16(1,4,1);
1989 if(sector->isValidArea(p_min, p_max,
1990 &changed_blocks_sector))
1993 n.d = MATERIAL_TREE;
1994 sector->setNode(p+v3s16(0,0,0), n);
1995 sector->setNode(p+v3s16(0,1,0), n);
1996 sector->setNode(p+v3s16(0,2,0), n);
1997 sector->setNode(p+v3s16(0,3,0), n);
1999 n.d = MATERIAL_LEAVES;
2001 sector->setNode(p+v3s16(0,4,0), n);
2003 sector->setNode(p+v3s16(-1,4,0), n);
2004 sector->setNode(p+v3s16(1,4,0), n);
2005 sector->setNode(p+v3s16(0,4,-1), n);
2006 sector->setNode(p+v3s16(0,4,1), n);
2007 sector->setNode(p+v3s16(1,4,1), n);
2008 sector->setNode(p+v3s16(-1,4,1), n);
2009 sector->setNode(p+v3s16(-1,4,-1), n);
2010 sector->setNode(p+v3s16(1,4,-1), n);
2012 sector->setNode(p+v3s16(-1,3,0), n);
2013 sector->setNode(p+v3s16(1,3,0), n);
2014 sector->setNode(p+v3s16(0,3,-1), n);
2015 sector->setNode(p+v3s16(0,3,1), n);
2016 sector->setNode(p+v3s16(1,3,1), n);
2017 sector->setNode(p+v3s16(-1,3,1), n);
2018 sector->setNode(p+v3s16(-1,3,-1), n);
2019 sector->setNode(p+v3s16(1,3,-1), n);
2021 objects_to_remove.push_back(p);
2023 // Lighting has to be recalculated for this one.
2024 sector->getBlocksInArea(p_min, p_max,
2025 lighting_invalidated_blocks);
2028 else if(d == SECTOR_OBJECT_BUSH_1)
2030 if(sector->isValidArea(p + v3s16(0,0,0),
2031 p + v3s16(0,0,0), &changed_blocks_sector))
2034 n.d = MATERIAL_LEAVES;
2035 sector->setNode(p+v3s16(0,0,0), n);
2037 objects_to_remove.push_back(p);
2040 else if(d == SECTOR_OBJECT_RAVINE)
2043 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2044 v3s16 p_max = p + v3s16(6,6,6);
2045 if(sector->isValidArea(p_min, p_max,
2046 &changed_blocks_sector))
2049 n.d = MATERIAL_STONE;
2051 n2.d = MATERIAL_AIR;
2052 s16 depth = maxdepth + (rand()%10);
2054 s16 minz = -6 - (-2);
2056 for(s16 x=-6; x<=6; x++)
2058 z += -1 + (rand()%3);
2063 for(s16 y=depth+(rand()%2); y<=6; y++)
2065 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2068 v3s16 p2 = p + v3s16(x,y,z-2);
2069 if(is_ground_material(sector->getNode(p2).d))
2070 sector->setNode(p2, n);
2073 v3s16 p2 = p + v3s16(x,y,z-1);
2074 if(is_ground_material(sector->getNode(p2).d))
2075 sector->setNode(p2, n2);
2078 v3s16 p2 = p + v3s16(x,y,z+0);
2079 if(is_ground_material(sector->getNode(p2).d))
2080 sector->setNode(p2, n2);
2083 v3s16 p2 = p + v3s16(x,y,z+1);
2084 if(is_ground_material(sector->getNode(p2).d))
2085 sector->setNode(p2, n);
2088 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2089 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2093 objects_to_remove.push_back(p);
2095 // Lighting has to be recalculated for this one.
2096 sector->getBlocksInArea(p_min, p_max,
2097 lighting_invalidated_blocks);
2102 dstream<<"ServerMap::emergeBlock(): "
2103 "Invalid heightmap object"
2108 catch(InvalidPositionException &e)
2110 dstream<<"WARNING: "<<__FUNCTION_NAME
2111 <<": while inserting object "<<(int)d
2112 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2113 <<" InvalidPositionException.what()="
2114 <<e.what()<<std::endl;
2115 // This is not too fatal and seems to happen sometimes.
2120 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2121 i != objects_to_remove.end(); i++)
2123 objects->remove(*i);
2126 for(core::map<s16, MapBlock*>::Iterator
2127 i = changed_blocks_sector.getIterator();
2128 i.atEnd() == false; i++)
2130 MapBlock *block = i.getNode()->getValue();
2132 changed_blocks.insert(block->getPos(), block);
2138 void ServerMap::createDir(std::string path)
2140 if(fs::CreateDir(path) == false)
2142 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2143 <<"\""<<path<<"\""<<std::endl;
2144 throw BaseException("ServerMap failed to create directory");
2148 std::string ServerMap::getSectorSubDir(v2s16 pos)
2151 snprintf(cc, 9, "%.4x%.4x",
2152 (unsigned int)pos.X&0xffff,
2153 (unsigned int)pos.Y&0xffff);
2155 return std::string(cc);
2158 std::string ServerMap::getSectorDir(v2s16 pos)
2160 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2163 v2s16 ServerMap::getSectorPos(std::string dirname)
2165 if(dirname.size() != 8)
2166 throw InvalidFilenameException("Invalid sector directory name");
2168 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2170 throw InvalidFilenameException("Invalid sector directory name");
2171 v2s16 pos((s16)x, (s16)y);
2175 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2177 v2s16 p2d = getSectorPos(sectordir);
2179 if(blockfile.size() != 4){
2180 throw InvalidFilenameException("Invalid block filename");
2183 int r = sscanf(blockfile.c_str(), "%4x", &y);
2185 throw InvalidFilenameException("Invalid block filename");
2186 return v3s16(p2d.X, y, p2d.Y);
2190 #define ENABLE_SECTOR_SAVING 1
2191 #define ENABLE_SECTOR_LOADING 1
2192 #define ENABLE_BLOCK_SAVING 1
2193 #define ENABLE_BLOCK_LOADING 1
2195 void ServerMap::save(bool only_changed)
2197 DSTACK(__FUNCTION_NAME);
2198 if(m_map_saving_enabled == false)
2200 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2204 if(only_changed == false)
2205 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2208 saveMasterHeightmap();
2210 u32 sector_meta_count = 0;
2211 u32 block_count = 0;
2214 JMutexAutoLock lock(m_sector_mutex);
2216 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2217 for(; i.atEnd() == false; i++)
2219 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2220 assert(sector->getId() == MAPSECTOR_SERVER);
2222 if(ENABLE_SECTOR_SAVING)
2224 if(sector->differs_from_disk || only_changed == false)
2226 saveSectorMeta(sector);
2227 sector_meta_count++;
2230 if(ENABLE_BLOCK_SAVING)
2232 core::list<MapBlock*> blocks;
2233 sector->getBlocks(blocks);
2234 core::list<MapBlock*>::Iterator j;
2235 for(j=blocks.begin(); j!=blocks.end(); j++)
2237 MapBlock *block = *j;
2238 if(block->getChangedFlag() || only_changed == false)
2249 u32 deleted_count = 0;
2250 deleted_count = deleteUnusedSectors
2251 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2254 Only print if something happened or saved whole map
2256 if(only_changed == false || sector_meta_count != 0
2257 || block_count != 0 || deleted_count != 0)
2259 dstream<<DTIME<<"ServerMap: Written: "
2260 <<sector_meta_count<<" sector metadata files, "
2261 <<block_count<<" block files, "
2262 <<deleted_count<<" sectors unloaded from memory."
2267 void ServerMap::loadAll()
2269 DSTACK(__FUNCTION_NAME);
2270 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2272 loadMasterHeightmap();
2274 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2276 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2278 JMutexAutoLock lock(m_sector_mutex);
2281 s32 printed_counter = -100000;
2282 s32 count = list.size();
2284 std::vector<fs::DirListNode>::iterator i;
2285 for(i=list.begin(); i!=list.end(); i++)
2287 if(counter > printed_counter + 10)
2289 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2290 printed_counter = counter;
2294 MapSector *sector = NULL;
2296 // We want directories
2300 sector = loadSectorMeta(i->name);
2302 catch(InvalidFilenameException &e)
2304 // This catches unknown crap in directory
2307 if(ENABLE_BLOCK_LOADING)
2309 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2310 (m_savedir+"/sectors/"+i->name);
2311 std::vector<fs::DirListNode>::iterator i2;
2312 for(i2=list2.begin(); i2!=list2.end(); i2++)
2318 loadBlock(i->name, i2->name, sector);
2320 catch(InvalidFilenameException &e)
2322 // This catches unknown crap in directory
2327 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2330 void ServerMap::saveMasterHeightmap()
2332 DSTACK(__FUNCTION_NAME);
2333 createDir(m_savedir);
2335 std::string fullpath = m_savedir + "/master_heightmap";
2336 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2337 if(o.good() == false)
2338 throw FileNotGoodException("Cannot open master heightmap");
2340 // Format used for writing
2341 u8 version = SER_FMT_VER_HIGHEST;
2344 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2346 [0] u8 serialization version
2347 [1] X master heightmap
2349 u32 fullsize = 1 + hmdata.getSize();
2350 SharedBuffer<u8> data(fullsize);
2353 memcpy(&data[1], *hmdata, hmdata.getSize());
2355 o.write((const char*)*data, fullsize);
2358 m_heightmap->serialize(o, version);
2361 void ServerMap::loadMasterHeightmap()
2363 DSTACK(__FUNCTION_NAME);
2364 std::string fullpath = m_savedir + "/master_heightmap";
2365 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2366 if(is.good() == false)
2367 throw FileNotGoodException("Cannot open master heightmap");
2369 if(m_heightmap != NULL)
2372 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2375 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2377 DSTACK(__FUNCTION_NAME);
2378 // Format used for writing
2379 u8 version = SER_FMT_VER_HIGHEST;
2381 v2s16 pos = sector->getPos();
2382 createDir(m_savedir);
2383 createDir(m_savedir+"/sectors");
2384 std::string dir = getSectorDir(pos);
2387 std::string fullpath = dir + "/heightmap";
2388 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2389 if(o.good() == false)
2390 throw FileNotGoodException("Cannot open master heightmap");
2392 sector->serialize(o, version);
2394 sector->differs_from_disk = false;
2397 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2399 DSTACK(__FUNCTION_NAME);
2401 v2s16 p2d = getSectorPos(dirname);
2402 std::string dir = m_savedir + "/sectors/" + dirname;
2404 std::string fullpath = dir + "/heightmap";
2405 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2406 if(is.good() == false)
2407 throw FileNotGoodException("Cannot open sector heightmap");
2409 ServerMapSector *sector = ServerMapSector::deSerialize
2410 (is, this, p2d, &m_hwrapper, m_sectors);
2412 sector->differs_from_disk = false;
2417 bool ServerMap::loadSectorFull(v2s16 p2d)
2419 DSTACK(__FUNCTION_NAME);
2420 std::string sectorsubdir = getSectorSubDir(p2d);
2422 MapSector *sector = NULL;
2424 JMutexAutoLock lock(m_sector_mutex);
2427 sector = loadSectorMeta(sectorsubdir);
2429 catch(InvalidFilenameException &e)
2433 catch(FileNotGoodException &e)
2437 catch(std::exception &e)
2442 if(ENABLE_BLOCK_LOADING)
2444 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2445 (m_savedir+"/sectors/"+sectorsubdir);
2446 std::vector<fs::DirListNode>::iterator i2;
2447 for(i2=list2.begin(); i2!=list2.end(); i2++)
2453 loadBlock(sectorsubdir, i2->name, sector);
2455 catch(InvalidFilenameException &e)
2457 // This catches unknown crap in directory
2465 bool ServerMap::deFlushSector(v2s16 p2d)
2467 DSTACK(__FUNCTION_NAME);
2468 // See if it already exists in memory
2470 MapSector *sector = getSectorNoGenerate(p2d);
2473 catch(InvalidPositionException &e)
2476 Try to load the sector from disk.
2478 if(loadSectorFull(p2d) == true)
2487 void ServerMap::saveBlock(MapBlock *block)
2489 DSTACK(__FUNCTION_NAME);
2491 Dummy blocks are not written
2493 if(block->isDummy())
2495 /*v3s16 p = block->getPos();
2496 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2497 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2501 // Format used for writing
2502 u8 version = SER_FMT_VER_HIGHEST;
2504 v3s16 p3d = block->getPos();
2505 v2s16 p2d(p3d.X, p3d.Z);
2506 createDir(m_savedir);
2507 createDir(m_savedir+"/sectors");
2508 std::string dir = getSectorDir(p2d);
2511 // Block file is map/sectors/xxxxxxxx/xxxx
2513 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2514 std::string fullpath = dir + "/" + cc;
2515 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2516 if(o.good() == false)
2517 throw FileNotGoodException("Cannot open block data");
2520 [0] u8 serialization version
2523 o.write((char*)&version, 1);
2525 block->serialize(o, version);
2528 Versions up from 9 have block objects.
2532 block->serializeObjects(o, version);
2535 // We just wrote it to the disk
2536 block->resetChangedFlag();
2539 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2541 DSTACK(__FUNCTION_NAME);
2545 // Block file is map/sectors/xxxxxxxx/xxxx
2546 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2547 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2548 if(is.good() == false)
2549 throw FileNotGoodException("Cannot open block file");
2551 v3s16 p3d = getBlockPos(sectordir, blockfile);
2552 v2s16 p2d(p3d.X, p3d.Z);
2554 assert(sector->getPos() == p2d);
2556 u8 version = SER_FMT_VER_INVALID;
2557 is.read((char*)&version, 1);
2559 /*u32 block_size = MapBlock::serializedLength(version);
2560 SharedBuffer<u8> data(block_size);
2561 is.read((char*)*data, block_size);*/
2563 // This will always return a sector because we're the server
2564 //MapSector *sector = emergeSector(p2d);
2566 MapBlock *block = NULL;
2567 bool created_new = false;
2569 block = sector->getBlockNoCreate(p3d.Y);
2571 catch(InvalidPositionException &e)
2573 block = sector->createBlankBlockNoInsert(p3d.Y);
2577 // deserialize block data
2578 block->deSerialize(is, version);
2581 Versions up from 9 have block objects.
2585 block->updateObjects(is, version, NULL);
2589 sector->insertBlock(block);
2592 Convert old formats to new and save
2595 // Save old format blocks in new format
2596 if(version < SER_FMT_VER_HIGHEST)
2601 // We just loaded it from the disk, so it's up-to-date.
2602 block->resetChangedFlag();
2605 catch(SerializationError &e)
2607 dstream<<"WARNING: Invalid block data on disk "
2608 "(SerializationError). Ignoring."
2613 // Gets from master heightmap
2614 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2616 assert(m_heightmap != NULL);
2624 corners[0] = m_heightmap->getGroundHeight
2625 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2626 corners[1] = m_heightmap->getGroundHeight
2627 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2628 corners[2] = m_heightmap->getGroundHeight
2629 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2630 corners[3] = m_heightmap->getGroundHeight
2631 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2634 void ServerMap::PrintInfo(std::ostream &out)
2643 ClientMap::ClientMap(
2645 video::SMaterial *materials,
2646 scene::ISceneNode* parent,
2647 scene::ISceneManager* mgr,
2651 scene::ISceneNode(parent, mgr, id),
2653 m_materials(materials),
2656 /*m_box = core::aabbox3d<f32>(0,0,0,
2657 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2658 /*m_box = core::aabbox3d<f32>(0,0,0,
2659 map->getSizeNodes().X * BS,
2660 map->getSizeNodes().Y * BS,
2661 map->getSizeNodes().Z * BS);*/
2662 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2663 BS*1000000,BS*1000000,BS*1000000);
2668 ClientMap::~ClientMap()
2670 JMutexAutoLock lock(mesh_mutex);
2679 MapSector * ClientMap::emergeSector(v2s16 p2d)
2681 DSTACK(__FUNCTION_NAME);
2682 // Check that it doesn't exist already
2684 return getSectorNoGenerate(p2d);
2686 catch(InvalidPositionException &e)
2690 // Create a sector with no heightmaps
2691 ClientMapSector *sector = new ClientMapSector(this, p2d);
2694 JMutexAutoLock lock(m_sector_mutex);
2695 m_sectors.insert(p2d, sector);
2701 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2703 DSTACK(__FUNCTION_NAME);
2704 ClientMapSector *sector = NULL;
2706 JMutexAutoLock lock(m_sector_mutex);
2708 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2712 sector = (ClientMapSector*)n->getValue();
2713 assert(sector->getId() == MAPSECTOR_CLIENT);
2717 sector = new ClientMapSector(this, p2d);
2719 JMutexAutoLock lock(m_sector_mutex);
2720 m_sectors.insert(p2d, sector);
2724 sector->deSerialize(is);
2727 void ClientMap::renderMap(video::IVideoDriver* driver,
2728 video::SMaterial *materials, s32 pass)
2730 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2731 DSTACK(__FUNCTION_NAME);
2733 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2736 Draw master heightmap mesh
2740 JMutexAutoLock lock(mesh_mutex);
2743 u32 c = mesh->getMeshBufferCount();
2745 for(u32 i=0; i<c; i++)
2747 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2748 const video::SMaterial& material = buf->getMaterial();
2749 video::IMaterialRenderer* rnd =
2750 driver->getMaterialRenderer(material.MaterialType);
2751 bool transparent = (rnd && rnd->isTransparent());
2752 // Render transparent on transparent pass and likewise.
2753 if(transparent == is_transparent_pass)
2755 driver->setMaterial(buf->getMaterial());
2756 driver->drawMeshBuffer(buf);
2764 Get time for measuring timeout.
2766 Measuring time is very useful for long delays when the
2767 machine is swapping a lot.
2769 int time1 = time(0);
2772 Collect all blocks that are in the view range
2774 Should not optimize more here as we want to auto-update
2775 all changed nodes in viewing range at the next step.
2778 s16 viewing_range_nodes;
2779 bool viewing_range_all;
2781 JMutexAutoLock lock(g_range_mutex);
2782 viewing_range_nodes = g_viewing_range_nodes;
2783 viewing_range_all = g_viewing_range_all;
2786 m_camera_mutex.Lock();
2787 v3f camera_position = m_camera_position;
2788 v3f camera_direction = m_camera_direction;
2789 m_camera_mutex.Unlock();
2792 Get all blocks and draw all visible ones
2795 v3s16 cam_pos_nodes(
2796 camera_position.X / BS,
2797 camera_position.Y / BS,
2798 camera_position.Z / BS);
2800 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2802 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2803 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2805 // Take a fair amount as we will be dropping more out later
2807 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2808 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2809 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2811 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2812 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2813 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2815 u32 vertex_count = 0;
2817 core::map<v2s16, MapSector*>::Iterator si;
2819 //NOTE: The sectors map should be locked but we're not doing it
2820 // because it'd cause too much delays
2822 si = m_sectors.getIterator();
2823 for(; si.atEnd() == false; si++)
2826 static int timecheck_counter = 0;
2827 timecheck_counter++;
2828 if(timecheck_counter > 50)
2830 int time2 = time(0);
2831 if(time2 > time1 + 4)
2833 dstream<<"ClientMap::renderMap(): "
2834 "Rendering takes ages, returning."
2841 MapSector *sector = si.getNode()->getValue();
2842 v2s16 sp = sector->getPos();
2844 if(viewing_range_all == false)
2846 if(sp.X < p_blocks_min.X
2847 || sp.X > p_blocks_max.X
2848 || sp.Y < p_blocks_min.Z
2849 || sp.Y > p_blocks_max.Z)
2853 core::list< MapBlock * > sectorblocks;
2854 sector->getBlocks(sectorblocks);
2860 core::list< MapBlock * >::Iterator i;
2861 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2863 MapBlock *block = *i;
2866 Compare block position to camera position, skip
2867 if not seen on display
2870 v3s16 blockpos_nodes = block->getPosRelative();
2872 // Block center position
2874 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2875 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2876 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2879 // Block position relative to camera
2880 v3f blockpos_relative = blockpos - camera_position;
2882 // Distance in camera direction (+=front, -=back)
2883 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2886 f32 d = blockpos_relative.getLength();
2888 if(viewing_range_all == false)
2890 // If block is far away, don't draw it
2891 if(d > viewing_range_nodes * BS)
2895 // Maximum radius of a block
2896 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2898 // If block is (nearly) touching the camera, don't
2899 // bother validating further (that is, render it anyway)
2900 if(d > block_max_radius * 1.5)
2902 // Cosine of the angle between the camera direction
2903 // and the block direction (camera_direction is an unit vector)
2904 f32 cosangle = dforward / d;
2906 // Compensate for the size of the block
2907 // (as the block has to be shown even if it's a bit off FOV)
2908 // This is an estimate.
2909 cosangle += block_max_radius / dforward;
2911 // If block is not in the field of view, skip it
2912 //if(cosangle < cos(FOV_ANGLE/2))
2913 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2918 Draw the faces of the block
2922 JMutexAutoLock lock(block->mesh_mutex);
2924 // Cancel if block has no mesh
2925 if(block->mesh == NULL)
2928 u32 c = block->mesh->getMeshBufferCount();
2930 for(u32 i=0; i<c; i++)
2932 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2933 const video::SMaterial& material = buf->getMaterial();
2934 video::IMaterialRenderer* rnd =
2935 driver->getMaterialRenderer(material.MaterialType);
2936 bool transparent = (rnd && rnd->isTransparent());
2937 // Render transparent on transparent pass and likewise.
2938 if(transparent == is_transparent_pass)
2940 driver->setMaterial(buf->getMaterial());
2941 driver->drawMeshBuffer(buf);
2942 vertex_count += buf->getVertexCount();
2946 } // foreach sectorblocks
2949 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2950 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2953 void ClientMap::updateMesh()
2956 DSTACK(__FUNCTION_NAME);
2959 Check what sectors don't draw anything useful at ground level
2960 and create a mesh of the rough heightmap at those positions.
2963 m_camera_mutex.Lock();
2964 v3f camera_position = m_camera_position;
2965 v3f camera_direction = m_camera_direction;
2966 m_camera_mutex.Unlock();
2968 v3s16 cam_pos_nodes(
2969 camera_position.X / BS,
2970 camera_position.Y / BS,
2971 camera_position.Z / BS);
2973 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2975 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2976 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2978 // Take a fair amount as we will be dropping more out later
2980 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2981 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2982 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2984 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2985 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2986 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2992 scene::SMesh *mesh_new = new scene::SMesh();
2993 //scene::IMeshBuffer *buf = NULL;
2994 scene::SMeshBuffer *buf = NULL;
2996 u8 material_in_use = 0;
2999 Loop through sectors
3002 for(core::map<v2s16, MapSector*>::Iterator
3003 si = m_sectors.getIterator();
3004 si.atEnd() == false; si++)
3006 MapSector *sector = si.getNode()->getValue();
3008 if(sector->getId() != MAPSECTOR_CLIENT)
3010 dstream<<"WARNING: Client has a non-client sector"
3015 ClientMapSector *cs = (ClientMapSector*)sector;
3017 v2s16 sp = sector->getPos();
3019 if(sp.X < p_blocks_min.X
3020 || sp.X > p_blocks_max.X
3021 || sp.Y < p_blocks_min.Z
3022 || sp.Y > p_blocks_max.Z)
3026 Get some ground level info
3038 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
3040 s16 cn_max = -32768;
3041 for(s16 i=0; i<4; i++)
3048 s16 cn_slope = cn_max - cn_min;
3051 Generate this part of the heightmap mesh
3055 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
3057 else if(cn_slope <= MAP_BLOCKSIZE)
3062 if(material != material_in_use || buf == NULL)
3064 // Try to get a meshbuffer associated with the material
3065 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
3066 (g_mesh_materials[material]);
3067 // If not found, create one
3070 // This is a "Standard MeshBuffer",
3071 // it's a typedeffed CMeshBuffer<video::S3DVertex>
3072 buf = new scene::SMeshBuffer();
3075 buf->Material = g_mesh_materials[material];
3077 //buf->setHardwareMappingHint(scene::EHM_STATIC);
3079 mesh_new->addMeshBuffer(buf);
3083 material_in_use = material;
3086 // Sector side width in floating-point units
3087 f32 sd = BS * MAP_BLOCKSIZE;
3088 // Sector position in global floating-point units
3089 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
3091 //video::SColor c(255,255,255,255);
3093 video::SColor c(255,cc,cc,cc);
3095 video::S3DVertex vertices[4] =
3097 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
3098 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
3099 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3100 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3102 u16 indices[] = {0,1,2,2,3,0};
3104 buf->append(vertices, 4, indices, 6);
3108 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3116 scene::SMesh *mesh_old = mesh;
3123 mesh_mutex.Unlock();
3125 if(mesh_old != NULL)
3127 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3129 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3130 (g_materials[MATERIAL_GRASS]);
3132 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3139 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3144 void ClientMap::PrintInfo(std::ostream &out)
3154 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3159 MapVoxelManipulator::~MapVoxelManipulator()
3161 dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3165 void MapVoxelManipulator::emerge(VoxelArea a)
3167 TimeTaker timer1("emerge", g_device, &emerge_time);
3169 // Units of these are MapBlocks
3170 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3171 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3173 VoxelArea block_area_nodes
3174 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3176 addArea(block_area_nodes);
3178 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3179 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3180 for(s32 x=p_min.X; x<=p_max.X; x++)
3183 core::map<v3s16, bool>::Node *n;
3184 n = m_loaded_blocks.find(p);
3188 bool block_data_inexistent = false;
3191 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3193 dstream<<"Loading block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3196 MapBlock *block = m_map->getBlockNoCreate(p);
3197 if(block->isDummy())
3198 block_data_inexistent = true;
3200 block->copyTo(*this);
3202 catch(InvalidPositionException &e)
3204 block_data_inexistent = true;
3207 if(block_data_inexistent)
3209 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3210 // Fill with VOXELFLAG_INEXISTENT
3211 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3212 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3214 s32 i = m_area.index(a.MinEdge.X,y,z);
3215 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3219 m_loaded_blocks.insert(p, true);
3222 //dstream<<"emerge done"<<std::endl;
3226 TODO: Add an option to only update eg. water and air nodes.
3227 This will make it interfere less with important stuff if
3230 void MapVoxelManipulator::blitBack
3231 (core::map<v3s16, MapBlock*> & modified_blocks)
3233 TimeTaker timer1("blitBack", g_device);
3236 Initialize block cache
3238 v3s16 blockpos_last;
3239 MapBlock *block = NULL;
3240 bool block_checked_in_modified = false;
3242 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3243 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3244 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3248 u8 f = m_flags[m_area.index(p)];
3249 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3252 MapNode &n = m_data[m_area.index(p)];
3254 v3s16 blockpos = getNodeBlockPos(p);
3259 if(block == NULL || blockpos != blockpos_last){
3260 block = m_map->getBlockNoCreate(blockpos);
3261 blockpos_last = blockpos;
3262 block_checked_in_modified = false;
3265 // Calculate relative position in block
3266 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3268 // Don't continue if nothing has changed here
3269 if(block->getNode(relpos) == n)
3272 //m_map->setNode(m_area.MinEdge + p, n);
3273 block->setNode(relpos, n);
3276 Make sure block is in modified_blocks
3278 if(block_checked_in_modified == false)
3280 modified_blocks[blockpos] = block;
3281 block_checked_in_modified = true;
3284 catch(InvalidPositionException &e)