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;
1953 for(s16 i=0; i<underground_level*1; i++)
1958 (rand()%(MAP_BLOCKSIZE-2))+1,
1959 (rand()%(MAP_BLOCKSIZE-2))+1,
1960 (rand()%(MAP_BLOCKSIZE-2))+1
1966 //if(is_ground_content(block->getNode(cp).d))
1967 if(block->getNode(cp).d == CONTENT_STONE)
1969 block->setNode(cp, n);
1971 for(u16 i=0; i<26; i++)
1973 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1974 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1976 block->setNode(cp+g_26dirs[i], n);
1983 for(s16 i=0; i<20; i++)
1986 (rand()%(MAP_BLOCKSIZE-2))+1,
1987 (rand()%(MAP_BLOCKSIZE-2))+1,
1988 (rand()%(MAP_BLOCKSIZE-2))+1
1992 n.d = CONTENT_COALSTONE;
1994 //dstream<<"Adding coalstone"<<std::endl;
1996 //if(is_ground_content(block->getNode(cp).d))
1997 if(block->getNode(cp).d == CONTENT_STONE)
1999 block->setNode(cp, n);
2001 for(u16 i=0; i<26; i++)
2003 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2004 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2006 block->setNode(cp+g_26dirs[i], n);
2013 Create a few rats in empty blocks underground
2017 //for(u16 i=0; i<2; i++)
2020 (rand()%(MAP_BLOCKSIZE-2))+1,
2021 (rand()%(MAP_BLOCKSIZE-2))+1,
2022 (rand()%(MAP_BLOCKSIZE-2))+1
2025 // Check that the place is empty
2026 //if(!is_ground_content(block->getNode(cp).d))
2029 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2030 block->addObject(obj);
2036 Add block to sector.
2038 sector->insertBlock(block);
2044 // An y-wise container of changed blocks
2045 core::map<s16, MapBlock*> changed_blocks_sector;
2048 Check if any sector's objects can be placed now.
2051 core::map<v3s16, u8> *objects = sector->getObjects();
2052 core::list<v3s16> objects_to_remove;
2053 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2054 i.atEnd() == false; i++)
2056 v3s16 p = i.getNode()->getKey();
2058 u8 d = i.getNode()->getValue();
2060 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2065 if(d == SECTOR_OBJECT_TEST)
2067 if(sector->isValidArea(p + v3s16(0,0,0),
2068 p + v3s16(0,0,0), &changed_blocks_sector))
2071 n.d = CONTENT_TORCH;
2072 sector->setNode(p, n);
2073 objects_to_remove.push_back(p);
2076 else if(d == SECTOR_OBJECT_TREE_1)
2078 v3s16 p_min = p + v3s16(-1,0,-1);
2079 v3s16 p_max = p + v3s16(1,4,1);
2080 if(sector->isValidArea(p_min, p_max,
2081 &changed_blocks_sector))
2085 sector->setNode(p+v3s16(0,0,0), n);
2086 sector->setNode(p+v3s16(0,1,0), n);
2087 sector->setNode(p+v3s16(0,2,0), n);
2088 sector->setNode(p+v3s16(0,3,0), n);
2090 n.d = CONTENT_LEAVES;
2092 sector->setNode(p+v3s16(0,4,0), n);
2094 sector->setNode(p+v3s16(-1,4,0), n);
2095 sector->setNode(p+v3s16(1,4,0), n);
2096 sector->setNode(p+v3s16(0,4,-1), n);
2097 sector->setNode(p+v3s16(0,4,1), n);
2098 sector->setNode(p+v3s16(1,4,1), n);
2099 sector->setNode(p+v3s16(-1,4,1), n);
2100 sector->setNode(p+v3s16(-1,4,-1), n);
2101 sector->setNode(p+v3s16(1,4,-1), n);
2103 sector->setNode(p+v3s16(-1,3,0), n);
2104 sector->setNode(p+v3s16(1,3,0), n);
2105 sector->setNode(p+v3s16(0,3,-1), n);
2106 sector->setNode(p+v3s16(0,3,1), n);
2107 sector->setNode(p+v3s16(1,3,1), n);
2108 sector->setNode(p+v3s16(-1,3,1), n);
2109 sector->setNode(p+v3s16(-1,3,-1), n);
2110 sector->setNode(p+v3s16(1,3,-1), n);
2112 objects_to_remove.push_back(p);
2114 // Lighting has to be recalculated for this one.
2115 sector->getBlocksInArea(p_min, p_max,
2116 lighting_invalidated_blocks);
2119 else if(d == SECTOR_OBJECT_BUSH_1)
2121 if(sector->isValidArea(p + v3s16(0,0,0),
2122 p + v3s16(0,0,0), &changed_blocks_sector))
2125 n.d = CONTENT_LEAVES;
2126 sector->setNode(p+v3s16(0,0,0), n);
2128 objects_to_remove.push_back(p);
2131 else if(d == SECTOR_OBJECT_RAVINE)
2134 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2135 v3s16 p_max = p + v3s16(6,6,6);
2136 if(sector->isValidArea(p_min, p_max,
2137 &changed_blocks_sector))
2140 n.d = CONTENT_STONE;
2143 s16 depth = maxdepth + (rand()%10);
2145 s16 minz = -6 - (-2);
2147 for(s16 x=-6; x<=6; x++)
2149 z += -1 + (rand()%3);
2154 for(s16 y=depth+(rand()%2); y<=6; y++)
2156 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2159 v3s16 p2 = p + v3s16(x,y,z-2);
2160 if(is_ground_content(sector->getNode(p2).d))
2161 sector->setNode(p2, n);
2164 v3s16 p2 = p + v3s16(x,y,z-1);
2165 if(is_ground_content(sector->getNode(p2).d))
2166 sector->setNode(p2, n2);
2169 v3s16 p2 = p + v3s16(x,y,z+0);
2170 if(is_ground_content(sector->getNode(p2).d))
2171 sector->setNode(p2, n2);
2174 v3s16 p2 = p + v3s16(x,y,z+1);
2175 if(is_ground_content(sector->getNode(p2).d))
2176 sector->setNode(p2, n);
2179 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2180 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2184 objects_to_remove.push_back(p);
2186 // Lighting has to be recalculated for this one.
2187 sector->getBlocksInArea(p_min, p_max,
2188 lighting_invalidated_blocks);
2193 dstream<<"ServerMap::emergeBlock(): "
2194 "Invalid heightmap object"
2199 catch(InvalidPositionException &e)
2201 dstream<<"WARNING: "<<__FUNCTION_NAME
2202 <<": while inserting object "<<(int)d
2203 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2204 <<" InvalidPositionException.what()="
2205 <<e.what()<<std::endl;
2206 // This is not too fatal and seems to happen sometimes.
2211 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2212 i != objects_to_remove.end(); i++)
2214 objects->remove(*i);
2217 for(core::map<s16, MapBlock*>::Iterator
2218 i = changed_blocks_sector.getIterator();
2219 i.atEnd() == false; i++)
2221 MapBlock *block = i.getNode()->getValue();
2223 changed_blocks.insert(block->getPos(), block);
2229 void ServerMap::createDir(std::string path)
2231 if(fs::CreateDir(path) == false)
2233 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2234 <<"\""<<path<<"\""<<std::endl;
2235 throw BaseException("ServerMap failed to create directory");
2239 std::string ServerMap::getSectorSubDir(v2s16 pos)
2242 snprintf(cc, 9, "%.4x%.4x",
2243 (unsigned int)pos.X&0xffff,
2244 (unsigned int)pos.Y&0xffff);
2246 return std::string(cc);
2249 std::string ServerMap::getSectorDir(v2s16 pos)
2251 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2254 v2s16 ServerMap::getSectorPos(std::string dirname)
2256 if(dirname.size() != 8)
2257 throw InvalidFilenameException("Invalid sector directory name");
2259 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2261 throw InvalidFilenameException("Invalid sector directory name");
2262 v2s16 pos((s16)x, (s16)y);
2266 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2268 v2s16 p2d = getSectorPos(sectordir);
2270 if(blockfile.size() != 4){
2271 throw InvalidFilenameException("Invalid block filename");
2274 int r = sscanf(blockfile.c_str(), "%4x", &y);
2276 throw InvalidFilenameException("Invalid block filename");
2277 return v3s16(p2d.X, y, p2d.Y);
2281 #define ENABLE_SECTOR_SAVING 1
2282 #define ENABLE_SECTOR_LOADING 1
2283 #define ENABLE_BLOCK_SAVING 1
2284 #define ENABLE_BLOCK_LOADING 1
2286 void ServerMap::save(bool only_changed)
2288 DSTACK(__FUNCTION_NAME);
2289 if(m_map_saving_enabled == false)
2291 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2295 if(only_changed == false)
2296 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2299 saveMasterHeightmap();
2301 u32 sector_meta_count = 0;
2302 u32 block_count = 0;
2305 JMutexAutoLock lock(m_sector_mutex);
2307 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2308 for(; i.atEnd() == false; i++)
2310 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2311 assert(sector->getId() == MAPSECTOR_SERVER);
2313 if(ENABLE_SECTOR_SAVING)
2315 if(sector->differs_from_disk || only_changed == false)
2317 saveSectorMeta(sector);
2318 sector_meta_count++;
2321 if(ENABLE_BLOCK_SAVING)
2323 core::list<MapBlock*> blocks;
2324 sector->getBlocks(blocks);
2325 core::list<MapBlock*>::Iterator j;
2326 for(j=blocks.begin(); j!=blocks.end(); j++)
2328 MapBlock *block = *j;
2329 if(block->getChangedFlag() || only_changed == false)
2341 Only print if something happened or saved whole map
2343 if(only_changed == false || sector_meta_count != 0
2344 || block_count != 0)
2346 dstream<<DTIME<<"ServerMap: Written: "
2347 <<sector_meta_count<<" sector metadata files, "
2348 <<block_count<<" block files"
2353 void ServerMap::loadAll()
2355 DSTACK(__FUNCTION_NAME);
2356 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2358 loadMasterHeightmap();
2360 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2362 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2364 JMutexAutoLock lock(m_sector_mutex);
2367 s32 printed_counter = -100000;
2368 s32 count = list.size();
2370 std::vector<fs::DirListNode>::iterator i;
2371 for(i=list.begin(); i!=list.end(); i++)
2373 if(counter > printed_counter + 10)
2375 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2376 printed_counter = counter;
2380 MapSector *sector = NULL;
2382 // We want directories
2386 sector = loadSectorMeta(i->name);
2388 catch(InvalidFilenameException &e)
2390 // This catches unknown crap in directory
2393 if(ENABLE_BLOCK_LOADING)
2395 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2396 (m_savedir+"/sectors/"+i->name);
2397 std::vector<fs::DirListNode>::iterator i2;
2398 for(i2=list2.begin(); i2!=list2.end(); i2++)
2404 loadBlock(i->name, i2->name, sector);
2406 catch(InvalidFilenameException &e)
2408 // This catches unknown crap in directory
2413 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2416 void ServerMap::saveMasterHeightmap()
2418 DSTACK(__FUNCTION_NAME);
2419 createDir(m_savedir);
2421 std::string fullpath = m_savedir + "/master_heightmap";
2422 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2423 if(o.good() == false)
2424 throw FileNotGoodException("Cannot open master heightmap");
2426 // Format used for writing
2427 u8 version = SER_FMT_VER_HIGHEST;
2430 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2432 [0] u8 serialization version
2433 [1] X master heightmap
2435 u32 fullsize = 1 + hmdata.getSize();
2436 SharedBuffer<u8> data(fullsize);
2439 memcpy(&data[1], *hmdata, hmdata.getSize());
2441 o.write((const char*)*data, fullsize);
2444 m_heightmap->serialize(o, version);
2447 void ServerMap::loadMasterHeightmap()
2449 DSTACK(__FUNCTION_NAME);
2450 std::string fullpath = m_savedir + "/master_heightmap";
2451 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2452 if(is.good() == false)
2453 throw FileNotGoodException("Cannot open master heightmap");
2455 if(m_heightmap != NULL)
2458 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2461 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2463 DSTACK(__FUNCTION_NAME);
2464 // Format used for writing
2465 u8 version = SER_FMT_VER_HIGHEST;
2467 v2s16 pos = sector->getPos();
2468 createDir(m_savedir);
2469 createDir(m_savedir+"/sectors");
2470 std::string dir = getSectorDir(pos);
2473 std::string fullpath = dir + "/heightmap";
2474 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2475 if(o.good() == false)
2476 throw FileNotGoodException("Cannot open master heightmap");
2478 sector->serialize(o, version);
2480 sector->differs_from_disk = false;
2483 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2485 DSTACK(__FUNCTION_NAME);
2487 v2s16 p2d = getSectorPos(dirname);
2488 std::string dir = m_savedir + "/sectors/" + dirname;
2490 std::string fullpath = dir + "/heightmap";
2491 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2492 if(is.good() == false)
2493 throw FileNotGoodException("Cannot open sector heightmap");
2495 ServerMapSector *sector = ServerMapSector::deSerialize
2496 (is, this, p2d, &m_hwrapper, m_sectors);
2498 sector->differs_from_disk = false;
2503 bool ServerMap::loadSectorFull(v2s16 p2d)
2505 DSTACK(__FUNCTION_NAME);
2506 std::string sectorsubdir = getSectorSubDir(p2d);
2508 MapSector *sector = NULL;
2510 JMutexAutoLock lock(m_sector_mutex);
2513 sector = loadSectorMeta(sectorsubdir);
2515 catch(InvalidFilenameException &e)
2519 catch(FileNotGoodException &e)
2523 catch(std::exception &e)
2528 if(ENABLE_BLOCK_LOADING)
2530 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2531 (m_savedir+"/sectors/"+sectorsubdir);
2532 std::vector<fs::DirListNode>::iterator i2;
2533 for(i2=list2.begin(); i2!=list2.end(); i2++)
2539 loadBlock(sectorsubdir, i2->name, sector);
2541 catch(InvalidFilenameException &e)
2543 // This catches unknown crap in directory
2551 bool ServerMap::deFlushSector(v2s16 p2d)
2553 DSTACK(__FUNCTION_NAME);
2554 // See if it already exists in memory
2556 MapSector *sector = getSectorNoGenerate(p2d);
2559 catch(InvalidPositionException &e)
2562 Try to load the sector from disk.
2564 if(loadSectorFull(p2d) == true)
2573 void ServerMap::saveBlock(MapBlock *block)
2575 DSTACK(__FUNCTION_NAME);
2577 Dummy blocks are not written
2579 if(block->isDummy())
2581 /*v3s16 p = block->getPos();
2582 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2583 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2587 // Format used for writing
2588 u8 version = SER_FMT_VER_HIGHEST;
2590 v3s16 p3d = block->getPos();
2591 v2s16 p2d(p3d.X, p3d.Z);
2592 createDir(m_savedir);
2593 createDir(m_savedir+"/sectors");
2594 std::string dir = getSectorDir(p2d);
2597 // Block file is map/sectors/xxxxxxxx/xxxx
2599 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2600 std::string fullpath = dir + "/" + cc;
2601 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2602 if(o.good() == false)
2603 throw FileNotGoodException("Cannot open block data");
2606 [0] u8 serialization version
2609 o.write((char*)&version, 1);
2611 block->serialize(o, version);
2614 Versions up from 9 have block objects.
2618 block->serializeObjects(o, version);
2621 // We just wrote it to the disk
2622 block->resetChangedFlag();
2625 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2627 DSTACK(__FUNCTION_NAME);
2631 // Block file is map/sectors/xxxxxxxx/xxxx
2632 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2633 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2634 if(is.good() == false)
2635 throw FileNotGoodException("Cannot open block file");
2637 v3s16 p3d = getBlockPos(sectordir, blockfile);
2638 v2s16 p2d(p3d.X, p3d.Z);
2640 assert(sector->getPos() == p2d);
2642 u8 version = SER_FMT_VER_INVALID;
2643 is.read((char*)&version, 1);
2645 /*u32 block_size = MapBlock::serializedLength(version);
2646 SharedBuffer<u8> data(block_size);
2647 is.read((char*)*data, block_size);*/
2649 // This will always return a sector because we're the server
2650 //MapSector *sector = emergeSector(p2d);
2652 MapBlock *block = NULL;
2653 bool created_new = false;
2655 block = sector->getBlockNoCreate(p3d.Y);
2657 catch(InvalidPositionException &e)
2659 block = sector->createBlankBlockNoInsert(p3d.Y);
2663 // deserialize block data
2664 block->deSerialize(is, version);
2667 Versions up from 9 have block objects.
2671 block->updateObjects(is, version, NULL, 0);
2675 sector->insertBlock(block);
2678 Convert old formats to new and save
2681 // Save old format blocks in new format
2682 if(version < SER_FMT_VER_HIGHEST)
2687 // We just loaded it from the disk, so it's up-to-date.
2688 block->resetChangedFlag();
2691 catch(SerializationError &e)
2693 dstream<<"WARNING: Invalid block data on disk "
2694 "(SerializationError). Ignoring."
2699 // Gets from master heightmap
2700 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2702 assert(m_heightmap != NULL);
2710 corners[0] = m_heightmap->getGroundHeight
2711 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2712 corners[1] = m_heightmap->getGroundHeight
2713 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2714 corners[2] = m_heightmap->getGroundHeight
2715 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2716 corners[3] = m_heightmap->getGroundHeight
2717 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2720 void ServerMap::PrintInfo(std::ostream &out)
2731 ClientMap::ClientMap(
2733 JMutex &range_mutex,
2734 s16 &viewing_range_nodes,
2735 bool &viewing_range_all,
2736 scene::ISceneNode* parent,
2737 scene::ISceneManager* mgr,
2741 scene::ISceneNode(parent, mgr, id),
2744 m_range_mutex(range_mutex),
2745 m_viewing_range_nodes(viewing_range_nodes),
2746 m_viewing_range_all(viewing_range_all)
2750 /*m_box = core::aabbox3d<f32>(0,0,0,
2751 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2752 /*m_box = core::aabbox3d<f32>(0,0,0,
2753 map->getSizeNodes().X * BS,
2754 map->getSizeNodes().Y * BS,
2755 map->getSizeNodes().Z * BS);*/
2756 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2757 BS*1000000,BS*1000000,BS*1000000);
2759 //setPosition(v3f(BS,BS,BS));
2762 ClientMap::~ClientMap()
2764 JMutexAutoLock lock(mesh_mutex);
2773 MapSector * ClientMap::emergeSector(v2s16 p2d)
2775 DSTACK(__FUNCTION_NAME);
2776 // Check that it doesn't exist already
2778 return getSectorNoGenerate(p2d);
2780 catch(InvalidPositionException &e)
2784 // Create a sector with no heightmaps
2785 ClientMapSector *sector = new ClientMapSector(this, p2d);
2788 JMutexAutoLock lock(m_sector_mutex);
2789 m_sectors.insert(p2d, sector);
2795 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2797 DSTACK(__FUNCTION_NAME);
2798 ClientMapSector *sector = NULL;
2800 JMutexAutoLock lock(m_sector_mutex);
2802 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2806 sector = (ClientMapSector*)n->getValue();
2807 assert(sector->getId() == MAPSECTOR_CLIENT);
2811 sector = new ClientMapSector(this, p2d);
2813 JMutexAutoLock lock(m_sector_mutex);
2814 m_sectors.insert(p2d, sector);
2818 sector->deSerialize(is);
2821 void ClientMap::OnRegisterSceneNode()
2825 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2826 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2829 ISceneNode::OnRegisterSceneNode();
2832 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2834 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2835 DSTACK(__FUNCTION_NAME);
2837 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2840 Get time for measuring timeout.
2842 Measuring time is very useful for long delays when the
2843 machine is swapping a lot.
2845 int time1 = time(0);
2847 //s32 daynight_i = m_client->getDayNightIndex();
2848 u32 daynight_ratio = m_client->getDayNightRatio();
2851 Collect all blocks that are in the view range
2853 Should not optimize more here as we want to auto-update
2854 all changed nodes in viewing range at the next step.
2857 s16 viewing_range_nodes;
2858 bool viewing_range_all;
2860 JMutexAutoLock lock(m_range_mutex);
2861 viewing_range_nodes = m_viewing_range_nodes;
2862 viewing_range_all = m_viewing_range_all;
2865 m_camera_mutex.Lock();
2866 v3f camera_position = m_camera_position;
2867 v3f camera_direction = m_camera_direction;
2868 m_camera_mutex.Unlock();
2871 Get all blocks and draw all visible ones
2874 v3s16 cam_pos_nodes(
2875 camera_position.X / BS,
2876 camera_position.Y / BS,
2877 camera_position.Z / BS);
2879 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2881 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2882 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2884 // Take a fair amount as we will be dropping more out later
2886 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2887 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2888 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2890 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2891 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2892 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2894 u32 vertex_count = 0;
2896 // For limiting number of mesh updates per frame
2897 u32 mesh_update_count = 0;
2899 //NOTE: The sectors map should be locked but we're not doing it
2900 // because it'd cause too much delays
2902 int timecheck_counter = 0;
2904 core::map<v2s16, MapSector*>::Iterator si;
2905 si = m_sectors.getIterator();
2906 for(; si.atEnd() == false; si++)
2909 timecheck_counter++;
2910 if(timecheck_counter > 50)
2912 int time2 = time(0);
2913 if(time2 > time1 + 4)
2915 dstream<<"ClientMap::renderMap(): "
2916 "Rendering takes ages, returning."
2923 MapSector *sector = si.getNode()->getValue();
2924 v2s16 sp = sector->getPos();
2926 if(viewing_range_all == false)
2928 if(sp.X < p_blocks_min.X
2929 || sp.X > p_blocks_max.X
2930 || sp.Y < p_blocks_min.Z
2931 || sp.Y > p_blocks_max.Z)
2935 core::list< MapBlock * > sectorblocks;
2936 sector->getBlocks(sectorblocks);
2942 core::list< MapBlock * >::Iterator i;
2943 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2945 MapBlock *block = *i;
2948 Compare block position to camera position, skip
2949 if not seen on display
2952 v3s16 blockpos_nodes = block->getPosRelative();
2954 // Block center position
2956 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2957 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2958 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2961 // Block position relative to camera
2962 v3f blockpos_relative = blockpos - camera_position;
2964 // Distance in camera direction (+=front, -=back)
2965 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2968 f32 d = blockpos_relative.getLength();
2970 if(viewing_range_all == false)
2972 // If block is far away, don't draw it
2973 if(d > viewing_range_nodes * BS)
2974 // This is nicer when fog is used
2975 //if((dforward+d)/2 > viewing_range_nodes * BS)
2979 // Maximum radius of a block
2980 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2982 // If block is (nearly) touching the camera, don't
2983 // bother validating further (that is, render it anyway)
2984 if(d > block_max_radius * 1.5)
2986 // Cosine of the angle between the camera direction
2987 // and the block direction (camera_direction is an unit vector)
2988 f32 cosangle = dforward / d;
2990 // Compensate for the size of the block
2991 // (as the block has to be shown even if it's a bit off FOV)
2992 // This is an estimate.
2993 cosangle += block_max_radius / dforward;
2995 // If block is not in the field of view, skip it
2996 //if(cosangle < cos(FOV_ANGLE/2))
2997 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3002 Draw the faces of the block
3005 bool mesh_expired = false;
3008 JMutexAutoLock lock(block->mesh_mutex);
3010 mesh_expired = block->getMeshExpired();
3012 // Mesh has not been expired and there is no mesh:
3013 // block has no content
3014 if(block->mesh == NULL && mesh_expired == false)
3018 f32 faraway = BS*50;
3019 //f32 faraway = viewing_range_nodes * BS;
3022 This has to be done with the mesh_mutex unlocked
3024 if(mesh_expired && mesh_update_count < 6
3025 && (d < faraway || mesh_update_count < 3))
3026 //if(mesh_expired && mesh_update_count < 4)
3028 mesh_update_count++;
3030 // Mesh has been expired: generate new mesh
3031 //block->updateMeshes(daynight_i);
3032 block->updateMesh(daynight_ratio);
3034 mesh_expired = false;
3038 Don't draw an expired mesh that is far away
3040 /*if(mesh_expired && d >= faraway)
3043 // Instead, delete it
3044 JMutexAutoLock lock(block->mesh_mutex);
3047 block->mesh->drop();
3050 // And continue to next block
3055 JMutexAutoLock lock(block->mesh_mutex);
3057 scene::SMesh *mesh = block->mesh;
3062 u32 c = mesh->getMeshBufferCount();
3064 for(u32 i=0; i<c; i++)
3066 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3067 const video::SMaterial& material = buf->getMaterial();
3068 video::IMaterialRenderer* rnd =
3069 driver->getMaterialRenderer(material.MaterialType);
3070 bool transparent = (rnd && rnd->isTransparent());
3071 // Render transparent on transparent pass and likewise.
3072 if(transparent == is_transparent_pass)
3074 driver->setMaterial(buf->getMaterial());
3075 driver->drawMeshBuffer(buf);
3076 vertex_count += buf->getVertexCount();
3080 } // foreach sectorblocks
3083 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3084 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3087 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3090 Add it to all blocks touching it
3093 v3s16(0,0,0), // this
3094 v3s16(0,0,1), // back
3095 v3s16(0,1,0), // top
3096 v3s16(1,0,0), // right
3097 v3s16(0,0,-1), // front
3098 v3s16(0,-1,0), // bottom
3099 v3s16(-1,0,0), // left
3101 for(u16 i=0; i<7; i++)
3103 v3s16 p2 = p + dirs[i];
3104 // Block position of neighbor (or requested) node
3105 v3s16 blockpos = getNodeBlockPos(p2);
3106 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3107 if(blockref == NULL)
3109 // Relative position of requested node
3110 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3111 blockref->setTempMod(relpos, mod);
3113 return getNodeBlockPos(p);
3115 v3s16 ClientMap::clearTempMod(v3s16 p)
3118 v3s16(0,0,0), // this
3119 v3s16(0,0,1), // back
3120 v3s16(0,1,0), // top
3121 v3s16(1,0,0), // right
3122 v3s16(0,0,-1), // front
3123 v3s16(0,-1,0), // bottom
3124 v3s16(-1,0,0), // left
3126 for(u16 i=0; i<7; i++)
3128 v3s16 p2 = p + dirs[i];
3129 // Block position of neighbor (or requested) node
3130 v3s16 blockpos = getNodeBlockPos(p2);
3131 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3132 if(blockref == NULL)
3134 // Relative position of requested node
3135 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3136 blockref->clearTempMod(relpos);
3138 return getNodeBlockPos(p);
3141 void ClientMap::PrintInfo(std::ostream &out)
3152 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3157 MapVoxelManipulator::~MapVoxelManipulator()
3159 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3164 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3166 TimeTaker timer1("emerge", &emerge_time);
3168 // Units of these are MapBlocks
3169 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3170 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3172 VoxelArea block_area_nodes
3173 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3175 addArea(block_area_nodes);
3177 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3178 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3179 for(s32 x=p_min.X; x<=p_max.X; x++)
3182 core::map<v3s16, bool>::Node *n;
3183 n = m_loaded_blocks.find(p);
3187 bool block_data_inexistent = false;
3190 TimeTaker timer1("emerge load", &emerge_load_time);
3192 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3193 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3196 dstream<<std::endl;*/
3198 MapBlock *block = m_map->getBlockNoCreate(p);
3199 if(block->isDummy())
3200 block_data_inexistent = true;
3202 block->copyTo(*this);
3204 catch(InvalidPositionException &e)
3206 block_data_inexistent = true;
3209 if(block_data_inexistent)
3211 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3212 // Fill with VOXELFLAG_INEXISTENT
3213 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3214 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3216 s32 i = m_area.index(a.MinEdge.X,y,z);
3217 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3221 m_loaded_blocks.insert(p, true);
3224 //dstream<<"emerge done"<<std::endl;
3229 void MapVoxelManipulator::emerge(VoxelArea a)
3231 TimeTaker timer1("emerge", &emerge_time);
3233 v3s16 size = a.getExtent();
3235 VoxelArea padded = a;
3236 padded.pad(m_area.getExtent() / 4);
3239 for(s16 z=0; z<size.Z; z++)
3240 for(s16 y=0; y<size.Y; y++)
3241 for(s16 x=0; x<size.X; x++)
3244 s32 i = m_area.index(a.MinEdge + p);
3245 // Don't touch nodes that have already been loaded
3246 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3250 TimeTaker timer1("emerge load", &emerge_load_time);
3251 MapNode n = m_map->getNode(a.MinEdge + p);
3255 catch(InvalidPositionException &e)
3257 m_flags[i] = VOXELFLAG_INEXISTENT;
3265 TODO: Add an option to only update eg. water and air nodes.
3266 This will make it interfere less with important stuff if
3269 void MapVoxelManipulator::blitBack
3270 (core::map<v3s16, MapBlock*> & modified_blocks)
3272 if(m_area.getExtent() == v3s16(0,0,0))
3275 //TimeTaker timer1("blitBack");
3278 Initialize block cache
3280 v3s16 blockpos_last;
3281 MapBlock *block = NULL;
3282 bool block_checked_in_modified = false;
3284 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3285 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3286 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3290 u8 f = m_flags[m_area.index(p)];
3291 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3294 MapNode &n = m_data[m_area.index(p)];
3296 v3s16 blockpos = getNodeBlockPos(p);
3301 if(block == NULL || blockpos != blockpos_last){
3302 block = m_map->getBlockNoCreate(blockpos);
3303 blockpos_last = blockpos;
3304 block_checked_in_modified = false;
3307 // Calculate relative position in block
3308 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3310 // Don't continue if nothing has changed here
3311 if(block->getNode(relpos) == n)
3314 //m_map->setNode(m_area.MinEdge + p, n);
3315 block->setNode(relpos, n);
3318 Make sure block is in modified_blocks
3320 if(block_checked_in_modified == false)
3322 modified_blocks[blockpos] = block;
3323 block_checked_in_modified = true;
3326 catch(InvalidPositionException &e)