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;
1625 //TimeTaker("emergeBlock()", g_irrlicht);
1628 Do not generate over-limit
1630 if(blockpos_over_limit(p))
1631 throw InvalidPositionException("emergeBlock(): pos. over limit");
1636 Go on generating the block.
1638 TODO: If a dungeon gets generated so that it's side gets
1639 revealed to the outside air, the lighting should be
1644 If block doesn't exist, create one.
1645 If it exists, it is a dummy. In that case unDummify() it.
1649 block = sector->createBlankBlockNoInsert(block_y);
1653 // Remove the block so that nobody can get a half-generated one.
1654 sector->removeBlock(block);
1655 // Allocate the block to be a proper one.
1659 // Randomize a bit. This makes dungeons.
1660 /*bool low_block_is_empty = false;
1662 low_block_is_empty = true;*/
1665 //const s32 ued = 8;
1666 bool underground_emptiness[ued*ued*ued];
1667 for(s32 i=0; i<ued*ued*ued; i++)
1669 underground_emptiness[i] = ((rand() % 5) == 0);
1674 This is a messy hack to sort the emptiness a bit
1676 for(s32 j=0; j<2; j++)
1677 for(s32 y0=0; y0<ued; y0++)
1678 for(s32 z0=0; z0<ued; z0++)
1679 for(s32 x0=0; x0<ued; x0++)
1682 bool &e0 = underground_emptiness[
1683 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1684 +ued*(y0*ued/MAP_BLOCKSIZE)
1685 +(x0*ued/MAP_BLOCKSIZE)];
1688 v3s16(0,0,1), // back
1689 v3s16(1,0,0), // right
1690 v3s16(0,0,-1), // front
1691 v3s16(-1,0,0), // left
1692 /*v3s16(0,1,0), // top
1693 v3s16(0,-1,0), // bottom*/
1695 for(s32 i=0; i<4; i++)
1697 v3s16 p1 = p0 + dirs[i];
1698 if(isInArea(p1, ued) == false)
1700 bool &e1 = underground_emptiness[
1701 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1702 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1703 +(p1.X*ued/MAP_BLOCKSIZE)];
1708 v3s16(0,1,0), // top
1709 v3s16(0,-1,0), // bottom
1710 /*v3s16(0,0,1), // back
1711 v3s16(1,0,0), // right
1712 v3s16(0,0,-1), // front
1713 v3s16(-1,0,0), // left*/
1715 for(s32 i=0; i<2; i++)
1717 v3s16 p2 = p1 + dirs[i];
1720 if(isInArea(p2, ued) == false)
1722 bool &e2 = underground_emptiness[
1723 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1724 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1725 +(p2.X*ued/MAP_BLOCKSIZE)];
1740 // This is the basic material of what the visible flat ground
1742 u8 material = CONTENT_GRASS;
1744 u8 water_material = CONTENT_WATER;
1745 if(g_settings.getBool("endless_water"))
1746 water_material = CONTENT_OCEAN;
1748 s32 lowest_ground_y = 32767;
1749 s32 highest_ground_y = -32768;
1752 //sector->printHeightmaps();
1754 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1755 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1757 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1759 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1760 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1761 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1763 dstream<<"WARNING: Surface height not found in sector "
1764 "for block that is being emerged"<<std::endl;
1768 s16 surface_y = surface_y_f;
1769 //avg_ground_y += surface_y;
1770 if(surface_y < lowest_ground_y)
1771 lowest_ground_y = surface_y;
1772 if(surface_y > highest_ground_y)
1773 highest_ground_y = surface_y;
1775 s32 surface_depth = 0;
1777 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1779 //float min_slope = 0.45;
1780 //float max_slope = 0.85;
1781 float min_slope = 0.60;
1782 float max_slope = 1.20;
1783 float min_slope_depth = 5.0;
1784 float max_slope_depth = 0;
1785 if(slope < min_slope)
1786 surface_depth = min_slope_depth;
1787 else if(slope > max_slope)
1788 surface_depth = max_slope_depth;
1790 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1792 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1794 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1799 NOTE: If there are some man-made structures above the
1800 newly created block, they won't be taken into account.
1802 if(real_y > surface_y)
1803 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1809 if(real_y <= surface_y - surface_depth)
1812 if(underground_emptiness[
1813 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1814 +ued*(y0*ued/MAP_BLOCKSIZE)
1815 +(x0*ued/MAP_BLOCKSIZE)])
1821 n.d = CONTENT_STONE;
1824 // If node is at or under heightmap y
1825 else if(real_y <= surface_y)
1827 // If under water level, it's mud
1828 if(real_y < WATER_LEVEL)
1830 // Only the topmost node is grass
1831 else if(real_y <= surface_y - 1)
1833 // Else it's the main material
1837 // If node is over heightmap y
1839 // If under water level, it's water
1840 if(real_y < WATER_LEVEL)
1842 n.d = water_material;
1843 n.setLight(LIGHTBANK_DAY,
1844 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1850 block->setNode(v3s16(x0,y0,z0), n);
1855 Calculate is_underground
1857 // Probably underground if the highest part of block is under lowest
1859 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1860 block->setIsUnderground(is_underground);
1863 Force lighting update if some part of block is underground
1864 This is needed because of caves.
1867 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1868 if(some_part_underground)
1869 //if(is_underground)
1871 lighting_invalidated_blocks[block->getPos()] = block;
1878 //if(is_underground)
1879 if(some_part_underground)
1881 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1882 for(s16 i=0; i<underground_level*3; i++)
1887 (rand()%(MAP_BLOCKSIZE-2))+1,
1888 (rand()%(MAP_BLOCKSIZE-2))+1,
1889 (rand()%(MAP_BLOCKSIZE-2))+1
1895 //if(is_ground_content(block->getNode(cp).d))
1896 if(block->getNode(cp).d == CONTENT_STONE)
1898 block->setNode(cp, n);
1900 for(u16 i=0; i<26; i++)
1902 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1903 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1905 block->setNode(cp+g_26dirs[i], n);
1912 Create a few rats in empty blocks underground
1916 //for(u16 i=0; i<2; i++)
1919 (rand()%(MAP_BLOCKSIZE-2))+1,
1920 (rand()%(MAP_BLOCKSIZE-2))+1,
1921 (rand()%(MAP_BLOCKSIZE-2))+1
1924 // Check that the place is empty
1925 //if(!is_ground_content(block->getNode(cp).d))
1928 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1929 block->addObject(obj);
1935 Add block to sector.
1937 sector->insertBlock(block);
1943 // An y-wise container of changed blocks
1944 core::map<s16, MapBlock*> changed_blocks_sector;
1947 Check if any sector's objects can be placed now.
1950 core::map<v3s16, u8> *objects = sector->getObjects();
1951 core::list<v3s16> objects_to_remove;
1952 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1953 i.atEnd() == false; i++)
1955 v3s16 p = i.getNode()->getKey();
1957 u8 d = i.getNode()->getValue();
1959 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1964 if(d == SECTOR_OBJECT_TEST)
1966 if(sector->isValidArea(p + v3s16(0,0,0),
1967 p + v3s16(0,0,0), &changed_blocks_sector))
1970 n.d = CONTENT_TORCH;
1971 sector->setNode(p, n);
1972 objects_to_remove.push_back(p);
1975 else if(d == SECTOR_OBJECT_TREE_1)
1977 v3s16 p_min = p + v3s16(-1,0,-1);
1978 v3s16 p_max = p + v3s16(1,4,1);
1979 if(sector->isValidArea(p_min, p_max,
1980 &changed_blocks_sector))
1984 sector->setNode(p+v3s16(0,0,0), n);
1985 sector->setNode(p+v3s16(0,1,0), n);
1986 sector->setNode(p+v3s16(0,2,0), n);
1987 sector->setNode(p+v3s16(0,3,0), n);
1989 n.d = CONTENT_LEAVES;
1991 sector->setNode(p+v3s16(0,4,0), n);
1993 sector->setNode(p+v3s16(-1,4,0), n);
1994 sector->setNode(p+v3s16(1,4,0), n);
1995 sector->setNode(p+v3s16(0,4,-1), n);
1996 sector->setNode(p+v3s16(0,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);
2000 sector->setNode(p+v3s16(1,4,-1), n);
2002 sector->setNode(p+v3s16(-1,3,0), n);
2003 sector->setNode(p+v3s16(1,3,0), n);
2004 sector->setNode(p+v3s16(0,3,-1), n);
2005 sector->setNode(p+v3s16(0,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);
2009 sector->setNode(p+v3s16(1,3,-1), n);
2011 objects_to_remove.push_back(p);
2013 // Lighting has to be recalculated for this one.
2014 sector->getBlocksInArea(p_min, p_max,
2015 lighting_invalidated_blocks);
2018 else if(d == SECTOR_OBJECT_BUSH_1)
2020 if(sector->isValidArea(p + v3s16(0,0,0),
2021 p + v3s16(0,0,0), &changed_blocks_sector))
2024 n.d = CONTENT_LEAVES;
2025 sector->setNode(p+v3s16(0,0,0), n);
2027 objects_to_remove.push_back(p);
2030 else if(d == SECTOR_OBJECT_RAVINE)
2033 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2034 v3s16 p_max = p + v3s16(6,6,6);
2035 if(sector->isValidArea(p_min, p_max,
2036 &changed_blocks_sector))
2039 n.d = CONTENT_STONE;
2042 s16 depth = maxdepth + (rand()%10);
2044 s16 minz = -6 - (-2);
2046 for(s16 x=-6; x<=6; x++)
2048 z += -1 + (rand()%3);
2053 for(s16 y=depth+(rand()%2); y<=6; y++)
2055 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2058 v3s16 p2 = p + v3s16(x,y,z-2);
2059 if(is_ground_content(sector->getNode(p2).d))
2060 sector->setNode(p2, n);
2063 v3s16 p2 = p + v3s16(x,y,z-1);
2064 if(is_ground_content(sector->getNode(p2).d))
2065 sector->setNode(p2, n2);
2068 v3s16 p2 = p + v3s16(x,y,z+0);
2069 if(is_ground_content(sector->getNode(p2).d))
2070 sector->setNode(p2, n2);
2073 v3s16 p2 = p + v3s16(x,y,z+1);
2074 if(is_ground_content(sector->getNode(p2).d))
2075 sector->setNode(p2, n);
2078 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2079 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2083 objects_to_remove.push_back(p);
2085 // Lighting has to be recalculated for this one.
2086 sector->getBlocksInArea(p_min, p_max,
2087 lighting_invalidated_blocks);
2092 dstream<<"ServerMap::emergeBlock(): "
2093 "Invalid heightmap object"
2098 catch(InvalidPositionException &e)
2100 dstream<<"WARNING: "<<__FUNCTION_NAME
2101 <<": while inserting object "<<(int)d
2102 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2103 <<" InvalidPositionException.what()="
2104 <<e.what()<<std::endl;
2105 // This is not too fatal and seems to happen sometimes.
2110 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2111 i != objects_to_remove.end(); i++)
2113 objects->remove(*i);
2116 for(core::map<s16, MapBlock*>::Iterator
2117 i = changed_blocks_sector.getIterator();
2118 i.atEnd() == false; i++)
2120 MapBlock *block = i.getNode()->getValue();
2122 changed_blocks.insert(block->getPos(), block);
2128 void ServerMap::createDir(std::string path)
2130 if(fs::CreateDir(path) == false)
2132 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2133 <<"\""<<path<<"\""<<std::endl;
2134 throw BaseException("ServerMap failed to create directory");
2138 std::string ServerMap::getSectorSubDir(v2s16 pos)
2141 snprintf(cc, 9, "%.4x%.4x",
2142 (unsigned int)pos.X&0xffff,
2143 (unsigned int)pos.Y&0xffff);
2145 return std::string(cc);
2148 std::string ServerMap::getSectorDir(v2s16 pos)
2150 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2153 v2s16 ServerMap::getSectorPos(std::string dirname)
2155 if(dirname.size() != 8)
2156 throw InvalidFilenameException("Invalid sector directory name");
2158 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2160 throw InvalidFilenameException("Invalid sector directory name");
2161 v2s16 pos((s16)x, (s16)y);
2165 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2167 v2s16 p2d = getSectorPos(sectordir);
2169 if(blockfile.size() != 4){
2170 throw InvalidFilenameException("Invalid block filename");
2173 int r = sscanf(blockfile.c_str(), "%4x", &y);
2175 throw InvalidFilenameException("Invalid block filename");
2176 return v3s16(p2d.X, y, p2d.Y);
2180 #define ENABLE_SECTOR_SAVING 1
2181 #define ENABLE_SECTOR_LOADING 1
2182 #define ENABLE_BLOCK_SAVING 1
2183 #define ENABLE_BLOCK_LOADING 1
2185 void ServerMap::save(bool only_changed)
2187 DSTACK(__FUNCTION_NAME);
2188 if(m_map_saving_enabled == false)
2190 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2194 if(only_changed == false)
2195 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2198 saveMasterHeightmap();
2200 u32 sector_meta_count = 0;
2201 u32 block_count = 0;
2204 JMutexAutoLock lock(m_sector_mutex);
2206 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2207 for(; i.atEnd() == false; i++)
2209 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2210 assert(sector->getId() == MAPSECTOR_SERVER);
2212 if(ENABLE_SECTOR_SAVING)
2214 if(sector->differs_from_disk || only_changed == false)
2216 saveSectorMeta(sector);
2217 sector_meta_count++;
2220 if(ENABLE_BLOCK_SAVING)
2222 core::list<MapBlock*> blocks;
2223 sector->getBlocks(blocks);
2224 core::list<MapBlock*>::Iterator j;
2225 for(j=blocks.begin(); j!=blocks.end(); j++)
2227 MapBlock *block = *j;
2228 if(block->getChangedFlag() || only_changed == false)
2240 Only print if something happened or saved whole map
2242 if(only_changed == false || sector_meta_count != 0
2243 || block_count != 0)
2245 dstream<<DTIME<<"ServerMap: Written: "
2246 <<sector_meta_count<<" sector metadata files, "
2247 <<block_count<<" block files"
2252 void ServerMap::loadAll()
2254 DSTACK(__FUNCTION_NAME);
2255 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2257 loadMasterHeightmap();
2259 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2261 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2263 JMutexAutoLock lock(m_sector_mutex);
2266 s32 printed_counter = -100000;
2267 s32 count = list.size();
2269 std::vector<fs::DirListNode>::iterator i;
2270 for(i=list.begin(); i!=list.end(); i++)
2272 if(counter > printed_counter + 10)
2274 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2275 printed_counter = counter;
2279 MapSector *sector = NULL;
2281 // We want directories
2285 sector = loadSectorMeta(i->name);
2287 catch(InvalidFilenameException &e)
2289 // This catches unknown crap in directory
2292 if(ENABLE_BLOCK_LOADING)
2294 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2295 (m_savedir+"/sectors/"+i->name);
2296 std::vector<fs::DirListNode>::iterator i2;
2297 for(i2=list2.begin(); i2!=list2.end(); i2++)
2303 loadBlock(i->name, i2->name, sector);
2305 catch(InvalidFilenameException &e)
2307 // This catches unknown crap in directory
2312 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2315 void ServerMap::saveMasterHeightmap()
2317 DSTACK(__FUNCTION_NAME);
2318 createDir(m_savedir);
2320 std::string fullpath = m_savedir + "/master_heightmap";
2321 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2322 if(o.good() == false)
2323 throw FileNotGoodException("Cannot open master heightmap");
2325 // Format used for writing
2326 u8 version = SER_FMT_VER_HIGHEST;
2329 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2331 [0] u8 serialization version
2332 [1] X master heightmap
2334 u32 fullsize = 1 + hmdata.getSize();
2335 SharedBuffer<u8> data(fullsize);
2338 memcpy(&data[1], *hmdata, hmdata.getSize());
2340 o.write((const char*)*data, fullsize);
2343 m_heightmap->serialize(o, version);
2346 void ServerMap::loadMasterHeightmap()
2348 DSTACK(__FUNCTION_NAME);
2349 std::string fullpath = m_savedir + "/master_heightmap";
2350 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2351 if(is.good() == false)
2352 throw FileNotGoodException("Cannot open master heightmap");
2354 if(m_heightmap != NULL)
2357 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2360 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2362 DSTACK(__FUNCTION_NAME);
2363 // Format used for writing
2364 u8 version = SER_FMT_VER_HIGHEST;
2366 v2s16 pos = sector->getPos();
2367 createDir(m_savedir);
2368 createDir(m_savedir+"/sectors");
2369 std::string dir = getSectorDir(pos);
2372 std::string fullpath = dir + "/heightmap";
2373 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2374 if(o.good() == false)
2375 throw FileNotGoodException("Cannot open master heightmap");
2377 sector->serialize(o, version);
2379 sector->differs_from_disk = false;
2382 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2384 DSTACK(__FUNCTION_NAME);
2386 v2s16 p2d = getSectorPos(dirname);
2387 std::string dir = m_savedir + "/sectors/" + dirname;
2389 std::string fullpath = dir + "/heightmap";
2390 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2391 if(is.good() == false)
2392 throw FileNotGoodException("Cannot open sector heightmap");
2394 ServerMapSector *sector = ServerMapSector::deSerialize
2395 (is, this, p2d, &m_hwrapper, m_sectors);
2397 sector->differs_from_disk = false;
2402 bool ServerMap::loadSectorFull(v2s16 p2d)
2404 DSTACK(__FUNCTION_NAME);
2405 std::string sectorsubdir = getSectorSubDir(p2d);
2407 MapSector *sector = NULL;
2409 JMutexAutoLock lock(m_sector_mutex);
2412 sector = loadSectorMeta(sectorsubdir);
2414 catch(InvalidFilenameException &e)
2418 catch(FileNotGoodException &e)
2422 catch(std::exception &e)
2427 if(ENABLE_BLOCK_LOADING)
2429 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2430 (m_savedir+"/sectors/"+sectorsubdir);
2431 std::vector<fs::DirListNode>::iterator i2;
2432 for(i2=list2.begin(); i2!=list2.end(); i2++)
2438 loadBlock(sectorsubdir, i2->name, sector);
2440 catch(InvalidFilenameException &e)
2442 // This catches unknown crap in directory
2450 bool ServerMap::deFlushSector(v2s16 p2d)
2452 DSTACK(__FUNCTION_NAME);
2453 // See if it already exists in memory
2455 MapSector *sector = getSectorNoGenerate(p2d);
2458 catch(InvalidPositionException &e)
2461 Try to load the sector from disk.
2463 if(loadSectorFull(p2d) == true)
2472 void ServerMap::saveBlock(MapBlock *block)
2474 DSTACK(__FUNCTION_NAME);
2476 Dummy blocks are not written
2478 if(block->isDummy())
2480 /*v3s16 p = block->getPos();
2481 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2482 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2486 // Format used for writing
2487 u8 version = SER_FMT_VER_HIGHEST;
2489 v3s16 p3d = block->getPos();
2490 v2s16 p2d(p3d.X, p3d.Z);
2491 createDir(m_savedir);
2492 createDir(m_savedir+"/sectors");
2493 std::string dir = getSectorDir(p2d);
2496 // Block file is map/sectors/xxxxxxxx/xxxx
2498 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2499 std::string fullpath = dir + "/" + cc;
2500 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2501 if(o.good() == false)
2502 throw FileNotGoodException("Cannot open block data");
2505 [0] u8 serialization version
2508 o.write((char*)&version, 1);
2510 block->serialize(o, version);
2513 Versions up from 9 have block objects.
2517 block->serializeObjects(o, version);
2520 // We just wrote it to the disk
2521 block->resetChangedFlag();
2524 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2526 DSTACK(__FUNCTION_NAME);
2530 // Block file is map/sectors/xxxxxxxx/xxxx
2531 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2532 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2533 if(is.good() == false)
2534 throw FileNotGoodException("Cannot open block file");
2536 v3s16 p3d = getBlockPos(sectordir, blockfile);
2537 v2s16 p2d(p3d.X, p3d.Z);
2539 assert(sector->getPos() == p2d);
2541 u8 version = SER_FMT_VER_INVALID;
2542 is.read((char*)&version, 1);
2544 /*u32 block_size = MapBlock::serializedLength(version);
2545 SharedBuffer<u8> data(block_size);
2546 is.read((char*)*data, block_size);*/
2548 // This will always return a sector because we're the server
2549 //MapSector *sector = emergeSector(p2d);
2551 MapBlock *block = NULL;
2552 bool created_new = false;
2554 block = sector->getBlockNoCreate(p3d.Y);
2556 catch(InvalidPositionException &e)
2558 block = sector->createBlankBlockNoInsert(p3d.Y);
2562 // deserialize block data
2563 block->deSerialize(is, version);
2566 Versions up from 9 have block objects.
2570 block->updateObjects(is, version, NULL);
2574 sector->insertBlock(block);
2577 Convert old formats to new and save
2580 // Save old format blocks in new format
2581 if(version < SER_FMT_VER_HIGHEST)
2586 // We just loaded it from the disk, so it's up-to-date.
2587 block->resetChangedFlag();
2590 catch(SerializationError &e)
2592 dstream<<"WARNING: Invalid block data on disk "
2593 "(SerializationError). Ignoring."
2598 // Gets from master heightmap
2599 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2601 assert(m_heightmap != NULL);
2609 corners[0] = m_heightmap->getGroundHeight
2610 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2611 corners[1] = m_heightmap->getGroundHeight
2612 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2613 corners[2] = m_heightmap->getGroundHeight
2614 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2615 corners[3] = m_heightmap->getGroundHeight
2616 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2619 void ServerMap::PrintInfo(std::ostream &out)
2630 ClientMap::ClientMap(
2632 JMutex &range_mutex,
2633 s16 &viewing_range_nodes,
2634 bool &viewing_range_all,
2635 scene::ISceneNode* parent,
2636 scene::ISceneManager* mgr,
2640 scene::ISceneNode(parent, mgr, id),
2643 m_range_mutex(range_mutex),
2644 m_viewing_range_nodes(viewing_range_nodes),
2645 m_viewing_range_all(viewing_range_all)
2649 /*m_box = core::aabbox3d<f32>(0,0,0,
2650 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2651 /*m_box = core::aabbox3d<f32>(0,0,0,
2652 map->getSizeNodes().X * BS,
2653 map->getSizeNodes().Y * BS,
2654 map->getSizeNodes().Z * BS);*/
2655 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2656 BS*1000000,BS*1000000,BS*1000000);
2658 //setPosition(v3f(BS,BS,BS));
2661 ClientMap::~ClientMap()
2663 JMutexAutoLock lock(mesh_mutex);
2672 MapSector * ClientMap::emergeSector(v2s16 p2d)
2674 DSTACK(__FUNCTION_NAME);
2675 // Check that it doesn't exist already
2677 return getSectorNoGenerate(p2d);
2679 catch(InvalidPositionException &e)
2683 // Create a sector with no heightmaps
2684 ClientMapSector *sector = new ClientMapSector(this, p2d);
2687 JMutexAutoLock lock(m_sector_mutex);
2688 m_sectors.insert(p2d, sector);
2694 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2696 DSTACK(__FUNCTION_NAME);
2697 ClientMapSector *sector = NULL;
2699 JMutexAutoLock lock(m_sector_mutex);
2701 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2705 sector = (ClientMapSector*)n->getValue();
2706 assert(sector->getId() == MAPSECTOR_CLIENT);
2710 sector = new ClientMapSector(this, p2d);
2712 JMutexAutoLock lock(m_sector_mutex);
2713 m_sectors.insert(p2d, sector);
2717 sector->deSerialize(is);
2720 void ClientMap::OnRegisterSceneNode()
2724 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2725 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2728 ISceneNode::OnRegisterSceneNode();
2731 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2733 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2734 DSTACK(__FUNCTION_NAME);
2736 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2739 Get time for measuring timeout.
2741 Measuring time is very useful for long delays when the
2742 machine is swapping a lot.
2744 int time1 = time(0);
2746 //s32 daynight_i = m_client->getDayNightIndex();
2747 u32 daynight_ratio = m_client->getDayNightRatio();
2750 Collect all blocks that are in the view range
2752 Should not optimize more here as we want to auto-update
2753 all changed nodes in viewing range at the next step.
2756 s16 viewing_range_nodes;
2757 bool viewing_range_all;
2759 JMutexAutoLock lock(m_range_mutex);
2760 viewing_range_nodes = m_viewing_range_nodes;
2761 viewing_range_all = m_viewing_range_all;
2764 m_camera_mutex.Lock();
2765 v3f camera_position = m_camera_position;
2766 v3f camera_direction = m_camera_direction;
2767 m_camera_mutex.Unlock();
2770 Get all blocks and draw all visible ones
2773 v3s16 cam_pos_nodes(
2774 camera_position.X / BS,
2775 camera_position.Y / BS,
2776 camera_position.Z / BS);
2778 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2780 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2781 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2783 // Take a fair amount as we will be dropping more out later
2785 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2786 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2787 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2789 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2790 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2791 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2793 u32 vertex_count = 0;
2795 // For limiting number of mesh updates per frame
2796 u32 mesh_update_count = 0;
2798 //NOTE: The sectors map should be locked but we're not doing it
2799 // because it'd cause too much delays
2801 int timecheck_counter = 0;
2803 core::map<v2s16, MapSector*>::Iterator si;
2804 si = m_sectors.getIterator();
2805 for(; si.atEnd() == false; si++)
2808 timecheck_counter++;
2809 if(timecheck_counter > 50)
2811 int time2 = time(0);
2812 if(time2 > time1 + 4)
2814 dstream<<"ClientMap::renderMap(): "
2815 "Rendering takes ages, returning."
2822 MapSector *sector = si.getNode()->getValue();
2823 v2s16 sp = sector->getPos();
2825 if(viewing_range_all == false)
2827 if(sp.X < p_blocks_min.X
2828 || sp.X > p_blocks_max.X
2829 || sp.Y < p_blocks_min.Z
2830 || sp.Y > p_blocks_max.Z)
2834 core::list< MapBlock * > sectorblocks;
2835 sector->getBlocks(sectorblocks);
2841 core::list< MapBlock * >::Iterator i;
2842 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2844 MapBlock *block = *i;
2847 Compare block position to camera position, skip
2848 if not seen on display
2851 v3s16 blockpos_nodes = block->getPosRelative();
2853 // Block center position
2855 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2856 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2857 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2860 // Block position relative to camera
2861 v3f blockpos_relative = blockpos - camera_position;
2863 // Distance in camera direction (+=front, -=back)
2864 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2867 f32 d = blockpos_relative.getLength();
2869 if(viewing_range_all == false)
2871 // If block is far away, don't draw it
2872 if(d > viewing_range_nodes * BS)
2873 // This is nicer when fog is used
2874 //if((dforward+d)/2 > viewing_range_nodes * BS)
2878 // Maximum radius of a block
2879 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2881 // If block is (nearly) touching the camera, don't
2882 // bother validating further (that is, render it anyway)
2883 if(d > block_max_radius * 1.5)
2885 // Cosine of the angle between the camera direction
2886 // and the block direction (camera_direction is an unit vector)
2887 f32 cosangle = dforward / d;
2889 // Compensate for the size of the block
2890 // (as the block has to be shown even if it's a bit off FOV)
2891 // This is an estimate.
2892 cosangle += block_max_radius / dforward;
2894 // If block is not in the field of view, skip it
2895 //if(cosangle < cos(FOV_ANGLE/2))
2896 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2901 Draw the faces of the block
2904 bool mesh_expired = false;
2907 JMutexAutoLock lock(block->mesh_mutex);
2909 mesh_expired = block->getMeshExpired();
2911 // Mesh has not been expired and there is no mesh:
2912 // block has no content
2913 if(block->mesh == NULL && mesh_expired == false)
2917 f32 faraway = BS*50;
2918 //f32 faraway = viewing_range_nodes * BS;
2921 This has to be done with the mesh_mutex unlocked
2923 if(mesh_expired && mesh_update_count < 6
2924 && (d < faraway || mesh_update_count < 3))
2925 //if(mesh_expired && mesh_update_count < 4)
2927 mesh_update_count++;
2929 // Mesh has been expired: generate new mesh
2930 //block->updateMeshes(daynight_i);
2931 block->updateMesh(daynight_ratio);
2933 mesh_expired = false;
2937 Don't draw an expired mesh that is far away
2939 /*if(mesh_expired && d >= faraway)
2942 // Instead, delete it
2943 JMutexAutoLock lock(block->mesh_mutex);
2946 block->mesh->drop();
2949 // And continue to next block
2954 JMutexAutoLock lock(block->mesh_mutex);
2956 scene::SMesh *mesh = block->mesh;
2961 u32 c = mesh->getMeshBufferCount();
2963 for(u32 i=0; i<c; i++)
2965 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2966 const video::SMaterial& material = buf->getMaterial();
2967 video::IMaterialRenderer* rnd =
2968 driver->getMaterialRenderer(material.MaterialType);
2969 bool transparent = (rnd && rnd->isTransparent());
2970 // Render transparent on transparent pass and likewise.
2971 if(transparent == is_transparent_pass)
2973 driver->setMaterial(buf->getMaterial());
2974 driver->drawMeshBuffer(buf);
2975 vertex_count += buf->getVertexCount();
2979 } // foreach sectorblocks
2982 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2983 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2986 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
2988 v3s16 blockpos = getNodeBlockPos(p);
2989 MapBlock * blockref = getBlockNoCreate(blockpos);
2990 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
2992 blockref->setTempMod(relpos, mod);
2995 v3s16 ClientMap::clearTempMod(v3s16 p)
2997 v3s16 blockpos = getNodeBlockPos(p);
2998 MapBlock * blockref = getBlockNoCreate(blockpos);
2999 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3001 blockref->clearTempMod(relpos);
3005 void ClientMap::PrintInfo(std::ostream &out)
3016 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3021 MapVoxelManipulator::~MapVoxelManipulator()
3023 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3028 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3030 TimeTaker timer1("emerge", g_irrlicht, &emerge_time);
3032 // Units of these are MapBlocks
3033 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3034 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3036 VoxelArea block_area_nodes
3037 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3039 addArea(block_area_nodes);
3041 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3042 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3043 for(s32 x=p_min.X; x<=p_max.X; x++)
3046 core::map<v3s16, bool>::Node *n;
3047 n = m_loaded_blocks.find(p);
3051 bool block_data_inexistent = false;
3054 TimeTaker timer1("emerge load", g_irrlicht, &emerge_load_time);
3056 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3057 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3060 dstream<<std::endl;*/
3062 MapBlock *block = m_map->getBlockNoCreate(p);
3063 if(block->isDummy())
3064 block_data_inexistent = true;
3066 block->copyTo(*this);
3068 catch(InvalidPositionException &e)
3070 block_data_inexistent = true;
3073 if(block_data_inexistent)
3075 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3076 // Fill with VOXELFLAG_INEXISTENT
3077 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3078 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3080 s32 i = m_area.index(a.MinEdge.X,y,z);
3081 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3085 m_loaded_blocks.insert(p, true);
3088 //dstream<<"emerge done"<<std::endl;
3093 void MapVoxelManipulator::emerge(VoxelArea a)
3095 TimeTaker timer1("emerge", g_irrlicht, &emerge_time);
3097 v3s16 size = a.getExtent();
3099 VoxelArea padded = a;
3100 padded.pad(m_area.getExtent() / 4);
3103 for(s16 z=0; z<size.Z; z++)
3104 for(s16 y=0; y<size.Y; y++)
3105 for(s16 x=0; x<size.X; x++)
3108 s32 i = m_area.index(a.MinEdge + p);
3109 // Don't touch nodes that have already been loaded
3110 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3114 TimeTaker timer1("emerge load", g_irrlicht, &emerge_load_time);
3115 MapNode n = m_map->getNode(a.MinEdge + p);
3119 catch(InvalidPositionException &e)
3121 m_flags[i] = VOXELFLAG_INEXISTENT;
3129 TODO: Add an option to only update eg. water and air nodes.
3130 This will make it interfere less with important stuff if
3133 void MapVoxelManipulator::blitBack
3134 (core::map<v3s16, MapBlock*> & modified_blocks)
3136 if(m_area.getExtent() == v3s16(0,0,0))
3139 //TimeTaker timer1("blitBack", g_irrlicht);
3142 Initialize block cache
3144 v3s16 blockpos_last;
3145 MapBlock *block = NULL;
3146 bool block_checked_in_modified = false;
3148 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3149 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3150 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3154 u8 f = m_flags[m_area.index(p)];
3155 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3158 MapNode &n = m_data[m_area.index(p)];
3160 v3s16 blockpos = getNodeBlockPos(p);
3165 if(block == NULL || blockpos != blockpos_last){
3166 block = m_map->getBlockNoCreate(blockpos);
3167 blockpos_last = blockpos;
3168 block_checked_in_modified = false;
3171 // Calculate relative position in block
3172 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3174 // Don't continue if nothing has changed here
3175 if(block->getNode(relpos) == n)
3178 //m_map->setNode(m_area.MinEdge + p, n);
3179 block->setNode(relpos, n);
3182 Make sure block is in modified_blocks
3184 if(block_checked_in_modified == false)
3186 modified_blocks[blockpos] = block;
3187 block_checked_in_modified = true;
3190 catch(InvalidPositionException &e)