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(1.0, 0.2, corners);
1508 //hm->generateContinued(2.0, 0.2, corners);
1518 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1519 sector->setObjects(objects);
1521 v2s16 mhm_p = p2d * hm_split;
1523 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1524 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1525 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1526 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1529 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1530 float avgslope = 0.0;
1531 avgslope += fabs(avgheight - corners[0]);
1532 avgslope += fabs(avgheight - corners[1]);
1533 avgslope += fabs(avgheight - corners[2]);
1534 avgslope += fabs(avgheight - corners[3]);
1536 avgslope /= MAP_BLOCKSIZE;
1537 //dstream<<"avgslope="<<avgslope<<std::endl;
1539 float pitness = 0.0;
1541 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1544 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1547 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1550 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1554 pitness /= MAP_BLOCKSIZE;
1555 //dstream<<"pitness="<<pitness<<std::endl;
1558 Plant some trees if there is not much slope
1561 // Avgslope is the derivative of a hill
1562 float t = avgslope * avgslope;
1563 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1566 tree_max = a / (t/0.03);
1569 u32 count = (rand()%(tree_max+1));
1570 //u32 count = tree_max;
1571 for(u32 i=0; i<count; i++)
1573 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1574 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1575 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1578 objects->insert(v3s16(x, y, z),
1579 SECTOR_OBJECT_TREE_1);
1583 Plant some bushes if sector is pit-like
1586 // Pitness usually goes at around -0.5...0.5
1588 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1590 bush_max = (pitness*a*4);
1593 u32 count = (rand()%(bush_max+1));
1594 for(u32 i=0; i<count; i++)
1596 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1597 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1598 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1601 objects->insert(v3s16(x, y, z),
1602 SECTOR_OBJECT_BUSH_1);
1606 Add ravine (randomly)
1608 if(m_params.ravines_amount != 0)
1610 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1613 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1614 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1617 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1618 objects->insert(v3s16(x, y, z),
1619 SECTOR_OBJECT_RAVINE);
1626 JMutexAutoLock lock(m_sector_mutex);
1627 m_sectors.insert(p2d, sector);
1632 MapBlock * ServerMap::emergeBlock(
1634 bool only_from_disk,
1635 core::map<v3s16, MapBlock*> &changed_blocks,
1636 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1639 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1641 p.X, p.Y, p.Z, only_from_disk);
1643 /*dstream<<"ServerMap::emergeBlock(): "
1644 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1645 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1646 v2s16 p2d(p.X, p.Z);
1649 This will create or load a sector if not found in memory.
1650 If block exists on disk, it will be loaded.
1652 NOTE: On old save formats, this will be slow, as it generates
1653 lighting on blocks for them.
1655 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1656 assert(sector->getId() == MAPSECTOR_SERVER);
1658 // Try to get a block from the sector
1659 MapBlock *block = NULL;
1660 bool not_on_disk = false;
1662 block = sector->getBlockNoCreate(block_y);
1663 if(block->isDummy() == true)
1668 catch(InvalidPositionException &e)
1674 If block was not found on disk and not going to generate a
1675 new one, make sure there is a dummy block in place.
1677 if(not_on_disk && only_from_disk)
1681 // Create dummy block
1682 block = new MapBlock(this, p, true);
1684 // Add block to sector
1685 sector->insertBlock(block);
1691 //dstream<<"Not found on disk, generating."<<std::endl;
1694 Do not generate over-limit
1696 if(blockpos_over_limit(p))
1697 throw InvalidPositionException("emergeBlock(): pos. over limit");
1702 Go on generating the block.
1704 TODO: If a dungeon gets generated so that it's side gets
1705 revealed to the outside air, the lighting should be
1710 If block doesn't exist, create one.
1711 If it exists, it is a dummy. In that case unDummify() it.
1715 block = sector->createBlankBlockNoInsert(block_y);
1719 // Remove the block so that nobody can get a half-generated one.
1720 sector->removeBlock(block);
1721 // Allocate the block to be a proper one.
1725 // Randomize a bit. This makes dungeons.
1726 /*bool low_block_is_empty = false;
1728 low_block_is_empty = true;*/
1731 //const s32 ued = 8;
1732 bool underground_emptiness[ued*ued*ued];
1733 for(s32 i=0; i<ued*ued*ued; i++)
1735 underground_emptiness[i] = ((rand() % 5) == 0);
1740 This is a messy hack to sort the emptiness a bit
1742 for(s32 j=0; j<2; j++)
1743 for(s32 y0=0; y0<ued; y0++)
1744 for(s32 z0=0; z0<ued; z0++)
1745 for(s32 x0=0; x0<ued; x0++)
1748 bool &e0 = underground_emptiness[
1749 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1750 +ued*(y0*ued/MAP_BLOCKSIZE)
1751 +(x0*ued/MAP_BLOCKSIZE)];
1754 v3s16(0,0,1), // back
1755 v3s16(1,0,0), // right
1756 v3s16(0,0,-1), // front
1757 v3s16(-1,0,0), // left
1758 /*v3s16(0,1,0), // top
1759 v3s16(0,-1,0), // bottom*/
1761 for(s32 i=0; i<4; i++)
1763 v3s16 p1 = p0 + dirs[i];
1764 if(isInArea(p1, ued) == false)
1766 bool &e1 = underground_emptiness[
1767 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1768 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1769 +(p1.X*ued/MAP_BLOCKSIZE)];
1774 v3s16(0,1,0), // top
1775 v3s16(0,-1,0), // bottom
1776 /*v3s16(0,0,1), // back
1777 v3s16(1,0,0), // right
1778 v3s16(0,0,-1), // front
1779 v3s16(-1,0,0), // left*/
1781 for(s32 i=0; i<2; i++)
1783 v3s16 p2 = p1 + dirs[i];
1786 if(isInArea(p2, ued) == false)
1788 bool &e2 = underground_emptiness[
1789 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1790 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1791 +(p2.X*ued/MAP_BLOCKSIZE)];
1806 // This is the basic material of what the visible flat ground
1808 u8 material = CONTENT_GRASS;
1810 u8 water_material = CONTENT_WATER;
1811 if(g_settings.getBool("endless_water"))
1812 water_material = CONTENT_OCEAN;
1814 s32 lowest_ground_y = 32767;
1815 s32 highest_ground_y = -32768;
1818 //sector->printHeightmaps();
1820 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1821 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1823 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1825 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1826 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1827 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1829 dstream<<"WARNING: Surface height not found in sector "
1830 "for block that is being emerged"<<std::endl;
1834 s16 surface_y = surface_y_f;
1835 //avg_ground_y += surface_y;
1836 if(surface_y < lowest_ground_y)
1837 lowest_ground_y = surface_y;
1838 if(surface_y > highest_ground_y)
1839 highest_ground_y = surface_y;
1841 s32 surface_depth = 0;
1843 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1845 //float min_slope = 0.45;
1846 //float max_slope = 0.85;
1847 float min_slope = 0.70;
1848 float max_slope = 1.20;
1849 float min_slope_depth = 4.0;
1850 //float min_slope_depth = 5.0;
1851 float max_slope_depth = 0;
1852 if(slope < min_slope)
1853 surface_depth = min_slope_depth;
1854 else if(slope > max_slope)
1855 surface_depth = max_slope_depth;
1857 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1859 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1861 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1866 NOTE: If there are some man-made structures above the
1867 newly created block, they won't be taken into account.
1869 if(real_y > surface_y)
1870 n.setLight(LIGHT_SUN);
1876 // If node is very low
1877 /*if(real_y <= surface_y - 7)
1880 if(underground_emptiness[
1881 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1882 +ued*(y0*ued/MAP_BLOCKSIZE)
1883 +(x0*ued/MAP_BLOCKSIZE)])
1889 n.d = CONTENT_STONE;
1892 // If node is under surface level
1893 else if(real_y <= surface_y - surface_depth)
1894 n.d = CONTENT_STONE;
1896 if(real_y <= surface_y - surface_depth)
1899 if(underground_emptiness[
1900 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1901 +ued*(y0*ued/MAP_BLOCKSIZE)
1902 +(x0*ued/MAP_BLOCKSIZE)])
1908 n.d = CONTENT_STONE;
1911 // If node is at or under heightmap y
1912 else if(real_y <= surface_y)
1914 // If under water level, it's mud
1915 if(real_y < WATER_LEVEL)
1917 // Only the topmost node is grass
1918 else if(real_y <= surface_y - 1)
1920 // Else it's the main material
1924 // If node is over heightmap y
1926 // If under water level, it's water
1927 if(real_y < WATER_LEVEL)
1929 n.d = water_material;
1930 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1936 block->setNode(v3s16(x0,y0,z0), n);
1941 Calculate is_underground
1943 // Probably underground if the highest part of block is under lowest
1945 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1946 block->setIsUnderground(is_underground);
1949 Force lighting update if some part of block is underground
1950 This is needed because of caves.
1953 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1954 if(some_part_underground)
1955 //if(is_underground)
1957 lighting_invalidated_blocks[block->getPos()] = block;
1964 //if(is_underground)
1965 if(some_part_underground)
1967 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1968 for(s16 i=0; i<underground_level*3; i++)
1973 (rand()%(MAP_BLOCKSIZE-2))+1,
1974 (rand()%(MAP_BLOCKSIZE-2))+1,
1975 (rand()%(MAP_BLOCKSIZE-2))+1
1981 //if(is_ground_content(block->getNode(cp).d))
1982 if(block->getNode(cp).d == CONTENT_STONE)
1984 block->setNode(cp, n);
1986 for(u16 i=0; i<26; i++)
1988 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1989 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1991 block->setNode(cp+g_26dirs[i], n);
1998 Create a few rats in empty blocks underground
2002 //for(u16 i=0; i<2; i++)
2005 (rand()%(MAP_BLOCKSIZE-2))+1,
2006 (rand()%(MAP_BLOCKSIZE-2))+1,
2007 (rand()%(MAP_BLOCKSIZE-2))+1
2010 // Check that the place is empty
2011 //if(!is_ground_content(block->getNode(cp).d))
2014 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2015 block->addObject(obj);
2021 Add block to sector.
2023 sector->insertBlock(block);
2026 Do some interpolation for dungeons
2031 TimeTaker timer("interpolation", g_device);
2033 MapVoxelManipulator vmanip(this);
2035 v3s16 relpos = block->getPosRelative();
2037 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
2038 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
2039 /*vmanip.interpolate(VoxelArea(relpos,
2040 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
2042 core::map<v3s16, MapBlock*> modified_blocks;
2043 vmanip.blitBack(modified_blocks);
2044 dstream<<"blitBack modified "<<modified_blocks.size()
2045 <<" blocks"<<std::endl;
2047 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
2048 for(core::map<v3s16, MapBlock*>::Iterator
2049 i = modified_blocks.getIterator();
2050 i.atEnd() == false; i++)
2052 MapBlock *block = i.getNode()->getValue();
2054 changed_blocks.insert(block->getPos(), block);
2055 //lighting_invalidated_blocks.insert(block->getPos(), block);
2065 // An y-wise container of changed blocks
2066 core::map<s16, MapBlock*> changed_blocks_sector;
2069 Check if any sector's objects can be placed now.
2072 core::map<v3s16, u8> *objects = sector->getObjects();
2073 core::list<v3s16> objects_to_remove;
2074 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2075 i.atEnd() == false; i++)
2077 v3s16 p = i.getNode()->getKey();
2079 u8 d = i.getNode()->getValue();
2081 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2086 if(d == SECTOR_OBJECT_TEST)
2088 if(sector->isValidArea(p + v3s16(0,0,0),
2089 p + v3s16(0,0,0), &changed_blocks_sector))
2092 n.d = CONTENT_LIGHT;
2093 sector->setNode(p, n);
2094 objects_to_remove.push_back(p);
2097 else if(d == SECTOR_OBJECT_TREE_1)
2099 v3s16 p_min = p + v3s16(-1,0,-1);
2100 v3s16 p_max = p + v3s16(1,4,1);
2101 if(sector->isValidArea(p_min, p_max,
2102 &changed_blocks_sector))
2106 sector->setNode(p+v3s16(0,0,0), n);
2107 sector->setNode(p+v3s16(0,1,0), n);
2108 sector->setNode(p+v3s16(0,2,0), n);
2109 sector->setNode(p+v3s16(0,3,0), n);
2111 n.d = CONTENT_LEAVES;
2113 sector->setNode(p+v3s16(0,4,0), n);
2115 sector->setNode(p+v3s16(-1,4,0), n);
2116 sector->setNode(p+v3s16(1,4,0), n);
2117 sector->setNode(p+v3s16(0,4,-1), n);
2118 sector->setNode(p+v3s16(0,4,1), n);
2119 sector->setNode(p+v3s16(1,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);
2124 sector->setNode(p+v3s16(-1,3,0), n);
2125 sector->setNode(p+v3s16(1,3,0), n);
2126 sector->setNode(p+v3s16(0,3,-1), n);
2127 sector->setNode(p+v3s16(0,3,1), n);
2128 sector->setNode(p+v3s16(1,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);
2133 objects_to_remove.push_back(p);
2135 // Lighting has to be recalculated for this one.
2136 sector->getBlocksInArea(p_min, p_max,
2137 lighting_invalidated_blocks);
2140 else if(d == SECTOR_OBJECT_BUSH_1)
2142 if(sector->isValidArea(p + v3s16(0,0,0),
2143 p + v3s16(0,0,0), &changed_blocks_sector))
2146 n.d = CONTENT_LEAVES;
2147 sector->setNode(p+v3s16(0,0,0), n);
2149 objects_to_remove.push_back(p);
2152 else if(d == SECTOR_OBJECT_RAVINE)
2155 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2156 v3s16 p_max = p + v3s16(6,6,6);
2157 if(sector->isValidArea(p_min, p_max,
2158 &changed_blocks_sector))
2161 n.d = CONTENT_STONE;
2164 s16 depth = maxdepth + (rand()%10);
2166 s16 minz = -6 - (-2);
2168 for(s16 x=-6; x<=6; x++)
2170 z += -1 + (rand()%3);
2175 for(s16 y=depth+(rand()%2); y<=6; y++)
2177 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2180 v3s16 p2 = p + v3s16(x,y,z-2);
2181 if(is_ground_content(sector->getNode(p2).d))
2182 sector->setNode(p2, n);
2185 v3s16 p2 = p + v3s16(x,y,z-1);
2186 if(is_ground_content(sector->getNode(p2).d))
2187 sector->setNode(p2, n2);
2190 v3s16 p2 = p + v3s16(x,y,z+0);
2191 if(is_ground_content(sector->getNode(p2).d))
2192 sector->setNode(p2, n2);
2195 v3s16 p2 = p + v3s16(x,y,z+1);
2196 if(is_ground_content(sector->getNode(p2).d))
2197 sector->setNode(p2, n);
2200 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2201 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2205 objects_to_remove.push_back(p);
2207 // Lighting has to be recalculated for this one.
2208 sector->getBlocksInArea(p_min, p_max,
2209 lighting_invalidated_blocks);
2214 dstream<<"ServerMap::emergeBlock(): "
2215 "Invalid heightmap object"
2220 catch(InvalidPositionException &e)
2222 dstream<<"WARNING: "<<__FUNCTION_NAME
2223 <<": while inserting object "<<(int)d
2224 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2225 <<" InvalidPositionException.what()="
2226 <<e.what()<<std::endl;
2227 // This is not too fatal and seems to happen sometimes.
2232 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2233 i != objects_to_remove.end(); i++)
2235 objects->remove(*i);
2238 for(core::map<s16, MapBlock*>::Iterator
2239 i = changed_blocks_sector.getIterator();
2240 i.atEnd() == false; i++)
2242 MapBlock *block = i.getNode()->getValue();
2244 changed_blocks.insert(block->getPos(), block);
2250 void ServerMap::createDir(std::string path)
2252 if(fs::CreateDir(path) == false)
2254 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2255 <<"\""<<path<<"\""<<std::endl;
2256 throw BaseException("ServerMap failed to create directory");
2260 std::string ServerMap::getSectorSubDir(v2s16 pos)
2263 snprintf(cc, 9, "%.4x%.4x",
2264 (unsigned int)pos.X&0xffff,
2265 (unsigned int)pos.Y&0xffff);
2267 return std::string(cc);
2270 std::string ServerMap::getSectorDir(v2s16 pos)
2272 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2275 v2s16 ServerMap::getSectorPos(std::string dirname)
2277 if(dirname.size() != 8)
2278 throw InvalidFilenameException("Invalid sector directory name");
2280 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2282 throw InvalidFilenameException("Invalid sector directory name");
2283 v2s16 pos((s16)x, (s16)y);
2287 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2289 v2s16 p2d = getSectorPos(sectordir);
2291 if(blockfile.size() != 4){
2292 throw InvalidFilenameException("Invalid block filename");
2295 int r = sscanf(blockfile.c_str(), "%4x", &y);
2297 throw InvalidFilenameException("Invalid block filename");
2298 return v3s16(p2d.X, y, p2d.Y);
2302 #define ENABLE_SECTOR_SAVING 1
2303 #define ENABLE_SECTOR_LOADING 1
2304 #define ENABLE_BLOCK_SAVING 1
2305 #define ENABLE_BLOCK_LOADING 1
2307 void ServerMap::save(bool only_changed)
2309 DSTACK(__FUNCTION_NAME);
2310 if(m_map_saving_enabled == false)
2312 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2316 if(only_changed == false)
2317 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2320 saveMasterHeightmap();
2322 u32 sector_meta_count = 0;
2323 u32 block_count = 0;
2326 JMutexAutoLock lock(m_sector_mutex);
2328 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2329 for(; i.atEnd() == false; i++)
2331 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2332 assert(sector->getId() == MAPSECTOR_SERVER);
2334 if(ENABLE_SECTOR_SAVING)
2336 if(sector->differs_from_disk || only_changed == false)
2338 saveSectorMeta(sector);
2339 sector_meta_count++;
2342 if(ENABLE_BLOCK_SAVING)
2344 core::list<MapBlock*> blocks;
2345 sector->getBlocks(blocks);
2346 core::list<MapBlock*>::Iterator j;
2347 for(j=blocks.begin(); j!=blocks.end(); j++)
2349 MapBlock *block = *j;
2350 if(block->getChangedFlag() || only_changed == false)
2361 u32 deleted_count = 0;
2362 deleted_count = deleteUnusedSectors
2363 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2366 Only print if something happened or saved whole map
2368 if(only_changed == false || sector_meta_count != 0
2369 || block_count != 0 || deleted_count != 0)
2371 dstream<<DTIME<<"ServerMap: Written: "
2372 <<sector_meta_count<<" sector metadata files, "
2373 <<block_count<<" block files, "
2374 <<deleted_count<<" sectors unloaded from memory."
2379 void ServerMap::loadAll()
2381 DSTACK(__FUNCTION_NAME);
2382 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2384 loadMasterHeightmap();
2386 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2388 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2390 JMutexAutoLock lock(m_sector_mutex);
2393 s32 printed_counter = -100000;
2394 s32 count = list.size();
2396 std::vector<fs::DirListNode>::iterator i;
2397 for(i=list.begin(); i!=list.end(); i++)
2399 if(counter > printed_counter + 10)
2401 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2402 printed_counter = counter;
2406 MapSector *sector = NULL;
2408 // We want directories
2412 sector = loadSectorMeta(i->name);
2414 catch(InvalidFilenameException &e)
2416 // This catches unknown crap in directory
2419 if(ENABLE_BLOCK_LOADING)
2421 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2422 (m_savedir+"/sectors/"+i->name);
2423 std::vector<fs::DirListNode>::iterator i2;
2424 for(i2=list2.begin(); i2!=list2.end(); i2++)
2430 loadBlock(i->name, i2->name, sector);
2432 catch(InvalidFilenameException &e)
2434 // This catches unknown crap in directory
2439 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2442 void ServerMap::saveMasterHeightmap()
2444 DSTACK(__FUNCTION_NAME);
2445 createDir(m_savedir);
2447 std::string fullpath = m_savedir + "/master_heightmap";
2448 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2449 if(o.good() == false)
2450 throw FileNotGoodException("Cannot open master heightmap");
2452 // Format used for writing
2453 u8 version = SER_FMT_VER_HIGHEST;
2456 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2458 [0] u8 serialization version
2459 [1] X master heightmap
2461 u32 fullsize = 1 + hmdata.getSize();
2462 SharedBuffer<u8> data(fullsize);
2465 memcpy(&data[1], *hmdata, hmdata.getSize());
2467 o.write((const char*)*data, fullsize);
2470 m_heightmap->serialize(o, version);
2473 void ServerMap::loadMasterHeightmap()
2475 DSTACK(__FUNCTION_NAME);
2476 std::string fullpath = m_savedir + "/master_heightmap";
2477 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2478 if(is.good() == false)
2479 throw FileNotGoodException("Cannot open master heightmap");
2481 if(m_heightmap != NULL)
2484 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2487 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2489 DSTACK(__FUNCTION_NAME);
2490 // Format used for writing
2491 u8 version = SER_FMT_VER_HIGHEST;
2493 v2s16 pos = sector->getPos();
2494 createDir(m_savedir);
2495 createDir(m_savedir+"/sectors");
2496 std::string dir = getSectorDir(pos);
2499 std::string fullpath = dir + "/heightmap";
2500 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2501 if(o.good() == false)
2502 throw FileNotGoodException("Cannot open master heightmap");
2504 sector->serialize(o, version);
2506 sector->differs_from_disk = false;
2509 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2511 DSTACK(__FUNCTION_NAME);
2513 v2s16 p2d = getSectorPos(dirname);
2514 std::string dir = m_savedir + "/sectors/" + dirname;
2516 std::string fullpath = dir + "/heightmap";
2517 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2518 if(is.good() == false)
2519 throw FileNotGoodException("Cannot open sector heightmap");
2521 ServerMapSector *sector = ServerMapSector::deSerialize
2522 (is, this, p2d, &m_hwrapper, m_sectors);
2524 sector->differs_from_disk = false;
2529 bool ServerMap::loadSectorFull(v2s16 p2d)
2531 DSTACK(__FUNCTION_NAME);
2532 std::string sectorsubdir = getSectorSubDir(p2d);
2534 MapSector *sector = NULL;
2536 JMutexAutoLock lock(m_sector_mutex);
2539 sector = loadSectorMeta(sectorsubdir);
2541 catch(InvalidFilenameException &e)
2545 catch(FileNotGoodException &e)
2549 catch(std::exception &e)
2554 if(ENABLE_BLOCK_LOADING)
2556 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2557 (m_savedir+"/sectors/"+sectorsubdir);
2558 std::vector<fs::DirListNode>::iterator i2;
2559 for(i2=list2.begin(); i2!=list2.end(); i2++)
2565 loadBlock(sectorsubdir, i2->name, sector);
2567 catch(InvalidFilenameException &e)
2569 // This catches unknown crap in directory
2577 bool ServerMap::deFlushSector(v2s16 p2d)
2579 DSTACK(__FUNCTION_NAME);
2580 // See if it already exists in memory
2582 MapSector *sector = getSectorNoGenerate(p2d);
2585 catch(InvalidPositionException &e)
2588 Try to load the sector from disk.
2590 if(loadSectorFull(p2d) == true)
2599 void ServerMap::saveBlock(MapBlock *block)
2601 DSTACK(__FUNCTION_NAME);
2603 Dummy blocks are not written
2605 if(block->isDummy())
2607 /*v3s16 p = block->getPos();
2608 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2609 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2613 // Format used for writing
2614 u8 version = SER_FMT_VER_HIGHEST;
2616 v3s16 p3d = block->getPos();
2617 v2s16 p2d(p3d.X, p3d.Z);
2618 createDir(m_savedir);
2619 createDir(m_savedir+"/sectors");
2620 std::string dir = getSectorDir(p2d);
2623 // Block file is map/sectors/xxxxxxxx/xxxx
2625 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2626 std::string fullpath = dir + "/" + cc;
2627 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2628 if(o.good() == false)
2629 throw FileNotGoodException("Cannot open block data");
2632 [0] u8 serialization version
2635 o.write((char*)&version, 1);
2637 block->serialize(o, version);
2640 Versions up from 9 have block objects.
2644 block->serializeObjects(o, version);
2647 // We just wrote it to the disk
2648 block->resetChangedFlag();
2651 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2653 DSTACK(__FUNCTION_NAME);
2657 // Block file is map/sectors/xxxxxxxx/xxxx
2658 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2659 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2660 if(is.good() == false)
2661 throw FileNotGoodException("Cannot open block file");
2663 v3s16 p3d = getBlockPos(sectordir, blockfile);
2664 v2s16 p2d(p3d.X, p3d.Z);
2666 assert(sector->getPos() == p2d);
2668 u8 version = SER_FMT_VER_INVALID;
2669 is.read((char*)&version, 1);
2671 /*u32 block_size = MapBlock::serializedLength(version);
2672 SharedBuffer<u8> data(block_size);
2673 is.read((char*)*data, block_size);*/
2675 // This will always return a sector because we're the server
2676 //MapSector *sector = emergeSector(p2d);
2678 MapBlock *block = NULL;
2679 bool created_new = false;
2681 block = sector->getBlockNoCreate(p3d.Y);
2683 catch(InvalidPositionException &e)
2685 block = sector->createBlankBlockNoInsert(p3d.Y);
2689 // deserialize block data
2690 block->deSerialize(is, version);
2693 Versions up from 9 have block objects.
2697 block->updateObjects(is, version, NULL);
2701 sector->insertBlock(block);
2704 Convert old formats to new and save
2707 // Save old format blocks in new format
2708 if(version < SER_FMT_VER_HIGHEST)
2713 // We just loaded it from the disk, so it's up-to-date.
2714 block->resetChangedFlag();
2717 catch(SerializationError &e)
2719 dstream<<"WARNING: Invalid block data on disk "
2720 "(SerializationError). Ignoring."
2725 // Gets from master heightmap
2726 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2728 assert(m_heightmap != NULL);
2736 corners[0] = m_heightmap->getGroundHeight
2737 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2738 corners[1] = m_heightmap->getGroundHeight
2739 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2740 corners[2] = m_heightmap->getGroundHeight
2741 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2742 corners[3] = m_heightmap->getGroundHeight
2743 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2746 void ServerMap::PrintInfo(std::ostream &out)
2755 ClientMap::ClientMap(
2757 video::SMaterial *materials,
2758 scene::ISceneNode* parent,
2759 scene::ISceneManager* mgr,
2763 scene::ISceneNode(parent, mgr, id),
2765 m_materials(materials),
2768 /*m_box = core::aabbox3d<f32>(0,0,0,
2769 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2770 /*m_box = core::aabbox3d<f32>(0,0,0,
2771 map->getSizeNodes().X * BS,
2772 map->getSizeNodes().Y * BS,
2773 map->getSizeNodes().Z * BS);*/
2774 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2775 BS*1000000,BS*1000000,BS*1000000);
2780 ClientMap::~ClientMap()
2782 JMutexAutoLock lock(mesh_mutex);
2791 MapSector * ClientMap::emergeSector(v2s16 p2d)
2793 DSTACK(__FUNCTION_NAME);
2794 // Check that it doesn't exist already
2796 return getSectorNoGenerate(p2d);
2798 catch(InvalidPositionException &e)
2802 // Create a sector with no heightmaps
2803 ClientMapSector *sector = new ClientMapSector(this, p2d);
2806 JMutexAutoLock lock(m_sector_mutex);
2807 m_sectors.insert(p2d, sector);
2813 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2815 DSTACK(__FUNCTION_NAME);
2816 ClientMapSector *sector = NULL;
2818 JMutexAutoLock lock(m_sector_mutex);
2820 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2824 sector = (ClientMapSector*)n->getValue();
2825 assert(sector->getId() == MAPSECTOR_CLIENT);
2829 sector = new ClientMapSector(this, p2d);
2831 JMutexAutoLock lock(m_sector_mutex);
2832 m_sectors.insert(p2d, sector);
2836 sector->deSerialize(is);
2839 void ClientMap::renderMap(video::IVideoDriver* driver,
2840 video::SMaterial *materials, s32 pass)
2842 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2843 DSTACK(__FUNCTION_NAME);
2845 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2848 Get time for measuring timeout.
2850 Measuring time is very useful for long delays when the
2851 machine is swapping a lot.
2853 int time1 = time(0);
2856 Collect all blocks that are in the view range
2858 Should not optimize more here as we want to auto-update
2859 all changed nodes in viewing range at the next step.
2862 s16 viewing_range_nodes;
2863 bool viewing_range_all;
2865 JMutexAutoLock lock(g_range_mutex);
2866 viewing_range_nodes = g_viewing_range_nodes;
2867 viewing_range_all = g_viewing_range_all;
2870 m_camera_mutex.Lock();
2871 v3f camera_position = m_camera_position;
2872 v3f camera_direction = m_camera_direction;
2873 m_camera_mutex.Unlock();
2876 Get all blocks and draw all visible ones
2879 v3s16 cam_pos_nodes(
2880 camera_position.X / BS,
2881 camera_position.Y / BS,
2882 camera_position.Z / BS);
2884 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2886 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2887 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2889 // Take a fair amount as we will be dropping more out later
2891 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2892 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2893 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2895 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2896 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2897 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2899 u32 vertex_count = 0;
2901 core::map<v2s16, MapSector*>::Iterator si;
2903 //NOTE: The sectors map should be locked but we're not doing it
2904 // because it'd cause too much delays
2906 si = m_sectors.getIterator();
2907 for(; si.atEnd() == false; si++)
2910 static int timecheck_counter = 0;
2911 timecheck_counter++;
2912 if(timecheck_counter > 50)
2914 int time2 = time(0);
2915 if(time2 > time1 + 4)
2917 dstream<<"ClientMap::renderMap(): "
2918 "Rendering takes ages, returning."
2925 MapSector *sector = si.getNode()->getValue();
2926 v2s16 sp = sector->getPos();
2928 if(viewing_range_all == false)
2930 if(sp.X < p_blocks_min.X
2931 || sp.X > p_blocks_max.X
2932 || sp.Y < p_blocks_min.Z
2933 || sp.Y > p_blocks_max.Z)
2937 core::list< MapBlock * > sectorblocks;
2938 sector->getBlocks(sectorblocks);
2944 core::list< MapBlock * >::Iterator i;
2945 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2947 MapBlock *block = *i;
2950 Compare block position to camera position, skip
2951 if not seen on display
2954 v3s16 blockpos_nodes = block->getPosRelative();
2956 // Block center position
2958 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2959 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2960 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2963 // Block position relative to camera
2964 v3f blockpos_relative = blockpos - camera_position;
2966 // Distance in camera direction (+=front, -=back)
2967 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2970 f32 d = blockpos_relative.getLength();
2972 if(viewing_range_all == false)
2974 // If block is far away, don't draw it
2975 if(d > viewing_range_nodes * BS)
2979 // Maximum radius of a block
2980 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2982 // If block is (nearly) touching the camera, don't
2983 // bother validating further (that is, render it anyway)
2984 if(d > block_max_radius * 1.5)
2986 // Cosine of the angle between the camera direction
2987 // and the block direction (camera_direction is an unit vector)
2988 f32 cosangle = dforward / d;
2990 // Compensate for the size of the block
2991 // (as the block has to be shown even if it's a bit off FOV)
2992 // This is an estimate.
2993 cosangle += block_max_radius / dforward;
2995 // If block is not in the field of view, skip it
2996 //if(cosangle < cos(FOV_ANGLE/2))
2997 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3002 Draw the faces of the block
3006 JMutexAutoLock lock(block->mesh_mutex);
3008 // Cancel if block has no mesh
3009 if(block->mesh == NULL)
3012 u32 c = block->mesh->getMeshBufferCount();
3014 for(u32 i=0; i<c; i++)
3016 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
3017 const video::SMaterial& material = buf->getMaterial();
3018 video::IMaterialRenderer* rnd =
3019 driver->getMaterialRenderer(material.MaterialType);
3020 bool transparent = (rnd && rnd->isTransparent());
3021 // Render transparent on transparent pass and likewise.
3022 if(transparent == is_transparent_pass)
3024 driver->setMaterial(buf->getMaterial());
3025 driver->drawMeshBuffer(buf);
3026 vertex_count += buf->getVertexCount();
3030 } // foreach sectorblocks
3033 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3034 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3037 void ClientMap::updateMesh()
3040 DSTACK(__FUNCTION_NAME);
3043 Check what sectors don't draw anything useful at ground level
3044 and create a mesh of the rough heightmap at those positions.
3047 m_camera_mutex.Lock();
3048 v3f camera_position = m_camera_position;
3049 v3f camera_direction = m_camera_direction;
3050 m_camera_mutex.Unlock();
3052 v3s16 cam_pos_nodes(
3053 camera_position.X / BS,
3054 camera_position.Y / BS,
3055 camera_position.Z / BS);
3057 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
3059 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3060 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3062 // Take a fair amount as we will be dropping more out later
3064 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3065 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3066 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3068 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3069 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3070 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3076 scene::SMesh *mesh_new = new scene::SMesh();
3077 //scene::IMeshBuffer *buf = NULL;
3078 scene::SMeshBuffer *buf = NULL;
3080 u8 material_in_use = 0;
3083 Loop through sectors
3086 for(core::map<v2s16, MapSector*>::Iterator
3087 si = m_sectors.getIterator();
3088 si.atEnd() == false; si++)
3090 MapSector *sector = si.getNode()->getValue();
3092 if(sector->getId() != MAPSECTOR_CLIENT)
3094 dstream<<"WARNING: Client has a non-client sector"
3099 ClientMapSector *cs = (ClientMapSector*)sector;
3101 v2s16 sp = sector->getPos();
3103 if(sp.X < p_blocks_min.X
3104 || sp.X > p_blocks_max.X
3105 || sp.Y < p_blocks_min.Z
3106 || sp.Y > p_blocks_max.Z)
3110 Get some ground level info
3122 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
3124 s16 cn_max = -32768;
3125 for(s16 i=0; i<4; i++)
3132 s16 cn_slope = cn_max - cn_min;
3135 Generate this part of the heightmap mesh
3139 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
3141 else if(cn_slope <= MAP_BLOCKSIZE)
3146 if(material != material_in_use || buf == NULL)
3148 // Try to get a meshbuffer associated with the material
3149 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
3150 (g_mesh_materials[material]);
3151 // If not found, create one
3154 // This is a "Standard MeshBuffer",
3155 // it's a typedeffed CMeshBuffer<video::S3DVertex>
3156 buf = new scene::SMeshBuffer();
3159 buf->Material = g_mesh_materials[material];
3161 //buf->setHardwareMappingHint(scene::EHM_STATIC);
3163 mesh_new->addMeshBuffer(buf);
3167 material_in_use = material;
3170 // Sector side width in floating-point units
3171 f32 sd = BS * MAP_BLOCKSIZE;
3172 // Sector position in global floating-point units
3173 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
3175 //video::SColor c(255,255,255,255);
3177 video::SColor c(255,cc,cc,cc);
3179 video::S3DVertex vertices[4] =
3181 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
3182 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
3183 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3184 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3186 u16 indices[] = {0,1,2,2,3,0};
3188 buf->append(vertices, 4, indices, 6);
3192 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3200 scene::SMesh *mesh_old = mesh;
3207 mesh_mutex.Unlock();
3209 if(mesh_old != NULL)
3211 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3213 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3214 (g_materials[CONTENT_GRASS]);
3216 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3223 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3228 void ClientMap::PrintInfo(std::ostream &out)
3238 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3243 MapVoxelManipulator::~MapVoxelManipulator()
3245 dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3250 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3252 TimeTaker timer1("emerge", g_device, &emerge_time);
3254 // Units of these are MapBlocks
3255 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3256 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3258 VoxelArea block_area_nodes
3259 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3261 addArea(block_area_nodes);
3263 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3264 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3265 for(s32 x=p_min.X; x<=p_max.X; x++)
3268 core::map<v3s16, bool>::Node *n;
3269 n = m_loaded_blocks.find(p);
3273 bool block_data_inexistent = false;
3276 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3278 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3279 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3282 dstream<<std::endl;*/
3284 MapBlock *block = m_map->getBlockNoCreate(p);
3285 if(block->isDummy())
3286 block_data_inexistent = true;
3288 block->copyTo(*this);
3290 catch(InvalidPositionException &e)
3292 block_data_inexistent = true;
3295 if(block_data_inexistent)
3297 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3298 // Fill with VOXELFLAG_INEXISTENT
3299 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3300 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3302 s32 i = m_area.index(a.MinEdge.X,y,z);
3303 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3307 m_loaded_blocks.insert(p, true);
3310 //dstream<<"emerge done"<<std::endl;
3315 void MapVoxelManipulator::emerge(VoxelArea a)
3317 TimeTaker timer1("emerge", g_device, &emerge_time);
3319 v3s16 size = a.getExtent();
3321 VoxelArea padded = a;
3322 padded.pad(m_area.getExtent() / 4);
3325 for(s16 z=0; z<size.Z; z++)
3326 for(s16 y=0; y<size.Y; y++)
3327 for(s16 x=0; x<size.X; x++)
3330 s32 i = m_area.index(a.MinEdge + p);
3331 // Don't touch nodes that have already been loaded
3332 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3336 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3337 MapNode n = m_map->getNode(a.MinEdge + p);
3341 catch(InvalidPositionException &e)
3343 m_flags[i] = VOXELFLAG_INEXISTENT;
3351 TODO: Add an option to only update eg. water and air nodes.
3352 This will make it interfere less with important stuff if
3355 void MapVoxelManipulator::blitBack
3356 (core::map<v3s16, MapBlock*> & modified_blocks)
3358 if(m_area.getExtent() == v3s16(0,0,0))
3361 TimeTaker timer1("blitBack", g_device);
3364 Initialize block cache
3366 v3s16 blockpos_last;
3367 MapBlock *block = NULL;
3368 bool block_checked_in_modified = false;
3370 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3371 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3372 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3376 u8 f = m_flags[m_area.index(p)];
3377 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3380 MapNode &n = m_data[m_area.index(p)];
3382 v3s16 blockpos = getNodeBlockPos(p);
3387 if(block == NULL || blockpos != blockpos_last){
3388 block = m_map->getBlockNoCreate(blockpos);
3389 blockpos_last = blockpos;
3390 block_checked_in_modified = false;
3393 // Calculate relative position in block
3394 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3396 // Don't continue if nothing has changed here
3397 if(block->getNode(relpos) == n)
3400 //m_map->setNode(m_area.MinEdge + p, n);
3401 block->setNode(relpos, n);
3404 Make sure block is in modified_blocks
3406 if(block_checked_in_modified == false)
3408 modified_blocks[blockpos] = block;
3409 block_checked_in_modified = true;
3412 catch(InvalidPositionException &e)