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.d = MATERIAL_OCEAN;
1824 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1830 block->setNode(v3s16(x0,y0,z0), n);
1835 Calculate is_underground
1837 // Probably underground if the highest part of block is under lowest
1839 bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1840 block->setIsUnderground(is_underground);
1843 Force lighting update if underground.
1844 This is needed because of ravines.
1849 lighting_invalidated_blocks[block->getPos()] = block;
1858 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1859 for(s16 i=0; i<underground_level*3; i++)
1864 (rand()%(MAP_BLOCKSIZE-2))+1,
1865 (rand()%(MAP_BLOCKSIZE-2))+1,
1866 (rand()%(MAP_BLOCKSIZE-2))+1
1870 n.d = MATERIAL_MESE;
1872 if(is_ground_material(block->getNode(cp).d))
1874 block->setNode(cp, n);
1876 for(u16 i=0; i<26; i++)
1878 if(is_ground_material(block->getNode(cp+g_26dirs[i]).d))
1880 block->setNode(cp+g_26dirs[i], n);
1887 Create a few rats in empty blocks underground
1891 //for(u16 i=0; i<2; i++)
1894 (rand()%(MAP_BLOCKSIZE-2))+1,
1895 (rand()%(MAP_BLOCKSIZE-2))+1,
1896 (rand()%(MAP_BLOCKSIZE-2))+1
1899 // Check that the place is empty
1900 //if(!is_ground_material(block->getNode(cp).d))
1903 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1904 block->addObject(obj);
1910 Add block to sector.
1912 sector->insertBlock(block);
1915 Do some interpolation for dungeons
1920 TimeTaker timer("interpolation", g_device);
1922 MapVoxelManipulator vmanip(this);
1924 v3s16 relpos = block->getPosRelative();
1926 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1927 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1928 /*vmanip.interpolate(VoxelArea(relpos,
1929 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1931 core::map<v3s16, MapBlock*> modified_blocks;
1932 vmanip.blitBack(modified_blocks);
1933 dstream<<"blitBack modified "<<modified_blocks.size()
1934 <<" blocks"<<std::endl;
1936 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1937 for(core::map<v3s16, MapBlock*>::Iterator
1938 i = modified_blocks.getIterator();
1939 i.atEnd() == false; i++)
1941 MapBlock *block = i.getNode()->getValue();
1943 changed_blocks.insert(block->getPos(), block);
1944 //lighting_invalidated_blocks.insert(block->getPos(), block);
1954 // An y-wise container of changed blocks
1955 core::map<s16, MapBlock*> changed_blocks_sector;
1958 Check if any sector's objects can be placed now.
1961 core::map<v3s16, u8> *objects = sector->getObjects();
1962 core::list<v3s16> objects_to_remove;
1963 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1964 i.atEnd() == false; i++)
1966 v3s16 p = i.getNode()->getKey();
1968 u8 d = i.getNode()->getValue();
1970 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1975 if(d == SECTOR_OBJECT_TEST)
1977 if(sector->isValidArea(p + v3s16(0,0,0),
1978 p + v3s16(0,0,0), &changed_blocks_sector))
1981 n.d = MATERIAL_LIGHT;
1982 sector->setNode(p, n);
1983 objects_to_remove.push_back(p);
1986 else if(d == SECTOR_OBJECT_TREE_1)
1988 v3s16 p_min = p + v3s16(-1,0,-1);
1989 v3s16 p_max = p + v3s16(1,4,1);
1990 if(sector->isValidArea(p_min, p_max,
1991 &changed_blocks_sector))
1994 n.d = MATERIAL_TREE;
1995 sector->setNode(p+v3s16(0,0,0), n);
1996 sector->setNode(p+v3s16(0,1,0), n);
1997 sector->setNode(p+v3s16(0,2,0), n);
1998 sector->setNode(p+v3s16(0,3,0), n);
2000 n.d = MATERIAL_LEAVES;
2002 sector->setNode(p+v3s16(0,4,0), n);
2004 sector->setNode(p+v3s16(-1,4,0), n);
2005 sector->setNode(p+v3s16(1,4,0), n);
2006 sector->setNode(p+v3s16(0,4,-1), n);
2007 sector->setNode(p+v3s16(0,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);
2011 sector->setNode(p+v3s16(1,4,-1), n);
2013 sector->setNode(p+v3s16(-1,3,0), n);
2014 sector->setNode(p+v3s16(1,3,0), n);
2015 sector->setNode(p+v3s16(0,3,-1), n);
2016 sector->setNode(p+v3s16(0,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);
2020 sector->setNode(p+v3s16(1,3,-1), n);
2022 objects_to_remove.push_back(p);
2024 // Lighting has to be recalculated for this one.
2025 sector->getBlocksInArea(p_min, p_max,
2026 lighting_invalidated_blocks);
2029 else if(d == SECTOR_OBJECT_BUSH_1)
2031 if(sector->isValidArea(p + v3s16(0,0,0),
2032 p + v3s16(0,0,0), &changed_blocks_sector))
2035 n.d = MATERIAL_LEAVES;
2036 sector->setNode(p+v3s16(0,0,0), n);
2038 objects_to_remove.push_back(p);
2041 else if(d == SECTOR_OBJECT_RAVINE)
2044 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2045 v3s16 p_max = p + v3s16(6,6,6);
2046 if(sector->isValidArea(p_min, p_max,
2047 &changed_blocks_sector))
2050 n.d = MATERIAL_STONE;
2052 n2.d = MATERIAL_AIR;
2053 s16 depth = maxdepth + (rand()%10);
2055 s16 minz = -6 - (-2);
2057 for(s16 x=-6; x<=6; x++)
2059 z += -1 + (rand()%3);
2064 for(s16 y=depth+(rand()%2); y<=6; y++)
2066 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2069 v3s16 p2 = p + v3s16(x,y,z-2);
2070 if(is_ground_material(sector->getNode(p2).d))
2071 sector->setNode(p2, n);
2074 v3s16 p2 = p + v3s16(x,y,z-1);
2075 if(is_ground_material(sector->getNode(p2).d))
2076 sector->setNode(p2, n2);
2079 v3s16 p2 = p + v3s16(x,y,z+0);
2080 if(is_ground_material(sector->getNode(p2).d))
2081 sector->setNode(p2, n2);
2084 v3s16 p2 = p + v3s16(x,y,z+1);
2085 if(is_ground_material(sector->getNode(p2).d))
2086 sector->setNode(p2, n);
2089 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2090 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2094 objects_to_remove.push_back(p);
2096 // Lighting has to be recalculated for this one.
2097 sector->getBlocksInArea(p_min, p_max,
2098 lighting_invalidated_blocks);
2103 dstream<<"ServerMap::emergeBlock(): "
2104 "Invalid heightmap object"
2109 catch(InvalidPositionException &e)
2111 dstream<<"WARNING: "<<__FUNCTION_NAME
2112 <<": while inserting object "<<(int)d
2113 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2114 <<" InvalidPositionException.what()="
2115 <<e.what()<<std::endl;
2116 // This is not too fatal and seems to happen sometimes.
2121 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2122 i != objects_to_remove.end(); i++)
2124 objects->remove(*i);
2127 for(core::map<s16, MapBlock*>::Iterator
2128 i = changed_blocks_sector.getIterator();
2129 i.atEnd() == false; i++)
2131 MapBlock *block = i.getNode()->getValue();
2133 changed_blocks.insert(block->getPos(), block);
2139 void ServerMap::createDir(std::string path)
2141 if(fs::CreateDir(path) == false)
2143 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2144 <<"\""<<path<<"\""<<std::endl;
2145 throw BaseException("ServerMap failed to create directory");
2149 std::string ServerMap::getSectorSubDir(v2s16 pos)
2152 snprintf(cc, 9, "%.4x%.4x",
2153 (unsigned int)pos.X&0xffff,
2154 (unsigned int)pos.Y&0xffff);
2156 return std::string(cc);
2159 std::string ServerMap::getSectorDir(v2s16 pos)
2161 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2164 v2s16 ServerMap::getSectorPos(std::string dirname)
2166 if(dirname.size() != 8)
2167 throw InvalidFilenameException("Invalid sector directory name");
2169 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2171 throw InvalidFilenameException("Invalid sector directory name");
2172 v2s16 pos((s16)x, (s16)y);
2176 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2178 v2s16 p2d = getSectorPos(sectordir);
2180 if(blockfile.size() != 4){
2181 throw InvalidFilenameException("Invalid block filename");
2184 int r = sscanf(blockfile.c_str(), "%4x", &y);
2186 throw InvalidFilenameException("Invalid block filename");
2187 return v3s16(p2d.X, y, p2d.Y);
2191 #define ENABLE_SECTOR_SAVING 1
2192 #define ENABLE_SECTOR_LOADING 1
2193 #define ENABLE_BLOCK_SAVING 1
2194 #define ENABLE_BLOCK_LOADING 1
2196 void ServerMap::save(bool only_changed)
2198 DSTACK(__FUNCTION_NAME);
2199 if(m_map_saving_enabled == false)
2201 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2205 if(only_changed == false)
2206 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2209 saveMasterHeightmap();
2211 u32 sector_meta_count = 0;
2212 u32 block_count = 0;
2215 JMutexAutoLock lock(m_sector_mutex);
2217 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2218 for(; i.atEnd() == false; i++)
2220 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2221 assert(sector->getId() == MAPSECTOR_SERVER);
2223 if(ENABLE_SECTOR_SAVING)
2225 if(sector->differs_from_disk || only_changed == false)
2227 saveSectorMeta(sector);
2228 sector_meta_count++;
2231 if(ENABLE_BLOCK_SAVING)
2233 core::list<MapBlock*> blocks;
2234 sector->getBlocks(blocks);
2235 core::list<MapBlock*>::Iterator j;
2236 for(j=blocks.begin(); j!=blocks.end(); j++)
2238 MapBlock *block = *j;
2239 if(block->getChangedFlag() || only_changed == false)
2250 u32 deleted_count = 0;
2251 deleted_count = deleteUnusedSectors
2252 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2255 Only print if something happened or saved whole map
2257 if(only_changed == false || sector_meta_count != 0
2258 || block_count != 0 || deleted_count != 0)
2260 dstream<<DTIME<<"ServerMap: Written: "
2261 <<sector_meta_count<<" sector metadata files, "
2262 <<block_count<<" block files, "
2263 <<deleted_count<<" sectors unloaded from memory."
2268 void ServerMap::loadAll()
2270 DSTACK(__FUNCTION_NAME);
2271 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2273 loadMasterHeightmap();
2275 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2277 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2279 JMutexAutoLock lock(m_sector_mutex);
2282 s32 printed_counter = -100000;
2283 s32 count = list.size();
2285 std::vector<fs::DirListNode>::iterator i;
2286 for(i=list.begin(); i!=list.end(); i++)
2288 if(counter > printed_counter + 10)
2290 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2291 printed_counter = counter;
2295 MapSector *sector = NULL;
2297 // We want directories
2301 sector = loadSectorMeta(i->name);
2303 catch(InvalidFilenameException &e)
2305 // This catches unknown crap in directory
2308 if(ENABLE_BLOCK_LOADING)
2310 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2311 (m_savedir+"/sectors/"+i->name);
2312 std::vector<fs::DirListNode>::iterator i2;
2313 for(i2=list2.begin(); i2!=list2.end(); i2++)
2319 loadBlock(i->name, i2->name, sector);
2321 catch(InvalidFilenameException &e)
2323 // This catches unknown crap in directory
2328 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2331 void ServerMap::saveMasterHeightmap()
2333 DSTACK(__FUNCTION_NAME);
2334 createDir(m_savedir);
2336 std::string fullpath = m_savedir + "/master_heightmap";
2337 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2338 if(o.good() == false)
2339 throw FileNotGoodException("Cannot open master heightmap");
2341 // Format used for writing
2342 u8 version = SER_FMT_VER_HIGHEST;
2345 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2347 [0] u8 serialization version
2348 [1] X master heightmap
2350 u32 fullsize = 1 + hmdata.getSize();
2351 SharedBuffer<u8> data(fullsize);
2354 memcpy(&data[1], *hmdata, hmdata.getSize());
2356 o.write((const char*)*data, fullsize);
2359 m_heightmap->serialize(o, version);
2362 void ServerMap::loadMasterHeightmap()
2364 DSTACK(__FUNCTION_NAME);
2365 std::string fullpath = m_savedir + "/master_heightmap";
2366 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2367 if(is.good() == false)
2368 throw FileNotGoodException("Cannot open master heightmap");
2370 if(m_heightmap != NULL)
2373 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2376 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2378 DSTACK(__FUNCTION_NAME);
2379 // Format used for writing
2380 u8 version = SER_FMT_VER_HIGHEST;
2382 v2s16 pos = sector->getPos();
2383 createDir(m_savedir);
2384 createDir(m_savedir+"/sectors");
2385 std::string dir = getSectorDir(pos);
2388 std::string fullpath = dir + "/heightmap";
2389 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2390 if(o.good() == false)
2391 throw FileNotGoodException("Cannot open master heightmap");
2393 sector->serialize(o, version);
2395 sector->differs_from_disk = false;
2398 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2400 DSTACK(__FUNCTION_NAME);
2402 v2s16 p2d = getSectorPos(dirname);
2403 std::string dir = m_savedir + "/sectors/" + dirname;
2405 std::string fullpath = dir + "/heightmap";
2406 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2407 if(is.good() == false)
2408 throw FileNotGoodException("Cannot open sector heightmap");
2410 ServerMapSector *sector = ServerMapSector::deSerialize
2411 (is, this, p2d, &m_hwrapper, m_sectors);
2413 sector->differs_from_disk = false;
2418 bool ServerMap::loadSectorFull(v2s16 p2d)
2420 DSTACK(__FUNCTION_NAME);
2421 std::string sectorsubdir = getSectorSubDir(p2d);
2423 MapSector *sector = NULL;
2425 JMutexAutoLock lock(m_sector_mutex);
2428 sector = loadSectorMeta(sectorsubdir);
2430 catch(InvalidFilenameException &e)
2434 catch(FileNotGoodException &e)
2438 catch(std::exception &e)
2443 if(ENABLE_BLOCK_LOADING)
2445 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2446 (m_savedir+"/sectors/"+sectorsubdir);
2447 std::vector<fs::DirListNode>::iterator i2;
2448 for(i2=list2.begin(); i2!=list2.end(); i2++)
2454 loadBlock(sectorsubdir, i2->name, sector);
2456 catch(InvalidFilenameException &e)
2458 // This catches unknown crap in directory
2466 bool ServerMap::deFlushSector(v2s16 p2d)
2468 DSTACK(__FUNCTION_NAME);
2469 // See if it already exists in memory
2471 MapSector *sector = getSectorNoGenerate(p2d);
2474 catch(InvalidPositionException &e)
2477 Try to load the sector from disk.
2479 if(loadSectorFull(p2d) == true)
2488 void ServerMap::saveBlock(MapBlock *block)
2490 DSTACK(__FUNCTION_NAME);
2492 Dummy blocks are not written
2494 if(block->isDummy())
2496 /*v3s16 p = block->getPos();
2497 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2498 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2502 // Format used for writing
2503 u8 version = SER_FMT_VER_HIGHEST;
2505 v3s16 p3d = block->getPos();
2506 v2s16 p2d(p3d.X, p3d.Z);
2507 createDir(m_savedir);
2508 createDir(m_savedir+"/sectors");
2509 std::string dir = getSectorDir(p2d);
2512 // Block file is map/sectors/xxxxxxxx/xxxx
2514 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2515 std::string fullpath = dir + "/" + cc;
2516 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2517 if(o.good() == false)
2518 throw FileNotGoodException("Cannot open block data");
2521 [0] u8 serialization version
2524 o.write((char*)&version, 1);
2526 block->serialize(o, version);
2529 Versions up from 9 have block objects.
2533 block->serializeObjects(o, version);
2536 // We just wrote it to the disk
2537 block->resetChangedFlag();
2540 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2542 DSTACK(__FUNCTION_NAME);
2546 // Block file is map/sectors/xxxxxxxx/xxxx
2547 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2548 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2549 if(is.good() == false)
2550 throw FileNotGoodException("Cannot open block file");
2552 v3s16 p3d = getBlockPos(sectordir, blockfile);
2553 v2s16 p2d(p3d.X, p3d.Z);
2555 assert(sector->getPos() == p2d);
2557 u8 version = SER_FMT_VER_INVALID;
2558 is.read((char*)&version, 1);
2560 /*u32 block_size = MapBlock::serializedLength(version);
2561 SharedBuffer<u8> data(block_size);
2562 is.read((char*)*data, block_size);*/
2564 // This will always return a sector because we're the server
2565 //MapSector *sector = emergeSector(p2d);
2567 MapBlock *block = NULL;
2568 bool created_new = false;
2570 block = sector->getBlockNoCreate(p3d.Y);
2572 catch(InvalidPositionException &e)
2574 block = sector->createBlankBlockNoInsert(p3d.Y);
2578 // deserialize block data
2579 block->deSerialize(is, version);
2582 Versions up from 9 have block objects.
2586 block->updateObjects(is, version, NULL);
2590 sector->insertBlock(block);
2593 Convert old formats to new and save
2596 // Save old format blocks in new format
2597 if(version < SER_FMT_VER_HIGHEST)
2602 // We just loaded it from the disk, so it's up-to-date.
2603 block->resetChangedFlag();
2606 catch(SerializationError &e)
2608 dstream<<"WARNING: Invalid block data on disk "
2609 "(SerializationError). Ignoring."
2614 // Gets from master heightmap
2615 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2617 assert(m_heightmap != NULL);
2625 corners[0] = m_heightmap->getGroundHeight
2626 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2627 corners[1] = m_heightmap->getGroundHeight
2628 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2629 corners[2] = m_heightmap->getGroundHeight
2630 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2631 corners[3] = m_heightmap->getGroundHeight
2632 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2635 void ServerMap::PrintInfo(std::ostream &out)
2644 ClientMap::ClientMap(
2646 video::SMaterial *materials,
2647 scene::ISceneNode* parent,
2648 scene::ISceneManager* mgr,
2652 scene::ISceneNode(parent, mgr, id),
2654 m_materials(materials),
2657 /*m_box = core::aabbox3d<f32>(0,0,0,
2658 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2659 /*m_box = core::aabbox3d<f32>(0,0,0,
2660 map->getSizeNodes().X * BS,
2661 map->getSizeNodes().Y * BS,
2662 map->getSizeNodes().Z * BS);*/
2663 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2664 BS*1000000,BS*1000000,BS*1000000);
2669 ClientMap::~ClientMap()
2671 JMutexAutoLock lock(mesh_mutex);
2680 MapSector * ClientMap::emergeSector(v2s16 p2d)
2682 DSTACK(__FUNCTION_NAME);
2683 // Check that it doesn't exist already
2685 return getSectorNoGenerate(p2d);
2687 catch(InvalidPositionException &e)
2691 // Create a sector with no heightmaps
2692 ClientMapSector *sector = new ClientMapSector(this, p2d);
2695 JMutexAutoLock lock(m_sector_mutex);
2696 m_sectors.insert(p2d, sector);
2702 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2704 DSTACK(__FUNCTION_NAME);
2705 ClientMapSector *sector = NULL;
2707 JMutexAutoLock lock(m_sector_mutex);
2709 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2713 sector = (ClientMapSector*)n->getValue();
2714 assert(sector->getId() == MAPSECTOR_CLIENT);
2718 sector = new ClientMapSector(this, p2d);
2720 JMutexAutoLock lock(m_sector_mutex);
2721 m_sectors.insert(p2d, sector);
2725 sector->deSerialize(is);
2728 void ClientMap::renderMap(video::IVideoDriver* driver,
2729 video::SMaterial *materials, s32 pass)
2731 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2732 DSTACK(__FUNCTION_NAME);
2734 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2737 Get time for measuring timeout.
2739 Measuring time is very useful for long delays when the
2740 machine is swapping a lot.
2742 int time1 = time(0);
2745 Collect all blocks that are in the view range
2747 Should not optimize more here as we want to auto-update
2748 all changed nodes in viewing range at the next step.
2751 s16 viewing_range_nodes;
2752 bool viewing_range_all;
2754 JMutexAutoLock lock(g_range_mutex);
2755 viewing_range_nodes = g_viewing_range_nodes;
2756 viewing_range_all = g_viewing_range_all;
2759 m_camera_mutex.Lock();
2760 v3f camera_position = m_camera_position;
2761 v3f camera_direction = m_camera_direction;
2762 m_camera_mutex.Unlock();
2765 Get all blocks and draw all visible ones
2768 v3s16 cam_pos_nodes(
2769 camera_position.X / BS,
2770 camera_position.Y / BS,
2771 camera_position.Z / BS);
2773 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2775 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2776 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2778 // Take a fair amount as we will be dropping more out later
2780 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2781 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2782 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2784 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2785 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2786 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2788 u32 vertex_count = 0;
2790 core::map<v2s16, MapSector*>::Iterator si;
2792 //NOTE: The sectors map should be locked but we're not doing it
2793 // because it'd cause too much delays
2795 si = m_sectors.getIterator();
2796 for(; si.atEnd() == false; si++)
2799 static int timecheck_counter = 0;
2800 timecheck_counter++;
2801 if(timecheck_counter > 50)
2803 int time2 = time(0);
2804 if(time2 > time1 + 4)
2806 dstream<<"ClientMap::renderMap(): "
2807 "Rendering takes ages, returning."
2814 MapSector *sector = si.getNode()->getValue();
2815 v2s16 sp = sector->getPos();
2817 if(viewing_range_all == false)
2819 if(sp.X < p_blocks_min.X
2820 || sp.X > p_blocks_max.X
2821 || sp.Y < p_blocks_min.Z
2822 || sp.Y > p_blocks_max.Z)
2826 core::list< MapBlock * > sectorblocks;
2827 sector->getBlocks(sectorblocks);
2833 core::list< MapBlock * >::Iterator i;
2834 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2836 MapBlock *block = *i;
2839 Compare block position to camera position, skip
2840 if not seen on display
2843 v3s16 blockpos_nodes = block->getPosRelative();
2845 // Block center position
2847 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2848 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2849 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2852 // Block position relative to camera
2853 v3f blockpos_relative = blockpos - camera_position;
2855 // Distance in camera direction (+=front, -=back)
2856 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2859 f32 d = blockpos_relative.getLength();
2861 if(viewing_range_all == false)
2863 // If block is far away, don't draw it
2864 if(d > viewing_range_nodes * BS)
2868 // Maximum radius of a block
2869 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2871 // If block is (nearly) touching the camera, don't
2872 // bother validating further (that is, render it anyway)
2873 if(d > block_max_radius * 1.5)
2875 // Cosine of the angle between the camera direction
2876 // and the block direction (camera_direction is an unit vector)
2877 f32 cosangle = dforward / d;
2879 // Compensate for the size of the block
2880 // (as the block has to be shown even if it's a bit off FOV)
2881 // This is an estimate.
2882 cosangle += block_max_radius / dforward;
2884 // If block is not in the field of view, skip it
2885 //if(cosangle < cos(FOV_ANGLE/2))
2886 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2891 Draw the faces of the block
2895 JMutexAutoLock lock(block->mesh_mutex);
2897 // Cancel if block has no mesh
2898 if(block->mesh == NULL)
2901 u32 c = block->mesh->getMeshBufferCount();
2903 for(u32 i=0; i<c; i++)
2905 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2906 const video::SMaterial& material = buf->getMaterial();
2907 video::IMaterialRenderer* rnd =
2908 driver->getMaterialRenderer(material.MaterialType);
2909 bool transparent = (rnd && rnd->isTransparent());
2910 // Render transparent on transparent pass and likewise.
2911 if(transparent == is_transparent_pass)
2913 driver->setMaterial(buf->getMaterial());
2914 driver->drawMeshBuffer(buf);
2915 vertex_count += buf->getVertexCount();
2919 } // foreach sectorblocks
2922 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2923 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2926 void ClientMap::updateMesh()
2929 DSTACK(__FUNCTION_NAME);
2932 Check what sectors don't draw anything useful at ground level
2933 and create a mesh of the rough heightmap at those positions.
2936 m_camera_mutex.Lock();
2937 v3f camera_position = m_camera_position;
2938 v3f camera_direction = m_camera_direction;
2939 m_camera_mutex.Unlock();
2941 v3s16 cam_pos_nodes(
2942 camera_position.X / BS,
2943 camera_position.Y / BS,
2944 camera_position.Z / BS);
2946 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2948 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2949 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2951 // Take a fair amount as we will be dropping more out later
2953 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2954 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2955 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2957 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2958 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2959 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2965 scene::SMesh *mesh_new = new scene::SMesh();
2966 //scene::IMeshBuffer *buf = NULL;
2967 scene::SMeshBuffer *buf = NULL;
2969 u8 material_in_use = 0;
2972 Loop through sectors
2975 for(core::map<v2s16, MapSector*>::Iterator
2976 si = m_sectors.getIterator();
2977 si.atEnd() == false; si++)
2979 MapSector *sector = si.getNode()->getValue();
2981 if(sector->getId() != MAPSECTOR_CLIENT)
2983 dstream<<"WARNING: Client has a non-client sector"
2988 ClientMapSector *cs = (ClientMapSector*)sector;
2990 v2s16 sp = sector->getPos();
2992 if(sp.X < p_blocks_min.X
2993 || sp.X > p_blocks_max.X
2994 || sp.Y < p_blocks_min.Z
2995 || sp.Y > p_blocks_max.Z)
2999 Get some ground level info
3011 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
3013 s16 cn_max = -32768;
3014 for(s16 i=0; i<4; i++)
3021 s16 cn_slope = cn_max - cn_min;
3024 Generate this part of the heightmap mesh
3028 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
3030 else if(cn_slope <= MAP_BLOCKSIZE)
3035 if(material != material_in_use || buf == NULL)
3037 // Try to get a meshbuffer associated with the material
3038 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
3039 (g_mesh_materials[material]);
3040 // If not found, create one
3043 // This is a "Standard MeshBuffer",
3044 // it's a typedeffed CMeshBuffer<video::S3DVertex>
3045 buf = new scene::SMeshBuffer();
3048 buf->Material = g_mesh_materials[material];
3050 //buf->setHardwareMappingHint(scene::EHM_STATIC);
3052 mesh_new->addMeshBuffer(buf);
3056 material_in_use = material;
3059 // Sector side width in floating-point units
3060 f32 sd = BS * MAP_BLOCKSIZE;
3061 // Sector position in global floating-point units
3062 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
3064 //video::SColor c(255,255,255,255);
3066 video::SColor c(255,cc,cc,cc);
3068 video::S3DVertex vertices[4] =
3070 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
3071 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
3072 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3073 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3075 u16 indices[] = {0,1,2,2,3,0};
3077 buf->append(vertices, 4, indices, 6);
3081 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3089 scene::SMesh *mesh_old = mesh;
3096 mesh_mutex.Unlock();
3098 if(mesh_old != NULL)
3100 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3102 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3103 (g_materials[MATERIAL_GRASS]);
3105 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3112 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3117 void ClientMap::PrintInfo(std::ostream &out)
3127 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3132 MapVoxelManipulator::~MapVoxelManipulator()
3134 dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3139 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3141 TimeTaker timer1("emerge", g_device, &emerge_time);
3143 // Units of these are MapBlocks
3144 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3145 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3147 VoxelArea block_area_nodes
3148 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3150 addArea(block_area_nodes);
3152 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3153 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3154 for(s32 x=p_min.X; x<=p_max.X; x++)
3157 core::map<v3s16, bool>::Node *n;
3158 n = m_loaded_blocks.find(p);
3162 bool block_data_inexistent = false;
3165 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3167 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3168 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3171 dstream<<std::endl;*/
3173 MapBlock *block = m_map->getBlockNoCreate(p);
3174 if(block->isDummy())
3175 block_data_inexistent = true;
3177 block->copyTo(*this);
3179 catch(InvalidPositionException &e)
3181 block_data_inexistent = true;
3184 if(block_data_inexistent)
3186 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3187 // Fill with VOXELFLAG_INEXISTENT
3188 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3189 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3191 s32 i = m_area.index(a.MinEdge.X,y,z);
3192 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3196 m_loaded_blocks.insert(p, true);
3199 //dstream<<"emerge done"<<std::endl;
3204 void MapVoxelManipulator::emerge(VoxelArea a)
3206 TimeTaker timer1("emerge", g_device, &emerge_time);
3208 v3s16 size = a.getExtent();
3210 VoxelArea padded = a;
3211 padded.pad(m_area.getExtent() / 4);
3214 for(s16 z=0; z<size.Z; z++)
3215 for(s16 y=0; y<size.Y; y++)
3216 for(s16 x=0; x<size.X; x++)
3219 s32 i = m_area.index(a.MinEdge + p);
3220 // Don't touch nodes that have already been loaded
3221 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3225 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3226 MapNode n = m_map->getNode(a.MinEdge + p);
3230 catch(InvalidPositionException &e)
3232 m_flags[i] = VOXELFLAG_INEXISTENT;
3240 TODO: Add an option to only update eg. water and air nodes.
3241 This will make it interfere less with important stuff if
3244 void MapVoxelManipulator::blitBack
3245 (core::map<v3s16, MapBlock*> & modified_blocks)
3247 if(m_area.getExtent() == v3s16(0,0,0))
3250 TimeTaker timer1("blitBack", g_device);
3253 Initialize block cache
3255 v3s16 blockpos_last;
3256 MapBlock *block = NULL;
3257 bool block_checked_in_modified = false;
3259 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3260 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3261 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3265 u8 f = m_flags[m_area.index(p)];
3266 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3269 MapNode &n = m_data[m_area.index(p)];
3271 v3s16 blockpos = getNodeBlockPos(p);
3276 if(block == NULL || blockpos != blockpos_last){
3277 block = m_map->getBlockNoCreate(blockpos);
3278 blockpos_last = blockpos;
3279 block_checked_in_modified = false;
3282 // Calculate relative position in block
3283 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3285 // Don't continue if nothing has changed here
3286 if(block->getNode(relpos) == n)
3289 //m_map->setNode(m_area.MinEdge + p, n);
3290 block->setNode(relpos, n);
3293 Make sure block is in modified_blocks
3295 if(block_checked_in_modified == false)
3297 modified_blocks[blockpos] = block;
3298 block_checked_in_modified = true;
3301 catch(InvalidPositionException &e)