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