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"
30 #define sleep_ms(x) Sleep(x)
33 #define sleep_ms(x) usleep(x*1000)
36 MapBlockPointerCache::MapBlockPointerCache(Map *map)
39 m_map->m_blockcachelock.cacheCreated();
41 m_from_cache_count = 0;
45 MapBlockPointerCache::~MapBlockPointerCache()
47 m_map->m_blockcachelock.cacheRemoved();
49 /*dstream<<"MapBlockPointerCache:"
50 <<" from_cache_count="<<m_from_cache_count
51 <<" from_map_count="<<m_from_map_count
55 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
57 core::map<v3s16, MapBlock*>::Node *n = NULL;
67 // Throws InvalidPositionException if not found
68 MapBlock *b = m_map->getBlockNoCreate(p);
77 Map::Map(std::ostream &dout):
79 m_camera_position(0,0,0),
80 m_camera_direction(0,0,1),
85 m_sector_mutex.Init();
86 m_camera_mutex.Init();
87 assert(m_sector_mutex.IsInitialized());
88 assert(m_camera_mutex.IsInitialized());
90 // Get this so that the player can stay on it at first
91 //getSector(v2s16(0,0));
99 /*updater.setRun(false);
100 while(updater.IsRunning())
106 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
107 for(; i.atEnd() == false; i++)
109 MapSector *sector = i.getNode()->getValue();
114 MapSector * Map::getSectorNoGenerate(v2s16 p)
116 JMutexAutoLock lock(m_sector_mutex);
118 if(m_sector_cache != NULL && p == m_sector_cache_p){
119 MapSector * sector = m_sector_cache;
120 // Reset inactivity timer
121 sector->usage_timer = 0.0;
125 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
126 // If sector doesn't exist, throw an exception
129 throw InvalidPositionException();
132 MapSector *sector = n->getValue();
134 // Cache the last result
135 m_sector_cache_p = p;
136 m_sector_cache = sector;
138 //MapSector * ref(sector);
140 // Reset inactivity timer
141 sector->usage_timer = 0.0;
145 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorNoGenerate(p2d);
150 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
155 f32 Map::getGroundHeight(v2s16 p, bool generate)
158 v2s16 sectorpos = getNodeSectorPos(p);
159 MapSector * sref = getSectorNoGenerate(sectorpos);
160 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
161 f32 y = sref->getGroundHeight(relpos);
164 catch(InvalidPositionException &e)
166 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
170 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
172 /*m_dout<<DTIME<<"Map::setGroundHeight(("
174 <<"), "<<y<<")"<<std::endl;*/
175 v2s16 sectorpos = getNodeSectorPos(p);
176 MapSector * sref = getSectorNoGenerate(sectorpos);
177 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
178 //sref->mutex.Lock();
179 sref->setGroundHeight(relpos, y);
180 //sref->mutex.Unlock();
183 bool Map::isNodeUnderground(v3s16 p)
185 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock * block = getBlockNoCreate(blockpos);
188 return block->getIsUnderground();
190 catch(InvalidPositionException &e)
197 Goes recursively through the neighbours of the node.
199 Alters only transparent nodes.
201 If the lighting of the neighbour is lower than the lighting of
202 the node was (before changing it to 0 at the step before), the
203 lighting of the neighbour is set to 0 and then the same stuff
204 repeats for the neighbour.
206 The ending nodes of the routine are stored in light_sources.
207 This is useful when a light is removed. In such case, this
208 routine can be called for the light node and then again for
209 light_sources to re-light the area without the removed light.
211 values of from_nodes are lighting values.
213 void Map::unspreadLight(enum LightBank bank,
214 core::map<v3s16, u8> & from_nodes,
215 core::map<v3s16, bool> & light_sources,
216 core::map<v3s16, MapBlock*> & modified_blocks)
219 v3s16(0,0,1), // back
221 v3s16(1,0,0), // right
222 v3s16(0,0,-1), // front
223 v3s16(0,-1,0), // bottom
224 v3s16(-1,0,0), // left
227 if(from_nodes.size() == 0)
230 u32 blockchangecount = 0;
232 core::map<v3s16, u8> unlighted_nodes;
233 core::map<v3s16, u8>::Iterator j;
234 j = from_nodes.getIterator();
237 Initialize block cache
240 MapBlock *block = NULL;
241 // Cache this a bit, too
242 bool block_checked_in_modified = false;
244 for(; j.atEnd() == false; j++)
246 v3s16 pos = j.getNode()->getKey();
247 v3s16 blockpos = getNodeBlockPos(pos);
249 // Only fetch a new block if the block position has changed
251 if(block == NULL || blockpos != blockpos_last){
252 block = getBlockNoCreate(blockpos);
253 blockpos_last = blockpos;
255 block_checked_in_modified = false;
259 catch(InvalidPositionException &e)
267 // Calculate relative position in block
268 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
270 // Get node straight from the block
271 MapNode n = block->getNode(relpos);
273 u8 oldlight = j.getNode()->getValue();
275 // Loop through 6 neighbors
276 for(u16 i=0; i<6; i++)
278 // Get the position of the neighbor node
279 v3s16 n2pos = pos + dirs[i];
281 // Get the block where the node is located
282 v3s16 blockpos = getNodeBlockPos(n2pos);
286 // Only fetch a new block if the block position has changed
288 if(block == NULL || blockpos != blockpos_last){
289 block = getBlockNoCreate(blockpos);
290 blockpos_last = blockpos;
292 block_checked_in_modified = false;
296 catch(InvalidPositionException &e)
301 // Calculate relative position in block
302 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
303 // Get node straight from the block
304 MapNode n2 = block->getNode(relpos);
306 bool changed = false;
308 //TODO: Optimize output by optimizing light_sources?
311 If the neighbor is dimmer than what was specified
312 as oldlight (the light of the previous node)
314 if(n2.getLight(bank) < oldlight)
317 And the neighbor is transparent and it has some light
319 if(n2.light_propagates() && n2.getLight(bank) != 0)
322 Set light to 0 and add to queue
325 u8 current_light = n2.getLight(bank);
326 n2.setLight(bank, 0);
327 block->setNode(relpos, n2);
329 unlighted_nodes.insert(n2pos, current_light);
333 Remove from light_sources if it is there
334 NOTE: This doesn't happen nearly at all
336 /*if(light_sources.find(n2pos))
338 std::cout<<"Removed from light_sources"<<std::endl;
339 light_sources.remove(n2pos);
344 if(light_sources.find(n2pos) != NULL)
345 light_sources.remove(n2pos);*/
348 light_sources.insert(n2pos, true);
351 // Add to modified_blocks
352 if(changed == true && block_checked_in_modified == false)
354 // If the block is not found in modified_blocks, add.
355 if(modified_blocks.find(blockpos) == NULL)
357 modified_blocks.insert(blockpos, block);
359 block_checked_in_modified = true;
362 catch(InvalidPositionException &e)
369 /*dstream<<"unspreadLight(): Changed block "
370 <<blockchangecount<<" times"
371 <<" for "<<from_nodes.size()<<" nodes"
374 if(unlighted_nodes.size() > 0)
375 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
379 A single-node wrapper of the above
381 void Map::unLightNeighbors(enum LightBank bank,
382 v3s16 pos, u8 lightwas,
383 core::map<v3s16, bool> & light_sources,
384 core::map<v3s16, MapBlock*> & modified_blocks)
386 core::map<v3s16, u8> from_nodes;
387 from_nodes.insert(pos, lightwas);
389 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
393 Lights neighbors of from_nodes, collects all them and then
396 void Map::spreadLight(enum LightBank bank,
397 core::map<v3s16, bool> & from_nodes,
398 core::map<v3s16, MapBlock*> & modified_blocks)
400 const v3s16 dirs[6] = {
401 v3s16(0,0,1), // back
403 v3s16(1,0,0), // right
404 v3s16(0,0,-1), // front
405 v3s16(0,-1,0), // bottom
406 v3s16(-1,0,0), // left
409 if(from_nodes.size() == 0)
412 u32 blockchangecount = 0;
414 core::map<v3s16, bool> lighted_nodes;
415 core::map<v3s16, bool>::Iterator j;
416 j = from_nodes.getIterator();
419 Initialize block cache
422 MapBlock *block = NULL;
423 // Cache this a bit, too
424 bool block_checked_in_modified = false;
426 for(; j.atEnd() == false; j++)
427 //for(; j != from_nodes.end(); j++)
429 v3s16 pos = j.getNode()->getKey();
431 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
432 v3s16 blockpos = getNodeBlockPos(pos);
434 // Only fetch a new block if the block position has changed
436 if(block == NULL || blockpos != blockpos_last){
437 block = getBlockNoCreate(blockpos);
438 blockpos_last = blockpos;
440 block_checked_in_modified = false;
444 catch(InvalidPositionException &e)
452 // Calculate relative position in block
453 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
455 // Get node straight from the block
456 MapNode n = block->getNode(relpos);
458 u8 oldlight = n.getLight(bank);
459 u8 newlight = diminish_light(oldlight);
461 // Loop through 6 neighbors
462 for(u16 i=0; i<6; i++){
463 // Get the position of the neighbor node
464 v3s16 n2pos = pos + dirs[i];
466 // Get the block where the node is located
467 v3s16 blockpos = getNodeBlockPos(n2pos);
471 // Only fetch a new block if the block position has changed
473 if(block == NULL || blockpos != blockpos_last){
474 block = getBlockNoCreate(blockpos);
475 blockpos_last = blockpos;
477 block_checked_in_modified = false;
481 catch(InvalidPositionException &e)
486 // Calculate relative position in block
487 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
488 // Get node straight from the block
489 MapNode n2 = block->getNode(relpos);
491 bool changed = false;
493 If the neighbor is brighter than the current node,
494 add to list (it will light up this node on its turn)
496 if(n2.getLight(bank) > undiminish_light(oldlight))
498 lighted_nodes.insert(n2pos, true);
499 //lighted_nodes.push_back(n2pos);
503 If the neighbor is dimmer than how much light this node
504 would spread on it, add to list
506 if(n2.getLight(bank) < newlight)
508 if(n2.light_propagates())
510 n2.setLight(bank, newlight);
511 block->setNode(relpos, n2);
512 lighted_nodes.insert(n2pos, true);
513 //lighted_nodes.push_back(n2pos);
518 // Add to modified_blocks
519 if(changed == true && block_checked_in_modified == false)
521 // If the block is not found in modified_blocks, add.
522 if(modified_blocks.find(blockpos) == NULL)
524 modified_blocks.insert(blockpos, block);
526 block_checked_in_modified = true;
529 catch(InvalidPositionException &e)
536 /*dstream<<"spreadLight(): Changed block "
537 <<blockchangecount<<" times"
538 <<" for "<<from_nodes.size()<<" nodes"
541 if(lighted_nodes.size() > 0)
542 spreadLight(bank, lighted_nodes, modified_blocks);
546 A single-node source variation of the above.
548 void Map::lightNeighbors(enum LightBank bank,
550 core::map<v3s16, MapBlock*> & modified_blocks)
552 core::map<v3s16, bool> from_nodes;
553 from_nodes.insert(pos, true);
554 spreadLight(bank, from_nodes, modified_blocks);
557 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
560 v3s16(0,0,1), // back
562 v3s16(1,0,0), // right
563 v3s16(0,0,-1), // front
564 v3s16(0,-1,0), // bottom
565 v3s16(-1,0,0), // left
568 u8 brightest_light = 0;
569 v3s16 brightest_pos(0,0,0);
570 bool found_something = false;
572 // Loop through 6 neighbors
573 for(u16 i=0; i<6; i++){
574 // Get the position of the neighbor node
575 v3s16 n2pos = p + dirs[i];
580 catch(InvalidPositionException &e)
584 if(n2.getLight(bank) > brightest_light || found_something == false){
585 brightest_light = n2.getLight(bank);
586 brightest_pos = n2pos;
587 found_something = true;
591 if(found_something == false)
592 throw InvalidPositionException();
594 return brightest_pos;
598 Propagates sunlight down from a node.
599 Starting point gets sunlight.
601 Returns the lowest y value of where the sunlight went.
603 s16 Map::propagateSunlight(v3s16 start,
604 core::map<v3s16, MapBlock*> & modified_blocks)
609 v3s16 pos(start.X, y, start.Z);
611 v3s16 blockpos = getNodeBlockPos(pos);
614 block = getBlockNoCreate(blockpos);
616 catch(InvalidPositionException &e)
621 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
622 MapNode n = block->getNode(relpos);
624 if(n.sunlight_propagates())
626 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
627 block->setNode(relpos, n);
629 modified_blocks.insert(blockpos, block);
638 void Map::updateLighting(enum LightBank bank,
639 core::map<v3s16, MapBlock*> & a_blocks,
640 core::map<v3s16, MapBlock*> & modified_blocks)
642 /*m_dout<<DTIME<<"Map::updateLighting(): "
643 <<a_blocks.getSize()<<" blocks... ";*/
647 u32 count_was = modified_blocks.size();
649 core::map<v3s16, bool> light_sources;
651 core::map<v3s16, u8> unlight_from;
653 core::map<v3s16, MapBlock*>::Iterator i;
654 i = a_blocks.getIterator();
655 for(; i.atEnd() == false; i++)
657 MapBlock *block = i.getNode()->getValue();
661 // Don't bother with dummy blocks.
665 v3s16 pos = block->getPos();
666 modified_blocks.insert(pos, block);
669 Clear all light from block
671 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
672 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
673 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
678 MapNode n = block->getNode(v3s16(x,y,z));
679 u8 oldlight = n.getLight(bank);
681 block->setNode(v3s16(x,y,z), n);
683 // Collect borders for unlighting
684 if(x==0 || x == MAP_BLOCKSIZE-1
685 || y==0 || y == MAP_BLOCKSIZE-1
686 || z==0 || z == MAP_BLOCKSIZE-1)
688 v3s16 p_map = p + v3s16(
691 MAP_BLOCKSIZE*pos.Z);
692 unlight_from.insert(p_map, oldlight);
695 catch(InvalidPositionException &e)
698 This would happen when dealing with a
702 dstream<<"updateLighting(): InvalidPositionException"
707 if(bank == LIGHTBANK_DAY)
709 bool bottom_valid = block->propagateSunlight(light_sources);
711 // If bottom is valid, we're done.
715 else if(bank == LIGHTBANK_NIGHT)
724 /*dstream<<"Bottom for sunlight-propagated block ("
725 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
728 // Else get the block below and loop to it
732 block = getBlockNoCreate(pos);
734 catch(InvalidPositionException &e)
743 //TimeTaker timer("unspreadLight", g_irrlicht);
744 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
749 u32 diff = modified_blocks.size() - count_was;
750 count_was = modified_blocks.size();
751 dstream<<"unspreadLight modified "<<diff<<std::endl;
754 // TODO: Spread light from propagated sunlight?
755 // Yes, add it to light_sources... somehow.
756 // It has to be added at somewhere above, in the loop.
758 // NOTE: This actually works fine without doing so
759 // - Find out why it works
762 //TimeTaker timer("spreadLight", g_irrlicht);
763 spreadLight(bank, light_sources, modified_blocks);
768 u32 diff = modified_blocks.size() - count_was;
769 count_was = modified_blocks.size();
770 dstream<<"spreadLight modified "<<diff<<std::endl;
773 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
776 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
777 core::map<v3s16, MapBlock*> & modified_blocks)
779 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
780 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
783 Update information about whether day and night light differ
785 for(core::map<v3s16, MapBlock*>::Iterator
786 i = modified_blocks.getIterator();
787 i.atEnd() == false; i++)
789 MapBlock *block = i.getNode()->getValue();
790 block->updateDayNightDiff();
795 This is called after changing a node from transparent to opaque.
796 The lighting value of the node should be left as-is after changing
797 other values. This sets the lighting value to 0.
799 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
800 core::map<v3s16, MapBlock*> &modified_blocks)*/
801 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
802 core::map<v3s16, MapBlock*> &modified_blocks)
805 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
806 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
809 From this node to nodes underneath:
810 If lighting is sunlight (1.0), unlight neighbours and
815 v3s16 toppos = p + v3s16(0,1,0);
817 bool node_under_sunlight = true;
818 core::map<v3s16, bool> light_sources;
821 If there is a node at top and it doesn't have sunlight,
822 there has not been any sunlight going down.
824 Otherwise there probably is.
827 MapNode topnode = getNode(toppos);
829 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
830 node_under_sunlight = false;
832 catch(InvalidPositionException &e)
836 enum LightBank banks[] =
841 for(s32 i=0; i<2; i++)
843 enum LightBank bank = banks[i];
845 u8 lightwas = getNode(p).getLight(bank);
847 // Add the block of the added node to modified_blocks
848 v3s16 blockpos = getNodeBlockPos(p);
849 MapBlock * block = getBlockNoCreate(blockpos);
850 assert(block != NULL);
851 modified_blocks.insert(blockpos, block);
853 if(isValidPosition(p) == false)
856 // Unlight neighbours of node.
857 // This means setting light of all consequent dimmer nodes
859 // This also collects the nodes at the border which will spread
860 // light again into this.
861 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
869 If node is under sunlight, take all sunlighted nodes under
870 it and clear light from them and from where the light has
872 TODO: This could be optimized by mass-unlighting instead
875 if(node_under_sunlight)
879 //m_dout<<DTIME<<"y="<<y<<std::endl;
880 v3s16 n2pos(p.X, y, p.Z);
886 catch(InvalidPositionException &e)
891 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
893 //m_dout<<DTIME<<"doing"<<std::endl;
894 unLightNeighbors(LIGHTBANK_DAY,
895 n2pos, n2.getLight(LIGHTBANK_DAY),
896 light_sources, modified_blocks);
897 n2.setLight(LIGHTBANK_DAY, 0);
905 for(s32 i=0; i<2; i++)
907 enum LightBank bank = banks[i];
910 Spread light from all nodes that might be capable of doing so
911 TODO: Convert to spreadLight
913 spreadLight(bank, light_sources, modified_blocks);
917 Update information about whether day and night light differ
919 for(core::map<v3s16, MapBlock*>::Iterator
920 i = modified_blocks.getIterator();
921 i.atEnd() == false; i++)
923 MapBlock *block = i.getNode()->getValue();
924 block->updateDayNightDiff();
930 void Map::removeNodeAndUpdate(v3s16 p,
931 core::map<v3s16, MapBlock*> &modified_blocks)
934 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
935 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
937 bool node_under_sunlight = true;
939 v3s16 toppos = p + v3s16(0,1,0);
941 // Node will be replaced with this
942 u8 replace_material = CONTENT_AIR;
945 If there is a node at top and it doesn't have sunlight,
946 there will be no sunlight going down.
949 MapNode topnode = getNode(toppos);
951 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
952 node_under_sunlight = false;
954 catch(InvalidPositionException &e)
958 core::map<v3s16, bool> light_sources;
960 enum LightBank banks[] =
965 for(s32 i=0; i<2; i++)
967 enum LightBank bank = banks[i];
970 Unlight neighbors (in case the node is a light source)
972 unLightNeighbors(bank, p,
973 getNode(p).getLight(bank),
974 light_sources, modified_blocks);
979 This also clears the lighting.
983 n.d = replace_material;
986 for(s32 i=0; i<2; i++)
988 enum LightBank bank = banks[i];
993 spreadLight(bank, light_sources, modified_blocks);
996 // Add the block of the removed node to modified_blocks
997 v3s16 blockpos = getNodeBlockPos(p);
998 MapBlock * block = getBlockNoCreate(blockpos);
999 assert(block != NULL);
1000 modified_blocks.insert(blockpos, block);
1003 If the removed node was under sunlight, propagate the
1004 sunlight down from it and then light all neighbors
1005 of the propagated blocks.
1007 if(node_under_sunlight)
1009 s16 ybottom = propagateSunlight(p, modified_blocks);
1010 /*m_dout<<DTIME<<"Node was under sunlight. "
1011 "Propagating sunlight";
1012 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1014 for(; y >= ybottom; y--)
1016 v3s16 p2(p.X, y, p.Z);
1017 /*m_dout<<DTIME<<"lighting neighbors of node ("
1018 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1020 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1025 // Set the lighting of this node to 0
1026 // TODO: Is this needed? Lighting is cleared up there already.
1028 MapNode n = getNode(p);
1029 n.setLight(LIGHTBANK_DAY, 0);
1032 catch(InvalidPositionException &e)
1038 for(s32 i=0; i<2; i++)
1040 enum LightBank bank = banks[i];
1042 // Get the brightest neighbour node and propagate light from it
1043 v3s16 n2p = getBrightestNeighbour(bank, p);
1045 MapNode n2 = getNode(n2p);
1046 lightNeighbors(bank, n2p, modified_blocks);
1048 catch(InvalidPositionException &e)
1054 Update information about whether day and night light differ
1056 for(core::map<v3s16, MapBlock*>::Iterator
1057 i = modified_blocks.getIterator();
1058 i.atEnd() == false; i++)
1060 MapBlock *block = i.getNode()->getValue();
1061 block->updateDayNightDiff();
1066 void Map::expireMeshes(bool only_daynight_diffed)
1068 TimeTaker timer("expireMeshes()", g_irrlicht);
1070 core::map<v2s16, MapSector*>::Iterator si;
1071 si = m_sectors.getIterator();
1072 for(; si.atEnd() == false; si++)
1074 MapSector *sector = si.getNode()->getValue();
1076 core::list< MapBlock * > sectorblocks;
1077 sector->getBlocks(sectorblocks);
1079 core::list< MapBlock * >::Iterator i;
1080 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1082 MapBlock *block = *i;
1084 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1090 JMutexAutoLock lock(block->mesh_mutex);
1091 if(block->mesh != NULL)
1093 /*block->mesh->drop();
1094 block->mesh = NULL;*/
1095 block->setMeshExpired(true);
1102 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1104 assert(mapType() == MAPTYPE_CLIENT);
1107 v3s16 p = blockpos + v3s16(0,0,0);
1108 MapBlock *b = getBlockNoCreate(p);
1109 b->updateMesh(daynight_ratio);
1111 catch(InvalidPositionException &e){}
1113 v3s16 p = blockpos + v3s16(-1,0,0);
1114 MapBlock *b = getBlockNoCreate(p);
1115 b->updateMesh(daynight_ratio);
1117 catch(InvalidPositionException &e){}
1119 v3s16 p = blockpos + v3s16(0,-1,0);
1120 MapBlock *b = getBlockNoCreate(p);
1121 b->updateMesh(daynight_ratio);
1123 catch(InvalidPositionException &e){}
1125 v3s16 p = blockpos + v3s16(0,0,-1);
1126 MapBlock *b = getBlockNoCreate(p);
1127 b->updateMesh(daynight_ratio);
1129 catch(InvalidPositionException &e){}
1134 bool Map::dayNightDiffed(v3s16 blockpos)
1137 v3s16 p = blockpos + v3s16(0,0,0);
1138 MapBlock *b = getBlockNoCreate(p);
1139 if(b->dayNightDiffed())
1142 catch(InvalidPositionException &e){}
1144 v3s16 p = blockpos + v3s16(-1,0,0);
1145 MapBlock *b = getBlockNoCreate(p);
1146 if(b->dayNightDiffed())
1149 catch(InvalidPositionException &e){}
1151 v3s16 p = blockpos + v3s16(0,-1,0);
1152 MapBlock *b = getBlockNoCreate(p);
1153 if(b->dayNightDiffed())
1156 catch(InvalidPositionException &e){}
1158 v3s16 p = blockpos + v3s16(0,0,-1);
1159 MapBlock *b = getBlockNoCreate(p);
1160 if(b->dayNightDiffed())
1163 catch(InvalidPositionException &e){}
1169 Updates usage timers
1171 void Map::timerUpdate(float dtime)
1173 JMutexAutoLock lock(m_sector_mutex);
1175 core::map<v2s16, MapSector*>::Iterator si;
1177 si = m_sectors.getIterator();
1178 for(; si.atEnd() == false; si++)
1180 MapSector *sector = si.getNode()->getValue();
1181 sector->usage_timer += dtime;
1185 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1188 Wait for caches to be removed before continuing.
1190 This disables the existence of caches while locked
1192 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1194 core::list<v2s16>::Iterator j;
1195 for(j=list.begin(); j!=list.end(); j++)
1197 MapSector *sector = m_sectors[*j];
1200 sector->deleteBlocks();
1205 If sector is in sector cache, remove it from there
1207 if(m_sector_cache == sector)
1209 m_sector_cache = NULL;
1212 Remove from map and delete
1214 m_sectors.remove(*j);
1220 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1221 core::list<v3s16> *deleted_blocks)
1223 JMutexAutoLock lock(m_sector_mutex);
1225 core::list<v2s16> sector_deletion_queue;
1226 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1227 for(; i.atEnd() == false; i++)
1229 MapSector *sector = i.getNode()->getValue();
1231 Delete sector from memory if it hasn't been used in a long time
1233 if(sector->usage_timer > timeout)
1235 sector_deletion_queue.push_back(i.getNode()->getKey());
1237 if(deleted_blocks != NULL)
1239 // Collect positions of blocks of sector
1240 MapSector *sector = i.getNode()->getValue();
1241 core::list<MapBlock*> blocks;
1242 sector->getBlocks(blocks);
1243 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1244 i != blocks.end(); i++)
1246 deleted_blocks->push_back((*i)->getPos());
1251 deleteSectors(sector_deletion_queue, only_blocks);
1252 return sector_deletion_queue.getSize();
1255 void Map::PrintInfo(std::ostream &out)
1264 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1268 m_savedir = savedir;
1269 m_map_saving_enabled = false;
1273 // If directory exists, check contents and load if possible
1274 if(fs::PathExists(m_savedir))
1276 // If directory is empty, it is safe to save into it.
1277 if(fs::GetDirListing(m_savedir).size() == 0)
1279 dstream<<DTIME<<"Server: Empty save directory is valid."
1281 m_map_saving_enabled = true;
1285 // Load master heightmap
1286 loadMasterHeightmap();
1288 // Load sector (0,0) and throw and exception on fail
1289 if(loadSectorFull(v2s16(0,0)) == false)
1290 throw LoadError("Failed to load sector (0,0)");
1292 dstream<<DTIME<<"Server: Successfully loaded master "
1293 "heightmap and sector (0,0) from "<<savedir<<
1294 ", assuming valid save directory."
1297 m_map_saving_enabled = true;
1298 // Map loaded, not creating new one
1302 // If directory doesn't exist, it is safe to save to it
1304 m_map_saving_enabled = true;
1307 catch(std::exception &e)
1309 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1310 <<", exception: "<<e.what()<<std::endl;
1311 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1312 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1315 dstream<<DTIME<<"Initializing new map."<<std::endl;
1317 // Create master heightmap
1318 ValueGenerator *maxgen =
1319 ValueGenerator::deSerialize(hmp.randmax);
1320 ValueGenerator *factorgen =
1321 ValueGenerator::deSerialize(hmp.randfactor);
1322 ValueGenerator *basegen =
1323 ValueGenerator::deSerialize(hmp.base);
1324 m_heightmap = new UnlimitedHeightmap
1325 (hmp.blocksize, maxgen, factorgen, basegen);
1327 // Set map parameters
1330 // Create zero sector
1331 emergeSector(v2s16(0,0));
1333 // Initially write whole map
1337 ServerMap::~ServerMap()
1341 if(m_map_saving_enabled)
1344 // Save only changed parts
1346 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1350 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1353 catch(std::exception &e)
1355 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1356 <<", exception: "<<e.what()<<std::endl;
1359 if(m_heightmap != NULL)
1363 MapSector * ServerMap::emergeSector(v2s16 p2d)
1365 DSTACK("%s: p2d=(%d,%d)",
1368 // Check that it doesn't exist already
1370 return getSectorNoGenerate(p2d);
1372 catch(InvalidPositionException &e)
1377 Try to load the sector from disk.
1379 if(loadSectorFull(p2d) == true)
1381 return getSectorNoGenerate(p2d);
1385 If there is no master heightmap, throw.
1387 if(m_heightmap == NULL)
1389 throw InvalidPositionException("emergeSector(): no heightmap");
1393 Do not generate over-limit
1395 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1396 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1397 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1398 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1399 throw InvalidPositionException("emergeSector(): pos. over limit");
1402 Generate sector and heightmaps
1405 // Number of heightmaps in sector in each direction
1406 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1408 // Heightmap side width
1409 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1411 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1413 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1414 " heightmaps and objects"<<std::endl;*/
1416 // Loop through sub-heightmaps
1417 for(s16 y=0; y<hm_split; y++)
1418 for(s16 x=0; x<hm_split; x++)
1420 v2s16 p_in_sector = v2s16(x,y);
1421 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1423 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1424 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1425 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1426 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1429 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1430 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1433 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1435 sector->setHeightmap(p_in_sector, hm);
1437 //TODO: Make these values configurable
1438 //hm->generateContinued(0.0, 0.0, corners);
1439 hm->generateContinued(0.5, 0.2, corners);
1440 //hm->generateContinued(1.0, 0.2, corners);
1441 //hm->generateContinued(2.0, 0.2, corners);
1451 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1452 sector->setObjects(objects);
1454 v2s16 mhm_p = p2d * hm_split;
1456 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1457 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1458 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1459 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1462 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1463 float avgslope = 0.0;
1464 avgslope += fabs(avgheight - corners[0]);
1465 avgslope += fabs(avgheight - corners[1]);
1466 avgslope += fabs(avgheight - corners[2]);
1467 avgslope += fabs(avgheight - corners[3]);
1469 avgslope /= MAP_BLOCKSIZE;
1470 //dstream<<"avgslope="<<avgslope<<std::endl;
1472 float pitness = 0.0;
1474 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1477 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1480 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1483 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1487 pitness /= MAP_BLOCKSIZE;
1488 //dstream<<"pitness="<<pitness<<std::endl;
1491 Plant some trees if there is not much slope
1494 // Avgslope is the derivative of a hill
1495 float t = avgslope * avgslope;
1496 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1499 tree_max = a / (t/0.03);
1502 u32 count = (rand()%(tree_max+1));
1503 //u32 count = tree_max;
1504 for(u32 i=0; i<count; i++)
1506 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1507 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1508 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1511 objects->insert(v3s16(x, y, z),
1512 SECTOR_OBJECT_TREE_1);
1516 Plant some bushes if sector is pit-like
1519 // Pitness usually goes at around -0.5...0.5
1521 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1523 bush_max = (pitness*a*4);
1526 u32 count = (rand()%(bush_max+1));
1527 for(u32 i=0; i<count; i++)
1529 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1530 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1531 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1534 objects->insert(v3s16(x, y, z),
1535 SECTOR_OBJECT_BUSH_1);
1539 Add ravine (randomly)
1541 if(m_params.ravines_amount != 0)
1543 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1546 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1547 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1550 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1551 objects->insert(v3s16(x, y, z),
1552 SECTOR_OBJECT_RAVINE);
1559 JMutexAutoLock lock(m_sector_mutex);
1560 m_sectors.insert(p2d, sector);
1565 MapBlock * ServerMap::emergeBlock(
1567 bool only_from_disk,
1568 core::map<v3s16, MapBlock*> &changed_blocks,
1569 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1572 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1574 p.X, p.Y, p.Z, only_from_disk);
1576 /*dstream<<"ServerMap::emergeBlock(): "
1577 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1578 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1579 v2s16 p2d(p.X, p.Z);
1582 This will create or load a sector if not found in memory.
1583 If block exists on disk, it will be loaded.
1585 NOTE: On old save formats, this will be slow, as it generates
1586 lighting on blocks for them.
1588 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1589 assert(sector->getId() == MAPSECTOR_SERVER);
1591 // Try to get a block from the sector
1592 MapBlock *block = NULL;
1593 bool not_on_disk = false;
1595 block = sector->getBlockNoCreate(block_y);
1596 if(block->isDummy() == true)
1601 catch(InvalidPositionException &e)
1607 If block was not found on disk and not going to generate a
1608 new one, make sure there is a dummy block in place.
1610 if(not_on_disk && only_from_disk)
1614 // Create dummy block
1615 block = new MapBlock(this, p, true);
1617 // Add block to sector
1618 sector->insertBlock(block);
1624 //dstream<<"Not found on disk, generating."<<std::endl;
1627 Do not generate over-limit
1629 if(blockpos_over_limit(p))
1630 throw InvalidPositionException("emergeBlock(): pos. over limit");
1635 Go on generating the block.
1637 TODO: If a dungeon gets generated so that it's side gets
1638 revealed to the outside air, the lighting should be
1643 If block doesn't exist, create one.
1644 If it exists, it is a dummy. In that case unDummify() it.
1648 block = sector->createBlankBlockNoInsert(block_y);
1652 // Remove the block so that nobody can get a half-generated one.
1653 sector->removeBlock(block);
1654 // Allocate the block to be a proper one.
1658 // Randomize a bit. This makes dungeons.
1659 /*bool low_block_is_empty = false;
1661 low_block_is_empty = true;*/
1664 //const s32 ued = 8;
1665 bool underground_emptiness[ued*ued*ued];
1666 for(s32 i=0; i<ued*ued*ued; i++)
1668 underground_emptiness[i] = ((rand() % 5) == 0);
1673 This is a messy hack to sort the emptiness a bit
1675 for(s32 j=0; j<2; j++)
1676 for(s32 y0=0; y0<ued; y0++)
1677 for(s32 z0=0; z0<ued; z0++)
1678 for(s32 x0=0; x0<ued; x0++)
1681 bool &e0 = underground_emptiness[
1682 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1683 +ued*(y0*ued/MAP_BLOCKSIZE)
1684 +(x0*ued/MAP_BLOCKSIZE)];
1687 v3s16(0,0,1), // back
1688 v3s16(1,0,0), // right
1689 v3s16(0,0,-1), // front
1690 v3s16(-1,0,0), // left
1691 /*v3s16(0,1,0), // top
1692 v3s16(0,-1,0), // bottom*/
1694 for(s32 i=0; i<4; i++)
1696 v3s16 p1 = p0 + dirs[i];
1697 if(isInArea(p1, ued) == false)
1699 bool &e1 = underground_emptiness[
1700 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1701 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1702 +(p1.X*ued/MAP_BLOCKSIZE)];
1707 v3s16(0,1,0), // top
1708 v3s16(0,-1,0), // bottom
1709 /*v3s16(0,0,1), // back
1710 v3s16(1,0,0), // right
1711 v3s16(0,0,-1), // front
1712 v3s16(-1,0,0), // left*/
1714 for(s32 i=0; i<2; i++)
1716 v3s16 p2 = p1 + dirs[i];
1719 if(isInArea(p2, ued) == false)
1721 bool &e2 = underground_emptiness[
1722 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1723 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1724 +(p2.X*ued/MAP_BLOCKSIZE)];
1739 // This is the basic material of what the visible flat ground
1741 u8 material = CONTENT_GRASS;
1743 u8 water_material = CONTENT_WATER;
1744 if(g_settings.getBool("endless_water"))
1745 water_material = CONTENT_OCEAN;
1747 s32 lowest_ground_y = 32767;
1748 s32 highest_ground_y = -32768;
1751 //sector->printHeightmaps();
1753 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1754 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1756 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1758 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1759 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1760 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1762 dstream<<"WARNING: Surface height not found in sector "
1763 "for block that is being emerged"<<std::endl;
1767 s16 surface_y = surface_y_f;
1768 //avg_ground_y += surface_y;
1769 if(surface_y < lowest_ground_y)
1770 lowest_ground_y = surface_y;
1771 if(surface_y > highest_ground_y)
1772 highest_ground_y = surface_y;
1774 s32 surface_depth = 0;
1776 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1778 //float min_slope = 0.45;
1779 //float max_slope = 0.85;
1780 float min_slope = 0.60;
1781 float max_slope = 1.20;
1782 float min_slope_depth = 5.0;
1783 float max_slope_depth = 0;
1784 if(slope < min_slope)
1785 surface_depth = min_slope_depth;
1786 else if(slope > max_slope)
1787 surface_depth = max_slope_depth;
1789 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1791 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1793 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1798 NOTE: If there are some man-made structures above the
1799 newly created block, they won't be taken into account.
1801 if(real_y > surface_y)
1802 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1808 if(real_y <= surface_y - surface_depth)
1811 if(underground_emptiness[
1812 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1813 +ued*(y0*ued/MAP_BLOCKSIZE)
1814 +(x0*ued/MAP_BLOCKSIZE)])
1820 n.d = CONTENT_STONE;
1823 // If node is at or under heightmap y
1824 else if(real_y <= surface_y)
1826 // If under water level, it's mud
1827 if(real_y < WATER_LEVEL)
1829 // Only the topmost node is grass
1830 else if(real_y <= surface_y - 1)
1832 // Else it's the main material
1836 // If node is over heightmap y
1838 // If under water level, it's water
1839 if(real_y < WATER_LEVEL)
1841 n.d = water_material;
1842 n.setLight(LIGHTBANK_DAY,
1843 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1849 block->setNode(v3s16(x0,y0,z0), n);
1854 Calculate is_underground
1856 // Probably underground if the highest part of block is under lowest
1858 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1859 block->setIsUnderground(is_underground);
1862 Force lighting update if some part of block is underground
1863 This is needed because of caves.
1866 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1867 if(some_part_underground)
1868 //if(is_underground)
1870 lighting_invalidated_blocks[block->getPos()] = block;
1877 //if(is_underground)
1878 if(some_part_underground)
1880 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1881 for(s16 i=0; i<underground_level*3; i++)
1886 (rand()%(MAP_BLOCKSIZE-2))+1,
1887 (rand()%(MAP_BLOCKSIZE-2))+1,
1888 (rand()%(MAP_BLOCKSIZE-2))+1
1894 //if(is_ground_content(block->getNode(cp).d))
1895 if(block->getNode(cp).d == CONTENT_STONE)
1897 block->setNode(cp, n);
1899 for(u16 i=0; i<26; i++)
1901 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1902 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1904 block->setNode(cp+g_26dirs[i], n);
1911 Create a few rats in empty blocks underground
1915 //for(u16 i=0; i<2; i++)
1918 (rand()%(MAP_BLOCKSIZE-2))+1,
1919 (rand()%(MAP_BLOCKSIZE-2))+1,
1920 (rand()%(MAP_BLOCKSIZE-2))+1
1923 // Check that the place is empty
1924 //if(!is_ground_content(block->getNode(cp).d))
1927 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1928 block->addObject(obj);
1934 Add block to sector.
1936 sector->insertBlock(block);
1942 // An y-wise container of changed blocks
1943 core::map<s16, MapBlock*> changed_blocks_sector;
1946 Check if any sector's objects can be placed now.
1949 core::map<v3s16, u8> *objects = sector->getObjects();
1950 core::list<v3s16> objects_to_remove;
1951 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1952 i.atEnd() == false; i++)
1954 v3s16 p = i.getNode()->getKey();
1956 u8 d = i.getNode()->getValue();
1958 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1963 if(d == SECTOR_OBJECT_TEST)
1965 if(sector->isValidArea(p + v3s16(0,0,0),
1966 p + v3s16(0,0,0), &changed_blocks_sector))
1969 n.d = CONTENT_TORCH;
1970 sector->setNode(p, n);
1971 objects_to_remove.push_back(p);
1974 else if(d == SECTOR_OBJECT_TREE_1)
1976 v3s16 p_min = p + v3s16(-1,0,-1);
1977 v3s16 p_max = p + v3s16(1,4,1);
1978 if(sector->isValidArea(p_min, p_max,
1979 &changed_blocks_sector))
1983 sector->setNode(p+v3s16(0,0,0), n);
1984 sector->setNode(p+v3s16(0,1,0), n);
1985 sector->setNode(p+v3s16(0,2,0), n);
1986 sector->setNode(p+v3s16(0,3,0), n);
1988 n.d = CONTENT_LEAVES;
1990 sector->setNode(p+v3s16(0,4,0), n);
1992 sector->setNode(p+v3s16(-1,4,0), n);
1993 sector->setNode(p+v3s16(1,4,0), n);
1994 sector->setNode(p+v3s16(0,4,-1), n);
1995 sector->setNode(p+v3s16(0,4,1), n);
1996 sector->setNode(p+v3s16(1,4,1), n);
1997 sector->setNode(p+v3s16(-1,4,1), n);
1998 sector->setNode(p+v3s16(-1,4,-1), n);
1999 sector->setNode(p+v3s16(1,4,-1), n);
2001 sector->setNode(p+v3s16(-1,3,0), n);
2002 sector->setNode(p+v3s16(1,3,0), n);
2003 sector->setNode(p+v3s16(0,3,-1), n);
2004 sector->setNode(p+v3s16(0,3,1), n);
2005 sector->setNode(p+v3s16(1,3,1), n);
2006 sector->setNode(p+v3s16(-1,3,1), n);
2007 sector->setNode(p+v3s16(-1,3,-1), n);
2008 sector->setNode(p+v3s16(1,3,-1), n);
2010 objects_to_remove.push_back(p);
2012 // Lighting has to be recalculated for this one.
2013 sector->getBlocksInArea(p_min, p_max,
2014 lighting_invalidated_blocks);
2017 else if(d == SECTOR_OBJECT_BUSH_1)
2019 if(sector->isValidArea(p + v3s16(0,0,0),
2020 p + v3s16(0,0,0), &changed_blocks_sector))
2023 n.d = CONTENT_LEAVES;
2024 sector->setNode(p+v3s16(0,0,0), n);
2026 objects_to_remove.push_back(p);
2029 else if(d == SECTOR_OBJECT_RAVINE)
2032 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2033 v3s16 p_max = p + v3s16(6,6,6);
2034 if(sector->isValidArea(p_min, p_max,
2035 &changed_blocks_sector))
2038 n.d = CONTENT_STONE;
2041 s16 depth = maxdepth + (rand()%10);
2043 s16 minz = -6 - (-2);
2045 for(s16 x=-6; x<=6; x++)
2047 z += -1 + (rand()%3);
2052 for(s16 y=depth+(rand()%2); y<=6; y++)
2054 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2057 v3s16 p2 = p + v3s16(x,y,z-2);
2058 if(is_ground_content(sector->getNode(p2).d))
2059 sector->setNode(p2, n);
2062 v3s16 p2 = p + v3s16(x,y,z-1);
2063 if(is_ground_content(sector->getNode(p2).d))
2064 sector->setNode(p2, n2);
2067 v3s16 p2 = p + v3s16(x,y,z+0);
2068 if(is_ground_content(sector->getNode(p2).d))
2069 sector->setNode(p2, n2);
2072 v3s16 p2 = p + v3s16(x,y,z+1);
2073 if(is_ground_content(sector->getNode(p2).d))
2074 sector->setNode(p2, n);
2077 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2078 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2082 objects_to_remove.push_back(p);
2084 // Lighting has to be recalculated for this one.
2085 sector->getBlocksInArea(p_min, p_max,
2086 lighting_invalidated_blocks);
2091 dstream<<"ServerMap::emergeBlock(): "
2092 "Invalid heightmap object"
2097 catch(InvalidPositionException &e)
2099 dstream<<"WARNING: "<<__FUNCTION_NAME
2100 <<": while inserting object "<<(int)d
2101 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2102 <<" InvalidPositionException.what()="
2103 <<e.what()<<std::endl;
2104 // This is not too fatal and seems to happen sometimes.
2109 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2110 i != objects_to_remove.end(); i++)
2112 objects->remove(*i);
2115 for(core::map<s16, MapBlock*>::Iterator
2116 i = changed_blocks_sector.getIterator();
2117 i.atEnd() == false; i++)
2119 MapBlock *block = i.getNode()->getValue();
2121 changed_blocks.insert(block->getPos(), block);
2127 void ServerMap::createDir(std::string path)
2129 if(fs::CreateDir(path) == false)
2131 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2132 <<"\""<<path<<"\""<<std::endl;
2133 throw BaseException("ServerMap failed to create directory");
2137 std::string ServerMap::getSectorSubDir(v2s16 pos)
2140 snprintf(cc, 9, "%.4x%.4x",
2141 (unsigned int)pos.X&0xffff,
2142 (unsigned int)pos.Y&0xffff);
2144 return std::string(cc);
2147 std::string ServerMap::getSectorDir(v2s16 pos)
2149 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2152 v2s16 ServerMap::getSectorPos(std::string dirname)
2154 if(dirname.size() != 8)
2155 throw InvalidFilenameException("Invalid sector directory name");
2157 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2159 throw InvalidFilenameException("Invalid sector directory name");
2160 v2s16 pos((s16)x, (s16)y);
2164 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2166 v2s16 p2d = getSectorPos(sectordir);
2168 if(blockfile.size() != 4){
2169 throw InvalidFilenameException("Invalid block filename");
2172 int r = sscanf(blockfile.c_str(), "%4x", &y);
2174 throw InvalidFilenameException("Invalid block filename");
2175 return v3s16(p2d.X, y, p2d.Y);
2179 #define ENABLE_SECTOR_SAVING 1
2180 #define ENABLE_SECTOR_LOADING 1
2181 #define ENABLE_BLOCK_SAVING 1
2182 #define ENABLE_BLOCK_LOADING 1
2184 void ServerMap::save(bool only_changed)
2186 DSTACK(__FUNCTION_NAME);
2187 if(m_map_saving_enabled == false)
2189 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2193 if(only_changed == false)
2194 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2197 saveMasterHeightmap();
2199 u32 sector_meta_count = 0;
2200 u32 block_count = 0;
2203 JMutexAutoLock lock(m_sector_mutex);
2205 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2206 for(; i.atEnd() == false; i++)
2208 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2209 assert(sector->getId() == MAPSECTOR_SERVER);
2211 if(ENABLE_SECTOR_SAVING)
2213 if(sector->differs_from_disk || only_changed == false)
2215 saveSectorMeta(sector);
2216 sector_meta_count++;
2219 if(ENABLE_BLOCK_SAVING)
2221 core::list<MapBlock*> blocks;
2222 sector->getBlocks(blocks);
2223 core::list<MapBlock*>::Iterator j;
2224 for(j=blocks.begin(); j!=blocks.end(); j++)
2226 MapBlock *block = *j;
2227 if(block->getChangedFlag() || only_changed == false)
2238 u32 deleted_count = 0;
2239 deleted_count = deleteUnusedSectors(
2240 SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2243 Only print if something happened or saved whole map
2245 if(only_changed == false || sector_meta_count != 0
2246 || block_count != 0 || deleted_count != 0)
2248 dstream<<DTIME<<"ServerMap: Written: "
2249 <<sector_meta_count<<" sector metadata files, "
2250 <<block_count<<" block files, "
2251 <<deleted_count<<" sectors unloaded from memory."
2256 void ServerMap::loadAll()
2258 DSTACK(__FUNCTION_NAME);
2259 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2261 loadMasterHeightmap();
2263 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2265 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2267 JMutexAutoLock lock(m_sector_mutex);
2270 s32 printed_counter = -100000;
2271 s32 count = list.size();
2273 std::vector<fs::DirListNode>::iterator i;
2274 for(i=list.begin(); i!=list.end(); i++)
2276 if(counter > printed_counter + 10)
2278 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2279 printed_counter = counter;
2283 MapSector *sector = NULL;
2285 // We want directories
2289 sector = loadSectorMeta(i->name);
2291 catch(InvalidFilenameException &e)
2293 // This catches unknown crap in directory
2296 if(ENABLE_BLOCK_LOADING)
2298 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2299 (m_savedir+"/sectors/"+i->name);
2300 std::vector<fs::DirListNode>::iterator i2;
2301 for(i2=list2.begin(); i2!=list2.end(); i2++)
2307 loadBlock(i->name, i2->name, sector);
2309 catch(InvalidFilenameException &e)
2311 // This catches unknown crap in directory
2316 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2319 void ServerMap::saveMasterHeightmap()
2321 DSTACK(__FUNCTION_NAME);
2322 createDir(m_savedir);
2324 std::string fullpath = m_savedir + "/master_heightmap";
2325 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2326 if(o.good() == false)
2327 throw FileNotGoodException("Cannot open master heightmap");
2329 // Format used for writing
2330 u8 version = SER_FMT_VER_HIGHEST;
2333 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2335 [0] u8 serialization version
2336 [1] X master heightmap
2338 u32 fullsize = 1 + hmdata.getSize();
2339 SharedBuffer<u8> data(fullsize);
2342 memcpy(&data[1], *hmdata, hmdata.getSize());
2344 o.write((const char*)*data, fullsize);
2347 m_heightmap->serialize(o, version);
2350 void ServerMap::loadMasterHeightmap()
2352 DSTACK(__FUNCTION_NAME);
2353 std::string fullpath = m_savedir + "/master_heightmap";
2354 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2355 if(is.good() == false)
2356 throw FileNotGoodException("Cannot open master heightmap");
2358 if(m_heightmap != NULL)
2361 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2364 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2366 DSTACK(__FUNCTION_NAME);
2367 // Format used for writing
2368 u8 version = SER_FMT_VER_HIGHEST;
2370 v2s16 pos = sector->getPos();
2371 createDir(m_savedir);
2372 createDir(m_savedir+"/sectors");
2373 std::string dir = getSectorDir(pos);
2376 std::string fullpath = dir + "/heightmap";
2377 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2378 if(o.good() == false)
2379 throw FileNotGoodException("Cannot open master heightmap");
2381 sector->serialize(o, version);
2383 sector->differs_from_disk = false;
2386 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2388 DSTACK(__FUNCTION_NAME);
2390 v2s16 p2d = getSectorPos(dirname);
2391 std::string dir = m_savedir + "/sectors/" + dirname;
2393 std::string fullpath = dir + "/heightmap";
2394 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2395 if(is.good() == false)
2396 throw FileNotGoodException("Cannot open sector heightmap");
2398 ServerMapSector *sector = ServerMapSector::deSerialize
2399 (is, this, p2d, &m_hwrapper, m_sectors);
2401 sector->differs_from_disk = false;
2406 bool ServerMap::loadSectorFull(v2s16 p2d)
2408 DSTACK(__FUNCTION_NAME);
2409 std::string sectorsubdir = getSectorSubDir(p2d);
2411 MapSector *sector = NULL;
2413 JMutexAutoLock lock(m_sector_mutex);
2416 sector = loadSectorMeta(sectorsubdir);
2418 catch(InvalidFilenameException &e)
2422 catch(FileNotGoodException &e)
2426 catch(std::exception &e)
2431 if(ENABLE_BLOCK_LOADING)
2433 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2434 (m_savedir+"/sectors/"+sectorsubdir);
2435 std::vector<fs::DirListNode>::iterator i2;
2436 for(i2=list2.begin(); i2!=list2.end(); i2++)
2442 loadBlock(sectorsubdir, i2->name, sector);
2444 catch(InvalidFilenameException &e)
2446 // This catches unknown crap in directory
2454 bool ServerMap::deFlushSector(v2s16 p2d)
2456 DSTACK(__FUNCTION_NAME);
2457 // See if it already exists in memory
2459 MapSector *sector = getSectorNoGenerate(p2d);
2462 catch(InvalidPositionException &e)
2465 Try to load the sector from disk.
2467 if(loadSectorFull(p2d) == true)
2476 void ServerMap::saveBlock(MapBlock *block)
2478 DSTACK(__FUNCTION_NAME);
2480 Dummy blocks are not written
2482 if(block->isDummy())
2484 /*v3s16 p = block->getPos();
2485 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2486 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2490 // Format used for writing
2491 u8 version = SER_FMT_VER_HIGHEST;
2493 v3s16 p3d = block->getPos();
2494 v2s16 p2d(p3d.X, p3d.Z);
2495 createDir(m_savedir);
2496 createDir(m_savedir+"/sectors");
2497 std::string dir = getSectorDir(p2d);
2500 // Block file is map/sectors/xxxxxxxx/xxxx
2502 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2503 std::string fullpath = dir + "/" + cc;
2504 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2505 if(o.good() == false)
2506 throw FileNotGoodException("Cannot open block data");
2509 [0] u8 serialization version
2512 o.write((char*)&version, 1);
2514 block->serialize(o, version);
2517 Versions up from 9 have block objects.
2521 block->serializeObjects(o, version);
2524 // We just wrote it to the disk
2525 block->resetChangedFlag();
2528 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2530 DSTACK(__FUNCTION_NAME);
2534 // Block file is map/sectors/xxxxxxxx/xxxx
2535 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2536 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2537 if(is.good() == false)
2538 throw FileNotGoodException("Cannot open block file");
2540 v3s16 p3d = getBlockPos(sectordir, blockfile);
2541 v2s16 p2d(p3d.X, p3d.Z);
2543 assert(sector->getPos() == p2d);
2545 u8 version = SER_FMT_VER_INVALID;
2546 is.read((char*)&version, 1);
2548 /*u32 block_size = MapBlock::serializedLength(version);
2549 SharedBuffer<u8> data(block_size);
2550 is.read((char*)*data, block_size);*/
2552 // This will always return a sector because we're the server
2553 //MapSector *sector = emergeSector(p2d);
2555 MapBlock *block = NULL;
2556 bool created_new = false;
2558 block = sector->getBlockNoCreate(p3d.Y);
2560 catch(InvalidPositionException &e)
2562 block = sector->createBlankBlockNoInsert(p3d.Y);
2566 // deserialize block data
2567 block->deSerialize(is, version);
2570 Versions up from 9 have block objects.
2574 block->updateObjects(is, version, NULL);
2578 sector->insertBlock(block);
2581 Convert old formats to new and save
2584 // Save old format blocks in new format
2585 if(version < SER_FMT_VER_HIGHEST)
2590 // We just loaded it from the disk, so it's up-to-date.
2591 block->resetChangedFlag();
2594 catch(SerializationError &e)
2596 dstream<<"WARNING: Invalid block data on disk "
2597 "(SerializationError). Ignoring."
2602 // Gets from master heightmap
2603 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2605 assert(m_heightmap != NULL);
2613 corners[0] = m_heightmap->getGroundHeight
2614 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2615 corners[1] = m_heightmap->getGroundHeight
2616 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2617 corners[2] = m_heightmap->getGroundHeight
2618 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2619 corners[3] = m_heightmap->getGroundHeight
2620 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2623 void ServerMap::PrintInfo(std::ostream &out)
2634 ClientMap::ClientMap(
2636 JMutex &range_mutex,
2637 s16 &viewing_range_nodes,
2638 bool &viewing_range_all,
2639 scene::ISceneNode* parent,
2640 scene::ISceneManager* mgr,
2644 scene::ISceneNode(parent, mgr, id),
2647 m_range_mutex(range_mutex),
2648 m_viewing_range_nodes(viewing_range_nodes),
2649 m_viewing_range_all(viewing_range_all)
2653 /*m_box = core::aabbox3d<f32>(0,0,0,
2654 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2655 /*m_box = core::aabbox3d<f32>(0,0,0,
2656 map->getSizeNodes().X * BS,
2657 map->getSizeNodes().Y * BS,
2658 map->getSizeNodes().Z * BS);*/
2659 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2660 BS*1000000,BS*1000000,BS*1000000);
2662 //setPosition(v3f(BS,BS,BS));
2665 ClientMap::~ClientMap()
2667 JMutexAutoLock lock(mesh_mutex);
2676 MapSector * ClientMap::emergeSector(v2s16 p2d)
2678 DSTACK(__FUNCTION_NAME);
2679 // Check that it doesn't exist already
2681 return getSectorNoGenerate(p2d);
2683 catch(InvalidPositionException &e)
2687 // Create a sector with no heightmaps
2688 ClientMapSector *sector = new ClientMapSector(this, p2d);
2691 JMutexAutoLock lock(m_sector_mutex);
2692 m_sectors.insert(p2d, sector);
2698 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2700 DSTACK(__FUNCTION_NAME);
2701 ClientMapSector *sector = NULL;
2703 JMutexAutoLock lock(m_sector_mutex);
2705 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2709 sector = (ClientMapSector*)n->getValue();
2710 assert(sector->getId() == MAPSECTOR_CLIENT);
2714 sector = new ClientMapSector(this, p2d);
2716 JMutexAutoLock lock(m_sector_mutex);
2717 m_sectors.insert(p2d, sector);
2721 sector->deSerialize(is);
2724 void ClientMap::OnRegisterSceneNode()
2728 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2729 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2732 ISceneNode::OnRegisterSceneNode();
2735 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2737 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2738 DSTACK(__FUNCTION_NAME);
2740 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2743 Get time for measuring timeout.
2745 Measuring time is very useful for long delays when the
2746 machine is swapping a lot.
2748 int time1 = time(0);
2750 //s32 daynight_i = m_client->getDayNightIndex();
2751 u32 daynight_ratio = m_client->getDayNightRatio();
2754 Collect all blocks that are in the view range
2756 Should not optimize more here as we want to auto-update
2757 all changed nodes in viewing range at the next step.
2760 s16 viewing_range_nodes;
2761 bool viewing_range_all;
2763 JMutexAutoLock lock(m_range_mutex);
2764 viewing_range_nodes = m_viewing_range_nodes;
2765 viewing_range_all = m_viewing_range_all;
2768 m_camera_mutex.Lock();
2769 v3f camera_position = m_camera_position;
2770 v3f camera_direction = m_camera_direction;
2771 m_camera_mutex.Unlock();
2774 Get all blocks and draw all visible ones
2777 v3s16 cam_pos_nodes(
2778 camera_position.X / BS,
2779 camera_position.Y / BS,
2780 camera_position.Z / BS);
2782 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2784 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2785 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2787 // Take a fair amount as we will be dropping more out later
2789 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2790 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2791 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2793 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2794 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2795 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2797 u32 vertex_count = 0;
2799 // For limiting number of mesh updates per frame
2800 u32 mesh_update_count = 0;
2802 //NOTE: The sectors map should be locked but we're not doing it
2803 // because it'd cause too much delays
2805 int timecheck_counter = 0;
2807 core::map<v2s16, MapSector*>::Iterator si;
2808 si = m_sectors.getIterator();
2809 for(; si.atEnd() == false; si++)
2812 timecheck_counter++;
2813 if(timecheck_counter > 50)
2815 int time2 = time(0);
2816 if(time2 > time1 + 4)
2818 dstream<<"ClientMap::renderMap(): "
2819 "Rendering takes ages, returning."
2826 MapSector *sector = si.getNode()->getValue();
2827 v2s16 sp = sector->getPos();
2829 if(viewing_range_all == false)
2831 if(sp.X < p_blocks_min.X
2832 || sp.X > p_blocks_max.X
2833 || sp.Y < p_blocks_min.Z
2834 || sp.Y > p_blocks_max.Z)
2838 core::list< MapBlock * > sectorblocks;
2839 sector->getBlocks(sectorblocks);
2845 core::list< MapBlock * >::Iterator i;
2846 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2848 MapBlock *block = *i;
2851 Compare block position to camera position, skip
2852 if not seen on display
2855 v3s16 blockpos_nodes = block->getPosRelative();
2857 // Block center position
2859 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2860 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2861 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2864 // Block position relative to camera
2865 v3f blockpos_relative = blockpos - camera_position;
2867 // Distance in camera direction (+=front, -=back)
2868 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2871 f32 d = blockpos_relative.getLength();
2873 if(viewing_range_all == false)
2875 // If block is far away, don't draw it
2876 if(d > viewing_range_nodes * BS)
2877 // This is nicer when fog is used
2878 //if((dforward+d)/2 > viewing_range_nodes * BS)
2882 // Maximum radius of a block
2883 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2885 // If block is (nearly) touching the camera, don't
2886 // bother validating further (that is, render it anyway)
2887 if(d > block_max_radius * 1.5)
2889 // Cosine of the angle between the camera direction
2890 // and the block direction (camera_direction is an unit vector)
2891 f32 cosangle = dforward / d;
2893 // Compensate for the size of the block
2894 // (as the block has to be shown even if it's a bit off FOV)
2895 // This is an estimate.
2896 cosangle += block_max_radius / dforward;
2898 // If block is not in the field of view, skip it
2899 //if(cosangle < cos(FOV_ANGLE/2))
2900 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2905 Draw the faces of the block
2908 bool mesh_expired = false;
2911 JMutexAutoLock lock(block->mesh_mutex);
2913 mesh_expired = block->getMeshExpired();
2915 // Mesh has not been expired and there is no mesh:
2916 // block has no content
2917 if(block->mesh == NULL && mesh_expired == false)
2921 f32 faraway = BS*50;
2922 //f32 faraway = viewing_range_nodes * BS;
2925 This has to be done with the mesh_mutex unlocked
2927 if(mesh_expired && mesh_update_count < 6
2928 && (d < faraway || mesh_update_count < 3))
2929 //if(mesh_expired && mesh_update_count < 4)
2931 mesh_update_count++;
2933 // Mesh has been expired: generate new mesh
2934 //block->updateMeshes(daynight_i);
2935 block->updateMesh(daynight_ratio);
2937 mesh_expired = false;
2941 Don't draw an expired mesh that is far away
2943 /*if(mesh_expired && d >= faraway)
2946 // Instead, delete it
2947 JMutexAutoLock lock(block->mesh_mutex);
2950 block->mesh->drop();
2953 // And continue to next block
2958 JMutexAutoLock lock(block->mesh_mutex);
2960 scene::SMesh *mesh = block->mesh;
2965 u32 c = mesh->getMeshBufferCount();
2967 for(u32 i=0; i<c; i++)
2969 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2970 const video::SMaterial& material = buf->getMaterial();
2971 video::IMaterialRenderer* rnd =
2972 driver->getMaterialRenderer(material.MaterialType);
2973 bool transparent = (rnd && rnd->isTransparent());
2974 // Render transparent on transparent pass and likewise.
2975 if(transparent == is_transparent_pass)
2977 driver->setMaterial(buf->getMaterial());
2978 driver->drawMeshBuffer(buf);
2979 vertex_count += buf->getVertexCount();
2983 } // foreach sectorblocks
2986 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2987 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2990 void ClientMap::updateMesh()
2995 void ClientMap::PrintInfo(std::ostream &out)
3006 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3011 MapVoxelManipulator::~MapVoxelManipulator()
3013 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3018 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3020 TimeTaker timer1("emerge", g_irrlicht, &emerge_time);
3022 // Units of these are MapBlocks
3023 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3024 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3026 VoxelArea block_area_nodes
3027 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3029 addArea(block_area_nodes);
3031 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3032 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3033 for(s32 x=p_min.X; x<=p_max.X; x++)
3036 core::map<v3s16, bool>::Node *n;
3037 n = m_loaded_blocks.find(p);
3041 bool block_data_inexistent = false;
3044 TimeTaker timer1("emerge load", g_irrlicht, &emerge_load_time);
3046 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3047 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3050 dstream<<std::endl;*/
3052 MapBlock *block = m_map->getBlockNoCreate(p);
3053 if(block->isDummy())
3054 block_data_inexistent = true;
3056 block->copyTo(*this);
3058 catch(InvalidPositionException &e)
3060 block_data_inexistent = true;
3063 if(block_data_inexistent)
3065 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3066 // Fill with VOXELFLAG_INEXISTENT
3067 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3068 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3070 s32 i = m_area.index(a.MinEdge.X,y,z);
3071 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3075 m_loaded_blocks.insert(p, true);
3078 //dstream<<"emerge done"<<std::endl;
3083 void MapVoxelManipulator::emerge(VoxelArea a)
3085 TimeTaker timer1("emerge", g_irrlicht, &emerge_time);
3087 v3s16 size = a.getExtent();
3089 VoxelArea padded = a;
3090 padded.pad(m_area.getExtent() / 4);
3093 for(s16 z=0; z<size.Z; z++)
3094 for(s16 y=0; y<size.Y; y++)
3095 for(s16 x=0; x<size.X; x++)
3098 s32 i = m_area.index(a.MinEdge + p);
3099 // Don't touch nodes that have already been loaded
3100 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3104 TimeTaker timer1("emerge load", g_irrlicht, &emerge_load_time);
3105 MapNode n = m_map->getNode(a.MinEdge + p);
3109 catch(InvalidPositionException &e)
3111 m_flags[i] = VOXELFLAG_INEXISTENT;
3119 TODO: Add an option to only update eg. water and air nodes.
3120 This will make it interfere less with important stuff if
3123 void MapVoxelManipulator::blitBack
3124 (core::map<v3s16, MapBlock*> & modified_blocks)
3126 if(m_area.getExtent() == v3s16(0,0,0))
3129 //TimeTaker timer1("blitBack", g_irrlicht);
3132 Initialize block cache
3134 v3s16 blockpos_last;
3135 MapBlock *block = NULL;
3136 bool block_checked_in_modified = false;
3138 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3139 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3140 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3144 u8 f = m_flags[m_area.index(p)];
3145 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3148 MapNode &n = m_data[m_area.index(p)];
3150 v3s16 blockpos = getNodeBlockPos(p);
3155 if(block == NULL || blockpos != blockpos_last){
3156 block = m_map->getBlockNoCreate(blockpos);
3157 blockpos_last = blockpos;
3158 block_checked_in_modified = false;
3161 // Calculate relative position in block
3162 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3164 // Don't continue if nothing has changed here
3165 if(block->getNode(relpos) == n)
3168 //m_map->setNode(m_area.MinEdge + p, n);
3169 block->setNode(relpos, n);
3172 Make sure block is in modified_blocks
3174 if(block_checked_in_modified == false)
3176 modified_blocks[blockpos] = block;
3177 block_checked_in_modified = true;
3180 catch(InvalidPositionException &e)