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_device);
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_device);
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_device);
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 node is very low
1809 /*if(real_y <= surface_y - 7)
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 under surface level
1825 else if(real_y <= surface_y - surface_depth)
1826 n.d = CONTENT_STONE;
1828 if(real_y <= surface_y - surface_depth)
1831 if(underground_emptiness[
1832 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1833 +ued*(y0*ued/MAP_BLOCKSIZE)
1834 +(x0*ued/MAP_BLOCKSIZE)])
1840 n.d = CONTENT_STONE;
1843 // If node is at or under heightmap y
1844 else if(real_y <= surface_y)
1846 // If under water level, it's mud
1847 if(real_y < WATER_LEVEL)
1849 // Only the topmost node is grass
1850 else if(real_y <= surface_y - 1)
1852 // Else it's the main material
1856 // If node is over heightmap y
1858 // If under water level, it's water
1859 if(real_y < WATER_LEVEL)
1861 n.d = water_material;
1862 n.setLight(LIGHTBANK_DAY,
1863 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1869 block->setNode(v3s16(x0,y0,z0), n);
1874 Calculate is_underground
1876 // Probably underground if the highest part of block is under lowest
1878 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1879 block->setIsUnderground(is_underground);
1882 Force lighting update if some part of block is underground
1883 This is needed because of caves.
1886 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1887 if(some_part_underground)
1888 //if(is_underground)
1890 lighting_invalidated_blocks[block->getPos()] = block;
1897 //if(is_underground)
1898 if(some_part_underground)
1900 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1901 for(s16 i=0; i<underground_level*3; i++)
1906 (rand()%(MAP_BLOCKSIZE-2))+1,
1907 (rand()%(MAP_BLOCKSIZE-2))+1,
1908 (rand()%(MAP_BLOCKSIZE-2))+1
1914 //if(is_ground_content(block->getNode(cp).d))
1915 if(block->getNode(cp).d == CONTENT_STONE)
1917 block->setNode(cp, n);
1919 for(u16 i=0; i<26; i++)
1921 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1922 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1924 block->setNode(cp+g_26dirs[i], n);
1931 Create a few rats in empty blocks underground
1935 //for(u16 i=0; i<2; i++)
1938 (rand()%(MAP_BLOCKSIZE-2))+1,
1939 (rand()%(MAP_BLOCKSIZE-2))+1,
1940 (rand()%(MAP_BLOCKSIZE-2))+1
1943 // Check that the place is empty
1944 //if(!is_ground_content(block->getNode(cp).d))
1947 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1948 block->addObject(obj);
1954 Add block to sector.
1956 sector->insertBlock(block);
1959 Do some interpolation for dungeons
1964 TimeTaker timer("interpolation", g_device);
1966 MapVoxelManipulator vmanip(this);
1968 v3s16 relpos = block->getPosRelative();
1970 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1971 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1972 /*vmanip.interpolate(VoxelArea(relpos,
1973 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1975 core::map<v3s16, MapBlock*> modified_blocks;
1976 vmanip.blitBack(modified_blocks);
1977 dstream<<"blitBack modified "<<modified_blocks.size()
1978 <<" blocks"<<std::endl;
1980 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1981 for(core::map<v3s16, MapBlock*>::Iterator
1982 i = modified_blocks.getIterator();
1983 i.atEnd() == false; i++)
1985 MapBlock *block = i.getNode()->getValue();
1987 changed_blocks.insert(block->getPos(), block);
1988 //lighting_invalidated_blocks.insert(block->getPos(), block);
1998 // An y-wise container of changed blocks
1999 core::map<s16, MapBlock*> changed_blocks_sector;
2002 Check if any sector's objects can be placed now.
2005 core::map<v3s16, u8> *objects = sector->getObjects();
2006 core::list<v3s16> objects_to_remove;
2007 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2008 i.atEnd() == false; i++)
2010 v3s16 p = i.getNode()->getKey();
2012 u8 d = i.getNode()->getValue();
2014 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2019 if(d == SECTOR_OBJECT_TEST)
2021 if(sector->isValidArea(p + v3s16(0,0,0),
2022 p + v3s16(0,0,0), &changed_blocks_sector))
2025 n.d = CONTENT_TORCH;
2026 sector->setNode(p, n);
2027 objects_to_remove.push_back(p);
2030 else if(d == SECTOR_OBJECT_TREE_1)
2032 v3s16 p_min = p + v3s16(-1,0,-1);
2033 v3s16 p_max = p + v3s16(1,4,1);
2034 if(sector->isValidArea(p_min, p_max,
2035 &changed_blocks_sector))
2039 sector->setNode(p+v3s16(0,0,0), n);
2040 sector->setNode(p+v3s16(0,1,0), n);
2041 sector->setNode(p+v3s16(0,2,0), n);
2042 sector->setNode(p+v3s16(0,3,0), n);
2044 n.d = CONTENT_LEAVES;
2046 sector->setNode(p+v3s16(0,4,0), n);
2048 sector->setNode(p+v3s16(-1,4,0), n);
2049 sector->setNode(p+v3s16(1,4,0), n);
2050 sector->setNode(p+v3s16(0,4,-1), n);
2051 sector->setNode(p+v3s16(0,4,1), n);
2052 sector->setNode(p+v3s16(1,4,1), n);
2053 sector->setNode(p+v3s16(-1,4,1), n);
2054 sector->setNode(p+v3s16(-1,4,-1), n);
2055 sector->setNode(p+v3s16(1,4,-1), n);
2057 sector->setNode(p+v3s16(-1,3,0), n);
2058 sector->setNode(p+v3s16(1,3,0), n);
2059 sector->setNode(p+v3s16(0,3,-1), n);
2060 sector->setNode(p+v3s16(0,3,1), n);
2061 sector->setNode(p+v3s16(1,3,1), n);
2062 sector->setNode(p+v3s16(-1,3,1), n);
2063 sector->setNode(p+v3s16(-1,3,-1), n);
2064 sector->setNode(p+v3s16(1,3,-1), n);
2066 objects_to_remove.push_back(p);
2068 // Lighting has to be recalculated for this one.
2069 sector->getBlocksInArea(p_min, p_max,
2070 lighting_invalidated_blocks);
2073 else if(d == SECTOR_OBJECT_BUSH_1)
2075 if(sector->isValidArea(p + v3s16(0,0,0),
2076 p + v3s16(0,0,0), &changed_blocks_sector))
2079 n.d = CONTENT_LEAVES;
2080 sector->setNode(p+v3s16(0,0,0), n);
2082 objects_to_remove.push_back(p);
2085 else if(d == SECTOR_OBJECT_RAVINE)
2088 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2089 v3s16 p_max = p + v3s16(6,6,6);
2090 if(sector->isValidArea(p_min, p_max,
2091 &changed_blocks_sector))
2094 n.d = CONTENT_STONE;
2097 s16 depth = maxdepth + (rand()%10);
2099 s16 minz = -6 - (-2);
2101 for(s16 x=-6; x<=6; x++)
2103 z += -1 + (rand()%3);
2108 for(s16 y=depth+(rand()%2); y<=6; y++)
2110 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2113 v3s16 p2 = p + v3s16(x,y,z-2);
2114 if(is_ground_content(sector->getNode(p2).d))
2115 sector->setNode(p2, n);
2118 v3s16 p2 = p + v3s16(x,y,z-1);
2119 if(is_ground_content(sector->getNode(p2).d))
2120 sector->setNode(p2, n2);
2123 v3s16 p2 = p + v3s16(x,y,z+0);
2124 if(is_ground_content(sector->getNode(p2).d))
2125 sector->setNode(p2, n2);
2128 v3s16 p2 = p + v3s16(x,y,z+1);
2129 if(is_ground_content(sector->getNode(p2).d))
2130 sector->setNode(p2, n);
2133 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2134 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2138 objects_to_remove.push_back(p);
2140 // Lighting has to be recalculated for this one.
2141 sector->getBlocksInArea(p_min, p_max,
2142 lighting_invalidated_blocks);
2147 dstream<<"ServerMap::emergeBlock(): "
2148 "Invalid heightmap object"
2153 catch(InvalidPositionException &e)
2155 dstream<<"WARNING: "<<__FUNCTION_NAME
2156 <<": while inserting object "<<(int)d
2157 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2158 <<" InvalidPositionException.what()="
2159 <<e.what()<<std::endl;
2160 // This is not too fatal and seems to happen sometimes.
2165 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2166 i != objects_to_remove.end(); i++)
2168 objects->remove(*i);
2171 for(core::map<s16, MapBlock*>::Iterator
2172 i = changed_blocks_sector.getIterator();
2173 i.atEnd() == false; i++)
2175 MapBlock *block = i.getNode()->getValue();
2177 changed_blocks.insert(block->getPos(), block);
2183 void ServerMap::createDir(std::string path)
2185 if(fs::CreateDir(path) == false)
2187 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2188 <<"\""<<path<<"\""<<std::endl;
2189 throw BaseException("ServerMap failed to create directory");
2193 std::string ServerMap::getSectorSubDir(v2s16 pos)
2196 snprintf(cc, 9, "%.4x%.4x",
2197 (unsigned int)pos.X&0xffff,
2198 (unsigned int)pos.Y&0xffff);
2200 return std::string(cc);
2203 std::string ServerMap::getSectorDir(v2s16 pos)
2205 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2208 v2s16 ServerMap::getSectorPos(std::string dirname)
2210 if(dirname.size() != 8)
2211 throw InvalidFilenameException("Invalid sector directory name");
2213 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2215 throw InvalidFilenameException("Invalid sector directory name");
2216 v2s16 pos((s16)x, (s16)y);
2220 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2222 v2s16 p2d = getSectorPos(sectordir);
2224 if(blockfile.size() != 4){
2225 throw InvalidFilenameException("Invalid block filename");
2228 int r = sscanf(blockfile.c_str(), "%4x", &y);
2230 throw InvalidFilenameException("Invalid block filename");
2231 return v3s16(p2d.X, y, p2d.Y);
2235 #define ENABLE_SECTOR_SAVING 1
2236 #define ENABLE_SECTOR_LOADING 1
2237 #define ENABLE_BLOCK_SAVING 1
2238 #define ENABLE_BLOCK_LOADING 1
2240 void ServerMap::save(bool only_changed)
2242 DSTACK(__FUNCTION_NAME);
2243 if(m_map_saving_enabled == false)
2245 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2249 if(only_changed == false)
2250 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2253 saveMasterHeightmap();
2255 u32 sector_meta_count = 0;
2256 u32 block_count = 0;
2259 JMutexAutoLock lock(m_sector_mutex);
2261 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2262 for(; i.atEnd() == false; i++)
2264 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2265 assert(sector->getId() == MAPSECTOR_SERVER);
2267 if(ENABLE_SECTOR_SAVING)
2269 if(sector->differs_from_disk || only_changed == false)
2271 saveSectorMeta(sector);
2272 sector_meta_count++;
2275 if(ENABLE_BLOCK_SAVING)
2277 core::list<MapBlock*> blocks;
2278 sector->getBlocks(blocks);
2279 core::list<MapBlock*>::Iterator j;
2280 for(j=blocks.begin(); j!=blocks.end(); j++)
2282 MapBlock *block = *j;
2283 if(block->getChangedFlag() || only_changed == false)
2294 u32 deleted_count = 0;
2295 deleted_count = deleteUnusedSectors(
2296 SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2299 Only print if something happened or saved whole map
2301 if(only_changed == false || sector_meta_count != 0
2302 || block_count != 0 || deleted_count != 0)
2304 dstream<<DTIME<<"ServerMap: Written: "
2305 <<sector_meta_count<<" sector metadata files, "
2306 <<block_count<<" block files, "
2307 <<deleted_count<<" sectors unloaded from memory."
2312 void ServerMap::loadAll()
2314 DSTACK(__FUNCTION_NAME);
2315 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2317 loadMasterHeightmap();
2319 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2321 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2323 JMutexAutoLock lock(m_sector_mutex);
2326 s32 printed_counter = -100000;
2327 s32 count = list.size();
2329 std::vector<fs::DirListNode>::iterator i;
2330 for(i=list.begin(); i!=list.end(); i++)
2332 if(counter > printed_counter + 10)
2334 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2335 printed_counter = counter;
2339 MapSector *sector = NULL;
2341 // We want directories
2345 sector = loadSectorMeta(i->name);
2347 catch(InvalidFilenameException &e)
2349 // This catches unknown crap in directory
2352 if(ENABLE_BLOCK_LOADING)
2354 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2355 (m_savedir+"/sectors/"+i->name);
2356 std::vector<fs::DirListNode>::iterator i2;
2357 for(i2=list2.begin(); i2!=list2.end(); i2++)
2363 loadBlock(i->name, i2->name, sector);
2365 catch(InvalidFilenameException &e)
2367 // This catches unknown crap in directory
2372 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2375 void ServerMap::saveMasterHeightmap()
2377 DSTACK(__FUNCTION_NAME);
2378 createDir(m_savedir);
2380 std::string fullpath = m_savedir + "/master_heightmap";
2381 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2382 if(o.good() == false)
2383 throw FileNotGoodException("Cannot open master heightmap");
2385 // Format used for writing
2386 u8 version = SER_FMT_VER_HIGHEST;
2389 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2391 [0] u8 serialization version
2392 [1] X master heightmap
2394 u32 fullsize = 1 + hmdata.getSize();
2395 SharedBuffer<u8> data(fullsize);
2398 memcpy(&data[1], *hmdata, hmdata.getSize());
2400 o.write((const char*)*data, fullsize);
2403 m_heightmap->serialize(o, version);
2406 void ServerMap::loadMasterHeightmap()
2408 DSTACK(__FUNCTION_NAME);
2409 std::string fullpath = m_savedir + "/master_heightmap";
2410 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2411 if(is.good() == false)
2412 throw FileNotGoodException("Cannot open master heightmap");
2414 if(m_heightmap != NULL)
2417 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2420 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2422 DSTACK(__FUNCTION_NAME);
2423 // Format used for writing
2424 u8 version = SER_FMT_VER_HIGHEST;
2426 v2s16 pos = sector->getPos();
2427 createDir(m_savedir);
2428 createDir(m_savedir+"/sectors");
2429 std::string dir = getSectorDir(pos);
2432 std::string fullpath = dir + "/heightmap";
2433 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2434 if(o.good() == false)
2435 throw FileNotGoodException("Cannot open master heightmap");
2437 sector->serialize(o, version);
2439 sector->differs_from_disk = false;
2442 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2444 DSTACK(__FUNCTION_NAME);
2446 v2s16 p2d = getSectorPos(dirname);
2447 std::string dir = m_savedir + "/sectors/" + dirname;
2449 std::string fullpath = dir + "/heightmap";
2450 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2451 if(is.good() == false)
2452 throw FileNotGoodException("Cannot open sector heightmap");
2454 ServerMapSector *sector = ServerMapSector::deSerialize
2455 (is, this, p2d, &m_hwrapper, m_sectors);
2457 sector->differs_from_disk = false;
2462 bool ServerMap::loadSectorFull(v2s16 p2d)
2464 DSTACK(__FUNCTION_NAME);
2465 std::string sectorsubdir = getSectorSubDir(p2d);
2467 MapSector *sector = NULL;
2469 JMutexAutoLock lock(m_sector_mutex);
2472 sector = loadSectorMeta(sectorsubdir);
2474 catch(InvalidFilenameException &e)
2478 catch(FileNotGoodException &e)
2482 catch(std::exception &e)
2487 if(ENABLE_BLOCK_LOADING)
2489 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2490 (m_savedir+"/sectors/"+sectorsubdir);
2491 std::vector<fs::DirListNode>::iterator i2;
2492 for(i2=list2.begin(); i2!=list2.end(); i2++)
2498 loadBlock(sectorsubdir, i2->name, sector);
2500 catch(InvalidFilenameException &e)
2502 // This catches unknown crap in directory
2510 bool ServerMap::deFlushSector(v2s16 p2d)
2512 DSTACK(__FUNCTION_NAME);
2513 // See if it already exists in memory
2515 MapSector *sector = getSectorNoGenerate(p2d);
2518 catch(InvalidPositionException &e)
2521 Try to load the sector from disk.
2523 if(loadSectorFull(p2d) == true)
2532 void ServerMap::saveBlock(MapBlock *block)
2534 DSTACK(__FUNCTION_NAME);
2536 Dummy blocks are not written
2538 if(block->isDummy())
2540 /*v3s16 p = block->getPos();
2541 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2542 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2546 // Format used for writing
2547 u8 version = SER_FMT_VER_HIGHEST;
2549 v3s16 p3d = block->getPos();
2550 v2s16 p2d(p3d.X, p3d.Z);
2551 createDir(m_savedir);
2552 createDir(m_savedir+"/sectors");
2553 std::string dir = getSectorDir(p2d);
2556 // Block file is map/sectors/xxxxxxxx/xxxx
2558 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2559 std::string fullpath = dir + "/" + cc;
2560 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2561 if(o.good() == false)
2562 throw FileNotGoodException("Cannot open block data");
2565 [0] u8 serialization version
2568 o.write((char*)&version, 1);
2570 block->serialize(o, version);
2573 Versions up from 9 have block objects.
2577 block->serializeObjects(o, version);
2580 // We just wrote it to the disk
2581 block->resetChangedFlag();
2584 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2586 DSTACK(__FUNCTION_NAME);
2590 // Block file is map/sectors/xxxxxxxx/xxxx
2591 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2592 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2593 if(is.good() == false)
2594 throw FileNotGoodException("Cannot open block file");
2596 v3s16 p3d = getBlockPos(sectordir, blockfile);
2597 v2s16 p2d(p3d.X, p3d.Z);
2599 assert(sector->getPos() == p2d);
2601 u8 version = SER_FMT_VER_INVALID;
2602 is.read((char*)&version, 1);
2604 /*u32 block_size = MapBlock::serializedLength(version);
2605 SharedBuffer<u8> data(block_size);
2606 is.read((char*)*data, block_size);*/
2608 // This will always return a sector because we're the server
2609 //MapSector *sector = emergeSector(p2d);
2611 MapBlock *block = NULL;
2612 bool created_new = false;
2614 block = sector->getBlockNoCreate(p3d.Y);
2616 catch(InvalidPositionException &e)
2618 block = sector->createBlankBlockNoInsert(p3d.Y);
2622 // deserialize block data
2623 block->deSerialize(is, version);
2626 Versions up from 9 have block objects.
2630 block->updateObjects(is, version, NULL);
2634 sector->insertBlock(block);
2637 Convert old formats to new and save
2640 // Save old format blocks in new format
2641 if(version < SER_FMT_VER_HIGHEST)
2646 // We just loaded it from the disk, so it's up-to-date.
2647 block->resetChangedFlag();
2650 catch(SerializationError &e)
2652 dstream<<"WARNING: Invalid block data on disk "
2653 "(SerializationError). Ignoring."
2658 // Gets from master heightmap
2659 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2661 assert(m_heightmap != NULL);
2669 corners[0] = m_heightmap->getGroundHeight
2670 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2671 corners[1] = m_heightmap->getGroundHeight
2672 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2673 corners[2] = m_heightmap->getGroundHeight
2674 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2675 corners[3] = m_heightmap->getGroundHeight
2676 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2679 void ServerMap::PrintInfo(std::ostream &out)
2690 ClientMap::ClientMap(
2692 JMutex &range_mutex,
2693 s16 &viewing_range_nodes,
2694 bool &viewing_range_all,
2695 scene::ISceneNode* parent,
2696 scene::ISceneManager* mgr,
2700 scene::ISceneNode(parent, mgr, id),
2703 m_range_mutex(range_mutex),
2704 m_viewing_range_nodes(viewing_range_nodes),
2705 m_viewing_range_all(viewing_range_all)
2709 /*m_box = core::aabbox3d<f32>(0,0,0,
2710 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2711 /*m_box = core::aabbox3d<f32>(0,0,0,
2712 map->getSizeNodes().X * BS,
2713 map->getSizeNodes().Y * BS,
2714 map->getSizeNodes().Z * BS);*/
2715 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2716 BS*1000000,BS*1000000,BS*1000000);
2718 //setPosition(v3f(BS,BS,BS));
2721 ClientMap::~ClientMap()
2723 JMutexAutoLock lock(mesh_mutex);
2732 MapSector * ClientMap::emergeSector(v2s16 p2d)
2734 DSTACK(__FUNCTION_NAME);
2735 // Check that it doesn't exist already
2737 return getSectorNoGenerate(p2d);
2739 catch(InvalidPositionException &e)
2743 // Create a sector with no heightmaps
2744 ClientMapSector *sector = new ClientMapSector(this, p2d);
2747 JMutexAutoLock lock(m_sector_mutex);
2748 m_sectors.insert(p2d, sector);
2754 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2756 DSTACK(__FUNCTION_NAME);
2757 ClientMapSector *sector = NULL;
2759 JMutexAutoLock lock(m_sector_mutex);
2761 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2765 sector = (ClientMapSector*)n->getValue();
2766 assert(sector->getId() == MAPSECTOR_CLIENT);
2770 sector = new ClientMapSector(this, p2d);
2772 JMutexAutoLock lock(m_sector_mutex);
2773 m_sectors.insert(p2d, sector);
2777 sector->deSerialize(is);
2780 void ClientMap::OnRegisterSceneNode()
2784 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2785 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2788 ISceneNode::OnRegisterSceneNode();
2791 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2793 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2794 DSTACK(__FUNCTION_NAME);
2796 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2799 Get time for measuring timeout.
2801 Measuring time is very useful for long delays when the
2802 machine is swapping a lot.
2804 int time1 = time(0);
2806 //s32 daynight_i = m_client->getDayNightIndex();
2807 u32 daynight_ratio = m_client->getDayNightRatio();
2810 Collect all blocks that are in the view range
2812 Should not optimize more here as we want to auto-update
2813 all changed nodes in viewing range at the next step.
2816 s16 viewing_range_nodes;
2817 bool viewing_range_all;
2819 JMutexAutoLock lock(m_range_mutex);
2820 viewing_range_nodes = m_viewing_range_nodes;
2821 viewing_range_all = m_viewing_range_all;
2824 m_camera_mutex.Lock();
2825 v3f camera_position = m_camera_position;
2826 v3f camera_direction = m_camera_direction;
2827 m_camera_mutex.Unlock();
2830 Get all blocks and draw all visible ones
2833 v3s16 cam_pos_nodes(
2834 camera_position.X / BS,
2835 camera_position.Y / BS,
2836 camera_position.Z / BS);
2838 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2840 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2841 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2843 // Take a fair amount as we will be dropping more out later
2845 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2846 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2847 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2849 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2850 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2851 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2853 u32 vertex_count = 0;
2855 // For limiting number of mesh updates per frame
2856 u32 mesh_update_count = 0;
2858 //NOTE: The sectors map should be locked but we're not doing it
2859 // because it'd cause too much delays
2861 int timecheck_counter = 0;
2863 core::map<v2s16, MapSector*>::Iterator si;
2864 si = m_sectors.getIterator();
2865 for(; si.atEnd() == false; si++)
2868 timecheck_counter++;
2869 if(timecheck_counter > 50)
2871 int time2 = time(0);
2872 if(time2 > time1 + 4)
2874 dstream<<"ClientMap::renderMap(): "
2875 "Rendering takes ages, returning."
2882 MapSector *sector = si.getNode()->getValue();
2883 v2s16 sp = sector->getPos();
2885 if(viewing_range_all == false)
2887 if(sp.X < p_blocks_min.X
2888 || sp.X > p_blocks_max.X
2889 || sp.Y < p_blocks_min.Z
2890 || sp.Y > p_blocks_max.Z)
2894 core::list< MapBlock * > sectorblocks;
2895 sector->getBlocks(sectorblocks);
2901 core::list< MapBlock * >::Iterator i;
2902 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2904 MapBlock *block = *i;
2907 Compare block position to camera position, skip
2908 if not seen on display
2911 v3s16 blockpos_nodes = block->getPosRelative();
2913 // Block center position
2915 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2916 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2917 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2920 // Block position relative to camera
2921 v3f blockpos_relative = blockpos - camera_position;
2923 // Distance in camera direction (+=front, -=back)
2924 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2927 f32 d = blockpos_relative.getLength();
2929 if(viewing_range_all == false)
2931 // If block is far away, don't draw it
2932 if(d > viewing_range_nodes * BS)
2933 // This is nicer when fog is used
2934 //if((dforward+d)/2 > viewing_range_nodes * BS)
2938 // Maximum radius of a block
2939 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2941 // If block is (nearly) touching the camera, don't
2942 // bother validating further (that is, render it anyway)
2943 if(d > block_max_radius * 1.5)
2945 // Cosine of the angle between the camera direction
2946 // and the block direction (camera_direction is an unit vector)
2947 f32 cosangle = dforward / d;
2949 // Compensate for the size of the block
2950 // (as the block has to be shown even if it's a bit off FOV)
2951 // This is an estimate.
2952 cosangle += block_max_radius / dforward;
2954 // If block is not in the field of view, skip it
2955 //if(cosangle < cos(FOV_ANGLE/2))
2956 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2961 Draw the faces of the block
2964 bool mesh_expired = false;
2967 JMutexAutoLock lock(block->mesh_mutex);
2969 mesh_expired = block->getMeshExpired();
2971 // Mesh has not been expired and there is no mesh:
2972 // block has no content
2973 if(block->mesh == NULL && mesh_expired == false)
2977 f32 faraway = BS*50;
2978 //f32 faraway = viewing_range_nodes * BS;
2981 This has to be done with the mesh_mutex unlocked
2983 if(mesh_expired && mesh_update_count < 6
2984 && (d < faraway || mesh_update_count < 3))
2985 //if(mesh_expired && mesh_update_count < 4)
2987 mesh_update_count++;
2989 // Mesh has been expired: generate new mesh
2990 //block->updateMeshes(daynight_i);
2991 block->updateMesh(daynight_ratio);
2993 mesh_expired = false;
2997 Don't draw an expired mesh that is far away
2999 /*if(mesh_expired && d >= faraway)
3002 // Instead, delete it
3003 JMutexAutoLock lock(block->mesh_mutex);
3006 block->mesh->drop();
3009 // And continue to next block
3014 JMutexAutoLock lock(block->mesh_mutex);
3016 scene::SMesh *mesh = block->mesh;
3021 u32 c = mesh->getMeshBufferCount();
3023 for(u32 i=0; i<c; i++)
3025 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3026 const video::SMaterial& material = buf->getMaterial();
3027 video::IMaterialRenderer* rnd =
3028 driver->getMaterialRenderer(material.MaterialType);
3029 bool transparent = (rnd && rnd->isTransparent());
3030 // Render transparent on transparent pass and likewise.
3031 if(transparent == is_transparent_pass)
3033 driver->setMaterial(buf->getMaterial());
3034 driver->drawMeshBuffer(buf);
3035 vertex_count += buf->getVertexCount();
3039 } // foreach sectorblocks
3042 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3043 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3046 void ClientMap::updateMesh()
3051 void ClientMap::PrintInfo(std::ostream &out)
3062 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3067 MapVoxelManipulator::~MapVoxelManipulator()
3069 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3074 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3076 TimeTaker timer1("emerge", g_device, &emerge_time);
3078 // Units of these are MapBlocks
3079 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3080 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3082 VoxelArea block_area_nodes
3083 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3085 addArea(block_area_nodes);
3087 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3088 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3089 for(s32 x=p_min.X; x<=p_max.X; x++)
3092 core::map<v3s16, bool>::Node *n;
3093 n = m_loaded_blocks.find(p);
3097 bool block_data_inexistent = false;
3100 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3102 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3103 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3106 dstream<<std::endl;*/
3108 MapBlock *block = m_map->getBlockNoCreate(p);
3109 if(block->isDummy())
3110 block_data_inexistent = true;
3112 block->copyTo(*this);
3114 catch(InvalidPositionException &e)
3116 block_data_inexistent = true;
3119 if(block_data_inexistent)
3121 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3122 // Fill with VOXELFLAG_INEXISTENT
3123 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3124 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3126 s32 i = m_area.index(a.MinEdge.X,y,z);
3127 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3131 m_loaded_blocks.insert(p, true);
3134 //dstream<<"emerge done"<<std::endl;
3139 void MapVoxelManipulator::emerge(VoxelArea a)
3141 TimeTaker timer1("emerge", g_device, &emerge_time);
3143 v3s16 size = a.getExtent();
3145 VoxelArea padded = a;
3146 padded.pad(m_area.getExtent() / 4);
3149 for(s16 z=0; z<size.Z; z++)
3150 for(s16 y=0; y<size.Y; y++)
3151 for(s16 x=0; x<size.X; x++)
3154 s32 i = m_area.index(a.MinEdge + p);
3155 // Don't touch nodes that have already been loaded
3156 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3160 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3161 MapNode n = m_map->getNode(a.MinEdge + p);
3165 catch(InvalidPositionException &e)
3167 m_flags[i] = VOXELFLAG_INEXISTENT;
3175 TODO: Add an option to only update eg. water and air nodes.
3176 This will make it interfere less with important stuff if
3179 void MapVoxelManipulator::blitBack
3180 (core::map<v3s16, MapBlock*> & modified_blocks)
3182 if(m_area.getExtent() == v3s16(0,0,0))
3185 //TimeTaker timer1("blitBack", g_device);
3188 Initialize block cache
3190 v3s16 blockpos_last;
3191 MapBlock *block = NULL;
3192 bool block_checked_in_modified = false;
3194 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3195 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3196 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3200 u8 f = m_flags[m_area.index(p)];
3201 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3204 MapNode &n = m_data[m_area.index(p)];
3206 v3s16 blockpos = getNodeBlockPos(p);
3211 if(block == NULL || blockpos != blockpos_last){
3212 block = m_map->getBlockNoCreate(blockpos);
3213 blockpos_last = blockpos;
3214 block_checked_in_modified = false;
3217 // Calculate relative position in block
3218 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3220 // Don't continue if nothing has changed here
3221 if(block->getNode(relpos) == n)
3224 //m_map->setNode(m_area.MinEdge + p, n);
3225 block->setNode(relpos, n);
3228 Make sure block is in modified_blocks
3230 if(block_checked_in_modified == false)
3232 modified_blocks[blockpos] = block;
3233 block_checked_in_modified = true;
3236 catch(InvalidPositionException &e)