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.
22 #include "jmutexautolock.h"
29 MapBlockPointerCache::MapBlockPointerCache(Map *map)
32 m_map->m_blockcachelock.cacheCreated();
34 m_from_cache_count = 0;
38 MapBlockPointerCache::~MapBlockPointerCache()
40 m_map->m_blockcachelock.cacheRemoved();
42 /*dstream<<"MapBlockPointerCache:"
43 <<" from_cache_count="<<m_from_cache_count
44 <<" from_map_count="<<m_from_map_count
48 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
50 core::map<v3s16, MapBlock*>::Node *n = NULL;
60 // Throws InvalidPositionException if not found
61 MapBlock *b = m_map->getBlockNoCreate(p);
70 Map::Map(std::ostream &dout):
72 m_camera_position(0,0,0),
73 m_camera_direction(0,0,1),
78 m_sector_mutex.Init();
79 m_camera_mutex.Init();
80 assert(m_sector_mutex.IsInitialized());
81 assert(m_camera_mutex.IsInitialized());
83 // Get this so that the player can stay on it at first
84 //getSector(v2s16(0,0));
92 /*updater.setRun(false);
93 while(updater.IsRunning())
99 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
100 for(; i.atEnd() == false; i++)
102 MapSector *sector = i.getNode()->getValue();
107 MapSector * Map::getSectorNoGenerate(v2s16 p)
109 JMutexAutoLock lock(m_sector_mutex);
111 if(m_sector_cache != NULL && p == m_sector_cache_p){
112 MapSector * sector = m_sector_cache;
113 // Reset inactivity timer
114 sector->usage_timer = 0.0;
118 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
119 // If sector doesn't exist, throw an exception
122 throw InvalidPositionException();
125 MapSector *sector = n->getValue();
127 // Cache the last result
128 m_sector_cache_p = p;
129 m_sector_cache = sector;
131 //MapSector * ref(sector);
133 // Reset inactivity timer
134 sector->usage_timer = 0.0;
138 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
140 v2s16 p2d(p3d.X, p3d.Z);
141 MapSector * sector = getSectorNoGenerate(p2d);
143 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
148 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
152 v2s16 p2d(p3d.X, p3d.Z);
153 MapSector * sector = getSectorNoGenerate(p2d);
154 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
157 catch(InvalidPositionException &e)
163 f32 Map::getGroundHeight(v2s16 p, bool generate)
166 v2s16 sectorpos = getNodeSectorPos(p);
167 MapSector * sref = getSectorNoGenerate(sectorpos);
168 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
169 f32 y = sref->getGroundHeight(relpos);
172 catch(InvalidPositionException &e)
174 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
178 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
180 /*m_dout<<DTIME<<"Map::setGroundHeight(("
182 <<"), "<<y<<")"<<std::endl;*/
183 v2s16 sectorpos = getNodeSectorPos(p);
184 MapSector * sref = getSectorNoGenerate(sectorpos);
185 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
186 //sref->mutex.Lock();
187 sref->setGroundHeight(relpos, y);
188 //sref->mutex.Unlock();
191 bool Map::isNodeUnderground(v3s16 p)
193 v3s16 blockpos = getNodeBlockPos(p);
195 MapBlock * block = getBlockNoCreate(blockpos);
196 return block->getIsUnderground();
198 catch(InvalidPositionException &e)
205 Goes recursively through the neighbours of the node.
207 Alters only transparent nodes.
209 If the lighting of the neighbour is lower than the lighting of
210 the node was (before changing it to 0 at the step before), the
211 lighting of the neighbour is set to 0 and then the same stuff
212 repeats for the neighbour.
214 The ending nodes of the routine are stored in light_sources.
215 This is useful when a light is removed. In such case, this
216 routine can be called for the light node and then again for
217 light_sources to re-light the area without the removed light.
219 values of from_nodes are lighting values.
221 void Map::unspreadLight(enum LightBank bank,
222 core::map<v3s16, u8> & from_nodes,
223 core::map<v3s16, bool> & light_sources,
224 core::map<v3s16, MapBlock*> & modified_blocks)
227 v3s16(0,0,1), // back
229 v3s16(1,0,0), // right
230 v3s16(0,0,-1), // front
231 v3s16(0,-1,0), // bottom
232 v3s16(-1,0,0), // left
235 if(from_nodes.size() == 0)
238 u32 blockchangecount = 0;
240 core::map<v3s16, u8> unlighted_nodes;
241 core::map<v3s16, u8>::Iterator j;
242 j = from_nodes.getIterator();
245 Initialize block cache
248 MapBlock *block = NULL;
249 // Cache this a bit, too
250 bool block_checked_in_modified = false;
252 for(; j.atEnd() == false; j++)
254 v3s16 pos = j.getNode()->getKey();
255 v3s16 blockpos = getNodeBlockPos(pos);
257 // Only fetch a new block if the block position has changed
259 if(block == NULL || blockpos != blockpos_last){
260 block = getBlockNoCreate(blockpos);
261 blockpos_last = blockpos;
263 block_checked_in_modified = false;
267 catch(InvalidPositionException &e)
275 // Calculate relative position in block
276 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
278 // Get node straight from the block
279 MapNode n = block->getNode(relpos);
281 u8 oldlight = j.getNode()->getValue();
283 // Loop through 6 neighbors
284 for(u16 i=0; i<6; i++)
286 // Get the position of the neighbor node
287 v3s16 n2pos = pos + dirs[i];
289 // Get the block where the node is located
290 v3s16 blockpos = getNodeBlockPos(n2pos);
294 // Only fetch a new block if the block position has changed
296 if(block == NULL || blockpos != blockpos_last){
297 block = getBlockNoCreate(blockpos);
298 blockpos_last = blockpos;
300 block_checked_in_modified = false;
304 catch(InvalidPositionException &e)
309 // Calculate relative position in block
310 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
311 // Get node straight from the block
312 MapNode n2 = block->getNode(relpos);
314 bool changed = false;
316 //TODO: Optimize output by optimizing light_sources?
319 If the neighbor is dimmer than what was specified
320 as oldlight (the light of the previous node)
322 if(n2.getLight(bank) < oldlight)
325 And the neighbor is transparent and it has some light
327 if(n2.light_propagates() && n2.getLight(bank) != 0)
330 Set light to 0 and add to queue
333 u8 current_light = n2.getLight(bank);
334 n2.setLight(bank, 0);
335 block->setNode(relpos, n2);
337 unlighted_nodes.insert(n2pos, current_light);
341 Remove from light_sources if it is there
342 NOTE: This doesn't happen nearly at all
344 /*if(light_sources.find(n2pos))
346 std::cout<<"Removed from light_sources"<<std::endl;
347 light_sources.remove(n2pos);
352 if(light_sources.find(n2pos) != NULL)
353 light_sources.remove(n2pos);*/
356 light_sources.insert(n2pos, true);
359 // Add to modified_blocks
360 if(changed == true && block_checked_in_modified == false)
362 // If the block is not found in modified_blocks, add.
363 if(modified_blocks.find(blockpos) == NULL)
365 modified_blocks.insert(blockpos, block);
367 block_checked_in_modified = true;
370 catch(InvalidPositionException &e)
377 /*dstream<<"unspreadLight(): Changed block "
378 <<blockchangecount<<" times"
379 <<" for "<<from_nodes.size()<<" nodes"
382 if(unlighted_nodes.size() > 0)
383 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
387 A single-node wrapper of the above
389 void Map::unLightNeighbors(enum LightBank bank,
390 v3s16 pos, u8 lightwas,
391 core::map<v3s16, bool> & light_sources,
392 core::map<v3s16, MapBlock*> & modified_blocks)
394 core::map<v3s16, u8> from_nodes;
395 from_nodes.insert(pos, lightwas);
397 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
401 Lights neighbors of from_nodes, collects all them and then
404 void Map::spreadLight(enum LightBank bank,
405 core::map<v3s16, bool> & from_nodes,
406 core::map<v3s16, MapBlock*> & modified_blocks)
408 const v3s16 dirs[6] = {
409 v3s16(0,0,1), // back
411 v3s16(1,0,0), // right
412 v3s16(0,0,-1), // front
413 v3s16(0,-1,0), // bottom
414 v3s16(-1,0,0), // left
417 if(from_nodes.size() == 0)
420 u32 blockchangecount = 0;
422 core::map<v3s16, bool> lighted_nodes;
423 core::map<v3s16, bool>::Iterator j;
424 j = from_nodes.getIterator();
427 Initialize block cache
430 MapBlock *block = NULL;
431 // Cache this a bit, too
432 bool block_checked_in_modified = false;
434 for(; j.atEnd() == false; j++)
435 //for(; j != from_nodes.end(); j++)
437 v3s16 pos = j.getNode()->getKey();
439 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
440 v3s16 blockpos = getNodeBlockPos(pos);
442 // Only fetch a new block if the block position has changed
444 if(block == NULL || blockpos != blockpos_last){
445 block = getBlockNoCreate(blockpos);
446 blockpos_last = blockpos;
448 block_checked_in_modified = false;
452 catch(InvalidPositionException &e)
460 // Calculate relative position in block
461 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
463 // Get node straight from the block
464 MapNode n = block->getNode(relpos);
466 u8 oldlight = n.getLight(bank);
467 u8 newlight = diminish_light(oldlight);
469 // Loop through 6 neighbors
470 for(u16 i=0; i<6; i++){
471 // Get the position of the neighbor node
472 v3s16 n2pos = pos + dirs[i];
474 // Get the block where the node is located
475 v3s16 blockpos = getNodeBlockPos(n2pos);
479 // Only fetch a new block if the block position has changed
481 if(block == NULL || blockpos != blockpos_last){
482 block = getBlockNoCreate(blockpos);
483 blockpos_last = blockpos;
485 block_checked_in_modified = false;
489 catch(InvalidPositionException &e)
494 // Calculate relative position in block
495 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
496 // Get node straight from the block
497 MapNode n2 = block->getNode(relpos);
499 bool changed = false;
501 If the neighbor is brighter than the current node,
502 add to list (it will light up this node on its turn)
504 if(n2.getLight(bank) > undiminish_light(oldlight))
506 lighted_nodes.insert(n2pos, true);
507 //lighted_nodes.push_back(n2pos);
511 If the neighbor is dimmer than how much light this node
512 would spread on it, add to list
514 if(n2.getLight(bank) < newlight)
516 if(n2.light_propagates())
518 n2.setLight(bank, newlight);
519 block->setNode(relpos, n2);
520 lighted_nodes.insert(n2pos, true);
521 //lighted_nodes.push_back(n2pos);
526 // Add to modified_blocks
527 if(changed == true && block_checked_in_modified == false)
529 // If the block is not found in modified_blocks, add.
530 if(modified_blocks.find(blockpos) == NULL)
532 modified_blocks.insert(blockpos, block);
534 block_checked_in_modified = true;
537 catch(InvalidPositionException &e)
544 /*dstream<<"spreadLight(): Changed block "
545 <<blockchangecount<<" times"
546 <<" for "<<from_nodes.size()<<" nodes"
549 if(lighted_nodes.size() > 0)
550 spreadLight(bank, lighted_nodes, modified_blocks);
554 A single-node source variation of the above.
556 void Map::lightNeighbors(enum LightBank bank,
558 core::map<v3s16, MapBlock*> & modified_blocks)
560 core::map<v3s16, bool> from_nodes;
561 from_nodes.insert(pos, true);
562 spreadLight(bank, from_nodes, modified_blocks);
565 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
568 v3s16(0,0,1), // back
570 v3s16(1,0,0), // right
571 v3s16(0,0,-1), // front
572 v3s16(0,-1,0), // bottom
573 v3s16(-1,0,0), // left
576 u8 brightest_light = 0;
577 v3s16 brightest_pos(0,0,0);
578 bool found_something = false;
580 // Loop through 6 neighbors
581 for(u16 i=0; i<6; i++){
582 // Get the position of the neighbor node
583 v3s16 n2pos = p + dirs[i];
588 catch(InvalidPositionException &e)
592 if(n2.getLight(bank) > brightest_light || found_something == false){
593 brightest_light = n2.getLight(bank);
594 brightest_pos = n2pos;
595 found_something = true;
599 if(found_something == false)
600 throw InvalidPositionException();
602 return brightest_pos;
606 Propagates sunlight down from a node.
607 Starting point gets sunlight.
609 Returns the lowest y value of where the sunlight went.
611 s16 Map::propagateSunlight(v3s16 start,
612 core::map<v3s16, MapBlock*> & modified_blocks)
617 v3s16 pos(start.X, y, start.Z);
619 v3s16 blockpos = getNodeBlockPos(pos);
622 block = getBlockNoCreate(blockpos);
624 catch(InvalidPositionException &e)
629 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
630 MapNode n = block->getNode(relpos);
632 if(n.sunlight_propagates())
634 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
635 block->setNode(relpos, n);
637 modified_blocks.insert(blockpos, block);
646 void Map::updateLighting(enum LightBank bank,
647 core::map<v3s16, MapBlock*> & a_blocks,
648 core::map<v3s16, MapBlock*> & modified_blocks)
650 /*m_dout<<DTIME<<"Map::updateLighting(): "
651 <<a_blocks.getSize()<<" blocks... ";*/
655 u32 count_was = modified_blocks.size();
657 core::map<v3s16, bool> light_sources;
659 core::map<v3s16, u8> unlight_from;
661 core::map<v3s16, MapBlock*>::Iterator i;
662 i = a_blocks.getIterator();
663 for(; i.atEnd() == false; i++)
665 MapBlock *block = i.getNode()->getValue();
669 // Don't bother with dummy blocks.
673 v3s16 pos = block->getPos();
674 modified_blocks.insert(pos, block);
677 Clear all light from block
679 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
680 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
681 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
686 MapNode n = block->getNode(v3s16(x,y,z));
687 u8 oldlight = n.getLight(bank);
689 block->setNode(v3s16(x,y,z), n);
691 // Collect borders for unlighting
692 if(x==0 || x == MAP_BLOCKSIZE-1
693 || y==0 || y == MAP_BLOCKSIZE-1
694 || z==0 || z == MAP_BLOCKSIZE-1)
696 v3s16 p_map = p + v3s16(
699 MAP_BLOCKSIZE*pos.Z);
700 unlight_from.insert(p_map, oldlight);
703 catch(InvalidPositionException &e)
706 This would happen when dealing with a
710 dstream<<"updateLighting(): InvalidPositionException"
715 if(bank == LIGHTBANK_DAY)
717 bool bottom_valid = block->propagateSunlight(light_sources);
719 // If bottom is valid, we're done.
723 else if(bank == LIGHTBANK_NIGHT)
732 /*dstream<<"Bottom for sunlight-propagated block ("
733 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
736 // Else get the block below and loop to it
740 block = getBlockNoCreate(pos);
742 catch(InvalidPositionException &e)
751 //TimeTaker timer("unspreadLight");
752 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
757 u32 diff = modified_blocks.size() - count_was;
758 count_was = modified_blocks.size();
759 dstream<<"unspreadLight modified "<<diff<<std::endl;
762 // TODO: Spread light from propagated sunlight?
763 // Yes, add it to light_sources... somehow.
764 // It has to be added at somewhere above, in the loop.
766 // NOTE: This actually works fine without doing so
767 // - Find out why it works
770 //TimeTaker timer("spreadLight");
771 spreadLight(bank, light_sources, modified_blocks);
776 u32 diff = modified_blocks.size() - count_was;
777 count_was = modified_blocks.size();
778 dstream<<"spreadLight modified "<<diff<<std::endl;
781 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
784 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
785 core::map<v3s16, MapBlock*> & modified_blocks)
787 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
788 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
791 Update information about whether day and night light differ
793 for(core::map<v3s16, MapBlock*>::Iterator
794 i = modified_blocks.getIterator();
795 i.atEnd() == false; i++)
797 MapBlock *block = i.getNode()->getValue();
798 block->updateDayNightDiff();
803 This is called after changing a node from transparent to opaque.
804 The lighting value of the node should be left as-is after changing
805 other values. This sets the lighting value to 0.
807 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
808 core::map<v3s16, MapBlock*> &modified_blocks)*/
809 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
810 core::map<v3s16, MapBlock*> &modified_blocks)
813 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
814 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
817 From this node to nodes underneath:
818 If lighting is sunlight (1.0), unlight neighbours and
823 v3s16 toppos = p + v3s16(0,1,0);
824 v3s16 bottompos = p + v3s16(0,-1,0);
826 bool node_under_sunlight = true;
827 core::map<v3s16, bool> light_sources;
830 If there is a node at top and it doesn't have sunlight,
831 there has not been any sunlight going down.
833 Otherwise there probably is.
836 MapNode topnode = getNode(toppos);
838 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
839 node_under_sunlight = false;
841 catch(InvalidPositionException &e)
845 if(n.d != CONTENT_TORCH)
848 If there is grass below, change it to mud
851 MapNode bottomnode = getNode(bottompos);
853 if(bottomnode.d == CONTENT_GRASS
854 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
856 bottomnode.d = CONTENT_MUD;
857 setNode(bottompos, bottomnode);
860 catch(InvalidPositionException &e)
865 enum LightBank banks[] =
870 for(s32 i=0; i<2; i++)
872 enum LightBank bank = banks[i];
874 u8 lightwas = getNode(p).getLight(bank);
876 // Add the block of the added node to modified_blocks
877 v3s16 blockpos = getNodeBlockPos(p);
878 MapBlock * block = getBlockNoCreate(blockpos);
879 assert(block != NULL);
880 modified_blocks.insert(blockpos, block);
882 if(isValidPosition(p) == false)
885 // Unlight neighbours of node.
886 // This means setting light of all consequent dimmer nodes
888 // This also collects the nodes at the border which will spread
889 // light again into this.
890 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
898 If node is under sunlight, take all sunlighted nodes under
899 it and clear light from them and from where the light has
901 TODO: This could be optimized by mass-unlighting instead
904 if(node_under_sunlight)
908 //m_dout<<DTIME<<"y="<<y<<std::endl;
909 v3s16 n2pos(p.X, y, p.Z);
915 catch(InvalidPositionException &e)
920 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
922 //m_dout<<DTIME<<"doing"<<std::endl;
923 unLightNeighbors(LIGHTBANK_DAY,
924 n2pos, n2.getLight(LIGHTBANK_DAY),
925 light_sources, modified_blocks);
926 n2.setLight(LIGHTBANK_DAY, 0);
934 for(s32 i=0; i<2; i++)
936 enum LightBank bank = banks[i];
939 Spread light from all nodes that might be capable of doing so
940 TODO: Convert to spreadLight
942 spreadLight(bank, light_sources, modified_blocks);
946 Update information about whether day and night light differ
948 for(core::map<v3s16, MapBlock*>::Iterator
949 i = modified_blocks.getIterator();
950 i.atEnd() == false; i++)
952 MapBlock *block = i.getNode()->getValue();
953 block->updateDayNightDiff();
959 void Map::removeNodeAndUpdate(v3s16 p,
960 core::map<v3s16, MapBlock*> &modified_blocks)
963 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
964 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
966 bool node_under_sunlight = true;
968 v3s16 toppos = p + v3s16(0,1,0);
970 // Node will be replaced with this
971 u8 replace_material = CONTENT_AIR;
974 If there is a node at top and it doesn't have sunlight,
975 there will be no sunlight going down.
978 MapNode topnode = getNode(toppos);
980 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
981 node_under_sunlight = false;
983 catch(InvalidPositionException &e)
987 core::map<v3s16, bool> light_sources;
989 enum LightBank banks[] =
994 for(s32 i=0; i<2; i++)
996 enum LightBank bank = banks[i];
999 Unlight neighbors (in case the node is a light source)
1001 unLightNeighbors(bank, p,
1002 getNode(p).getLight(bank),
1003 light_sources, modified_blocks);
1008 This also clears the lighting.
1012 n.d = replace_material;
1015 for(s32 i=0; i<2; i++)
1017 enum LightBank bank = banks[i];
1020 Recalculate lighting
1022 spreadLight(bank, light_sources, modified_blocks);
1025 // Add the block of the removed node to modified_blocks
1026 v3s16 blockpos = getNodeBlockPos(p);
1027 MapBlock * block = getBlockNoCreate(blockpos);
1028 assert(block != NULL);
1029 modified_blocks.insert(blockpos, block);
1032 If the removed node was under sunlight, propagate the
1033 sunlight down from it and then light all neighbors
1034 of the propagated blocks.
1036 if(node_under_sunlight)
1038 s16 ybottom = propagateSunlight(p, modified_blocks);
1039 /*m_dout<<DTIME<<"Node was under sunlight. "
1040 "Propagating sunlight";
1041 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1043 for(; y >= ybottom; y--)
1045 v3s16 p2(p.X, y, p.Z);
1046 /*m_dout<<DTIME<<"lighting neighbors of node ("
1047 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1049 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1054 // Set the lighting of this node to 0
1055 // TODO: Is this needed? Lighting is cleared up there already.
1057 MapNode n = getNode(p);
1058 n.setLight(LIGHTBANK_DAY, 0);
1061 catch(InvalidPositionException &e)
1067 for(s32 i=0; i<2; i++)
1069 enum LightBank bank = banks[i];
1071 // Get the brightest neighbour node and propagate light from it
1072 v3s16 n2p = getBrightestNeighbour(bank, p);
1074 MapNode n2 = getNode(n2p);
1075 lightNeighbors(bank, n2p, modified_blocks);
1077 catch(InvalidPositionException &e)
1083 Update information about whether day and night light differ
1085 for(core::map<v3s16, MapBlock*>::Iterator
1086 i = modified_blocks.getIterator();
1087 i.atEnd() == false; i++)
1089 MapBlock *block = i.getNode()->getValue();
1090 block->updateDayNightDiff();
1095 void Map::expireMeshes(bool only_daynight_diffed)
1097 TimeTaker timer("expireMeshes()");
1099 core::map<v2s16, MapSector*>::Iterator si;
1100 si = m_sectors.getIterator();
1101 for(; si.atEnd() == false; si++)
1103 MapSector *sector = si.getNode()->getValue();
1105 core::list< MapBlock * > sectorblocks;
1106 sector->getBlocks(sectorblocks);
1108 core::list< MapBlock * >::Iterator i;
1109 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1111 MapBlock *block = *i;
1113 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1119 JMutexAutoLock lock(block->mesh_mutex);
1120 if(block->mesh != NULL)
1122 /*block->mesh->drop();
1123 block->mesh = NULL;*/
1124 block->setMeshExpired(true);
1131 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1133 assert(mapType() == MAPTYPE_CLIENT);
1136 v3s16 p = blockpos + v3s16(0,0,0);
1137 MapBlock *b = getBlockNoCreate(p);
1138 b->updateMesh(daynight_ratio);
1140 catch(InvalidPositionException &e){}
1143 v3s16 p = blockpos + v3s16(-1,0,0);
1144 MapBlock *b = getBlockNoCreate(p);
1145 b->updateMesh(daynight_ratio);
1147 catch(InvalidPositionException &e){}
1149 v3s16 p = blockpos + v3s16(0,-1,0);
1150 MapBlock *b = getBlockNoCreate(p);
1151 b->updateMesh(daynight_ratio);
1153 catch(InvalidPositionException &e){}
1155 v3s16 p = blockpos + v3s16(0,0,-1);
1156 MapBlock *b = getBlockNoCreate(p);
1157 b->updateMesh(daynight_ratio);
1159 catch(InvalidPositionException &e){}
1162 v3s16 p = blockpos + v3s16(1,0,0);
1163 MapBlock *b = getBlockNoCreate(p);
1164 b->updateMesh(daynight_ratio);
1166 catch(InvalidPositionException &e){}
1168 v3s16 p = blockpos + v3s16(0,1,0);
1169 MapBlock *b = getBlockNoCreate(p);
1170 b->updateMesh(daynight_ratio);
1172 catch(InvalidPositionException &e){}
1174 v3s16 p = blockpos + v3s16(0,0,1);
1175 MapBlock *b = getBlockNoCreate(p);
1176 b->updateMesh(daynight_ratio);
1178 catch(InvalidPositionException &e){}*/
1183 bool Map::dayNightDiffed(v3s16 blockpos)
1186 v3s16 p = blockpos + v3s16(0,0,0);
1187 MapBlock *b = getBlockNoCreate(p);
1188 if(b->dayNightDiffed())
1191 catch(InvalidPositionException &e){}
1194 v3s16 p = blockpos + v3s16(-1,0,0);
1195 MapBlock *b = getBlockNoCreate(p);
1196 if(b->dayNightDiffed())
1199 catch(InvalidPositionException &e){}
1201 v3s16 p = blockpos + v3s16(0,-1,0);
1202 MapBlock *b = getBlockNoCreate(p);
1203 if(b->dayNightDiffed())
1206 catch(InvalidPositionException &e){}
1208 v3s16 p = blockpos + v3s16(0,0,-1);
1209 MapBlock *b = getBlockNoCreate(p);
1210 if(b->dayNightDiffed())
1213 catch(InvalidPositionException &e){}
1216 v3s16 p = blockpos + v3s16(1,0,0);
1217 MapBlock *b = getBlockNoCreate(p);
1218 if(b->dayNightDiffed())
1221 catch(InvalidPositionException &e){}
1223 v3s16 p = blockpos + v3s16(0,1,0);
1224 MapBlock *b = getBlockNoCreate(p);
1225 if(b->dayNightDiffed())
1228 catch(InvalidPositionException &e){}
1230 v3s16 p = blockpos + v3s16(0,0,1);
1231 MapBlock *b = getBlockNoCreate(p);
1232 if(b->dayNightDiffed())
1235 catch(InvalidPositionException &e){}
1241 Updates usage timers
1243 void Map::timerUpdate(float dtime)
1245 JMutexAutoLock lock(m_sector_mutex);
1247 core::map<v2s16, MapSector*>::Iterator si;
1249 si = m_sectors.getIterator();
1250 for(; si.atEnd() == false; si++)
1252 MapSector *sector = si.getNode()->getValue();
1253 sector->usage_timer += dtime;
1257 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1260 Wait for caches to be removed before continuing.
1262 This disables the existence of caches while locked
1264 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1266 core::list<v2s16>::Iterator j;
1267 for(j=list.begin(); j!=list.end(); j++)
1269 MapSector *sector = m_sectors[*j];
1272 sector->deleteBlocks();
1277 If sector is in sector cache, remove it from there
1279 if(m_sector_cache == sector)
1281 m_sector_cache = NULL;
1284 Remove from map and delete
1286 m_sectors.remove(*j);
1292 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1293 core::list<v3s16> *deleted_blocks)
1295 JMutexAutoLock lock(m_sector_mutex);
1297 core::list<v2s16> sector_deletion_queue;
1298 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1299 for(; i.atEnd() == false; i++)
1301 MapSector *sector = i.getNode()->getValue();
1303 Delete sector from memory if it hasn't been used in a long time
1305 if(sector->usage_timer > timeout)
1307 sector_deletion_queue.push_back(i.getNode()->getKey());
1309 if(deleted_blocks != NULL)
1311 // Collect positions of blocks of sector
1312 MapSector *sector = i.getNode()->getValue();
1313 core::list<MapBlock*> blocks;
1314 sector->getBlocks(blocks);
1315 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1316 i != blocks.end(); i++)
1318 deleted_blocks->push_back((*i)->getPos());
1323 deleteSectors(sector_deletion_queue, only_blocks);
1324 return sector_deletion_queue.getSize();
1327 void Map::PrintInfo(std::ostream &out)
1336 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1340 m_savedir = savedir;
1341 m_map_saving_enabled = false;
1345 // If directory exists, check contents and load if possible
1346 if(fs::PathExists(m_savedir))
1348 // If directory is empty, it is safe to save into it.
1349 if(fs::GetDirListing(m_savedir).size() == 0)
1351 dstream<<DTIME<<"Server: Empty save directory is valid."
1353 m_map_saving_enabled = true;
1357 // Load master heightmap
1358 loadMasterHeightmap();
1360 // Load sector (0,0) and throw and exception on fail
1361 if(loadSectorFull(v2s16(0,0)) == false)
1362 throw LoadError("Failed to load sector (0,0)");
1364 dstream<<DTIME<<"Server: Successfully loaded master "
1365 "heightmap and sector (0,0) from "<<savedir<<
1366 ", assuming valid save directory."
1369 m_map_saving_enabled = true;
1370 // Map loaded, not creating new one
1374 // If directory doesn't exist, it is safe to save to it
1376 m_map_saving_enabled = true;
1379 catch(std::exception &e)
1381 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1382 <<", exception: "<<e.what()<<std::endl;
1383 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1384 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1387 dstream<<DTIME<<"Initializing new map."<<std::endl;
1389 // Create master heightmap
1390 ValueGenerator *maxgen =
1391 ValueGenerator::deSerialize(hmp.randmax);
1392 ValueGenerator *factorgen =
1393 ValueGenerator::deSerialize(hmp.randfactor);
1394 ValueGenerator *basegen =
1395 ValueGenerator::deSerialize(hmp.base);
1396 m_heightmap = new UnlimitedHeightmap
1397 (hmp.blocksize, maxgen, factorgen, basegen);
1399 // Set map parameters
1402 // Create zero sector
1403 emergeSector(v2s16(0,0));
1405 // Initially write whole map
1409 ServerMap::~ServerMap()
1413 if(m_map_saving_enabled)
1416 // Save only changed parts
1418 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1422 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1425 catch(std::exception &e)
1427 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1428 <<", exception: "<<e.what()<<std::endl;
1431 if(m_heightmap != NULL)
1435 MapSector * ServerMap::emergeSector(v2s16 p2d)
1437 DSTACK("%s: p2d=(%d,%d)",
1440 // Check that it doesn't exist already
1442 return getSectorNoGenerate(p2d);
1444 catch(InvalidPositionException &e)
1449 Try to load the sector from disk.
1451 if(loadSectorFull(p2d) == true)
1453 return getSectorNoGenerate(p2d);
1457 If there is no master heightmap, throw.
1459 if(m_heightmap == NULL)
1461 throw InvalidPositionException("emergeSector(): no heightmap");
1465 Do not generate over-limit
1467 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1468 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1469 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1470 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1471 throw InvalidPositionException("emergeSector(): pos. over limit");
1474 Generate sector and heightmaps
1477 // Number of heightmaps in sector in each direction
1478 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1480 // Heightmap side width
1481 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1483 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1485 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1486 " heightmaps and objects"<<std::endl;*/
1488 // Loop through sub-heightmaps
1489 for(s16 y=0; y<hm_split; y++)
1490 for(s16 x=0; x<hm_split; x++)
1492 v2s16 p_in_sector = v2s16(x,y);
1493 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1495 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1496 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1497 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1498 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1501 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1502 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1505 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1507 sector->setHeightmap(p_in_sector, hm);
1509 //TODO: Make these values configurable
1510 //hm->generateContinued(0.0, 0.0, corners);
1511 hm->generateContinued(0.5, 0.2, corners);
1512 //hm->generateContinued(1.0, 0.2, corners);
1513 //hm->generateContinued(2.0, 0.2, corners);
1523 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1524 sector->setObjects(objects);
1526 v2s16 mhm_p = p2d * hm_split;
1528 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1529 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1530 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1531 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1534 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1535 float avgslope = 0.0;
1536 avgslope += fabs(avgheight - corners[0]);
1537 avgslope += fabs(avgheight - corners[1]);
1538 avgslope += fabs(avgheight - corners[2]);
1539 avgslope += fabs(avgheight - corners[3]);
1541 avgslope /= MAP_BLOCKSIZE;
1542 //dstream<<"avgslope="<<avgslope<<std::endl;
1544 float pitness = 0.0;
1546 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1549 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1552 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1555 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1559 pitness /= MAP_BLOCKSIZE;
1560 //dstream<<"pitness="<<pitness<<std::endl;
1563 Plant some trees if there is not much slope
1566 // Avgslope is the derivative of a hill
1567 float t = avgslope * avgslope;
1568 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1571 tree_max = a / (t/0.03);
1574 u32 count = (rand()%(tree_max+1));
1575 //u32 count = tree_max;
1576 for(u32 i=0; i<count; i++)
1578 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1579 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1580 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1583 objects->insert(v3s16(x, y, z),
1584 SECTOR_OBJECT_TREE_1);
1588 Plant some bushes if sector is pit-like
1591 // Pitness usually goes at around -0.5...0.5
1593 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1595 bush_max = (pitness*a*4);
1598 u32 count = (rand()%(bush_max+1));
1599 for(u32 i=0; i<count; i++)
1601 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1602 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1603 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1606 objects->insert(v3s16(x, y, z),
1607 SECTOR_OBJECT_BUSH_1);
1611 Add ravine (randomly)
1613 if(m_params.ravines_amount != 0)
1615 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1618 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1619 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1622 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1623 objects->insert(v3s16(x, y, z),
1624 SECTOR_OBJECT_RAVINE);
1631 JMutexAutoLock lock(m_sector_mutex);
1632 m_sectors.insert(p2d, sector);
1637 MapBlock * ServerMap::emergeBlock(
1639 bool only_from_disk,
1640 core::map<v3s16, MapBlock*> &changed_blocks,
1641 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1644 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1646 p.X, p.Y, p.Z, only_from_disk);
1648 /*dstream<<"ServerMap::emergeBlock(): "
1649 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1650 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1651 v2s16 p2d(p.X, p.Z);
1654 This will create or load a sector if not found in memory.
1655 If block exists on disk, it will be loaded.
1657 NOTE: On old save formats, this will be slow, as it generates
1658 lighting on blocks for them.
1660 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1661 assert(sector->getId() == MAPSECTOR_SERVER);
1663 // Try to get a block from the sector
1664 MapBlock *block = NULL;
1665 bool not_on_disk = false;
1667 block = sector->getBlockNoCreate(block_y);
1668 if(block->isDummy() == true)
1673 catch(InvalidPositionException &e)
1679 If block was not found on disk and not going to generate a
1680 new one, make sure there is a dummy block in place.
1682 if(not_on_disk && only_from_disk)
1686 // Create dummy block
1687 block = new MapBlock(this, p, true);
1689 // Add block to sector
1690 sector->insertBlock(block);
1696 //dstream<<"Not found on disk, generating."<<std::endl;
1697 //TimeTaker("emergeBlock()", g_irrlicht);
1700 Do not generate over-limit
1702 if(blockpos_over_limit(p))
1703 throw InvalidPositionException("emergeBlock(): pos. over limit");
1708 Go on generating the block.
1710 TODO: If a dungeon gets generated so that it's side gets
1711 revealed to the outside air, the lighting should be
1716 If block doesn't exist, create one.
1717 If it exists, it is a dummy. In that case unDummify() it.
1721 block = sector->createBlankBlockNoInsert(block_y);
1725 // Remove the block so that nobody can get a half-generated one.
1726 sector->removeBlock(block);
1727 // Allocate the block to be a proper one.
1731 // Randomize a bit. This makes dungeons.
1732 /*bool low_block_is_empty = false;
1734 low_block_is_empty = true;*/
1737 //const s32 ued = 8;
1738 bool underground_emptiness[ued*ued*ued];
1739 for(s32 i=0; i<ued*ued*ued; i++)
1741 underground_emptiness[i] = ((rand() % 5) == 0);
1746 This is a messy hack to sort the emptiness a bit
1748 for(s32 j=0; j<2; j++)
1749 for(s32 y0=0; y0<ued; y0++)
1750 for(s32 z0=0; z0<ued; z0++)
1751 for(s32 x0=0; x0<ued; x0++)
1754 bool &e0 = underground_emptiness[
1755 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1756 +ued*(y0*ued/MAP_BLOCKSIZE)
1757 +(x0*ued/MAP_BLOCKSIZE)];
1760 v3s16(0,0,1), // back
1761 v3s16(1,0,0), // right
1762 v3s16(0,0,-1), // front
1763 v3s16(-1,0,0), // left
1764 /*v3s16(0,1,0), // top
1765 v3s16(0,-1,0), // bottom*/
1767 for(s32 i=0; i<4; i++)
1769 v3s16 p1 = p0 + dirs[i];
1770 if(isInArea(p1, ued) == false)
1772 bool &e1 = underground_emptiness[
1773 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1774 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1775 +(p1.X*ued/MAP_BLOCKSIZE)];
1780 v3s16(0,1,0), // top
1781 v3s16(0,-1,0), // bottom
1782 /*v3s16(0,0,1), // back
1783 v3s16(1,0,0), // right
1784 v3s16(0,0,-1), // front
1785 v3s16(-1,0,0), // left*/
1787 for(s32 i=0; i<2; i++)
1789 v3s16 p2 = p1 + dirs[i];
1792 if(isInArea(p2, ued) == false)
1794 bool &e2 = underground_emptiness[
1795 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1796 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1797 +(p2.X*ued/MAP_BLOCKSIZE)];
1812 // This is the basic material of what the visible flat ground
1814 u8 material = CONTENT_GRASS;
1816 u8 water_material = CONTENT_WATER;
1817 if(g_settings.getBool("endless_water"))
1818 water_material = CONTENT_OCEAN;
1820 s32 lowest_ground_y = 32767;
1821 s32 highest_ground_y = -32768;
1824 //sector->printHeightmaps();
1826 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1827 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1829 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1831 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1832 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1833 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1835 dstream<<"WARNING: Surface height not found in sector "
1836 "for block that is being emerged"<<std::endl;
1840 s16 surface_y = surface_y_f;
1841 //avg_ground_y += surface_y;
1842 if(surface_y < lowest_ground_y)
1843 lowest_ground_y = surface_y;
1844 if(surface_y > highest_ground_y)
1845 highest_ground_y = surface_y;
1847 s32 surface_depth = 0;
1849 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1851 //float min_slope = 0.45;
1852 //float max_slope = 0.85;
1853 float min_slope = 0.60;
1854 float max_slope = 1.20;
1855 float min_slope_depth = 5.0;
1856 float max_slope_depth = 0;
1857 if(slope < min_slope)
1858 surface_depth = min_slope_depth;
1859 else if(slope > max_slope)
1860 surface_depth = max_slope_depth;
1862 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1864 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1866 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1871 NOTE: If there are some man-made structures above the
1872 newly created block, they won't be taken into account.
1874 if(real_y > surface_y)
1875 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1881 if(real_y <= surface_y - surface_depth)
1884 if(underground_emptiness[
1885 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1886 +ued*(y0*ued/MAP_BLOCKSIZE)
1887 +(x0*ued/MAP_BLOCKSIZE)])
1893 n.d = CONTENT_STONE;
1896 // If node is at or under heightmap y
1897 else if(real_y <= surface_y)
1899 // If under water level, it's mud
1900 if(real_y < WATER_LEVEL)
1902 // Only the topmost node is grass
1903 else if(real_y <= surface_y - 1)
1905 // Else it's the main material
1909 // If node is over heightmap y
1911 // If under water level, it's water
1912 if(real_y < WATER_LEVEL)
1914 n.d = water_material;
1915 n.setLight(LIGHTBANK_DAY,
1916 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1922 block->setNode(v3s16(x0,y0,z0), n);
1927 Calculate is_underground
1929 // Probably underground if the highest part of block is under lowest
1931 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1932 block->setIsUnderground(is_underground);
1935 Force lighting update if some part of block is underground
1936 This is needed because of caves.
1939 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1940 if(some_part_underground)
1941 //if(is_underground)
1943 lighting_invalidated_blocks[block->getPos()] = block;
1950 if(some_part_underground)
1952 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1957 for(s16 i=0; i<underground_level*1; i++)
1962 (rand()%(MAP_BLOCKSIZE-2))+1,
1963 (rand()%(MAP_BLOCKSIZE-2))+1,
1964 (rand()%(MAP_BLOCKSIZE-2))+1
1970 //if(is_ground_content(block->getNode(cp).d))
1971 if(block->getNode(cp).d == CONTENT_STONE)
1973 block->setNode(cp, n);
1975 for(u16 i=0; i<26; i++)
1977 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1978 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1980 block->setNode(cp+g_26dirs[i], n);
1988 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
1989 u16 coal_rareness = 60 / coal_amount;
1990 if(coal_rareness == 0)
1992 if(rand()%coal_rareness == 0)
1994 for(s16 i=0; i<coal_amount; i++)
1997 (rand()%(MAP_BLOCKSIZE-2))+1,
1998 (rand()%(MAP_BLOCKSIZE-2))+1,
1999 (rand()%(MAP_BLOCKSIZE-2))+1
2003 n.d = CONTENT_COALSTONE;
2005 //dstream<<"Adding coalstone"<<std::endl;
2007 //if(is_ground_content(block->getNode(cp).d))
2008 if(block->getNode(cp).d == CONTENT_STONE)
2010 block->setNode(cp, n);
2012 for(u16 i=0; i<26; i++)
2014 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2015 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2017 block->setNode(cp+g_26dirs[i], n);
2024 Create a few rats in empty blocks underground
2028 //for(u16 i=0; i<2; i++)
2031 (rand()%(MAP_BLOCKSIZE-2))+1,
2032 (rand()%(MAP_BLOCKSIZE-2))+1,
2033 (rand()%(MAP_BLOCKSIZE-2))+1
2036 // Check that the place is empty
2037 //if(!is_ground_content(block->getNode(cp).d))
2040 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2041 block->addObject(obj);
2047 Add block to sector.
2049 sector->insertBlock(block);
2055 // An y-wise container of changed blocks
2056 core::map<s16, MapBlock*> changed_blocks_sector;
2059 Check if any sector's objects can be placed now.
2062 core::map<v3s16, u8> *objects = sector->getObjects();
2063 core::list<v3s16> objects_to_remove;
2064 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2065 i.atEnd() == false; i++)
2067 v3s16 p = i.getNode()->getKey();
2069 u8 d = i.getNode()->getValue();
2071 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2076 if(d == SECTOR_OBJECT_TEST)
2078 if(sector->isValidArea(p + v3s16(0,0,0),
2079 p + v3s16(0,0,0), &changed_blocks_sector))
2082 n.d = CONTENT_TORCH;
2083 sector->setNode(p, n);
2084 objects_to_remove.push_back(p);
2087 else if(d == SECTOR_OBJECT_TREE_1)
2089 v3s16 p_min = p + v3s16(-1,0,-1);
2090 v3s16 p_max = p + v3s16(1,4,1);
2091 if(sector->isValidArea(p_min, p_max,
2092 &changed_blocks_sector))
2096 sector->setNode(p+v3s16(0,0,0), n);
2097 sector->setNode(p+v3s16(0,1,0), n);
2098 sector->setNode(p+v3s16(0,2,0), n);
2099 sector->setNode(p+v3s16(0,3,0), n);
2101 n.d = CONTENT_LEAVES;
2103 sector->setNode(p+v3s16(0,4,0), n);
2105 sector->setNode(p+v3s16(-1,4,0), n);
2106 sector->setNode(p+v3s16(1,4,0), n);
2107 sector->setNode(p+v3s16(0,4,-1), n);
2108 sector->setNode(p+v3s16(0,4,1), n);
2109 sector->setNode(p+v3s16(1,4,1), n);
2110 sector->setNode(p+v3s16(-1,4,1), n);
2111 sector->setNode(p+v3s16(-1,4,-1), n);
2112 sector->setNode(p+v3s16(1,4,-1), n);
2114 sector->setNode(p+v3s16(-1,3,0), n);
2115 sector->setNode(p+v3s16(1,3,0), n);
2116 sector->setNode(p+v3s16(0,3,-1), n);
2117 sector->setNode(p+v3s16(0,3,1), n);
2118 sector->setNode(p+v3s16(1,3,1), n);
2119 sector->setNode(p+v3s16(-1,3,1), n);
2120 sector->setNode(p+v3s16(-1,3,-1), n);
2121 sector->setNode(p+v3s16(1,3,-1), n);
2123 objects_to_remove.push_back(p);
2125 // Lighting has to be recalculated for this one.
2126 sector->getBlocksInArea(p_min, p_max,
2127 lighting_invalidated_blocks);
2130 else if(d == SECTOR_OBJECT_BUSH_1)
2132 if(sector->isValidArea(p + v3s16(0,0,0),
2133 p + v3s16(0,0,0), &changed_blocks_sector))
2136 n.d = CONTENT_LEAVES;
2137 sector->setNode(p+v3s16(0,0,0), n);
2139 objects_to_remove.push_back(p);
2142 else if(d == SECTOR_OBJECT_RAVINE)
2145 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2146 v3s16 p_max = p + v3s16(6,6,6);
2147 if(sector->isValidArea(p_min, p_max,
2148 &changed_blocks_sector))
2151 n.d = CONTENT_STONE;
2154 s16 depth = maxdepth + (rand()%10);
2156 s16 minz = -6 - (-2);
2158 for(s16 x=-6; x<=6; x++)
2160 z += -1 + (rand()%3);
2165 for(s16 y=depth+(rand()%2); y<=6; y++)
2167 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2170 v3s16 p2 = p + v3s16(x,y,z-2);
2171 if(is_ground_content(sector->getNode(p2).d)
2172 && !is_mineral(sector->getNode(p2).d))
2173 sector->setNode(p2, n);
2176 v3s16 p2 = p + v3s16(x,y,z-1);
2177 if(is_ground_content(sector->getNode(p2).d)
2178 && !is_mineral(sector->getNode(p2).d))
2179 sector->setNode(p2, n2);
2182 v3s16 p2 = p + v3s16(x,y,z+0);
2183 if(is_ground_content(sector->getNode(p2).d)
2184 && !is_mineral(sector->getNode(p2).d))
2185 sector->setNode(p2, n2);
2188 v3s16 p2 = p + v3s16(x,y,z+1);
2189 if(is_ground_content(sector->getNode(p2).d)
2190 && !is_mineral(sector->getNode(p2).d))
2191 sector->setNode(p2, n);
2194 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2195 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2199 objects_to_remove.push_back(p);
2201 // Lighting has to be recalculated for this one.
2202 sector->getBlocksInArea(p_min, p_max,
2203 lighting_invalidated_blocks);
2208 dstream<<"ServerMap::emergeBlock(): "
2209 "Invalid heightmap object"
2214 catch(InvalidPositionException &e)
2216 dstream<<"WARNING: "<<__FUNCTION_NAME
2217 <<": while inserting object "<<(int)d
2218 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2219 <<" InvalidPositionException.what()="
2220 <<e.what()<<std::endl;
2221 // This is not too fatal and seems to happen sometimes.
2226 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2227 i != objects_to_remove.end(); i++)
2229 objects->remove(*i);
2232 for(core::map<s16, MapBlock*>::Iterator
2233 i = changed_blocks_sector.getIterator();
2234 i.atEnd() == false; i++)
2236 MapBlock *block = i.getNode()->getValue();
2238 changed_blocks.insert(block->getPos(), block);
2244 void ServerMap::createDir(std::string path)
2246 if(fs::CreateDir(path) == false)
2248 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2249 <<"\""<<path<<"\""<<std::endl;
2250 throw BaseException("ServerMap failed to create directory");
2254 std::string ServerMap::getSectorSubDir(v2s16 pos)
2257 snprintf(cc, 9, "%.4x%.4x",
2258 (unsigned int)pos.X&0xffff,
2259 (unsigned int)pos.Y&0xffff);
2261 return std::string(cc);
2264 std::string ServerMap::getSectorDir(v2s16 pos)
2266 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2269 v2s16 ServerMap::getSectorPos(std::string dirname)
2271 if(dirname.size() != 8)
2272 throw InvalidFilenameException("Invalid sector directory name");
2274 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2276 throw InvalidFilenameException("Invalid sector directory name");
2277 v2s16 pos((s16)x, (s16)y);
2281 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2283 v2s16 p2d = getSectorPos(sectordir);
2285 if(blockfile.size() != 4){
2286 throw InvalidFilenameException("Invalid block filename");
2289 int r = sscanf(blockfile.c_str(), "%4x", &y);
2291 throw InvalidFilenameException("Invalid block filename");
2292 return v3s16(p2d.X, y, p2d.Y);
2296 #define ENABLE_SECTOR_SAVING 1
2297 #define ENABLE_SECTOR_LOADING 1
2298 #define ENABLE_BLOCK_SAVING 1
2299 #define ENABLE_BLOCK_LOADING 1
2301 void ServerMap::save(bool only_changed)
2303 DSTACK(__FUNCTION_NAME);
2304 if(m_map_saving_enabled == false)
2306 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2310 if(only_changed == false)
2311 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2314 saveMasterHeightmap();
2316 u32 sector_meta_count = 0;
2317 u32 block_count = 0;
2320 JMutexAutoLock lock(m_sector_mutex);
2322 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2323 for(; i.atEnd() == false; i++)
2325 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2326 assert(sector->getId() == MAPSECTOR_SERVER);
2328 if(ENABLE_SECTOR_SAVING)
2330 if(sector->differs_from_disk || only_changed == false)
2332 saveSectorMeta(sector);
2333 sector_meta_count++;
2336 if(ENABLE_BLOCK_SAVING)
2338 core::list<MapBlock*> blocks;
2339 sector->getBlocks(blocks);
2340 core::list<MapBlock*>::Iterator j;
2341 for(j=blocks.begin(); j!=blocks.end(); j++)
2343 MapBlock *block = *j;
2344 if(block->getChangedFlag() || only_changed == false)
2356 Only print if something happened or saved whole map
2358 if(only_changed == false || sector_meta_count != 0
2359 || block_count != 0)
2361 dstream<<DTIME<<"ServerMap: Written: "
2362 <<sector_meta_count<<" sector metadata files, "
2363 <<block_count<<" block files"
2368 void ServerMap::loadAll()
2370 DSTACK(__FUNCTION_NAME);
2371 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2373 loadMasterHeightmap();
2375 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2377 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2379 JMutexAutoLock lock(m_sector_mutex);
2382 s32 printed_counter = -100000;
2383 s32 count = list.size();
2385 std::vector<fs::DirListNode>::iterator i;
2386 for(i=list.begin(); i!=list.end(); i++)
2388 if(counter > printed_counter + 10)
2390 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2391 printed_counter = counter;
2395 MapSector *sector = NULL;
2397 // We want directories
2401 sector = loadSectorMeta(i->name);
2403 catch(InvalidFilenameException &e)
2405 // This catches unknown crap in directory
2408 if(ENABLE_BLOCK_LOADING)
2410 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2411 (m_savedir+"/sectors/"+i->name);
2412 std::vector<fs::DirListNode>::iterator i2;
2413 for(i2=list2.begin(); i2!=list2.end(); i2++)
2419 loadBlock(i->name, i2->name, sector);
2421 catch(InvalidFilenameException &e)
2423 // This catches unknown crap in directory
2428 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2431 void ServerMap::saveMasterHeightmap()
2433 DSTACK(__FUNCTION_NAME);
2434 createDir(m_savedir);
2436 std::string fullpath = m_savedir + "/master_heightmap";
2437 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2438 if(o.good() == false)
2439 throw FileNotGoodException("Cannot open master heightmap");
2441 // Format used for writing
2442 u8 version = SER_FMT_VER_HIGHEST;
2445 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2447 [0] u8 serialization version
2448 [1] X master heightmap
2450 u32 fullsize = 1 + hmdata.getSize();
2451 SharedBuffer<u8> data(fullsize);
2454 memcpy(&data[1], *hmdata, hmdata.getSize());
2456 o.write((const char*)*data, fullsize);
2459 m_heightmap->serialize(o, version);
2462 void ServerMap::loadMasterHeightmap()
2464 DSTACK(__FUNCTION_NAME);
2465 std::string fullpath = m_savedir + "/master_heightmap";
2466 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2467 if(is.good() == false)
2468 throw FileNotGoodException("Cannot open master heightmap");
2470 if(m_heightmap != NULL)
2473 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2476 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2478 DSTACK(__FUNCTION_NAME);
2479 // Format used for writing
2480 u8 version = SER_FMT_VER_HIGHEST;
2482 v2s16 pos = sector->getPos();
2483 createDir(m_savedir);
2484 createDir(m_savedir+"/sectors");
2485 std::string dir = getSectorDir(pos);
2488 std::string fullpath = dir + "/heightmap";
2489 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2490 if(o.good() == false)
2491 throw FileNotGoodException("Cannot open master heightmap");
2493 sector->serialize(o, version);
2495 sector->differs_from_disk = false;
2498 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2500 DSTACK(__FUNCTION_NAME);
2502 v2s16 p2d = getSectorPos(dirname);
2503 std::string dir = m_savedir + "/sectors/" + dirname;
2505 std::string fullpath = dir + "/heightmap";
2506 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2507 if(is.good() == false)
2508 throw FileNotGoodException("Cannot open sector heightmap");
2510 ServerMapSector *sector = ServerMapSector::deSerialize
2511 (is, this, p2d, &m_hwrapper, m_sectors);
2513 sector->differs_from_disk = false;
2518 bool ServerMap::loadSectorFull(v2s16 p2d)
2520 DSTACK(__FUNCTION_NAME);
2521 std::string sectorsubdir = getSectorSubDir(p2d);
2523 MapSector *sector = NULL;
2525 JMutexAutoLock lock(m_sector_mutex);
2528 sector = loadSectorMeta(sectorsubdir);
2530 catch(InvalidFilenameException &e)
2534 catch(FileNotGoodException &e)
2538 catch(std::exception &e)
2543 if(ENABLE_BLOCK_LOADING)
2545 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2546 (m_savedir+"/sectors/"+sectorsubdir);
2547 std::vector<fs::DirListNode>::iterator i2;
2548 for(i2=list2.begin(); i2!=list2.end(); i2++)
2554 loadBlock(sectorsubdir, i2->name, sector);
2556 catch(InvalidFilenameException &e)
2558 // This catches unknown crap in directory
2566 bool ServerMap::deFlushSector(v2s16 p2d)
2568 DSTACK(__FUNCTION_NAME);
2569 // See if it already exists in memory
2571 MapSector *sector = getSectorNoGenerate(p2d);
2574 catch(InvalidPositionException &e)
2577 Try to load the sector from disk.
2579 if(loadSectorFull(p2d) == true)
2588 void ServerMap::saveBlock(MapBlock *block)
2590 DSTACK(__FUNCTION_NAME);
2592 Dummy blocks are not written
2594 if(block->isDummy())
2596 /*v3s16 p = block->getPos();
2597 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2598 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2602 // Format used for writing
2603 u8 version = SER_FMT_VER_HIGHEST;
2605 v3s16 p3d = block->getPos();
2606 v2s16 p2d(p3d.X, p3d.Z);
2607 createDir(m_savedir);
2608 createDir(m_savedir+"/sectors");
2609 std::string dir = getSectorDir(p2d);
2612 // Block file is map/sectors/xxxxxxxx/xxxx
2614 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2615 std::string fullpath = dir + "/" + cc;
2616 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2617 if(o.good() == false)
2618 throw FileNotGoodException("Cannot open block data");
2621 [0] u8 serialization version
2624 o.write((char*)&version, 1);
2626 block->serialize(o, version);
2629 Versions up from 9 have block objects.
2633 block->serializeObjects(o, version);
2636 // We just wrote it to the disk
2637 block->resetChangedFlag();
2640 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2642 DSTACK(__FUNCTION_NAME);
2646 // Block file is map/sectors/xxxxxxxx/xxxx
2647 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2648 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2649 if(is.good() == false)
2650 throw FileNotGoodException("Cannot open block file");
2652 v3s16 p3d = getBlockPos(sectordir, blockfile);
2653 v2s16 p2d(p3d.X, p3d.Z);
2655 assert(sector->getPos() == p2d);
2657 u8 version = SER_FMT_VER_INVALID;
2658 is.read((char*)&version, 1);
2660 /*u32 block_size = MapBlock::serializedLength(version);
2661 SharedBuffer<u8> data(block_size);
2662 is.read((char*)*data, block_size);*/
2664 // This will always return a sector because we're the server
2665 //MapSector *sector = emergeSector(p2d);
2667 MapBlock *block = NULL;
2668 bool created_new = false;
2670 block = sector->getBlockNoCreate(p3d.Y);
2672 catch(InvalidPositionException &e)
2674 block = sector->createBlankBlockNoInsert(p3d.Y);
2678 // deserialize block data
2679 block->deSerialize(is, version);
2682 Versions up from 9 have block objects.
2686 block->updateObjects(is, version, NULL, 0);
2690 sector->insertBlock(block);
2693 Convert old formats to new and save
2696 // Save old format blocks in new format
2697 if(version < SER_FMT_VER_HIGHEST)
2702 // We just loaded it from the disk, so it's up-to-date.
2703 block->resetChangedFlag();
2706 catch(SerializationError &e)
2708 dstream<<"WARNING: Invalid block data on disk "
2709 "(SerializationError). Ignoring."
2714 // Gets from master heightmap
2715 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2717 assert(m_heightmap != NULL);
2725 corners[0] = m_heightmap->getGroundHeight
2726 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2727 corners[1] = m_heightmap->getGroundHeight
2728 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2729 corners[2] = m_heightmap->getGroundHeight
2730 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2731 corners[3] = m_heightmap->getGroundHeight
2732 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2735 void ServerMap::PrintInfo(std::ostream &out)
2746 ClientMap::ClientMap(
2748 JMutex &range_mutex,
2749 s16 &viewing_range_nodes,
2750 bool &viewing_range_all,
2751 scene::ISceneNode* parent,
2752 scene::ISceneManager* mgr,
2756 scene::ISceneNode(parent, mgr, id),
2759 m_range_mutex(range_mutex),
2760 m_viewing_range_nodes(viewing_range_nodes),
2761 m_viewing_range_all(viewing_range_all)
2765 /*m_box = core::aabbox3d<f32>(0,0,0,
2766 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2767 /*m_box = core::aabbox3d<f32>(0,0,0,
2768 map->getSizeNodes().X * BS,
2769 map->getSizeNodes().Y * BS,
2770 map->getSizeNodes().Z * BS);*/
2771 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2772 BS*1000000,BS*1000000,BS*1000000);
2774 //setPosition(v3f(BS,BS,BS));
2777 ClientMap::~ClientMap()
2779 JMutexAutoLock lock(mesh_mutex);
2788 MapSector * ClientMap::emergeSector(v2s16 p2d)
2790 DSTACK(__FUNCTION_NAME);
2791 // Check that it doesn't exist already
2793 return getSectorNoGenerate(p2d);
2795 catch(InvalidPositionException &e)
2799 // Create a sector with no heightmaps
2800 ClientMapSector *sector = new ClientMapSector(this, p2d);
2803 JMutexAutoLock lock(m_sector_mutex);
2804 m_sectors.insert(p2d, sector);
2810 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2812 DSTACK(__FUNCTION_NAME);
2813 ClientMapSector *sector = NULL;
2815 JMutexAutoLock lock(m_sector_mutex);
2817 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2821 sector = (ClientMapSector*)n->getValue();
2822 assert(sector->getId() == MAPSECTOR_CLIENT);
2826 sector = new ClientMapSector(this, p2d);
2828 JMutexAutoLock lock(m_sector_mutex);
2829 m_sectors.insert(p2d, sector);
2833 sector->deSerialize(is);
2836 void ClientMap::OnRegisterSceneNode()
2840 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2841 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2844 ISceneNode::OnRegisterSceneNode();
2847 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2849 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2850 DSTACK(__FUNCTION_NAME);
2852 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2855 Get time for measuring timeout.
2857 Measuring time is very useful for long delays when the
2858 machine is swapping a lot.
2860 int time1 = time(0);
2862 //s32 daynight_i = m_client->getDayNightIndex();
2863 u32 daynight_ratio = m_client->getDayNightRatio();
2866 Collect all blocks that are in the view range
2868 Should not optimize more here as we want to auto-update
2869 all changed nodes in viewing range at the next step.
2872 s16 viewing_range_nodes;
2873 bool viewing_range_all;
2875 JMutexAutoLock lock(m_range_mutex);
2876 viewing_range_nodes = m_viewing_range_nodes;
2877 viewing_range_all = m_viewing_range_all;
2880 m_camera_mutex.Lock();
2881 v3f camera_position = m_camera_position;
2882 v3f camera_direction = m_camera_direction;
2883 m_camera_mutex.Unlock();
2886 Get all blocks and draw all visible ones
2889 v3s16 cam_pos_nodes(
2890 camera_position.X / BS,
2891 camera_position.Y / BS,
2892 camera_position.Z / BS);
2894 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2896 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2897 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2899 // Take a fair amount as we will be dropping more out later
2901 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2902 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2903 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2905 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2906 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2907 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2909 u32 vertex_count = 0;
2911 // For limiting number of mesh updates per frame
2912 u32 mesh_update_count = 0;
2914 //NOTE: The sectors map should be locked but we're not doing it
2915 // because it'd cause too much delays
2917 int timecheck_counter = 0;
2919 core::map<v2s16, MapSector*>::Iterator si;
2920 si = m_sectors.getIterator();
2921 for(; si.atEnd() == false; si++)
2924 timecheck_counter++;
2925 if(timecheck_counter > 50)
2927 int time2 = time(0);
2928 if(time2 > time1 + 4)
2930 dstream<<"ClientMap::renderMap(): "
2931 "Rendering takes ages, returning."
2938 MapSector *sector = si.getNode()->getValue();
2939 v2s16 sp = sector->getPos();
2941 if(viewing_range_all == false)
2943 if(sp.X < p_blocks_min.X
2944 || sp.X > p_blocks_max.X
2945 || sp.Y < p_blocks_min.Z
2946 || sp.Y > p_blocks_max.Z)
2950 core::list< MapBlock * > sectorblocks;
2951 sector->getBlocks(sectorblocks);
2957 core::list< MapBlock * >::Iterator i;
2958 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2960 MapBlock *block = *i;
2963 Compare block position to camera position, skip
2964 if not seen on display
2967 v3s16 blockpos_nodes = block->getPosRelative();
2969 // Block center position
2971 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2972 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2973 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2976 // Block position relative to camera
2977 v3f blockpos_relative = blockpos - camera_position;
2979 // Distance in camera direction (+=front, -=back)
2980 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2983 f32 d = blockpos_relative.getLength();
2985 if(viewing_range_all == false)
2987 // If block is far away, don't draw it
2988 if(d > viewing_range_nodes * BS)
2989 // This is nicer when fog is used
2990 //if((dforward+d)/2 > viewing_range_nodes * BS)
2994 // Maximum radius of a block
2995 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2997 // If block is (nearly) touching the camera, don't
2998 // bother validating further (that is, render it anyway)
2999 if(d > block_max_radius * 1.5)
3001 // Cosine of the angle between the camera direction
3002 // and the block direction (camera_direction is an unit vector)
3003 f32 cosangle = dforward / d;
3005 // Compensate for the size of the block
3006 // (as the block has to be shown even if it's a bit off FOV)
3007 // This is an estimate.
3008 cosangle += block_max_radius / dforward;
3010 // If block is not in the field of view, skip it
3011 //if(cosangle < cos(FOV_ANGLE/2))
3012 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3017 Draw the faces of the block
3020 bool mesh_expired = false;
3023 JMutexAutoLock lock(block->mesh_mutex);
3025 mesh_expired = block->getMeshExpired();
3027 // Mesh has not been expired and there is no mesh:
3028 // block has no content
3029 if(block->mesh == NULL && mesh_expired == false)
3033 f32 faraway = BS*50;
3034 //f32 faraway = viewing_range_nodes * BS;
3037 This has to be done with the mesh_mutex unlocked
3039 if(mesh_expired && mesh_update_count < 6
3040 && (d < faraway || mesh_update_count < 3))
3041 //if(mesh_expired && mesh_update_count < 4)
3043 mesh_update_count++;
3045 // Mesh has been expired: generate new mesh
3046 //block->updateMeshes(daynight_i);
3047 block->updateMesh(daynight_ratio);
3049 mesh_expired = false;
3053 Don't draw an expired mesh that is far away
3055 /*if(mesh_expired && d >= faraway)
3058 // Instead, delete it
3059 JMutexAutoLock lock(block->mesh_mutex);
3062 block->mesh->drop();
3065 // And continue to next block
3070 JMutexAutoLock lock(block->mesh_mutex);
3072 scene::SMesh *mesh = block->mesh;
3077 u32 c = mesh->getMeshBufferCount();
3079 for(u32 i=0; i<c; i++)
3081 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3082 const video::SMaterial& material = buf->getMaterial();
3083 video::IMaterialRenderer* rnd =
3084 driver->getMaterialRenderer(material.MaterialType);
3085 bool transparent = (rnd && rnd->isTransparent());
3086 // Render transparent on transparent pass and likewise.
3087 if(transparent == is_transparent_pass)
3089 driver->setMaterial(buf->getMaterial());
3090 driver->drawMeshBuffer(buf);
3091 vertex_count += buf->getVertexCount();
3095 } // foreach sectorblocks
3098 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3099 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3102 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3105 Add it to all blocks touching it
3108 v3s16(0,0,0), // this
3109 v3s16(0,0,1), // back
3110 v3s16(0,1,0), // top
3111 v3s16(1,0,0), // right
3112 v3s16(0,0,-1), // front
3113 v3s16(0,-1,0), // bottom
3114 v3s16(-1,0,0), // left
3116 for(u16 i=0; i<7; i++)
3118 v3s16 p2 = p + dirs[i];
3119 // Block position of neighbor (or requested) node
3120 v3s16 blockpos = getNodeBlockPos(p2);
3121 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3122 if(blockref == NULL)
3124 // Relative position of requested node
3125 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3126 blockref->setTempMod(relpos, mod);
3128 return getNodeBlockPos(p);
3130 v3s16 ClientMap::clearTempMod(v3s16 p)
3133 v3s16(0,0,0), // this
3134 v3s16(0,0,1), // back
3135 v3s16(0,1,0), // top
3136 v3s16(1,0,0), // right
3137 v3s16(0,0,-1), // front
3138 v3s16(0,-1,0), // bottom
3139 v3s16(-1,0,0), // left
3141 for(u16 i=0; i<7; i++)
3143 v3s16 p2 = p + dirs[i];
3144 // Block position of neighbor (or requested) node
3145 v3s16 blockpos = getNodeBlockPos(p2);
3146 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3147 if(blockref == NULL)
3149 // Relative position of requested node
3150 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3151 blockref->clearTempMod(relpos);
3153 return getNodeBlockPos(p);
3156 void ClientMap::PrintInfo(std::ostream &out)
3167 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3172 MapVoxelManipulator::~MapVoxelManipulator()
3174 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3179 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3181 TimeTaker timer1("emerge", &emerge_time);
3183 // Units of these are MapBlocks
3184 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3185 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3187 VoxelArea block_area_nodes
3188 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3190 addArea(block_area_nodes);
3192 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3193 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3194 for(s32 x=p_min.X; x<=p_max.X; x++)
3197 core::map<v3s16, bool>::Node *n;
3198 n = m_loaded_blocks.find(p);
3202 bool block_data_inexistent = false;
3205 TimeTaker timer1("emerge load", &emerge_load_time);
3207 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3208 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3211 dstream<<std::endl;*/
3213 MapBlock *block = m_map->getBlockNoCreate(p);
3214 if(block->isDummy())
3215 block_data_inexistent = true;
3217 block->copyTo(*this);
3219 catch(InvalidPositionException &e)
3221 block_data_inexistent = true;
3224 if(block_data_inexistent)
3226 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3227 // Fill with VOXELFLAG_INEXISTENT
3228 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3229 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3231 s32 i = m_area.index(a.MinEdge.X,y,z);
3232 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3236 m_loaded_blocks.insert(p, true);
3239 //dstream<<"emerge done"<<std::endl;
3244 void MapVoxelManipulator::emerge(VoxelArea a)
3246 TimeTaker timer1("emerge", &emerge_time);
3248 v3s16 size = a.getExtent();
3250 VoxelArea padded = a;
3251 padded.pad(m_area.getExtent() / 4);
3254 for(s16 z=0; z<size.Z; z++)
3255 for(s16 y=0; y<size.Y; y++)
3256 for(s16 x=0; x<size.X; x++)
3259 s32 i = m_area.index(a.MinEdge + p);
3260 // Don't touch nodes that have already been loaded
3261 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3265 TimeTaker timer1("emerge load", &emerge_load_time);
3266 MapNode n = m_map->getNode(a.MinEdge + p);
3270 catch(InvalidPositionException &e)
3272 m_flags[i] = VOXELFLAG_INEXISTENT;
3280 TODO: Add an option to only update eg. water and air nodes.
3281 This will make it interfere less with important stuff if
3284 void MapVoxelManipulator::blitBack
3285 (core::map<v3s16, MapBlock*> & modified_blocks)
3287 if(m_area.getExtent() == v3s16(0,0,0))
3290 //TimeTaker timer1("blitBack");
3293 Initialize block cache
3295 v3s16 blockpos_last;
3296 MapBlock *block = NULL;
3297 bool block_checked_in_modified = false;
3299 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3300 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3301 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3305 u8 f = m_flags[m_area.index(p)];
3306 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3309 MapNode &n = m_data[m_area.index(p)];
3311 v3s16 blockpos = getNodeBlockPos(p);
3316 if(block == NULL || blockpos != blockpos_last){
3317 block = m_map->getBlockNoCreate(blockpos);
3318 blockpos_last = blockpos;
3319 block_checked_in_modified = false;
3322 // Calculate relative position in block
3323 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3325 // Don't continue if nothing has changed here
3326 if(block->getNode(relpos) == n)
3329 //m_map->setNode(m_area.MinEdge + p, n);
3330 block->setNode(relpos, n);
3333 Make sure block is in modified_blocks
3335 if(block_checked_in_modified == false)
3337 modified_blocks[blockpos] = block;
3338 block_checked_in_modified = true;
3341 catch(InvalidPositionException &e)