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(is_underground)
1951 if(some_part_underground)
1953 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1954 for(s16 i=0; i<underground_level*3; i++)
1959 (rand()%(MAP_BLOCKSIZE-2))+1,
1960 (rand()%(MAP_BLOCKSIZE-2))+1,
1961 (rand()%(MAP_BLOCKSIZE-2))+1
1967 //if(is_ground_content(block->getNode(cp).d))
1968 if(block->getNode(cp).d == CONTENT_STONE)
1970 block->setNode(cp, n);
1972 for(u16 i=0; i<26; i++)
1974 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1975 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1977 block->setNode(cp+g_26dirs[i], n);
1984 Create a few rats in empty blocks underground
1988 //for(u16 i=0; i<2; i++)
1991 (rand()%(MAP_BLOCKSIZE-2))+1,
1992 (rand()%(MAP_BLOCKSIZE-2))+1,
1993 (rand()%(MAP_BLOCKSIZE-2))+1
1996 // Check that the place is empty
1997 //if(!is_ground_content(block->getNode(cp).d))
2000 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2001 block->addObject(obj);
2007 Add block to sector.
2009 sector->insertBlock(block);
2015 // An y-wise container of changed blocks
2016 core::map<s16, MapBlock*> changed_blocks_sector;
2019 Check if any sector's objects can be placed now.
2022 core::map<v3s16, u8> *objects = sector->getObjects();
2023 core::list<v3s16> objects_to_remove;
2024 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2025 i.atEnd() == false; i++)
2027 v3s16 p = i.getNode()->getKey();
2029 u8 d = i.getNode()->getValue();
2031 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2036 if(d == SECTOR_OBJECT_TEST)
2038 if(sector->isValidArea(p + v3s16(0,0,0),
2039 p + v3s16(0,0,0), &changed_blocks_sector))
2042 n.d = CONTENT_TORCH;
2043 sector->setNode(p, n);
2044 objects_to_remove.push_back(p);
2047 else if(d == SECTOR_OBJECT_TREE_1)
2049 v3s16 p_min = p + v3s16(-1,0,-1);
2050 v3s16 p_max = p + v3s16(1,4,1);
2051 if(sector->isValidArea(p_min, p_max,
2052 &changed_blocks_sector))
2056 sector->setNode(p+v3s16(0,0,0), n);
2057 sector->setNode(p+v3s16(0,1,0), n);
2058 sector->setNode(p+v3s16(0,2,0), n);
2059 sector->setNode(p+v3s16(0,3,0), n);
2061 n.d = CONTENT_LEAVES;
2063 sector->setNode(p+v3s16(0,4,0), n);
2065 sector->setNode(p+v3s16(-1,4,0), n);
2066 sector->setNode(p+v3s16(1,4,0), n);
2067 sector->setNode(p+v3s16(0,4,-1), n);
2068 sector->setNode(p+v3s16(0,4,1), n);
2069 sector->setNode(p+v3s16(1,4,1), n);
2070 sector->setNode(p+v3s16(-1,4,1), n);
2071 sector->setNode(p+v3s16(-1,4,-1), n);
2072 sector->setNode(p+v3s16(1,4,-1), n);
2074 sector->setNode(p+v3s16(-1,3,0), n);
2075 sector->setNode(p+v3s16(1,3,0), n);
2076 sector->setNode(p+v3s16(0,3,-1), n);
2077 sector->setNode(p+v3s16(0,3,1), n);
2078 sector->setNode(p+v3s16(1,3,1), n);
2079 sector->setNode(p+v3s16(-1,3,1), n);
2080 sector->setNode(p+v3s16(-1,3,-1), n);
2081 sector->setNode(p+v3s16(1,3,-1), n);
2083 objects_to_remove.push_back(p);
2085 // Lighting has to be recalculated for this one.
2086 sector->getBlocksInArea(p_min, p_max,
2087 lighting_invalidated_blocks);
2090 else if(d == SECTOR_OBJECT_BUSH_1)
2092 if(sector->isValidArea(p + v3s16(0,0,0),
2093 p + v3s16(0,0,0), &changed_blocks_sector))
2096 n.d = CONTENT_LEAVES;
2097 sector->setNode(p+v3s16(0,0,0), n);
2099 objects_to_remove.push_back(p);
2102 else if(d == SECTOR_OBJECT_RAVINE)
2105 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2106 v3s16 p_max = p + v3s16(6,6,6);
2107 if(sector->isValidArea(p_min, p_max,
2108 &changed_blocks_sector))
2111 n.d = CONTENT_STONE;
2114 s16 depth = maxdepth + (rand()%10);
2116 s16 minz = -6 - (-2);
2118 for(s16 x=-6; x<=6; x++)
2120 z += -1 + (rand()%3);
2125 for(s16 y=depth+(rand()%2); y<=6; y++)
2127 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2130 v3s16 p2 = p + v3s16(x,y,z-2);
2131 if(is_ground_content(sector->getNode(p2).d))
2132 sector->setNode(p2, n);
2135 v3s16 p2 = p + v3s16(x,y,z-1);
2136 if(is_ground_content(sector->getNode(p2).d))
2137 sector->setNode(p2, n2);
2140 v3s16 p2 = p + v3s16(x,y,z+0);
2141 if(is_ground_content(sector->getNode(p2).d))
2142 sector->setNode(p2, n2);
2145 v3s16 p2 = p + v3s16(x,y,z+1);
2146 if(is_ground_content(sector->getNode(p2).d))
2147 sector->setNode(p2, n);
2150 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2151 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2155 objects_to_remove.push_back(p);
2157 // Lighting has to be recalculated for this one.
2158 sector->getBlocksInArea(p_min, p_max,
2159 lighting_invalidated_blocks);
2164 dstream<<"ServerMap::emergeBlock(): "
2165 "Invalid heightmap object"
2170 catch(InvalidPositionException &e)
2172 dstream<<"WARNING: "<<__FUNCTION_NAME
2173 <<": while inserting object "<<(int)d
2174 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2175 <<" InvalidPositionException.what()="
2176 <<e.what()<<std::endl;
2177 // This is not too fatal and seems to happen sometimes.
2182 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2183 i != objects_to_remove.end(); i++)
2185 objects->remove(*i);
2188 for(core::map<s16, MapBlock*>::Iterator
2189 i = changed_blocks_sector.getIterator();
2190 i.atEnd() == false; i++)
2192 MapBlock *block = i.getNode()->getValue();
2194 changed_blocks.insert(block->getPos(), block);
2200 void ServerMap::createDir(std::string path)
2202 if(fs::CreateDir(path) == false)
2204 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2205 <<"\""<<path<<"\""<<std::endl;
2206 throw BaseException("ServerMap failed to create directory");
2210 std::string ServerMap::getSectorSubDir(v2s16 pos)
2213 snprintf(cc, 9, "%.4x%.4x",
2214 (unsigned int)pos.X&0xffff,
2215 (unsigned int)pos.Y&0xffff);
2217 return std::string(cc);
2220 std::string ServerMap::getSectorDir(v2s16 pos)
2222 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2225 v2s16 ServerMap::getSectorPos(std::string dirname)
2227 if(dirname.size() != 8)
2228 throw InvalidFilenameException("Invalid sector directory name");
2230 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2232 throw InvalidFilenameException("Invalid sector directory name");
2233 v2s16 pos((s16)x, (s16)y);
2237 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2239 v2s16 p2d = getSectorPos(sectordir);
2241 if(blockfile.size() != 4){
2242 throw InvalidFilenameException("Invalid block filename");
2245 int r = sscanf(blockfile.c_str(), "%4x", &y);
2247 throw InvalidFilenameException("Invalid block filename");
2248 return v3s16(p2d.X, y, p2d.Y);
2252 #define ENABLE_SECTOR_SAVING 1
2253 #define ENABLE_SECTOR_LOADING 1
2254 #define ENABLE_BLOCK_SAVING 1
2255 #define ENABLE_BLOCK_LOADING 1
2257 void ServerMap::save(bool only_changed)
2259 DSTACK(__FUNCTION_NAME);
2260 if(m_map_saving_enabled == false)
2262 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2266 if(only_changed == false)
2267 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2270 saveMasterHeightmap();
2272 u32 sector_meta_count = 0;
2273 u32 block_count = 0;
2276 JMutexAutoLock lock(m_sector_mutex);
2278 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2279 for(; i.atEnd() == false; i++)
2281 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2282 assert(sector->getId() == MAPSECTOR_SERVER);
2284 if(ENABLE_SECTOR_SAVING)
2286 if(sector->differs_from_disk || only_changed == false)
2288 saveSectorMeta(sector);
2289 sector_meta_count++;
2292 if(ENABLE_BLOCK_SAVING)
2294 core::list<MapBlock*> blocks;
2295 sector->getBlocks(blocks);
2296 core::list<MapBlock*>::Iterator j;
2297 for(j=blocks.begin(); j!=blocks.end(); j++)
2299 MapBlock *block = *j;
2300 if(block->getChangedFlag() || only_changed == false)
2312 Only print if something happened or saved whole map
2314 if(only_changed == false || sector_meta_count != 0
2315 || block_count != 0)
2317 dstream<<DTIME<<"ServerMap: Written: "
2318 <<sector_meta_count<<" sector metadata files, "
2319 <<block_count<<" block files"
2324 void ServerMap::loadAll()
2326 DSTACK(__FUNCTION_NAME);
2327 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2329 loadMasterHeightmap();
2331 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2333 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2335 JMutexAutoLock lock(m_sector_mutex);
2338 s32 printed_counter = -100000;
2339 s32 count = list.size();
2341 std::vector<fs::DirListNode>::iterator i;
2342 for(i=list.begin(); i!=list.end(); i++)
2344 if(counter > printed_counter + 10)
2346 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2347 printed_counter = counter;
2351 MapSector *sector = NULL;
2353 // We want directories
2357 sector = loadSectorMeta(i->name);
2359 catch(InvalidFilenameException &e)
2361 // This catches unknown crap in directory
2364 if(ENABLE_BLOCK_LOADING)
2366 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2367 (m_savedir+"/sectors/"+i->name);
2368 std::vector<fs::DirListNode>::iterator i2;
2369 for(i2=list2.begin(); i2!=list2.end(); i2++)
2375 loadBlock(i->name, i2->name, sector);
2377 catch(InvalidFilenameException &e)
2379 // This catches unknown crap in directory
2384 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2387 void ServerMap::saveMasterHeightmap()
2389 DSTACK(__FUNCTION_NAME);
2390 createDir(m_savedir);
2392 std::string fullpath = m_savedir + "/master_heightmap";
2393 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2394 if(o.good() == false)
2395 throw FileNotGoodException("Cannot open master heightmap");
2397 // Format used for writing
2398 u8 version = SER_FMT_VER_HIGHEST;
2401 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2403 [0] u8 serialization version
2404 [1] X master heightmap
2406 u32 fullsize = 1 + hmdata.getSize();
2407 SharedBuffer<u8> data(fullsize);
2410 memcpy(&data[1], *hmdata, hmdata.getSize());
2412 o.write((const char*)*data, fullsize);
2415 m_heightmap->serialize(o, version);
2418 void ServerMap::loadMasterHeightmap()
2420 DSTACK(__FUNCTION_NAME);
2421 std::string fullpath = m_savedir + "/master_heightmap";
2422 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2423 if(is.good() == false)
2424 throw FileNotGoodException("Cannot open master heightmap");
2426 if(m_heightmap != NULL)
2429 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2432 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2434 DSTACK(__FUNCTION_NAME);
2435 // Format used for writing
2436 u8 version = SER_FMT_VER_HIGHEST;
2438 v2s16 pos = sector->getPos();
2439 createDir(m_savedir);
2440 createDir(m_savedir+"/sectors");
2441 std::string dir = getSectorDir(pos);
2444 std::string fullpath = dir + "/heightmap";
2445 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2446 if(o.good() == false)
2447 throw FileNotGoodException("Cannot open master heightmap");
2449 sector->serialize(o, version);
2451 sector->differs_from_disk = false;
2454 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2456 DSTACK(__FUNCTION_NAME);
2458 v2s16 p2d = getSectorPos(dirname);
2459 std::string dir = m_savedir + "/sectors/" + dirname;
2461 std::string fullpath = dir + "/heightmap";
2462 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2463 if(is.good() == false)
2464 throw FileNotGoodException("Cannot open sector heightmap");
2466 ServerMapSector *sector = ServerMapSector::deSerialize
2467 (is, this, p2d, &m_hwrapper, m_sectors);
2469 sector->differs_from_disk = false;
2474 bool ServerMap::loadSectorFull(v2s16 p2d)
2476 DSTACK(__FUNCTION_NAME);
2477 std::string sectorsubdir = getSectorSubDir(p2d);
2479 MapSector *sector = NULL;
2481 JMutexAutoLock lock(m_sector_mutex);
2484 sector = loadSectorMeta(sectorsubdir);
2486 catch(InvalidFilenameException &e)
2490 catch(FileNotGoodException &e)
2494 catch(std::exception &e)
2499 if(ENABLE_BLOCK_LOADING)
2501 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2502 (m_savedir+"/sectors/"+sectorsubdir);
2503 std::vector<fs::DirListNode>::iterator i2;
2504 for(i2=list2.begin(); i2!=list2.end(); i2++)
2510 loadBlock(sectorsubdir, i2->name, sector);
2512 catch(InvalidFilenameException &e)
2514 // This catches unknown crap in directory
2522 bool ServerMap::deFlushSector(v2s16 p2d)
2524 DSTACK(__FUNCTION_NAME);
2525 // See if it already exists in memory
2527 MapSector *sector = getSectorNoGenerate(p2d);
2530 catch(InvalidPositionException &e)
2533 Try to load the sector from disk.
2535 if(loadSectorFull(p2d) == true)
2544 void ServerMap::saveBlock(MapBlock *block)
2546 DSTACK(__FUNCTION_NAME);
2548 Dummy blocks are not written
2550 if(block->isDummy())
2552 /*v3s16 p = block->getPos();
2553 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2554 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2558 // Format used for writing
2559 u8 version = SER_FMT_VER_HIGHEST;
2561 v3s16 p3d = block->getPos();
2562 v2s16 p2d(p3d.X, p3d.Z);
2563 createDir(m_savedir);
2564 createDir(m_savedir+"/sectors");
2565 std::string dir = getSectorDir(p2d);
2568 // Block file is map/sectors/xxxxxxxx/xxxx
2570 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2571 std::string fullpath = dir + "/" + cc;
2572 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2573 if(o.good() == false)
2574 throw FileNotGoodException("Cannot open block data");
2577 [0] u8 serialization version
2580 o.write((char*)&version, 1);
2582 block->serialize(o, version);
2585 Versions up from 9 have block objects.
2589 block->serializeObjects(o, version);
2592 // We just wrote it to the disk
2593 block->resetChangedFlag();
2596 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2598 DSTACK(__FUNCTION_NAME);
2602 // Block file is map/sectors/xxxxxxxx/xxxx
2603 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2604 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2605 if(is.good() == false)
2606 throw FileNotGoodException("Cannot open block file");
2608 v3s16 p3d = getBlockPos(sectordir, blockfile);
2609 v2s16 p2d(p3d.X, p3d.Z);
2611 assert(sector->getPos() == p2d);
2613 u8 version = SER_FMT_VER_INVALID;
2614 is.read((char*)&version, 1);
2616 /*u32 block_size = MapBlock::serializedLength(version);
2617 SharedBuffer<u8> data(block_size);
2618 is.read((char*)*data, block_size);*/
2620 // This will always return a sector because we're the server
2621 //MapSector *sector = emergeSector(p2d);
2623 MapBlock *block = NULL;
2624 bool created_new = false;
2626 block = sector->getBlockNoCreate(p3d.Y);
2628 catch(InvalidPositionException &e)
2630 block = sector->createBlankBlockNoInsert(p3d.Y);
2634 // deserialize block data
2635 block->deSerialize(is, version);
2638 Versions up from 9 have block objects.
2642 block->updateObjects(is, version, NULL, 0);
2646 sector->insertBlock(block);
2649 Convert old formats to new and save
2652 // Save old format blocks in new format
2653 if(version < SER_FMT_VER_HIGHEST)
2658 // We just loaded it from the disk, so it's up-to-date.
2659 block->resetChangedFlag();
2662 catch(SerializationError &e)
2664 dstream<<"WARNING: Invalid block data on disk "
2665 "(SerializationError). Ignoring."
2670 // Gets from master heightmap
2671 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2673 assert(m_heightmap != NULL);
2681 corners[0] = m_heightmap->getGroundHeight
2682 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2683 corners[1] = m_heightmap->getGroundHeight
2684 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2685 corners[2] = m_heightmap->getGroundHeight
2686 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2687 corners[3] = m_heightmap->getGroundHeight
2688 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2691 void ServerMap::PrintInfo(std::ostream &out)
2702 ClientMap::ClientMap(
2704 JMutex &range_mutex,
2705 s16 &viewing_range_nodes,
2706 bool &viewing_range_all,
2707 scene::ISceneNode* parent,
2708 scene::ISceneManager* mgr,
2712 scene::ISceneNode(parent, mgr, id),
2715 m_range_mutex(range_mutex),
2716 m_viewing_range_nodes(viewing_range_nodes),
2717 m_viewing_range_all(viewing_range_all)
2721 /*m_box = core::aabbox3d<f32>(0,0,0,
2722 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2723 /*m_box = core::aabbox3d<f32>(0,0,0,
2724 map->getSizeNodes().X * BS,
2725 map->getSizeNodes().Y * BS,
2726 map->getSizeNodes().Z * BS);*/
2727 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2728 BS*1000000,BS*1000000,BS*1000000);
2730 //setPosition(v3f(BS,BS,BS));
2733 ClientMap::~ClientMap()
2735 JMutexAutoLock lock(mesh_mutex);
2744 MapSector * ClientMap::emergeSector(v2s16 p2d)
2746 DSTACK(__FUNCTION_NAME);
2747 // Check that it doesn't exist already
2749 return getSectorNoGenerate(p2d);
2751 catch(InvalidPositionException &e)
2755 // Create a sector with no heightmaps
2756 ClientMapSector *sector = new ClientMapSector(this, p2d);
2759 JMutexAutoLock lock(m_sector_mutex);
2760 m_sectors.insert(p2d, sector);
2766 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2768 DSTACK(__FUNCTION_NAME);
2769 ClientMapSector *sector = NULL;
2771 JMutexAutoLock lock(m_sector_mutex);
2773 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2777 sector = (ClientMapSector*)n->getValue();
2778 assert(sector->getId() == MAPSECTOR_CLIENT);
2782 sector = new ClientMapSector(this, p2d);
2784 JMutexAutoLock lock(m_sector_mutex);
2785 m_sectors.insert(p2d, sector);
2789 sector->deSerialize(is);
2792 void ClientMap::OnRegisterSceneNode()
2796 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2797 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2800 ISceneNode::OnRegisterSceneNode();
2803 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2805 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2806 DSTACK(__FUNCTION_NAME);
2808 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2811 Get time for measuring timeout.
2813 Measuring time is very useful for long delays when the
2814 machine is swapping a lot.
2816 int time1 = time(0);
2818 //s32 daynight_i = m_client->getDayNightIndex();
2819 u32 daynight_ratio = m_client->getDayNightRatio();
2822 Collect all blocks that are in the view range
2824 Should not optimize more here as we want to auto-update
2825 all changed nodes in viewing range at the next step.
2828 s16 viewing_range_nodes;
2829 bool viewing_range_all;
2831 JMutexAutoLock lock(m_range_mutex);
2832 viewing_range_nodes = m_viewing_range_nodes;
2833 viewing_range_all = m_viewing_range_all;
2836 m_camera_mutex.Lock();
2837 v3f camera_position = m_camera_position;
2838 v3f camera_direction = m_camera_direction;
2839 m_camera_mutex.Unlock();
2842 Get all blocks and draw all visible ones
2845 v3s16 cam_pos_nodes(
2846 camera_position.X / BS,
2847 camera_position.Y / BS,
2848 camera_position.Z / BS);
2850 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2852 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2853 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2855 // Take a fair amount as we will be dropping more out later
2857 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2858 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2859 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2861 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2862 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2863 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2865 u32 vertex_count = 0;
2867 // For limiting number of mesh updates per frame
2868 u32 mesh_update_count = 0;
2870 //NOTE: The sectors map should be locked but we're not doing it
2871 // because it'd cause too much delays
2873 int timecheck_counter = 0;
2875 core::map<v2s16, MapSector*>::Iterator si;
2876 si = m_sectors.getIterator();
2877 for(; si.atEnd() == false; si++)
2880 timecheck_counter++;
2881 if(timecheck_counter > 50)
2883 int time2 = time(0);
2884 if(time2 > time1 + 4)
2886 dstream<<"ClientMap::renderMap(): "
2887 "Rendering takes ages, returning."
2894 MapSector *sector = si.getNode()->getValue();
2895 v2s16 sp = sector->getPos();
2897 if(viewing_range_all == false)
2899 if(sp.X < p_blocks_min.X
2900 || sp.X > p_blocks_max.X
2901 || sp.Y < p_blocks_min.Z
2902 || sp.Y > p_blocks_max.Z)
2906 core::list< MapBlock * > sectorblocks;
2907 sector->getBlocks(sectorblocks);
2913 core::list< MapBlock * >::Iterator i;
2914 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2916 MapBlock *block = *i;
2919 Compare block position to camera position, skip
2920 if not seen on display
2923 v3s16 blockpos_nodes = block->getPosRelative();
2925 // Block center position
2927 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2928 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2929 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2932 // Block position relative to camera
2933 v3f blockpos_relative = blockpos - camera_position;
2935 // Distance in camera direction (+=front, -=back)
2936 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2939 f32 d = blockpos_relative.getLength();
2941 if(viewing_range_all == false)
2943 // If block is far away, don't draw it
2944 if(d > viewing_range_nodes * BS)
2945 // This is nicer when fog is used
2946 //if((dforward+d)/2 > viewing_range_nodes * BS)
2950 // Maximum radius of a block
2951 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2953 // If block is (nearly) touching the camera, don't
2954 // bother validating further (that is, render it anyway)
2955 if(d > block_max_radius * 1.5)
2957 // Cosine of the angle between the camera direction
2958 // and the block direction (camera_direction is an unit vector)
2959 f32 cosangle = dforward / d;
2961 // Compensate for the size of the block
2962 // (as the block has to be shown even if it's a bit off FOV)
2963 // This is an estimate.
2964 cosangle += block_max_radius / dforward;
2966 // If block is not in the field of view, skip it
2967 //if(cosangle < cos(FOV_ANGLE/2))
2968 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2973 Draw the faces of the block
2976 bool mesh_expired = false;
2979 JMutexAutoLock lock(block->mesh_mutex);
2981 mesh_expired = block->getMeshExpired();
2983 // Mesh has not been expired and there is no mesh:
2984 // block has no content
2985 if(block->mesh == NULL && mesh_expired == false)
2989 f32 faraway = BS*50;
2990 //f32 faraway = viewing_range_nodes * BS;
2993 This has to be done with the mesh_mutex unlocked
2995 if(mesh_expired && mesh_update_count < 6
2996 && (d < faraway || mesh_update_count < 3))
2997 //if(mesh_expired && mesh_update_count < 4)
2999 mesh_update_count++;
3001 // Mesh has been expired: generate new mesh
3002 //block->updateMeshes(daynight_i);
3003 block->updateMesh(daynight_ratio);
3005 mesh_expired = false;
3009 Don't draw an expired mesh that is far away
3011 /*if(mesh_expired && d >= faraway)
3014 // Instead, delete it
3015 JMutexAutoLock lock(block->mesh_mutex);
3018 block->mesh->drop();
3021 // And continue to next block
3026 JMutexAutoLock lock(block->mesh_mutex);
3028 scene::SMesh *mesh = block->mesh;
3033 u32 c = mesh->getMeshBufferCount();
3035 for(u32 i=0; i<c; i++)
3037 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3038 const video::SMaterial& material = buf->getMaterial();
3039 video::IMaterialRenderer* rnd =
3040 driver->getMaterialRenderer(material.MaterialType);
3041 bool transparent = (rnd && rnd->isTransparent());
3042 // Render transparent on transparent pass and likewise.
3043 if(transparent == is_transparent_pass)
3045 driver->setMaterial(buf->getMaterial());
3046 driver->drawMeshBuffer(buf);
3047 vertex_count += buf->getVertexCount();
3051 } // foreach sectorblocks
3054 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3055 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3058 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3061 Add it to all blocks touching it
3064 v3s16(0,0,0), // this
3065 v3s16(0,0,1), // back
3066 v3s16(0,1,0), // top
3067 v3s16(1,0,0), // right
3068 v3s16(0,0,-1), // front
3069 v3s16(0,-1,0), // bottom
3070 v3s16(-1,0,0), // left
3072 for(u16 i=0; i<7; i++)
3074 v3s16 p2 = p + dirs[i];
3075 // Block position of neighbor (or requested) node
3076 v3s16 blockpos = getNodeBlockPos(p2);
3077 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3078 if(blockref == NULL)
3080 // Relative position of requested node
3081 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3082 blockref->setTempMod(relpos, mod);
3084 return getNodeBlockPos(p);
3086 v3s16 ClientMap::clearTempMod(v3s16 p)
3089 v3s16(0,0,0), // this
3090 v3s16(0,0,1), // back
3091 v3s16(0,1,0), // top
3092 v3s16(1,0,0), // right
3093 v3s16(0,0,-1), // front
3094 v3s16(0,-1,0), // bottom
3095 v3s16(-1,0,0), // left
3097 for(u16 i=0; i<7; i++)
3099 v3s16 p2 = p + dirs[i];
3100 // Block position of neighbor (or requested) node
3101 v3s16 blockpos = getNodeBlockPos(p2);
3102 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3103 if(blockref == NULL)
3105 // Relative position of requested node
3106 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3107 blockref->clearTempMod(relpos);
3109 return getNodeBlockPos(p);
3112 void ClientMap::PrintInfo(std::ostream &out)
3123 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3128 MapVoxelManipulator::~MapVoxelManipulator()
3130 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3135 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3137 TimeTaker timer1("emerge", &emerge_time);
3139 // Units of these are MapBlocks
3140 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3141 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3143 VoxelArea block_area_nodes
3144 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3146 addArea(block_area_nodes);
3148 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3149 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3150 for(s32 x=p_min.X; x<=p_max.X; x++)
3153 core::map<v3s16, bool>::Node *n;
3154 n = m_loaded_blocks.find(p);
3158 bool block_data_inexistent = false;
3161 TimeTaker timer1("emerge load", &emerge_load_time);
3163 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3164 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3167 dstream<<std::endl;*/
3169 MapBlock *block = m_map->getBlockNoCreate(p);
3170 if(block->isDummy())
3171 block_data_inexistent = true;
3173 block->copyTo(*this);
3175 catch(InvalidPositionException &e)
3177 block_data_inexistent = true;
3180 if(block_data_inexistent)
3182 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3183 // Fill with VOXELFLAG_INEXISTENT
3184 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3185 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3187 s32 i = m_area.index(a.MinEdge.X,y,z);
3188 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3192 m_loaded_blocks.insert(p, true);
3195 //dstream<<"emerge done"<<std::endl;
3200 void MapVoxelManipulator::emerge(VoxelArea a)
3202 TimeTaker timer1("emerge", &emerge_time);
3204 v3s16 size = a.getExtent();
3206 VoxelArea padded = a;
3207 padded.pad(m_area.getExtent() / 4);
3210 for(s16 z=0; z<size.Z; z++)
3211 for(s16 y=0; y<size.Y; y++)
3212 for(s16 x=0; x<size.X; x++)
3215 s32 i = m_area.index(a.MinEdge + p);
3216 // Don't touch nodes that have already been loaded
3217 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3221 TimeTaker timer1("emerge load", &emerge_load_time);
3222 MapNode n = m_map->getNode(a.MinEdge + p);
3226 catch(InvalidPositionException &e)
3228 m_flags[i] = VOXELFLAG_INEXISTENT;
3236 TODO: Add an option to only update eg. water and air nodes.
3237 This will make it interfere less with important stuff if
3240 void MapVoxelManipulator::blitBack
3241 (core::map<v3s16, MapBlock*> & modified_blocks)
3243 if(m_area.getExtent() == v3s16(0,0,0))
3246 //TimeTaker timer1("blitBack");
3249 Initialize block cache
3251 v3s16 blockpos_last;
3252 MapBlock *block = NULL;
3253 bool block_checked_in_modified = false;
3255 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3256 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3257 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3261 u8 f = m_flags[m_area.index(p)];
3262 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3265 MapNode &n = m_data[m_area.index(p)];
3267 v3s16 blockpos = getNodeBlockPos(p);
3272 if(block == NULL || blockpos != blockpos_last){
3273 block = m_map->getBlockNoCreate(blockpos);
3274 blockpos_last = blockpos;
3275 block_checked_in_modified = false;
3278 // Calculate relative position in block
3279 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3281 // Don't continue if nothing has changed here
3282 if(block->getNode(relpos) == n)
3285 //m_map->setNode(m_area.MinEdge + p, n);
3286 block->setNode(relpos, n);
3289 Make sure block is in modified_blocks
3291 if(block_checked_in_modified == false)
3293 modified_blocks[blockpos] = block;
3294 block_checked_in_modified = true;
3297 catch(InvalidPositionException &e)