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 u16 a = rand() % 16;
1995 u16 amount = coal_amount * a*a*a / 1000;
1996 for(s16 i=0; i<amount; i++)
1999 (rand()%(MAP_BLOCKSIZE-2))+1,
2000 (rand()%(MAP_BLOCKSIZE-2))+1,
2001 (rand()%(MAP_BLOCKSIZE-2))+1
2005 n.d = CONTENT_COALSTONE;
2007 //dstream<<"Adding coalstone"<<std::endl;
2009 //if(is_ground_content(block->getNode(cp).d))
2010 if(block->getNode(cp).d == CONTENT_STONE)
2012 block->setNode(cp, n);
2014 for(u16 i=0; i<26; i++)
2016 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2017 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2019 block->setNode(cp+g_26dirs[i], n);
2026 Create a few rats in empty blocks underground
2030 //for(u16 i=0; i<2; i++)
2033 (rand()%(MAP_BLOCKSIZE-2))+1,
2034 (rand()%(MAP_BLOCKSIZE-2))+1,
2035 (rand()%(MAP_BLOCKSIZE-2))+1
2038 // Check that the place is empty
2039 //if(!is_ground_content(block->getNode(cp).d))
2042 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2043 block->addObject(obj);
2049 Add block to sector.
2051 sector->insertBlock(block);
2057 // An y-wise container of changed blocks
2058 core::map<s16, MapBlock*> changed_blocks_sector;
2061 Check if any sector's objects can be placed now.
2064 core::map<v3s16, u8> *objects = sector->getObjects();
2065 core::list<v3s16> objects_to_remove;
2066 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2067 i.atEnd() == false; i++)
2069 v3s16 p = i.getNode()->getKey();
2071 u8 d = i.getNode()->getValue();
2073 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2078 if(d == SECTOR_OBJECT_TEST)
2080 if(sector->isValidArea(p + v3s16(0,0,0),
2081 p + v3s16(0,0,0), &changed_blocks_sector))
2084 n.d = CONTENT_TORCH;
2085 sector->setNode(p, n);
2086 objects_to_remove.push_back(p);
2089 else if(d == SECTOR_OBJECT_TREE_1)
2091 v3s16 p_min = p + v3s16(-1,0,-1);
2092 v3s16 p_max = p + v3s16(1,4,1);
2093 if(sector->isValidArea(p_min, p_max,
2094 &changed_blocks_sector))
2098 sector->setNode(p+v3s16(0,0,0), n);
2099 sector->setNode(p+v3s16(0,1,0), n);
2100 sector->setNode(p+v3s16(0,2,0), n);
2101 sector->setNode(p+v3s16(0,3,0), n);
2103 n.d = CONTENT_LEAVES;
2105 sector->setNode(p+v3s16(0,4,0), n);
2107 sector->setNode(p+v3s16(-1,4,0), n);
2108 sector->setNode(p+v3s16(1,4,0), n);
2109 sector->setNode(p+v3s16(0,4,-1), n);
2110 sector->setNode(p+v3s16(0,4,1), n);
2111 sector->setNode(p+v3s16(1,4,1), n);
2112 sector->setNode(p+v3s16(-1,4,1), n);
2113 sector->setNode(p+v3s16(-1,4,-1), n);
2114 sector->setNode(p+v3s16(1,4,-1), n);
2116 sector->setNode(p+v3s16(-1,3,0), n);
2117 sector->setNode(p+v3s16(1,3,0), n);
2118 sector->setNode(p+v3s16(0,3,-1), n);
2119 sector->setNode(p+v3s16(0,3,1), n);
2120 sector->setNode(p+v3s16(1,3,1), n);
2121 sector->setNode(p+v3s16(-1,3,1), n);
2122 sector->setNode(p+v3s16(-1,3,-1), n);
2123 sector->setNode(p+v3s16(1,3,-1), n);
2125 objects_to_remove.push_back(p);
2127 // Lighting has to be recalculated for this one.
2128 sector->getBlocksInArea(p_min, p_max,
2129 lighting_invalidated_blocks);
2132 else if(d == SECTOR_OBJECT_BUSH_1)
2134 if(sector->isValidArea(p + v3s16(0,0,0),
2135 p + v3s16(0,0,0), &changed_blocks_sector))
2138 n.d = CONTENT_LEAVES;
2139 sector->setNode(p+v3s16(0,0,0), n);
2141 objects_to_remove.push_back(p);
2144 else if(d == SECTOR_OBJECT_RAVINE)
2147 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2148 v3s16 p_max = p + v3s16(6,6,6);
2149 if(sector->isValidArea(p_min, p_max,
2150 &changed_blocks_sector))
2153 n.d = CONTENT_STONE;
2156 s16 depth = maxdepth + (rand()%10);
2158 s16 minz = -6 - (-2);
2160 for(s16 x=-6; x<=6; x++)
2162 z += -1 + (rand()%3);
2167 for(s16 y=depth+(rand()%2); y<=6; y++)
2169 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2172 v3s16 p2 = p + v3s16(x,y,z-2);
2173 if(is_ground_content(sector->getNode(p2).d)
2174 && !is_mineral(sector->getNode(p2).d))
2175 sector->setNode(p2, n);
2178 v3s16 p2 = p + v3s16(x,y,z-1);
2179 if(is_ground_content(sector->getNode(p2).d)
2180 && !is_mineral(sector->getNode(p2).d))
2181 sector->setNode(p2, n2);
2184 v3s16 p2 = p + v3s16(x,y,z+0);
2185 if(is_ground_content(sector->getNode(p2).d)
2186 && !is_mineral(sector->getNode(p2).d))
2187 sector->setNode(p2, n2);
2190 v3s16 p2 = p + v3s16(x,y,z+1);
2191 if(is_ground_content(sector->getNode(p2).d)
2192 && !is_mineral(sector->getNode(p2).d))
2193 sector->setNode(p2, n);
2196 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2197 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2201 objects_to_remove.push_back(p);
2203 // Lighting has to be recalculated for this one.
2204 sector->getBlocksInArea(p_min, p_max,
2205 lighting_invalidated_blocks);
2210 dstream<<"ServerMap::emergeBlock(): "
2211 "Invalid heightmap object"
2216 catch(InvalidPositionException &e)
2218 dstream<<"WARNING: "<<__FUNCTION_NAME
2219 <<": while inserting object "<<(int)d
2220 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2221 <<" InvalidPositionException.what()="
2222 <<e.what()<<std::endl;
2223 // This is not too fatal and seems to happen sometimes.
2228 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2229 i != objects_to_remove.end(); i++)
2231 objects->remove(*i);
2234 for(core::map<s16, MapBlock*>::Iterator
2235 i = changed_blocks_sector.getIterator();
2236 i.atEnd() == false; i++)
2238 MapBlock *block = i.getNode()->getValue();
2240 changed_blocks.insert(block->getPos(), block);
2246 void ServerMap::createDir(std::string path)
2248 if(fs::CreateDir(path) == false)
2250 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2251 <<"\""<<path<<"\""<<std::endl;
2252 throw BaseException("ServerMap failed to create directory");
2256 std::string ServerMap::getSectorSubDir(v2s16 pos)
2259 snprintf(cc, 9, "%.4x%.4x",
2260 (unsigned int)pos.X&0xffff,
2261 (unsigned int)pos.Y&0xffff);
2263 return std::string(cc);
2266 std::string ServerMap::getSectorDir(v2s16 pos)
2268 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2271 v2s16 ServerMap::getSectorPos(std::string dirname)
2273 if(dirname.size() != 8)
2274 throw InvalidFilenameException("Invalid sector directory name");
2276 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2278 throw InvalidFilenameException("Invalid sector directory name");
2279 v2s16 pos((s16)x, (s16)y);
2283 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2285 v2s16 p2d = getSectorPos(sectordir);
2287 if(blockfile.size() != 4){
2288 throw InvalidFilenameException("Invalid block filename");
2291 int r = sscanf(blockfile.c_str(), "%4x", &y);
2293 throw InvalidFilenameException("Invalid block filename");
2294 return v3s16(p2d.X, y, p2d.Y);
2298 #define ENABLE_SECTOR_SAVING 1
2299 #define ENABLE_SECTOR_LOADING 1
2300 #define ENABLE_BLOCK_SAVING 1
2301 #define ENABLE_BLOCK_LOADING 1
2303 void ServerMap::save(bool only_changed)
2305 DSTACK(__FUNCTION_NAME);
2306 if(m_map_saving_enabled == false)
2308 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2312 if(only_changed == false)
2313 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2316 saveMasterHeightmap();
2318 u32 sector_meta_count = 0;
2319 u32 block_count = 0;
2322 JMutexAutoLock lock(m_sector_mutex);
2324 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2325 for(; i.atEnd() == false; i++)
2327 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2328 assert(sector->getId() == MAPSECTOR_SERVER);
2330 if(ENABLE_SECTOR_SAVING)
2332 if(sector->differs_from_disk || only_changed == false)
2334 saveSectorMeta(sector);
2335 sector_meta_count++;
2338 if(ENABLE_BLOCK_SAVING)
2340 core::list<MapBlock*> blocks;
2341 sector->getBlocks(blocks);
2342 core::list<MapBlock*>::Iterator j;
2343 for(j=blocks.begin(); j!=blocks.end(); j++)
2345 MapBlock *block = *j;
2346 if(block->getChangedFlag() || only_changed == false)
2358 Only print if something happened or saved whole map
2360 if(only_changed == false || sector_meta_count != 0
2361 || block_count != 0)
2363 dstream<<DTIME<<"ServerMap: Written: "
2364 <<sector_meta_count<<" sector metadata files, "
2365 <<block_count<<" block files"
2370 void ServerMap::loadAll()
2372 DSTACK(__FUNCTION_NAME);
2373 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2375 loadMasterHeightmap();
2377 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2379 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2381 JMutexAutoLock lock(m_sector_mutex);
2384 s32 printed_counter = -100000;
2385 s32 count = list.size();
2387 std::vector<fs::DirListNode>::iterator i;
2388 for(i=list.begin(); i!=list.end(); i++)
2390 if(counter > printed_counter + 10)
2392 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2393 printed_counter = counter;
2397 MapSector *sector = NULL;
2399 // We want directories
2403 sector = loadSectorMeta(i->name);
2405 catch(InvalidFilenameException &e)
2407 // This catches unknown crap in directory
2410 if(ENABLE_BLOCK_LOADING)
2412 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2413 (m_savedir+"/sectors/"+i->name);
2414 std::vector<fs::DirListNode>::iterator i2;
2415 for(i2=list2.begin(); i2!=list2.end(); i2++)
2421 loadBlock(i->name, i2->name, sector);
2423 catch(InvalidFilenameException &e)
2425 // This catches unknown crap in directory
2430 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2433 void ServerMap::saveMasterHeightmap()
2435 DSTACK(__FUNCTION_NAME);
2436 createDir(m_savedir);
2438 std::string fullpath = m_savedir + "/master_heightmap";
2439 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2440 if(o.good() == false)
2441 throw FileNotGoodException("Cannot open master heightmap");
2443 // Format used for writing
2444 u8 version = SER_FMT_VER_HIGHEST;
2447 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2449 [0] u8 serialization version
2450 [1] X master heightmap
2452 u32 fullsize = 1 + hmdata.getSize();
2453 SharedBuffer<u8> data(fullsize);
2456 memcpy(&data[1], *hmdata, hmdata.getSize());
2458 o.write((const char*)*data, fullsize);
2461 m_heightmap->serialize(o, version);
2464 void ServerMap::loadMasterHeightmap()
2466 DSTACK(__FUNCTION_NAME);
2467 std::string fullpath = m_savedir + "/master_heightmap";
2468 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2469 if(is.good() == false)
2470 throw FileNotGoodException("Cannot open master heightmap");
2472 if(m_heightmap != NULL)
2475 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2478 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2480 DSTACK(__FUNCTION_NAME);
2481 // Format used for writing
2482 u8 version = SER_FMT_VER_HIGHEST;
2484 v2s16 pos = sector->getPos();
2485 createDir(m_savedir);
2486 createDir(m_savedir+"/sectors");
2487 std::string dir = getSectorDir(pos);
2490 std::string fullpath = dir + "/heightmap";
2491 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2492 if(o.good() == false)
2493 throw FileNotGoodException("Cannot open master heightmap");
2495 sector->serialize(o, version);
2497 sector->differs_from_disk = false;
2500 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2502 DSTACK(__FUNCTION_NAME);
2504 v2s16 p2d = getSectorPos(dirname);
2505 std::string dir = m_savedir + "/sectors/" + dirname;
2507 std::string fullpath = dir + "/heightmap";
2508 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2509 if(is.good() == false)
2510 throw FileNotGoodException("Cannot open sector heightmap");
2512 ServerMapSector *sector = ServerMapSector::deSerialize
2513 (is, this, p2d, &m_hwrapper, m_sectors);
2515 sector->differs_from_disk = false;
2520 bool ServerMap::loadSectorFull(v2s16 p2d)
2522 DSTACK(__FUNCTION_NAME);
2523 std::string sectorsubdir = getSectorSubDir(p2d);
2525 MapSector *sector = NULL;
2527 JMutexAutoLock lock(m_sector_mutex);
2530 sector = loadSectorMeta(sectorsubdir);
2532 catch(InvalidFilenameException &e)
2536 catch(FileNotGoodException &e)
2540 catch(std::exception &e)
2545 if(ENABLE_BLOCK_LOADING)
2547 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2548 (m_savedir+"/sectors/"+sectorsubdir);
2549 std::vector<fs::DirListNode>::iterator i2;
2550 for(i2=list2.begin(); i2!=list2.end(); i2++)
2556 loadBlock(sectorsubdir, i2->name, sector);
2558 catch(InvalidFilenameException &e)
2560 // This catches unknown crap in directory
2568 bool ServerMap::deFlushSector(v2s16 p2d)
2570 DSTACK(__FUNCTION_NAME);
2571 // See if it already exists in memory
2573 MapSector *sector = getSectorNoGenerate(p2d);
2576 catch(InvalidPositionException &e)
2579 Try to load the sector from disk.
2581 if(loadSectorFull(p2d) == true)
2590 void ServerMap::saveBlock(MapBlock *block)
2592 DSTACK(__FUNCTION_NAME);
2594 Dummy blocks are not written
2596 if(block->isDummy())
2598 /*v3s16 p = block->getPos();
2599 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2600 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2604 // Format used for writing
2605 u8 version = SER_FMT_VER_HIGHEST;
2607 v3s16 p3d = block->getPos();
2608 v2s16 p2d(p3d.X, p3d.Z);
2609 createDir(m_savedir);
2610 createDir(m_savedir+"/sectors");
2611 std::string dir = getSectorDir(p2d);
2614 // Block file is map/sectors/xxxxxxxx/xxxx
2616 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2617 std::string fullpath = dir + "/" + cc;
2618 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2619 if(o.good() == false)
2620 throw FileNotGoodException("Cannot open block data");
2623 [0] u8 serialization version
2626 o.write((char*)&version, 1);
2628 block->serialize(o, version);
2631 Versions up from 9 have block objects.
2635 block->serializeObjects(o, version);
2638 // We just wrote it to the disk
2639 block->resetChangedFlag();
2642 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2644 DSTACK(__FUNCTION_NAME);
2648 // Block file is map/sectors/xxxxxxxx/xxxx
2649 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2650 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2651 if(is.good() == false)
2652 throw FileNotGoodException("Cannot open block file");
2654 v3s16 p3d = getBlockPos(sectordir, blockfile);
2655 v2s16 p2d(p3d.X, p3d.Z);
2657 assert(sector->getPos() == p2d);
2659 u8 version = SER_FMT_VER_INVALID;
2660 is.read((char*)&version, 1);
2662 /*u32 block_size = MapBlock::serializedLength(version);
2663 SharedBuffer<u8> data(block_size);
2664 is.read((char*)*data, block_size);*/
2666 // This will always return a sector because we're the server
2667 //MapSector *sector = emergeSector(p2d);
2669 MapBlock *block = NULL;
2670 bool created_new = false;
2672 block = sector->getBlockNoCreate(p3d.Y);
2674 catch(InvalidPositionException &e)
2676 block = sector->createBlankBlockNoInsert(p3d.Y);
2680 // deserialize block data
2681 block->deSerialize(is, version);
2684 Versions up from 9 have block objects.
2688 block->updateObjects(is, version, NULL, 0);
2692 sector->insertBlock(block);
2695 Convert old formats to new and save
2698 // Save old format blocks in new format
2699 if(version < SER_FMT_VER_HIGHEST)
2704 // We just loaded it from the disk, so it's up-to-date.
2705 block->resetChangedFlag();
2708 catch(SerializationError &e)
2710 dstream<<"WARNING: Invalid block data on disk "
2711 "(SerializationError). Ignoring."
2716 // Gets from master heightmap
2717 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2719 assert(m_heightmap != NULL);
2727 corners[0] = m_heightmap->getGroundHeight
2728 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2729 corners[1] = m_heightmap->getGroundHeight
2730 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2731 corners[2] = m_heightmap->getGroundHeight
2732 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2733 corners[3] = m_heightmap->getGroundHeight
2734 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2737 void ServerMap::PrintInfo(std::ostream &out)
2748 ClientMap::ClientMap(
2750 JMutex &range_mutex,
2751 s16 &viewing_range_nodes,
2752 bool &viewing_range_all,
2753 scene::ISceneNode* parent,
2754 scene::ISceneManager* mgr,
2758 scene::ISceneNode(parent, mgr, id),
2761 m_range_mutex(range_mutex),
2762 m_viewing_range_nodes(viewing_range_nodes),
2763 m_viewing_range_all(viewing_range_all)
2767 /*m_box = core::aabbox3d<f32>(0,0,0,
2768 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2769 /*m_box = core::aabbox3d<f32>(0,0,0,
2770 map->getSizeNodes().X * BS,
2771 map->getSizeNodes().Y * BS,
2772 map->getSizeNodes().Z * BS);*/
2773 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2774 BS*1000000,BS*1000000,BS*1000000);
2776 //setPosition(v3f(BS,BS,BS));
2779 ClientMap::~ClientMap()
2781 JMutexAutoLock lock(mesh_mutex);
2790 MapSector * ClientMap::emergeSector(v2s16 p2d)
2792 DSTACK(__FUNCTION_NAME);
2793 // Check that it doesn't exist already
2795 return getSectorNoGenerate(p2d);
2797 catch(InvalidPositionException &e)
2801 // Create a sector with no heightmaps
2802 ClientMapSector *sector = new ClientMapSector(this, p2d);
2805 JMutexAutoLock lock(m_sector_mutex);
2806 m_sectors.insert(p2d, sector);
2812 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2814 DSTACK(__FUNCTION_NAME);
2815 ClientMapSector *sector = NULL;
2817 JMutexAutoLock lock(m_sector_mutex);
2819 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2823 sector = (ClientMapSector*)n->getValue();
2824 assert(sector->getId() == MAPSECTOR_CLIENT);
2828 sector = new ClientMapSector(this, p2d);
2830 JMutexAutoLock lock(m_sector_mutex);
2831 m_sectors.insert(p2d, sector);
2835 sector->deSerialize(is);
2838 void ClientMap::OnRegisterSceneNode()
2842 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2843 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2846 ISceneNode::OnRegisterSceneNode();
2849 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2851 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2852 DSTACK(__FUNCTION_NAME);
2854 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2857 Get time for measuring timeout.
2859 Measuring time is very useful for long delays when the
2860 machine is swapping a lot.
2862 int time1 = time(0);
2864 //s32 daynight_i = m_client->getDayNightIndex();
2865 u32 daynight_ratio = m_client->getDayNightRatio();
2868 Collect all blocks that are in the view range
2870 Should not optimize more here as we want to auto-update
2871 all changed nodes in viewing range at the next step.
2874 s16 viewing_range_nodes;
2875 bool viewing_range_all;
2877 JMutexAutoLock lock(m_range_mutex);
2878 viewing_range_nodes = m_viewing_range_nodes;
2879 viewing_range_all = m_viewing_range_all;
2882 m_camera_mutex.Lock();
2883 v3f camera_position = m_camera_position;
2884 v3f camera_direction = m_camera_direction;
2885 m_camera_mutex.Unlock();
2888 Get all blocks and draw all visible ones
2891 v3s16 cam_pos_nodes(
2892 camera_position.X / BS,
2893 camera_position.Y / BS,
2894 camera_position.Z / BS);
2896 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2898 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2899 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2901 // Take a fair amount as we will be dropping more out later
2903 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2904 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2905 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2907 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2908 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2909 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2911 u32 vertex_count = 0;
2913 // For limiting number of mesh updates per frame
2914 u32 mesh_update_count = 0;
2916 //NOTE: The sectors map should be locked but we're not doing it
2917 // because it'd cause too much delays
2919 int timecheck_counter = 0;
2921 core::map<v2s16, MapSector*>::Iterator si;
2922 si = m_sectors.getIterator();
2923 for(; si.atEnd() == false; si++)
2926 timecheck_counter++;
2927 if(timecheck_counter > 50)
2929 int time2 = time(0);
2930 if(time2 > time1 + 4)
2932 dstream<<"ClientMap::renderMap(): "
2933 "Rendering takes ages, returning."
2940 MapSector *sector = si.getNode()->getValue();
2941 v2s16 sp = sector->getPos();
2943 if(viewing_range_all == false)
2945 if(sp.X < p_blocks_min.X
2946 || sp.X > p_blocks_max.X
2947 || sp.Y < p_blocks_min.Z
2948 || sp.Y > p_blocks_max.Z)
2952 core::list< MapBlock * > sectorblocks;
2953 sector->getBlocks(sectorblocks);
2959 core::list< MapBlock * >::Iterator i;
2960 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2962 MapBlock *block = *i;
2965 Compare block position to camera position, skip
2966 if not seen on display
2969 v3s16 blockpos_nodes = block->getPosRelative();
2971 // Block center position
2973 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2974 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2975 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2978 // Block position relative to camera
2979 v3f blockpos_relative = blockpos - camera_position;
2981 // Distance in camera direction (+=front, -=back)
2982 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2985 f32 d = blockpos_relative.getLength();
2987 if(viewing_range_all == false)
2989 // If block is far away, don't draw it
2990 if(d > viewing_range_nodes * BS)
2991 // This is nicer when fog is used
2992 //if((dforward+d)/2 > viewing_range_nodes * BS)
2996 // Maximum radius of a block
2997 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2999 // If block is (nearly) touching the camera, don't
3000 // bother validating further (that is, render it anyway)
3001 if(d > block_max_radius * 1.5)
3003 // Cosine of the angle between the camera direction
3004 // and the block direction (camera_direction is an unit vector)
3005 f32 cosangle = dforward / d;
3007 // Compensate for the size of the block
3008 // (as the block has to be shown even if it's a bit off FOV)
3009 // This is an estimate.
3010 cosangle += block_max_radius / dforward;
3012 // If block is not in the field of view, skip it
3013 //if(cosangle < cos(FOV_ANGLE/2))
3014 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3019 Draw the faces of the block
3022 bool mesh_expired = false;
3025 JMutexAutoLock lock(block->mesh_mutex);
3027 mesh_expired = block->getMeshExpired();
3029 // Mesh has not been expired and there is no mesh:
3030 // block has no content
3031 if(block->mesh == NULL && mesh_expired == false)
3035 f32 faraway = BS*50;
3036 //f32 faraway = viewing_range_nodes * BS;
3039 This has to be done with the mesh_mutex unlocked
3041 if(mesh_expired && mesh_update_count < 6
3042 && (d < faraway || mesh_update_count < 3))
3043 //if(mesh_expired && mesh_update_count < 4)
3045 mesh_update_count++;
3047 // Mesh has been expired: generate new mesh
3048 //block->updateMeshes(daynight_i);
3049 block->updateMesh(daynight_ratio);
3051 mesh_expired = false;
3055 Don't draw an expired mesh that is far away
3057 /*if(mesh_expired && d >= faraway)
3060 // Instead, delete it
3061 JMutexAutoLock lock(block->mesh_mutex);
3064 block->mesh->drop();
3067 // And continue to next block
3072 JMutexAutoLock lock(block->mesh_mutex);
3074 scene::SMesh *mesh = block->mesh;
3079 u32 c = mesh->getMeshBufferCount();
3081 for(u32 i=0; i<c; i++)
3083 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3084 const video::SMaterial& material = buf->getMaterial();
3085 video::IMaterialRenderer* rnd =
3086 driver->getMaterialRenderer(material.MaterialType);
3087 bool transparent = (rnd && rnd->isTransparent());
3088 // Render transparent on transparent pass and likewise.
3089 if(transparent == is_transparent_pass)
3091 driver->setMaterial(buf->getMaterial());
3092 driver->drawMeshBuffer(buf);
3093 vertex_count += buf->getVertexCount();
3097 } // foreach sectorblocks
3100 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3101 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3104 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3107 Add it to all blocks touching it
3110 v3s16(0,0,0), // this
3111 v3s16(0,0,1), // back
3112 v3s16(0,1,0), // top
3113 v3s16(1,0,0), // right
3114 v3s16(0,0,-1), // front
3115 v3s16(0,-1,0), // bottom
3116 v3s16(-1,0,0), // left
3118 for(u16 i=0; i<7; i++)
3120 v3s16 p2 = p + dirs[i];
3121 // Block position of neighbor (or requested) node
3122 v3s16 blockpos = getNodeBlockPos(p2);
3123 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3124 if(blockref == NULL)
3126 // Relative position of requested node
3127 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3128 blockref->setTempMod(relpos, mod);
3130 return getNodeBlockPos(p);
3132 v3s16 ClientMap::clearTempMod(v3s16 p)
3135 v3s16(0,0,0), // this
3136 v3s16(0,0,1), // back
3137 v3s16(0,1,0), // top
3138 v3s16(1,0,0), // right
3139 v3s16(0,0,-1), // front
3140 v3s16(0,-1,0), // bottom
3141 v3s16(-1,0,0), // left
3143 for(u16 i=0; i<7; i++)
3145 v3s16 p2 = p + dirs[i];
3146 // Block position of neighbor (or requested) node
3147 v3s16 blockpos = getNodeBlockPos(p2);
3148 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3149 if(blockref == NULL)
3151 // Relative position of requested node
3152 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3153 blockref->clearTempMod(relpos);
3155 return getNodeBlockPos(p);
3158 void ClientMap::PrintInfo(std::ostream &out)
3169 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3174 MapVoxelManipulator::~MapVoxelManipulator()
3176 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3181 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3183 TimeTaker timer1("emerge", &emerge_time);
3185 // Units of these are MapBlocks
3186 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3187 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3189 VoxelArea block_area_nodes
3190 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3192 addArea(block_area_nodes);
3194 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3195 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3196 for(s32 x=p_min.X; x<=p_max.X; x++)
3199 core::map<v3s16, bool>::Node *n;
3200 n = m_loaded_blocks.find(p);
3204 bool block_data_inexistent = false;
3207 TimeTaker timer1("emerge load", &emerge_load_time);
3209 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3210 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3213 dstream<<std::endl;*/
3215 MapBlock *block = m_map->getBlockNoCreate(p);
3216 if(block->isDummy())
3217 block_data_inexistent = true;
3219 block->copyTo(*this);
3221 catch(InvalidPositionException &e)
3223 block_data_inexistent = true;
3226 if(block_data_inexistent)
3228 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3229 // Fill with VOXELFLAG_INEXISTENT
3230 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3231 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3233 s32 i = m_area.index(a.MinEdge.X,y,z);
3234 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3238 m_loaded_blocks.insert(p, true);
3241 //dstream<<"emerge done"<<std::endl;
3246 void MapVoxelManipulator::emerge(VoxelArea a)
3248 TimeTaker timer1("emerge", &emerge_time);
3250 v3s16 size = a.getExtent();
3252 VoxelArea padded = a;
3253 padded.pad(m_area.getExtent() / 4);
3256 for(s16 z=0; z<size.Z; z++)
3257 for(s16 y=0; y<size.Y; y++)
3258 for(s16 x=0; x<size.X; x++)
3261 s32 i = m_area.index(a.MinEdge + p);
3262 // Don't touch nodes that have already been loaded
3263 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3267 TimeTaker timer1("emerge load", &emerge_load_time);
3268 MapNode n = m_map->getNode(a.MinEdge + p);
3272 catch(InvalidPositionException &e)
3274 m_flags[i] = VOXELFLAG_INEXISTENT;
3282 TODO: Add an option to only update eg. water and air nodes.
3283 This will make it interfere less with important stuff if
3286 void MapVoxelManipulator::blitBack
3287 (core::map<v3s16, MapBlock*> & modified_blocks)
3289 if(m_area.getExtent() == v3s16(0,0,0))
3292 //TimeTaker timer1("blitBack");
3295 Initialize block cache
3297 v3s16 blockpos_last;
3298 MapBlock *block = NULL;
3299 bool block_checked_in_modified = false;
3301 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3302 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3303 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3307 u8 f = m_flags[m_area.index(p)];
3308 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3311 MapNode &n = m_data[m_area.index(p)];
3313 v3s16 blockpos = getNodeBlockPos(p);
3318 if(block == NULL || blockpos != blockpos_last){
3319 block = m_map->getBlockNoCreate(blockpos);
3320 blockpos_last = blockpos;
3321 block_checked_in_modified = false;
3324 // Calculate relative position in block
3325 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3327 // Don't continue if nothing has changed here
3328 if(block->getNode(relpos) == n)
3331 //m_map->setNode(m_area.MinEdge + p, n);
3332 block->setNode(relpos, n);
3335 Make sure block is in modified_blocks
3337 if(block_checked_in_modified == false)
3339 modified_blocks[blockpos] = block;
3340 block_checked_in_modified = true;
3343 catch(InvalidPositionException &e)