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();
1065 void Map::expireMeshes(bool only_daynight_diffed)
1067 TimeTaker timer("expireMeshes()", g_device);
1069 core::map<v2s16, MapSector*>::Iterator si;
1070 si = m_sectors.getIterator();
1071 for(; si.atEnd() == false; si++)
1073 MapSector *sector = si.getNode()->getValue();
1075 core::list< MapBlock * > sectorblocks;
1076 sector->getBlocks(sectorblocks);
1078 core::list< MapBlock * >::Iterator i;
1079 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1081 MapBlock *block = *i;
1083 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1089 JMutexAutoLock lock(block->mesh_mutex);
1090 if(block->mesh != NULL)
1092 /*block->mesh->drop();
1093 block->mesh = NULL;*/
1094 block->setMeshExpired(true);
1101 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1103 assert(mapType() == MAPTYPE_CLIENT);
1106 v3s16 p = blockpos + v3s16(0,0,0);
1107 MapBlock *b = getBlockNoCreate(p);
1108 b->updateMesh(daynight_ratio);
1110 catch(InvalidPositionException &e){}
1112 v3s16 p = blockpos + v3s16(-1,0,0);
1113 MapBlock *b = getBlockNoCreate(p);
1114 b->updateMesh(daynight_ratio);
1116 catch(InvalidPositionException &e){}
1118 v3s16 p = blockpos + v3s16(0,-1,0);
1119 MapBlock *b = getBlockNoCreate(p);
1120 b->updateMesh(daynight_ratio);
1122 catch(InvalidPositionException &e){}
1124 v3s16 p = blockpos + v3s16(0,0,-1);
1125 MapBlock *b = getBlockNoCreate(p);
1126 b->updateMesh(daynight_ratio);
1128 catch(InvalidPositionException &e){}
1131 bool Map::dayNightDiffed(v3s16 blockpos)
1134 v3s16 p = blockpos + v3s16(0,0,0);
1135 MapBlock *b = getBlockNoCreate(p);
1136 if(b->dayNightDiffed())
1139 catch(InvalidPositionException &e){}
1141 v3s16 p = blockpos + v3s16(1,0,0);
1142 MapBlock *b = getBlockNoCreate(p);
1143 if(b->dayNightDiffed())
1146 catch(InvalidPositionException &e){}
1148 v3s16 p = blockpos + v3s16(0,1,0);
1149 MapBlock *b = getBlockNoCreate(p);
1150 if(b->dayNightDiffed())
1153 catch(InvalidPositionException &e){}
1155 v3s16 p = blockpos + v3s16(0,0,1);
1156 MapBlock *b = getBlockNoCreate(p);
1157 if(b->dayNightDiffed())
1160 catch(InvalidPositionException &e){}
1166 Updates usage timers
1168 void Map::timerUpdate(float dtime)
1170 JMutexAutoLock lock(m_sector_mutex);
1172 core::map<v2s16, MapSector*>::Iterator si;
1174 si = m_sectors.getIterator();
1175 for(; si.atEnd() == false; si++)
1177 MapSector *sector = si.getNode()->getValue();
1178 sector->usage_timer += dtime;
1182 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1185 Wait for caches to be removed before continuing.
1187 This disables the existence of caches while locked
1189 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1191 core::list<v2s16>::Iterator j;
1192 for(j=list.begin(); j!=list.end(); j++)
1194 MapSector *sector = m_sectors[*j];
1197 sector->deleteBlocks();
1202 If sector is in sector cache, remove it from there
1204 if(m_sector_cache == sector)
1206 m_sector_cache = NULL;
1209 Remove from map and delete
1211 m_sectors.remove(*j);
1217 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1218 core::list<v3s16> *deleted_blocks)
1220 JMutexAutoLock lock(m_sector_mutex);
1222 core::list<v2s16> sector_deletion_queue;
1223 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1224 for(; i.atEnd() == false; i++)
1226 MapSector *sector = i.getNode()->getValue();
1228 Delete sector from memory if it hasn't been used in a long time
1230 if(sector->usage_timer > timeout)
1232 sector_deletion_queue.push_back(i.getNode()->getKey());
1234 if(deleted_blocks != NULL)
1236 // Collect positions of blocks of sector
1237 MapSector *sector = i.getNode()->getValue();
1238 core::list<MapBlock*> blocks;
1239 sector->getBlocks(blocks);
1240 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1241 i != blocks.end(); i++)
1243 deleted_blocks->push_back((*i)->getPos());
1248 deleteSectors(sector_deletion_queue, only_blocks);
1249 return sector_deletion_queue.getSize();
1252 void Map::PrintInfo(std::ostream &out)
1261 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1265 m_savedir = savedir;
1266 m_map_saving_enabled = false;
1270 // If directory exists, check contents and load if possible
1271 if(fs::PathExists(m_savedir))
1273 // If directory is empty, it is safe to save into it.
1274 if(fs::GetDirListing(m_savedir).size() == 0)
1276 dstream<<DTIME<<"Server: Empty save directory is valid."
1278 m_map_saving_enabled = true;
1282 // Load master heightmap
1283 loadMasterHeightmap();
1285 // Load sector (0,0) and throw and exception on fail
1286 if(loadSectorFull(v2s16(0,0)) == false)
1287 throw LoadError("Failed to load sector (0,0)");
1289 dstream<<DTIME<<"Server: Successfully loaded master "
1290 "heightmap and sector (0,0) from "<<savedir<<
1291 ", assuming valid save directory."
1294 m_map_saving_enabled = true;
1295 // Map loaded, not creating new one
1299 // If directory doesn't exist, it is safe to save to it
1301 m_map_saving_enabled = true;
1304 catch(std::exception &e)
1306 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1307 <<", exception: "<<e.what()<<std::endl;
1308 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1309 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1312 dstream<<DTIME<<"Initializing new map."<<std::endl;
1314 // Create master heightmap
1315 ValueGenerator *maxgen =
1316 ValueGenerator::deSerialize(hmp.randmax);
1317 ValueGenerator *factorgen =
1318 ValueGenerator::deSerialize(hmp.randfactor);
1319 ValueGenerator *basegen =
1320 ValueGenerator::deSerialize(hmp.base);
1321 m_heightmap = new UnlimitedHeightmap
1322 (hmp.blocksize, maxgen, factorgen, basegen);
1324 // Set map parameters
1327 // Create zero sector
1328 emergeSector(v2s16(0,0));
1330 // Initially write whole map
1334 ServerMap::~ServerMap()
1338 if(m_map_saving_enabled)
1341 // Save only changed parts
1343 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1347 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1350 catch(std::exception &e)
1352 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1353 <<", exception: "<<e.what()<<std::endl;
1356 if(m_heightmap != NULL)
1360 MapSector * ServerMap::emergeSector(v2s16 p2d)
1362 DSTACK("%s: p2d=(%d,%d)",
1365 // Check that it doesn't exist already
1367 return getSectorNoGenerate(p2d);
1369 catch(InvalidPositionException &e)
1374 Try to load the sector from disk.
1376 if(loadSectorFull(p2d) == true)
1378 return getSectorNoGenerate(p2d);
1382 If there is no master heightmap, throw.
1384 if(m_heightmap == NULL)
1386 throw InvalidPositionException("emergeSector(): no heightmap");
1390 Do not generate over-limit
1392 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1393 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1394 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1395 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1396 throw InvalidPositionException("emergeSector(): pos. over limit");
1399 Generate sector and heightmaps
1402 // Number of heightmaps in sector in each direction
1403 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1405 // Heightmap side width
1406 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1408 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1410 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1411 " heightmaps and objects"<<std::endl;*/
1413 // Loop through sub-heightmaps
1414 for(s16 y=0; y<hm_split; y++)
1415 for(s16 x=0; x<hm_split; x++)
1417 v2s16 p_in_sector = v2s16(x,y);
1418 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1420 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1421 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1422 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1423 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1426 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1427 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1430 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1432 sector->setHeightmap(p_in_sector, hm);
1434 //TODO: Make these values configurable
1435 //hm->generateContinued(0.0, 0.0, corners);
1436 hm->generateContinued(0.5, 0.2, corners);
1437 //hm->generateContinued(1.0, 0.2, corners);
1438 //hm->generateContinued(2.0, 0.2, corners);
1448 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1449 sector->setObjects(objects);
1451 v2s16 mhm_p = p2d * hm_split;
1453 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1454 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1455 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1456 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1459 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1460 float avgslope = 0.0;
1461 avgslope += fabs(avgheight - corners[0]);
1462 avgslope += fabs(avgheight - corners[1]);
1463 avgslope += fabs(avgheight - corners[2]);
1464 avgslope += fabs(avgheight - corners[3]);
1466 avgslope /= MAP_BLOCKSIZE;
1467 //dstream<<"avgslope="<<avgslope<<std::endl;
1469 float pitness = 0.0;
1471 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1474 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1477 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1480 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1484 pitness /= MAP_BLOCKSIZE;
1485 //dstream<<"pitness="<<pitness<<std::endl;
1488 Plant some trees if there is not much slope
1491 // Avgslope is the derivative of a hill
1492 float t = avgslope * avgslope;
1493 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1496 tree_max = a / (t/0.03);
1499 u32 count = (rand()%(tree_max+1));
1500 //u32 count = tree_max;
1501 for(u32 i=0; i<count; i++)
1503 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1504 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1505 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1508 objects->insert(v3s16(x, y, z),
1509 SECTOR_OBJECT_TREE_1);
1513 Plant some bushes if sector is pit-like
1516 // Pitness usually goes at around -0.5...0.5
1518 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1520 bush_max = (pitness*a*4);
1523 u32 count = (rand()%(bush_max+1));
1524 for(u32 i=0; i<count; i++)
1526 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1527 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1528 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1531 objects->insert(v3s16(x, y, z),
1532 SECTOR_OBJECT_BUSH_1);
1536 Add ravine (randomly)
1538 if(m_params.ravines_amount != 0)
1540 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1543 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1544 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1547 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1548 objects->insert(v3s16(x, y, z),
1549 SECTOR_OBJECT_RAVINE);
1556 JMutexAutoLock lock(m_sector_mutex);
1557 m_sectors.insert(p2d, sector);
1562 MapBlock * ServerMap::emergeBlock(
1564 bool only_from_disk,
1565 core::map<v3s16, MapBlock*> &changed_blocks,
1566 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1569 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1571 p.X, p.Y, p.Z, only_from_disk);
1573 /*dstream<<"ServerMap::emergeBlock(): "
1574 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1575 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1576 v2s16 p2d(p.X, p.Z);
1579 This will create or load a sector if not found in memory.
1580 If block exists on disk, it will be loaded.
1582 NOTE: On old save formats, this will be slow, as it generates
1583 lighting on blocks for them.
1585 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1586 assert(sector->getId() == MAPSECTOR_SERVER);
1588 // Try to get a block from the sector
1589 MapBlock *block = NULL;
1590 bool not_on_disk = false;
1592 block = sector->getBlockNoCreate(block_y);
1593 if(block->isDummy() == true)
1598 catch(InvalidPositionException &e)
1604 If block was not found on disk and not going to generate a
1605 new one, make sure there is a dummy block in place.
1607 if(not_on_disk && only_from_disk)
1611 // Create dummy block
1612 block = new MapBlock(this, p, true);
1614 // Add block to sector
1615 sector->insertBlock(block);
1621 //dstream<<"Not found on disk, generating."<<std::endl;
1624 Do not generate over-limit
1626 if(blockpos_over_limit(p))
1627 throw InvalidPositionException("emergeBlock(): pos. over limit");
1632 Go on generating the block.
1634 TODO: If a dungeon gets generated so that it's side gets
1635 revealed to the outside air, the lighting should be
1640 If block doesn't exist, create one.
1641 If it exists, it is a dummy. In that case unDummify() it.
1645 block = sector->createBlankBlockNoInsert(block_y);
1649 // Remove the block so that nobody can get a half-generated one.
1650 sector->removeBlock(block);
1651 // Allocate the block to be a proper one.
1655 // Randomize a bit. This makes dungeons.
1656 /*bool low_block_is_empty = false;
1658 low_block_is_empty = true;*/
1661 //const s32 ued = 8;
1662 bool underground_emptiness[ued*ued*ued];
1663 for(s32 i=0; i<ued*ued*ued; i++)
1665 underground_emptiness[i] = ((rand() % 5) == 0);
1670 This is a messy hack to sort the emptiness a bit
1672 for(s32 j=0; j<2; j++)
1673 for(s32 y0=0; y0<ued; y0++)
1674 for(s32 z0=0; z0<ued; z0++)
1675 for(s32 x0=0; x0<ued; x0++)
1678 bool &e0 = underground_emptiness[
1679 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1680 +ued*(y0*ued/MAP_BLOCKSIZE)
1681 +(x0*ued/MAP_BLOCKSIZE)];
1684 v3s16(0,0,1), // back
1685 v3s16(1,0,0), // right
1686 v3s16(0,0,-1), // front
1687 v3s16(-1,0,0), // left
1688 /*v3s16(0,1,0), // top
1689 v3s16(0,-1,0), // bottom*/
1691 for(s32 i=0; i<4; i++)
1693 v3s16 p1 = p0 + dirs[i];
1694 if(isInArea(p1, ued) == false)
1696 bool &e1 = underground_emptiness[
1697 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1698 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1699 +(p1.X*ued/MAP_BLOCKSIZE)];
1704 v3s16(0,1,0), // top
1705 v3s16(0,-1,0), // bottom
1706 /*v3s16(0,0,1), // back
1707 v3s16(1,0,0), // right
1708 v3s16(0,0,-1), // front
1709 v3s16(-1,0,0), // left*/
1711 for(s32 i=0; i<2; i++)
1713 v3s16 p2 = p1 + dirs[i];
1716 if(isInArea(p2, ued) == false)
1718 bool &e2 = underground_emptiness[
1719 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1720 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1721 +(p2.X*ued/MAP_BLOCKSIZE)];
1736 // This is the basic material of what the visible flat ground
1738 u8 material = CONTENT_GRASS;
1740 u8 water_material = CONTENT_WATER;
1741 if(g_settings.getBool("endless_water"))
1742 water_material = CONTENT_OCEAN;
1744 s32 lowest_ground_y = 32767;
1745 s32 highest_ground_y = -32768;
1748 //sector->printHeightmaps();
1750 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1751 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1753 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1755 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1756 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1757 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1759 dstream<<"WARNING: Surface height not found in sector "
1760 "for block that is being emerged"<<std::endl;
1764 s16 surface_y = surface_y_f;
1765 //avg_ground_y += surface_y;
1766 if(surface_y < lowest_ground_y)
1767 lowest_ground_y = surface_y;
1768 if(surface_y > highest_ground_y)
1769 highest_ground_y = surface_y;
1771 s32 surface_depth = 0;
1773 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1775 //float min_slope = 0.45;
1776 //float max_slope = 0.85;
1777 float min_slope = 0.60;
1778 float max_slope = 1.20;
1779 float min_slope_depth = 5.0;
1780 float max_slope_depth = 0;
1781 if(slope < min_slope)
1782 surface_depth = min_slope_depth;
1783 else if(slope > max_slope)
1784 surface_depth = max_slope_depth;
1786 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1788 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1790 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1795 NOTE: If there are some man-made structures above the
1796 newly created block, they won't be taken into account.
1798 if(real_y > surface_y)
1799 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1805 // If node is very low
1806 /*if(real_y <= surface_y - 7)
1809 if(underground_emptiness[
1810 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1811 +ued*(y0*ued/MAP_BLOCKSIZE)
1812 +(x0*ued/MAP_BLOCKSIZE)])
1818 n.d = CONTENT_STONE;
1821 // If node is under surface level
1822 else if(real_y <= surface_y - surface_depth)
1823 n.d = CONTENT_STONE;
1825 if(real_y <= surface_y - surface_depth)
1828 if(underground_emptiness[
1829 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1830 +ued*(y0*ued/MAP_BLOCKSIZE)
1831 +(x0*ued/MAP_BLOCKSIZE)])
1837 n.d = CONTENT_STONE;
1840 // If node is at or under heightmap y
1841 else if(real_y <= surface_y)
1843 // If under water level, it's mud
1844 if(real_y < WATER_LEVEL)
1846 // Only the topmost node is grass
1847 else if(real_y <= surface_y - 1)
1849 // Else it's the main material
1853 // If node is over heightmap y
1855 // If under water level, it's water
1856 if(real_y < WATER_LEVEL)
1858 n.d = water_material;
1859 n.setLight(LIGHTBANK_DAY,
1860 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1866 block->setNode(v3s16(x0,y0,z0), n);
1871 Calculate is_underground
1873 // Probably underground if the highest part of block is under lowest
1875 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1876 block->setIsUnderground(is_underground);
1879 Force lighting update if some part of block is underground
1880 This is needed because of caves.
1883 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1884 if(some_part_underground)
1885 //if(is_underground)
1887 lighting_invalidated_blocks[block->getPos()] = block;
1894 //if(is_underground)
1895 if(some_part_underground)
1897 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1898 for(s16 i=0; i<underground_level*3; i++)
1903 (rand()%(MAP_BLOCKSIZE-2))+1,
1904 (rand()%(MAP_BLOCKSIZE-2))+1,
1905 (rand()%(MAP_BLOCKSIZE-2))+1
1911 //if(is_ground_content(block->getNode(cp).d))
1912 if(block->getNode(cp).d == CONTENT_STONE)
1914 block->setNode(cp, n);
1916 for(u16 i=0; i<26; i++)
1918 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1919 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1921 block->setNode(cp+g_26dirs[i], n);
1928 Create a few rats in empty blocks underground
1932 //for(u16 i=0; i<2; i++)
1935 (rand()%(MAP_BLOCKSIZE-2))+1,
1936 (rand()%(MAP_BLOCKSIZE-2))+1,
1937 (rand()%(MAP_BLOCKSIZE-2))+1
1940 // Check that the place is empty
1941 //if(!is_ground_content(block->getNode(cp).d))
1944 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1945 block->addObject(obj);
1951 Add block to sector.
1953 sector->insertBlock(block);
1956 Do some interpolation for dungeons
1961 TimeTaker timer("interpolation", g_device);
1963 MapVoxelManipulator vmanip(this);
1965 v3s16 relpos = block->getPosRelative();
1967 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1968 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1969 /*vmanip.interpolate(VoxelArea(relpos,
1970 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1972 core::map<v3s16, MapBlock*> modified_blocks;
1973 vmanip.blitBack(modified_blocks);
1974 dstream<<"blitBack modified "<<modified_blocks.size()
1975 <<" blocks"<<std::endl;
1977 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1978 for(core::map<v3s16, MapBlock*>::Iterator
1979 i = modified_blocks.getIterator();
1980 i.atEnd() == false; i++)
1982 MapBlock *block = i.getNode()->getValue();
1984 changed_blocks.insert(block->getPos(), block);
1985 //lighting_invalidated_blocks.insert(block->getPos(), block);
1995 // An y-wise container of changed blocks
1996 core::map<s16, MapBlock*> changed_blocks_sector;
1999 Check if any sector's objects can be placed now.
2002 core::map<v3s16, u8> *objects = sector->getObjects();
2003 core::list<v3s16> objects_to_remove;
2004 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2005 i.atEnd() == false; i++)
2007 v3s16 p = i.getNode()->getKey();
2009 u8 d = i.getNode()->getValue();
2011 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2016 if(d == SECTOR_OBJECT_TEST)
2018 if(sector->isValidArea(p + v3s16(0,0,0),
2019 p + v3s16(0,0,0), &changed_blocks_sector))
2022 n.d = CONTENT_TORCH;
2023 sector->setNode(p, n);
2024 objects_to_remove.push_back(p);
2027 else if(d == SECTOR_OBJECT_TREE_1)
2029 v3s16 p_min = p + v3s16(-1,0,-1);
2030 v3s16 p_max = p + v3s16(1,4,1);
2031 if(sector->isValidArea(p_min, p_max,
2032 &changed_blocks_sector))
2036 sector->setNode(p+v3s16(0,0,0), n);
2037 sector->setNode(p+v3s16(0,1,0), n);
2038 sector->setNode(p+v3s16(0,2,0), n);
2039 sector->setNode(p+v3s16(0,3,0), n);
2041 n.d = CONTENT_LEAVES;
2043 sector->setNode(p+v3s16(0,4,0), n);
2045 sector->setNode(p+v3s16(-1,4,0), n);
2046 sector->setNode(p+v3s16(1,4,0), n);
2047 sector->setNode(p+v3s16(0,4,-1), n);
2048 sector->setNode(p+v3s16(0,4,1), n);
2049 sector->setNode(p+v3s16(1,4,1), n);
2050 sector->setNode(p+v3s16(-1,4,1), n);
2051 sector->setNode(p+v3s16(-1,4,-1), n);
2052 sector->setNode(p+v3s16(1,4,-1), n);
2054 sector->setNode(p+v3s16(-1,3,0), n);
2055 sector->setNode(p+v3s16(1,3,0), n);
2056 sector->setNode(p+v3s16(0,3,-1), n);
2057 sector->setNode(p+v3s16(0,3,1), n);
2058 sector->setNode(p+v3s16(1,3,1), n);
2059 sector->setNode(p+v3s16(-1,3,1), n);
2060 sector->setNode(p+v3s16(-1,3,-1), n);
2061 sector->setNode(p+v3s16(1,3,-1), n);
2063 objects_to_remove.push_back(p);
2065 // Lighting has to be recalculated for this one.
2066 sector->getBlocksInArea(p_min, p_max,
2067 lighting_invalidated_blocks);
2070 else if(d == SECTOR_OBJECT_BUSH_1)
2072 if(sector->isValidArea(p + v3s16(0,0,0),
2073 p + v3s16(0,0,0), &changed_blocks_sector))
2076 n.d = CONTENT_LEAVES;
2077 sector->setNode(p+v3s16(0,0,0), n);
2079 objects_to_remove.push_back(p);
2082 else if(d == SECTOR_OBJECT_RAVINE)
2085 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2086 v3s16 p_max = p + v3s16(6,6,6);
2087 if(sector->isValidArea(p_min, p_max,
2088 &changed_blocks_sector))
2091 n.d = CONTENT_STONE;
2094 s16 depth = maxdepth + (rand()%10);
2096 s16 minz = -6 - (-2);
2098 for(s16 x=-6; x<=6; x++)
2100 z += -1 + (rand()%3);
2105 for(s16 y=depth+(rand()%2); y<=6; y++)
2107 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2110 v3s16 p2 = p + v3s16(x,y,z-2);
2111 if(is_ground_content(sector->getNode(p2).d))
2112 sector->setNode(p2, n);
2115 v3s16 p2 = p + v3s16(x,y,z-1);
2116 if(is_ground_content(sector->getNode(p2).d))
2117 sector->setNode(p2, n2);
2120 v3s16 p2 = p + v3s16(x,y,z+0);
2121 if(is_ground_content(sector->getNode(p2).d))
2122 sector->setNode(p2, n2);
2125 v3s16 p2 = p + v3s16(x,y,z+1);
2126 if(is_ground_content(sector->getNode(p2).d))
2127 sector->setNode(p2, n);
2130 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2131 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2135 objects_to_remove.push_back(p);
2137 // Lighting has to be recalculated for this one.
2138 sector->getBlocksInArea(p_min, p_max,
2139 lighting_invalidated_blocks);
2144 dstream<<"ServerMap::emergeBlock(): "
2145 "Invalid heightmap object"
2150 catch(InvalidPositionException &e)
2152 dstream<<"WARNING: "<<__FUNCTION_NAME
2153 <<": while inserting object "<<(int)d
2154 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2155 <<" InvalidPositionException.what()="
2156 <<e.what()<<std::endl;
2157 // This is not too fatal and seems to happen sometimes.
2162 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2163 i != objects_to_remove.end(); i++)
2165 objects->remove(*i);
2168 for(core::map<s16, MapBlock*>::Iterator
2169 i = changed_blocks_sector.getIterator();
2170 i.atEnd() == false; i++)
2172 MapBlock *block = i.getNode()->getValue();
2174 changed_blocks.insert(block->getPos(), block);
2180 void ServerMap::createDir(std::string path)
2182 if(fs::CreateDir(path) == false)
2184 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2185 <<"\""<<path<<"\""<<std::endl;
2186 throw BaseException("ServerMap failed to create directory");
2190 std::string ServerMap::getSectorSubDir(v2s16 pos)
2193 snprintf(cc, 9, "%.4x%.4x",
2194 (unsigned int)pos.X&0xffff,
2195 (unsigned int)pos.Y&0xffff);
2197 return std::string(cc);
2200 std::string ServerMap::getSectorDir(v2s16 pos)
2202 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2205 v2s16 ServerMap::getSectorPos(std::string dirname)
2207 if(dirname.size() != 8)
2208 throw InvalidFilenameException("Invalid sector directory name");
2210 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2212 throw InvalidFilenameException("Invalid sector directory name");
2213 v2s16 pos((s16)x, (s16)y);
2217 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2219 v2s16 p2d = getSectorPos(sectordir);
2221 if(blockfile.size() != 4){
2222 throw InvalidFilenameException("Invalid block filename");
2225 int r = sscanf(blockfile.c_str(), "%4x", &y);
2227 throw InvalidFilenameException("Invalid block filename");
2228 return v3s16(p2d.X, y, p2d.Y);
2232 #define ENABLE_SECTOR_SAVING 1
2233 #define ENABLE_SECTOR_LOADING 1
2234 #define ENABLE_BLOCK_SAVING 1
2235 #define ENABLE_BLOCK_LOADING 1
2237 void ServerMap::save(bool only_changed)
2239 DSTACK(__FUNCTION_NAME);
2240 if(m_map_saving_enabled == false)
2242 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2246 if(only_changed == false)
2247 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2250 saveMasterHeightmap();
2252 u32 sector_meta_count = 0;
2253 u32 block_count = 0;
2256 JMutexAutoLock lock(m_sector_mutex);
2258 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2259 for(; i.atEnd() == false; i++)
2261 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2262 assert(sector->getId() == MAPSECTOR_SERVER);
2264 if(ENABLE_SECTOR_SAVING)
2266 if(sector->differs_from_disk || only_changed == false)
2268 saveSectorMeta(sector);
2269 sector_meta_count++;
2272 if(ENABLE_BLOCK_SAVING)
2274 core::list<MapBlock*> blocks;
2275 sector->getBlocks(blocks);
2276 core::list<MapBlock*>::Iterator j;
2277 for(j=blocks.begin(); j!=blocks.end(); j++)
2279 MapBlock *block = *j;
2280 if(block->getChangedFlag() || only_changed == false)
2291 u32 deleted_count = 0;
2292 deleted_count = deleteUnusedSectors(
2293 SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2296 Only print if something happened or saved whole map
2298 if(only_changed == false || sector_meta_count != 0
2299 || block_count != 0 || deleted_count != 0)
2301 dstream<<DTIME<<"ServerMap: Written: "
2302 <<sector_meta_count<<" sector metadata files, "
2303 <<block_count<<" block files, "
2304 <<deleted_count<<" sectors unloaded from memory."
2309 void ServerMap::loadAll()
2311 DSTACK(__FUNCTION_NAME);
2312 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2314 loadMasterHeightmap();
2316 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2318 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2320 JMutexAutoLock lock(m_sector_mutex);
2323 s32 printed_counter = -100000;
2324 s32 count = list.size();
2326 std::vector<fs::DirListNode>::iterator i;
2327 for(i=list.begin(); i!=list.end(); i++)
2329 if(counter > printed_counter + 10)
2331 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2332 printed_counter = counter;
2336 MapSector *sector = NULL;
2338 // We want directories
2342 sector = loadSectorMeta(i->name);
2344 catch(InvalidFilenameException &e)
2346 // This catches unknown crap in directory
2349 if(ENABLE_BLOCK_LOADING)
2351 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2352 (m_savedir+"/sectors/"+i->name);
2353 std::vector<fs::DirListNode>::iterator i2;
2354 for(i2=list2.begin(); i2!=list2.end(); i2++)
2360 loadBlock(i->name, i2->name, sector);
2362 catch(InvalidFilenameException &e)
2364 // This catches unknown crap in directory
2369 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2372 void ServerMap::saveMasterHeightmap()
2374 DSTACK(__FUNCTION_NAME);
2375 createDir(m_savedir);
2377 std::string fullpath = m_savedir + "/master_heightmap";
2378 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2379 if(o.good() == false)
2380 throw FileNotGoodException("Cannot open master heightmap");
2382 // Format used for writing
2383 u8 version = SER_FMT_VER_HIGHEST;
2386 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2388 [0] u8 serialization version
2389 [1] X master heightmap
2391 u32 fullsize = 1 + hmdata.getSize();
2392 SharedBuffer<u8> data(fullsize);
2395 memcpy(&data[1], *hmdata, hmdata.getSize());
2397 o.write((const char*)*data, fullsize);
2400 m_heightmap->serialize(o, version);
2403 void ServerMap::loadMasterHeightmap()
2405 DSTACK(__FUNCTION_NAME);
2406 std::string fullpath = m_savedir + "/master_heightmap";
2407 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2408 if(is.good() == false)
2409 throw FileNotGoodException("Cannot open master heightmap");
2411 if(m_heightmap != NULL)
2414 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2417 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2419 DSTACK(__FUNCTION_NAME);
2420 // Format used for writing
2421 u8 version = SER_FMT_VER_HIGHEST;
2423 v2s16 pos = sector->getPos();
2424 createDir(m_savedir);
2425 createDir(m_savedir+"/sectors");
2426 std::string dir = getSectorDir(pos);
2429 std::string fullpath = dir + "/heightmap";
2430 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2431 if(o.good() == false)
2432 throw FileNotGoodException("Cannot open master heightmap");
2434 sector->serialize(o, version);
2436 sector->differs_from_disk = false;
2439 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2441 DSTACK(__FUNCTION_NAME);
2443 v2s16 p2d = getSectorPos(dirname);
2444 std::string dir = m_savedir + "/sectors/" + dirname;
2446 std::string fullpath = dir + "/heightmap";
2447 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2448 if(is.good() == false)
2449 throw FileNotGoodException("Cannot open sector heightmap");
2451 ServerMapSector *sector = ServerMapSector::deSerialize
2452 (is, this, p2d, &m_hwrapper, m_sectors);
2454 sector->differs_from_disk = false;
2459 bool ServerMap::loadSectorFull(v2s16 p2d)
2461 DSTACK(__FUNCTION_NAME);
2462 std::string sectorsubdir = getSectorSubDir(p2d);
2464 MapSector *sector = NULL;
2466 JMutexAutoLock lock(m_sector_mutex);
2469 sector = loadSectorMeta(sectorsubdir);
2471 catch(InvalidFilenameException &e)
2475 catch(FileNotGoodException &e)
2479 catch(std::exception &e)
2484 if(ENABLE_BLOCK_LOADING)
2486 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2487 (m_savedir+"/sectors/"+sectorsubdir);
2488 std::vector<fs::DirListNode>::iterator i2;
2489 for(i2=list2.begin(); i2!=list2.end(); i2++)
2495 loadBlock(sectorsubdir, i2->name, sector);
2497 catch(InvalidFilenameException &e)
2499 // This catches unknown crap in directory
2507 bool ServerMap::deFlushSector(v2s16 p2d)
2509 DSTACK(__FUNCTION_NAME);
2510 // See if it already exists in memory
2512 MapSector *sector = getSectorNoGenerate(p2d);
2515 catch(InvalidPositionException &e)
2518 Try to load the sector from disk.
2520 if(loadSectorFull(p2d) == true)
2529 void ServerMap::saveBlock(MapBlock *block)
2531 DSTACK(__FUNCTION_NAME);
2533 Dummy blocks are not written
2535 if(block->isDummy())
2537 /*v3s16 p = block->getPos();
2538 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2539 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2543 // Format used for writing
2544 u8 version = SER_FMT_VER_HIGHEST;
2546 v3s16 p3d = block->getPos();
2547 v2s16 p2d(p3d.X, p3d.Z);
2548 createDir(m_savedir);
2549 createDir(m_savedir+"/sectors");
2550 std::string dir = getSectorDir(p2d);
2553 // Block file is map/sectors/xxxxxxxx/xxxx
2555 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2556 std::string fullpath = dir + "/" + cc;
2557 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2558 if(o.good() == false)
2559 throw FileNotGoodException("Cannot open block data");
2562 [0] u8 serialization version
2565 o.write((char*)&version, 1);
2567 block->serialize(o, version);
2570 Versions up from 9 have block objects.
2574 block->serializeObjects(o, version);
2577 // We just wrote it to the disk
2578 block->resetChangedFlag();
2581 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2583 DSTACK(__FUNCTION_NAME);
2587 // Block file is map/sectors/xxxxxxxx/xxxx
2588 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2589 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2590 if(is.good() == false)
2591 throw FileNotGoodException("Cannot open block file");
2593 v3s16 p3d = getBlockPos(sectordir, blockfile);
2594 v2s16 p2d(p3d.X, p3d.Z);
2596 assert(sector->getPos() == p2d);
2598 u8 version = SER_FMT_VER_INVALID;
2599 is.read((char*)&version, 1);
2601 /*u32 block_size = MapBlock::serializedLength(version);
2602 SharedBuffer<u8> data(block_size);
2603 is.read((char*)*data, block_size);*/
2605 // This will always return a sector because we're the server
2606 //MapSector *sector = emergeSector(p2d);
2608 MapBlock *block = NULL;
2609 bool created_new = false;
2611 block = sector->getBlockNoCreate(p3d.Y);
2613 catch(InvalidPositionException &e)
2615 block = sector->createBlankBlockNoInsert(p3d.Y);
2619 // deserialize block data
2620 block->deSerialize(is, version);
2623 Versions up from 9 have block objects.
2627 block->updateObjects(is, version, NULL);
2631 sector->insertBlock(block);
2634 Convert old formats to new and save
2637 // Save old format blocks in new format
2638 if(version < SER_FMT_VER_HIGHEST)
2643 // We just loaded it from the disk, so it's up-to-date.
2644 block->resetChangedFlag();
2647 catch(SerializationError &e)
2649 dstream<<"WARNING: Invalid block data on disk "
2650 "(SerializationError). Ignoring."
2655 // Gets from master heightmap
2656 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2658 assert(m_heightmap != NULL);
2666 corners[0] = m_heightmap->getGroundHeight
2667 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2668 corners[1] = m_heightmap->getGroundHeight
2669 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2670 corners[2] = m_heightmap->getGroundHeight
2671 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2672 corners[3] = m_heightmap->getGroundHeight
2673 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2676 void ServerMap::PrintInfo(std::ostream &out)
2685 ClientMap::ClientMap(
2687 scene::ISceneNode* parent,
2688 scene::ISceneManager* mgr,
2692 scene::ISceneNode(parent, mgr, id),
2698 /*m_box = core::aabbox3d<f32>(0,0,0,
2699 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2700 /*m_box = core::aabbox3d<f32>(0,0,0,
2701 map->getSizeNodes().X * BS,
2702 map->getSizeNodes().Y * BS,
2703 map->getSizeNodes().Z * BS);*/
2704 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2705 BS*1000000,BS*1000000,BS*1000000);
2707 //setPosition(v3f(BS,BS,BS));
2710 ClientMap::~ClientMap()
2712 JMutexAutoLock lock(mesh_mutex);
2721 MapSector * ClientMap::emergeSector(v2s16 p2d)
2723 DSTACK(__FUNCTION_NAME);
2724 // Check that it doesn't exist already
2726 return getSectorNoGenerate(p2d);
2728 catch(InvalidPositionException &e)
2732 // Create a sector with no heightmaps
2733 ClientMapSector *sector = new ClientMapSector(this, p2d);
2736 JMutexAutoLock lock(m_sector_mutex);
2737 m_sectors.insert(p2d, sector);
2743 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2745 DSTACK(__FUNCTION_NAME);
2746 ClientMapSector *sector = NULL;
2748 JMutexAutoLock lock(m_sector_mutex);
2750 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2754 sector = (ClientMapSector*)n->getValue();
2755 assert(sector->getId() == MAPSECTOR_CLIENT);
2759 sector = new ClientMapSector(this, p2d);
2761 JMutexAutoLock lock(m_sector_mutex);
2762 m_sectors.insert(p2d, sector);
2766 sector->deSerialize(is);
2769 void ClientMap::OnRegisterSceneNode()
2773 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2774 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2777 ISceneNode::OnRegisterSceneNode();
2780 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2782 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2783 DSTACK(__FUNCTION_NAME);
2785 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2788 Get time for measuring timeout.
2790 Measuring time is very useful for long delays when the
2791 machine is swapping a lot.
2793 int time1 = time(0);
2795 //s32 daynight_i = m_client->getDayNightIndex();
2796 u32 daynight_ratio = m_client->getDayNightRatio();
2799 Collect all blocks that are in the view range
2801 Should not optimize more here as we want to auto-update
2802 all changed nodes in viewing range at the next step.
2805 s16 viewing_range_nodes;
2806 bool viewing_range_all;
2808 JMutexAutoLock lock(g_range_mutex);
2809 viewing_range_nodes = g_viewing_range_nodes;
2810 viewing_range_all = g_viewing_range_all;
2813 m_camera_mutex.Lock();
2814 v3f camera_position = m_camera_position;
2815 v3f camera_direction = m_camera_direction;
2816 m_camera_mutex.Unlock();
2819 Get all blocks and draw all visible ones
2822 v3s16 cam_pos_nodes(
2823 camera_position.X / BS,
2824 camera_position.Y / BS,
2825 camera_position.Z / BS);
2827 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2829 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2830 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2832 // Take a fair amount as we will be dropping more out later
2834 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2835 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2836 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2838 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2839 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2840 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2842 u32 vertex_count = 0;
2844 // For limiting number of mesh updates per frame
2845 u32 mesh_update_count = 0;
2847 //NOTE: The sectors map should be locked but we're not doing it
2848 // because it'd cause too much delays
2850 int timecheck_counter = 0;
2852 core::map<v2s16, MapSector*>::Iterator si;
2853 si = m_sectors.getIterator();
2854 for(; si.atEnd() == false; si++)
2857 timecheck_counter++;
2858 if(timecheck_counter > 50)
2860 int time2 = time(0);
2861 if(time2 > time1 + 4)
2863 dstream<<"ClientMap::renderMap(): "
2864 "Rendering takes ages, returning."
2871 MapSector *sector = si.getNode()->getValue();
2872 v2s16 sp = sector->getPos();
2874 if(viewing_range_all == false)
2876 if(sp.X < p_blocks_min.X
2877 || sp.X > p_blocks_max.X
2878 || sp.Y < p_blocks_min.Z
2879 || sp.Y > p_blocks_max.Z)
2883 core::list< MapBlock * > sectorblocks;
2884 sector->getBlocks(sectorblocks);
2890 core::list< MapBlock * >::Iterator i;
2891 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2893 MapBlock *block = *i;
2896 Compare block position to camera position, skip
2897 if not seen on display
2900 v3s16 blockpos_nodes = block->getPosRelative();
2902 // Block center position
2904 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2905 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2906 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2909 // Block position relative to camera
2910 v3f blockpos_relative = blockpos - camera_position;
2912 // Distance in camera direction (+=front, -=back)
2913 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2916 f32 d = blockpos_relative.getLength();
2918 if(viewing_range_all == false)
2920 // If block is far away, don't draw it
2921 if(d > viewing_range_nodes * BS)
2922 // This is nicer when fog is used
2923 //if((dforward+d)/2 > viewing_range_nodes * BS)
2927 // Maximum radius of a block
2928 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2930 // If block is (nearly) touching the camera, don't
2931 // bother validating further (that is, render it anyway)
2932 if(d > block_max_radius * 1.5)
2934 // Cosine of the angle between the camera direction
2935 // and the block direction (camera_direction is an unit vector)
2936 f32 cosangle = dforward / d;
2938 // Compensate for the size of the block
2939 // (as the block has to be shown even if it's a bit off FOV)
2940 // This is an estimate.
2941 cosangle += block_max_radius / dforward;
2943 // If block is not in the field of view, skip it
2944 //if(cosangle < cos(FOV_ANGLE/2))
2945 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2950 Draw the faces of the block
2953 bool mesh_expired = false;
2956 JMutexAutoLock lock(block->mesh_mutex);
2958 mesh_expired = block->getMeshExpired();
2960 // Mesh has not been expired and there is no mesh:
2961 // block has no content
2962 if(block->mesh == NULL && mesh_expired == false)
2966 f32 faraway = BS*50;
2967 //f32 faraway = viewing_range_nodes * BS;
2970 This has to be done with the mesh_mutex unlocked
2972 if(mesh_expired && mesh_update_count < 6
2973 && (d < faraway || mesh_update_count < 3))
2974 //if(mesh_expired && mesh_update_count < 4)
2976 mesh_update_count++;
2978 // Mesh has been expired: generate new mesh
2979 //block->updateMeshes(daynight_i);
2980 block->updateMesh(daynight_ratio);
2982 mesh_expired = false;
2986 Don't draw an expired mesh that is far away
2988 /*if(mesh_expired && d >= faraway)
2991 // Instead, delete it
2992 JMutexAutoLock lock(block->mesh_mutex);
2995 block->mesh->drop();
2998 // And continue to next block
3003 JMutexAutoLock lock(block->mesh_mutex);
3005 scene::SMesh *mesh = block->mesh;
3010 u32 c = mesh->getMeshBufferCount();
3012 for(u32 i=0; i<c; i++)
3014 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3015 const video::SMaterial& material = buf->getMaterial();
3016 video::IMaterialRenderer* rnd =
3017 driver->getMaterialRenderer(material.MaterialType);
3018 bool transparent = (rnd && rnd->isTransparent());
3019 // Render transparent on transparent pass and likewise.
3020 if(transparent == is_transparent_pass)
3022 driver->setMaterial(buf->getMaterial());
3023 driver->drawMeshBuffer(buf);
3024 vertex_count += buf->getVertexCount();
3028 } // foreach sectorblocks
3031 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3032 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3035 void ClientMap::updateMesh()
3040 void ClientMap::PrintInfo(std::ostream &out)
3050 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3055 MapVoxelManipulator::~MapVoxelManipulator()
3057 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3062 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3064 TimeTaker timer1("emerge", g_device, &emerge_time);
3066 // Units of these are MapBlocks
3067 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3068 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3070 VoxelArea block_area_nodes
3071 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3073 addArea(block_area_nodes);
3075 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3076 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3077 for(s32 x=p_min.X; x<=p_max.X; x++)
3080 core::map<v3s16, bool>::Node *n;
3081 n = m_loaded_blocks.find(p);
3085 bool block_data_inexistent = false;
3088 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3090 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3091 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3094 dstream<<std::endl;*/
3096 MapBlock *block = m_map->getBlockNoCreate(p);
3097 if(block->isDummy())
3098 block_data_inexistent = true;
3100 block->copyTo(*this);
3102 catch(InvalidPositionException &e)
3104 block_data_inexistent = true;
3107 if(block_data_inexistent)
3109 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3110 // Fill with VOXELFLAG_INEXISTENT
3111 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3112 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3114 s32 i = m_area.index(a.MinEdge.X,y,z);
3115 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3119 m_loaded_blocks.insert(p, true);
3122 //dstream<<"emerge done"<<std::endl;
3127 void MapVoxelManipulator::emerge(VoxelArea a)
3129 TimeTaker timer1("emerge", g_device, &emerge_time);
3131 v3s16 size = a.getExtent();
3133 VoxelArea padded = a;
3134 padded.pad(m_area.getExtent() / 4);
3137 for(s16 z=0; z<size.Z; z++)
3138 for(s16 y=0; y<size.Y; y++)
3139 for(s16 x=0; x<size.X; x++)
3142 s32 i = m_area.index(a.MinEdge + p);
3143 // Don't touch nodes that have already been loaded
3144 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3148 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3149 MapNode n = m_map->getNode(a.MinEdge + p);
3153 catch(InvalidPositionException &e)
3155 m_flags[i] = VOXELFLAG_INEXISTENT;
3163 TODO: Add an option to only update eg. water and air nodes.
3164 This will make it interfere less with important stuff if
3167 void MapVoxelManipulator::blitBack
3168 (core::map<v3s16, MapBlock*> & modified_blocks)
3170 if(m_area.getExtent() == v3s16(0,0,0))
3173 //TimeTaker timer1("blitBack", g_device);
3176 Initialize block cache
3178 v3s16 blockpos_last;
3179 MapBlock *block = NULL;
3180 bool block_checked_in_modified = false;
3182 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3183 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3184 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3188 u8 f = m_flags[m_area.index(p)];
3189 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3192 MapNode &n = m_data[m_area.index(p)];
3194 v3s16 blockpos = getNodeBlockPos(p);
3199 if(block == NULL || blockpos != blockpos_last){
3200 block = m_map->getBlockNoCreate(blockpos);
3201 blockpos_last = blockpos;
3202 block_checked_in_modified = false;
3205 // Calculate relative position in block
3206 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3208 // Don't continue if nothing has changed here
3209 if(block->getNode(relpos) == n)
3212 //m_map->setNode(m_area.MinEdge + p, n);
3213 block->setNode(relpos, n);
3216 Make sure block is in modified_blocks
3218 if(block_checked_in_modified == false)
3220 modified_blocks[blockpos] = block;
3221 block_checked_in_modified = true;
3224 catch(InvalidPositionException &e)