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 quite fine without it
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);
784 This is called after changing a node from transparent to opaque.
785 The lighting value of the node should be left as-is after changing
786 other values. This sets the lighting value to 0.
788 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
789 core::map<v3s16, MapBlock*> &modified_blocks)*/
790 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
791 core::map<v3s16, MapBlock*> &modified_blocks)
794 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
795 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
798 From this node to nodes underneath:
799 If lighting is sunlight (1.0), unlight neighbours and
804 v3s16 toppos = p + v3s16(0,1,0);
806 bool node_under_sunlight = true;
807 core::map<v3s16, bool> light_sources;
810 If there is a node at top and it doesn't have sunlight,
811 there has not been any sunlight going down.
813 Otherwise there probably is.
816 MapNode topnode = getNode(toppos);
818 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
819 node_under_sunlight = false;
821 catch(InvalidPositionException &e)
825 enum LightBank banks[] =
830 for(s32 i=0; i<2; i++)
832 enum LightBank bank = banks[i];
834 u8 lightwas = getNode(p).getLight(bank);
836 // Add the block of the added node to modified_blocks
837 v3s16 blockpos = getNodeBlockPos(p);
838 MapBlock * block = getBlockNoCreate(blockpos);
839 assert(block != NULL);
840 modified_blocks.insert(blockpos, block);
842 if(isValidPosition(p) == false)
845 // Unlight neighbours of node.
846 // This means setting light of all consequent dimmer nodes
848 // This also collects the nodes at the border which will spread
849 // light again into this.
850 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
858 If node is under sunlight, take all sunlighted nodes under
859 it and clear light from them and from where the light has
861 TODO: This could be optimized by mass-unlighting instead
864 if(node_under_sunlight)
868 //m_dout<<DTIME<<"y="<<y<<std::endl;
869 v3s16 n2pos(p.X, y, p.Z);
875 catch(InvalidPositionException &e)
880 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
882 //m_dout<<DTIME<<"doing"<<std::endl;
883 unLightNeighbors(LIGHTBANK_DAY,
884 n2pos, n2.getLight(LIGHTBANK_DAY),
885 light_sources, modified_blocks);
886 n2.setLight(LIGHTBANK_DAY, 0);
894 for(s32 i=0; i<2; i++)
896 enum LightBank bank = banks[i];
899 Spread light from all nodes that might be capable of doing so
900 TODO: Convert to spreadLight
902 spreadLight(bank, light_sources, modified_blocks);
908 void Map::removeNodeAndUpdate(v3s16 p,
909 core::map<v3s16, MapBlock*> &modified_blocks)
912 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
913 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
915 bool node_under_sunlight = true;
917 v3s16 toppos = p + v3s16(0,1,0);
919 // Node will be replaced with this
920 u8 replace_material = CONTENT_AIR;
923 If there is a node at top and it doesn't have sunlight,
924 there will be no sunlight going down.
927 MapNode topnode = getNode(toppos);
929 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
930 node_under_sunlight = false;
932 catch(InvalidPositionException &e)
936 core::map<v3s16, bool> light_sources;
938 enum LightBank banks[] =
943 for(s32 i=0; i<2; i++)
945 enum LightBank bank = banks[i];
948 Unlight neighbors (in case the node is a light source)
950 unLightNeighbors(bank, p,
951 getNode(p).getLight(bank),
952 light_sources, modified_blocks);
957 This also clears the lighting.
961 n.d = replace_material;
964 for(s32 i=0; i<2; i++)
966 enum LightBank bank = banks[i];
971 spreadLight(bank, light_sources, modified_blocks);
974 // Add the block of the removed node to modified_blocks
975 v3s16 blockpos = getNodeBlockPos(p);
976 MapBlock * block = getBlockNoCreate(blockpos);
977 assert(block != NULL);
978 modified_blocks.insert(blockpos, block);
981 If the removed node was under sunlight, propagate the
982 sunlight down from it and then light all neighbors
983 of the propagated blocks.
985 if(node_under_sunlight)
987 s16 ybottom = propagateSunlight(p, modified_blocks);
988 /*m_dout<<DTIME<<"Node was under sunlight. "
989 "Propagating sunlight";
990 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
992 for(; y >= ybottom; y--)
994 v3s16 p2(p.X, y, p.Z);
995 /*m_dout<<DTIME<<"lighting neighbors of node ("
996 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
998 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1003 // Set the lighting of this node to 0
1004 // TODO: Is this needed? Lighting is cleared up there already.
1006 MapNode n = getNode(p);
1007 n.setLight(LIGHTBANK_DAY, 0);
1010 catch(InvalidPositionException &e)
1016 for(s32 i=0; i<2; i++)
1018 enum LightBank bank = banks[i];
1020 // Get the brightest neighbour node and propagate light from it
1021 v3s16 n2p = getBrightestNeighbour(bank, p);
1023 MapNode n2 = getNode(n2p);
1024 lightNeighbors(bank, n2p, modified_blocks);
1026 catch(InvalidPositionException &e)
1032 void Map::expireMeshes()
1034 TimeTaker timer("expireMeshes()", g_device);
1036 core::map<v2s16, MapSector*>::Iterator si;
1037 si = m_sectors.getIterator();
1038 for(; si.atEnd() == false; si++)
1040 MapSector *sector = si.getNode()->getValue();
1042 core::list< MapBlock * > sectorblocks;
1043 sector->getBlocks(sectorblocks);
1045 core::list< MapBlock * >::Iterator i;
1046 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1048 MapBlock *block = *i;
1050 JMutexAutoLock lock(block->mesh_mutex);
1051 if(block->mesh != NULL)
1053 //block->mesh->drop();
1054 //block->mesh = NULL;
1055 block->setMeshExpired(true);
1062 void Map::updateMeshes(v3s16 blockpos, u32 daylight_factor)
1064 assert(mapType() == MAPTYPE_CLIENT);
1067 v3s16 p = blockpos + v3s16(0,0,0);
1068 MapBlock *b = getBlockNoCreate(p);
1069 b->updateMesh(daylight_factor);
1071 catch(InvalidPositionException &e){}
1073 v3s16 p = blockpos + v3s16(-1,0,0);
1074 MapBlock *b = getBlockNoCreate(p);
1075 b->updateMesh(daylight_factor);
1077 catch(InvalidPositionException &e){}
1079 v3s16 p = blockpos + v3s16(0,-1,0);
1080 MapBlock *b = getBlockNoCreate(p);
1081 b->updateMesh(daylight_factor);
1083 catch(InvalidPositionException &e){}
1085 v3s16 p = blockpos + v3s16(0,0,-1);
1086 MapBlock *b = getBlockNoCreate(p);
1087 b->updateMesh(daylight_factor);
1089 catch(InvalidPositionException &e){}
1093 Updates usage timers
1095 void Map::timerUpdate(float dtime)
1097 JMutexAutoLock lock(m_sector_mutex);
1099 core::map<v2s16, MapSector*>::Iterator si;
1101 si = m_sectors.getIterator();
1102 for(; si.atEnd() == false; si++)
1104 MapSector *sector = si.getNode()->getValue();
1105 sector->usage_timer += dtime;
1109 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1112 Wait for caches to be removed before continuing.
1114 This disables the existence of caches while locked
1116 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1118 core::list<v2s16>::Iterator j;
1119 for(j=list.begin(); j!=list.end(); j++)
1121 MapSector *sector = m_sectors[*j];
1124 sector->deleteBlocks();
1129 If sector is in sector cache, remove it from there
1131 if(m_sector_cache == sector)
1133 m_sector_cache = NULL;
1136 Remove from map and delete
1138 m_sectors.remove(*j);
1144 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1145 core::list<v3s16> *deleted_blocks)
1147 JMutexAutoLock lock(m_sector_mutex);
1149 core::list<v2s16> sector_deletion_queue;
1150 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1151 for(; i.atEnd() == false; i++)
1153 MapSector *sector = i.getNode()->getValue();
1155 Delete sector from memory if it hasn't been used in a long time
1157 if(sector->usage_timer > timeout)
1159 sector_deletion_queue.push_back(i.getNode()->getKey());
1161 if(deleted_blocks != NULL)
1163 // Collect positions of blocks of sector
1164 MapSector *sector = i.getNode()->getValue();
1165 core::list<MapBlock*> blocks;
1166 sector->getBlocks(blocks);
1167 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1168 i != blocks.end(); i++)
1170 deleted_blocks->push_back((*i)->getPos());
1175 deleteSectors(sector_deletion_queue, only_blocks);
1176 return sector_deletion_queue.getSize();
1179 void Map::PrintInfo(std::ostream &out)
1188 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1192 m_savedir = savedir;
1193 m_map_saving_enabled = false;
1197 // If directory exists, check contents and load if possible
1198 if(fs::PathExists(m_savedir))
1200 // If directory is empty, it is safe to save into it.
1201 if(fs::GetDirListing(m_savedir).size() == 0)
1203 dstream<<DTIME<<"Server: Empty save directory is valid."
1205 m_map_saving_enabled = true;
1209 // Load master heightmap
1210 loadMasterHeightmap();
1212 // Load sector (0,0) and throw and exception on fail
1213 if(loadSectorFull(v2s16(0,0)) == false)
1214 throw LoadError("Failed to load sector (0,0)");
1216 dstream<<DTIME<<"Server: Successfully loaded master "
1217 "heightmap and sector (0,0) from "<<savedir<<
1218 ", assuming valid save directory."
1221 m_map_saving_enabled = true;
1222 // Map loaded, not creating new one
1226 // If directory doesn't exist, it is safe to save to it
1228 m_map_saving_enabled = true;
1231 catch(std::exception &e)
1233 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1234 <<", exception: "<<e.what()<<std::endl;
1235 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1236 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1239 dstream<<DTIME<<"Initializing new map."<<std::endl;
1241 // Create master heightmap
1242 ValueGenerator *maxgen =
1243 ValueGenerator::deSerialize(hmp.randmax);
1244 ValueGenerator *factorgen =
1245 ValueGenerator::deSerialize(hmp.randfactor);
1246 ValueGenerator *basegen =
1247 ValueGenerator::deSerialize(hmp.base);
1248 m_heightmap = new UnlimitedHeightmap
1249 (hmp.blocksize, maxgen, factorgen, basegen);
1251 // Set map parameters
1254 // Create zero sector
1255 emergeSector(v2s16(0,0));
1257 // Initially write whole map
1261 ServerMap::~ServerMap()
1265 if(m_map_saving_enabled)
1268 // Save only changed parts
1270 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1274 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1277 catch(std::exception &e)
1279 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1280 <<", exception: "<<e.what()<<std::endl;
1283 if(m_heightmap != NULL)
1287 MapSector * ServerMap::emergeSector(v2s16 p2d)
1289 DSTACK("%s: p2d=(%d,%d)",
1292 // Check that it doesn't exist already
1294 return getSectorNoGenerate(p2d);
1296 catch(InvalidPositionException &e)
1301 Try to load the sector from disk.
1303 if(loadSectorFull(p2d) == true)
1305 return getSectorNoGenerate(p2d);
1309 If there is no master heightmap, throw.
1311 if(m_heightmap == NULL)
1313 throw InvalidPositionException("emergeSector(): no heightmap");
1317 Do not generate over-limit
1319 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1320 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1321 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1322 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1323 throw InvalidPositionException("emergeSector(): pos. over limit");
1326 Generate sector and heightmaps
1329 // Number of heightmaps in sector in each direction
1330 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1332 // Heightmap side width
1333 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1335 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1337 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1338 " heightmaps and objects"<<std::endl;*/
1340 // Loop through sub-heightmaps
1341 for(s16 y=0; y<hm_split; y++)
1342 for(s16 x=0; x<hm_split; x++)
1344 v2s16 p_in_sector = v2s16(x,y);
1345 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1347 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1348 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1349 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1350 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1353 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1354 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1357 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1359 sector->setHeightmap(p_in_sector, hm);
1361 //TODO: Make these values configurable
1362 //hm->generateContinued(0.0, 0.0, corners);
1363 hm->generateContinued(0.5, 0.2, corners);
1364 //hm->generateContinued(1.0, 0.2, corners);
1365 //hm->generateContinued(2.0, 0.2, corners);
1375 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1376 sector->setObjects(objects);
1378 v2s16 mhm_p = p2d * hm_split;
1380 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1381 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1382 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1383 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1386 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1387 float avgslope = 0.0;
1388 avgslope += fabs(avgheight - corners[0]);
1389 avgslope += fabs(avgheight - corners[1]);
1390 avgslope += fabs(avgheight - corners[2]);
1391 avgslope += fabs(avgheight - corners[3]);
1393 avgslope /= MAP_BLOCKSIZE;
1394 //dstream<<"avgslope="<<avgslope<<std::endl;
1396 float pitness = 0.0;
1398 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1401 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1404 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1407 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1411 pitness /= MAP_BLOCKSIZE;
1412 //dstream<<"pitness="<<pitness<<std::endl;
1415 Plant some trees if there is not much slope
1418 // Avgslope is the derivative of a hill
1419 float t = avgslope * avgslope;
1420 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1423 tree_max = a / (t/0.03);
1426 u32 count = (rand()%(tree_max+1));
1427 //u32 count = tree_max;
1428 for(u32 i=0; i<count; i++)
1430 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1431 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1432 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1435 objects->insert(v3s16(x, y, z),
1436 SECTOR_OBJECT_TREE_1);
1440 Plant some bushes if sector is pit-like
1443 // Pitness usually goes at around -0.5...0.5
1445 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1447 bush_max = (pitness*a*4);
1450 u32 count = (rand()%(bush_max+1));
1451 for(u32 i=0; i<count; i++)
1453 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1454 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1455 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1458 objects->insert(v3s16(x, y, z),
1459 SECTOR_OBJECT_BUSH_1);
1463 Add ravine (randomly)
1465 if(m_params.ravines_amount != 0)
1467 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1470 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1471 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1474 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1475 objects->insert(v3s16(x, y, z),
1476 SECTOR_OBJECT_RAVINE);
1483 JMutexAutoLock lock(m_sector_mutex);
1484 m_sectors.insert(p2d, sector);
1489 MapBlock * ServerMap::emergeBlock(
1491 bool only_from_disk,
1492 core::map<v3s16, MapBlock*> &changed_blocks,
1493 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1496 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1498 p.X, p.Y, p.Z, only_from_disk);
1500 /*dstream<<"ServerMap::emergeBlock(): "
1501 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1502 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1503 v2s16 p2d(p.X, p.Z);
1506 This will create or load a sector if not found in memory.
1507 If block exists on disk, it will be loaded.
1509 NOTE: On old save formats, this will be slow, as it generates
1510 lighting on blocks for them.
1512 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1513 assert(sector->getId() == MAPSECTOR_SERVER);
1515 // Try to get a block from the sector
1516 MapBlock *block = NULL;
1517 bool not_on_disk = false;
1519 block = sector->getBlockNoCreate(block_y);
1520 if(block->isDummy() == true)
1525 catch(InvalidPositionException &e)
1531 If block was not found on disk and not going to generate a
1532 new one, make sure there is a dummy block in place.
1534 if(not_on_disk && only_from_disk)
1538 // Create dummy block
1539 block = new MapBlock(this, p, true);
1541 // Add block to sector
1542 sector->insertBlock(block);
1548 //dstream<<"Not found on disk, generating."<<std::endl;
1551 Do not generate over-limit
1553 if(blockpos_over_limit(p))
1554 throw InvalidPositionException("emergeBlock(): pos. over limit");
1559 Go on generating the block.
1561 TODO: If a dungeon gets generated so that it's side gets
1562 revealed to the outside air, the lighting should be
1567 If block doesn't exist, create one.
1568 If it exists, it is a dummy. In that case unDummify() it.
1572 block = sector->createBlankBlockNoInsert(block_y);
1576 // Remove the block so that nobody can get a half-generated one.
1577 sector->removeBlock(block);
1578 // Allocate the block to be a proper one.
1582 // Randomize a bit. This makes dungeons.
1583 /*bool low_block_is_empty = false;
1585 low_block_is_empty = true;*/
1588 //const s32 ued = 8;
1589 bool underground_emptiness[ued*ued*ued];
1590 for(s32 i=0; i<ued*ued*ued; i++)
1592 underground_emptiness[i] = ((rand() % 5) == 0);
1597 This is a messy hack to sort the emptiness a bit
1599 for(s32 j=0; j<2; j++)
1600 for(s32 y0=0; y0<ued; y0++)
1601 for(s32 z0=0; z0<ued; z0++)
1602 for(s32 x0=0; x0<ued; x0++)
1605 bool &e0 = underground_emptiness[
1606 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1607 +ued*(y0*ued/MAP_BLOCKSIZE)
1608 +(x0*ued/MAP_BLOCKSIZE)];
1611 v3s16(0,0,1), // back
1612 v3s16(1,0,0), // right
1613 v3s16(0,0,-1), // front
1614 v3s16(-1,0,0), // left
1615 /*v3s16(0,1,0), // top
1616 v3s16(0,-1,0), // bottom*/
1618 for(s32 i=0; i<4; i++)
1620 v3s16 p1 = p0 + dirs[i];
1621 if(isInArea(p1, ued) == false)
1623 bool &e1 = underground_emptiness[
1624 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1625 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1626 +(p1.X*ued/MAP_BLOCKSIZE)];
1631 v3s16(0,1,0), // top
1632 v3s16(0,-1,0), // bottom
1633 /*v3s16(0,0,1), // back
1634 v3s16(1,0,0), // right
1635 v3s16(0,0,-1), // front
1636 v3s16(-1,0,0), // left*/
1638 for(s32 i=0; i<2; i++)
1640 v3s16 p2 = p1 + dirs[i];
1643 if(isInArea(p2, ued) == false)
1645 bool &e2 = underground_emptiness[
1646 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1647 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1648 +(p2.X*ued/MAP_BLOCKSIZE)];
1663 // This is the basic material of what the visible flat ground
1665 u8 material = CONTENT_GRASS;
1667 u8 water_material = CONTENT_WATER;
1668 if(g_settings.getBool("endless_water"))
1669 water_material = CONTENT_OCEAN;
1671 s32 lowest_ground_y = 32767;
1672 s32 highest_ground_y = -32768;
1675 //sector->printHeightmaps();
1677 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1678 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1680 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1682 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1683 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1684 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1686 dstream<<"WARNING: Surface height not found in sector "
1687 "for block that is being emerged"<<std::endl;
1691 s16 surface_y = surface_y_f;
1692 //avg_ground_y += surface_y;
1693 if(surface_y < lowest_ground_y)
1694 lowest_ground_y = surface_y;
1695 if(surface_y > highest_ground_y)
1696 highest_ground_y = surface_y;
1698 s32 surface_depth = 0;
1700 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1702 //float min_slope = 0.45;
1703 //float max_slope = 0.85;
1704 float min_slope = 0.60;
1705 float max_slope = 1.20;
1706 float min_slope_depth = 5.0;
1707 float max_slope_depth = 0;
1708 if(slope < min_slope)
1709 surface_depth = min_slope_depth;
1710 else if(slope > max_slope)
1711 surface_depth = max_slope_depth;
1713 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1715 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1717 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1722 NOTE: If there are some man-made structures above the
1723 newly created block, they won't be taken into account.
1725 if(real_y > surface_y)
1726 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1732 // If node is very low
1733 /*if(real_y <= surface_y - 7)
1736 if(underground_emptiness[
1737 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1738 +ued*(y0*ued/MAP_BLOCKSIZE)
1739 +(x0*ued/MAP_BLOCKSIZE)])
1745 n.d = CONTENT_STONE;
1748 // If node is under surface level
1749 else if(real_y <= surface_y - surface_depth)
1750 n.d = CONTENT_STONE;
1752 if(real_y <= surface_y - surface_depth)
1755 if(underground_emptiness[
1756 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1757 +ued*(y0*ued/MAP_BLOCKSIZE)
1758 +(x0*ued/MAP_BLOCKSIZE)])
1764 n.d = CONTENT_STONE;
1767 // If node is at or under heightmap y
1768 else if(real_y <= surface_y)
1770 // If under water level, it's mud
1771 if(real_y < WATER_LEVEL)
1773 // Only the topmost node is grass
1774 else if(real_y <= surface_y - 1)
1776 // Else it's the main material
1780 // If node is over heightmap y
1782 // If under water level, it's water
1783 if(real_y < WATER_LEVEL)
1785 n.d = water_material;
1786 n.setLight(LIGHTBANK_DAY,
1787 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1793 block->setNode(v3s16(x0,y0,z0), n);
1798 Calculate is_underground
1800 // Probably underground if the highest part of block is under lowest
1802 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1803 block->setIsUnderground(is_underground);
1806 Force lighting update if some part of block is underground
1807 This is needed because of caves.
1810 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1811 if(some_part_underground)
1812 //if(is_underground)
1814 lighting_invalidated_blocks[block->getPos()] = block;
1821 //if(is_underground)
1822 if(some_part_underground)
1824 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1825 for(s16 i=0; i<underground_level*3; i++)
1830 (rand()%(MAP_BLOCKSIZE-2))+1,
1831 (rand()%(MAP_BLOCKSIZE-2))+1,
1832 (rand()%(MAP_BLOCKSIZE-2))+1
1838 //if(is_ground_content(block->getNode(cp).d))
1839 if(block->getNode(cp).d == CONTENT_STONE)
1841 block->setNode(cp, n);
1843 for(u16 i=0; i<26; i++)
1845 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1846 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1848 block->setNode(cp+g_26dirs[i], n);
1855 Create a few rats in empty blocks underground
1859 //for(u16 i=0; i<2; i++)
1862 (rand()%(MAP_BLOCKSIZE-2))+1,
1863 (rand()%(MAP_BLOCKSIZE-2))+1,
1864 (rand()%(MAP_BLOCKSIZE-2))+1
1867 // Check that the place is empty
1868 //if(!is_ground_content(block->getNode(cp).d))
1871 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1872 block->addObject(obj);
1878 Add block to sector.
1880 sector->insertBlock(block);
1883 Do some interpolation for dungeons
1888 TimeTaker timer("interpolation", g_device);
1890 MapVoxelManipulator vmanip(this);
1892 v3s16 relpos = block->getPosRelative();
1894 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1895 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1896 /*vmanip.interpolate(VoxelArea(relpos,
1897 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1899 core::map<v3s16, MapBlock*> modified_blocks;
1900 vmanip.blitBack(modified_blocks);
1901 dstream<<"blitBack modified "<<modified_blocks.size()
1902 <<" blocks"<<std::endl;
1904 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1905 for(core::map<v3s16, MapBlock*>::Iterator
1906 i = modified_blocks.getIterator();
1907 i.atEnd() == false; i++)
1909 MapBlock *block = i.getNode()->getValue();
1911 changed_blocks.insert(block->getPos(), block);
1912 //lighting_invalidated_blocks.insert(block->getPos(), block);
1922 // An y-wise container of changed blocks
1923 core::map<s16, MapBlock*> changed_blocks_sector;
1926 Check if any sector's objects can be placed now.
1929 core::map<v3s16, u8> *objects = sector->getObjects();
1930 core::list<v3s16> objects_to_remove;
1931 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1932 i.atEnd() == false; i++)
1934 v3s16 p = i.getNode()->getKey();
1936 u8 d = i.getNode()->getValue();
1938 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1943 if(d == SECTOR_OBJECT_TEST)
1945 if(sector->isValidArea(p + v3s16(0,0,0),
1946 p + v3s16(0,0,0), &changed_blocks_sector))
1949 n.d = CONTENT_TORCH;
1950 sector->setNode(p, n);
1951 objects_to_remove.push_back(p);
1954 else if(d == SECTOR_OBJECT_TREE_1)
1956 v3s16 p_min = p + v3s16(-1,0,-1);
1957 v3s16 p_max = p + v3s16(1,4,1);
1958 if(sector->isValidArea(p_min, p_max,
1959 &changed_blocks_sector))
1963 sector->setNode(p+v3s16(0,0,0), n);
1964 sector->setNode(p+v3s16(0,1,0), n);
1965 sector->setNode(p+v3s16(0,2,0), n);
1966 sector->setNode(p+v3s16(0,3,0), n);
1968 n.d = CONTENT_LEAVES;
1970 sector->setNode(p+v3s16(0,4,0), n);
1972 sector->setNode(p+v3s16(-1,4,0), n);
1973 sector->setNode(p+v3s16(1,4,0), n);
1974 sector->setNode(p+v3s16(0,4,-1), n);
1975 sector->setNode(p+v3s16(0,4,1), n);
1976 sector->setNode(p+v3s16(1,4,1), n);
1977 sector->setNode(p+v3s16(-1,4,1), n);
1978 sector->setNode(p+v3s16(-1,4,-1), n);
1979 sector->setNode(p+v3s16(1,4,-1), n);
1981 sector->setNode(p+v3s16(-1,3,0), n);
1982 sector->setNode(p+v3s16(1,3,0), n);
1983 sector->setNode(p+v3s16(0,3,-1), n);
1984 sector->setNode(p+v3s16(0,3,1), n);
1985 sector->setNode(p+v3s16(1,3,1), n);
1986 sector->setNode(p+v3s16(-1,3,1), n);
1987 sector->setNode(p+v3s16(-1,3,-1), n);
1988 sector->setNode(p+v3s16(1,3,-1), n);
1990 objects_to_remove.push_back(p);
1992 // Lighting has to be recalculated for this one.
1993 sector->getBlocksInArea(p_min, p_max,
1994 lighting_invalidated_blocks);
1997 else if(d == SECTOR_OBJECT_BUSH_1)
1999 if(sector->isValidArea(p + v3s16(0,0,0),
2000 p + v3s16(0,0,0), &changed_blocks_sector))
2003 n.d = CONTENT_LEAVES;
2004 sector->setNode(p+v3s16(0,0,0), n);
2006 objects_to_remove.push_back(p);
2009 else if(d == SECTOR_OBJECT_RAVINE)
2012 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2013 v3s16 p_max = p + v3s16(6,6,6);
2014 if(sector->isValidArea(p_min, p_max,
2015 &changed_blocks_sector))
2018 n.d = CONTENT_STONE;
2021 s16 depth = maxdepth + (rand()%10);
2023 s16 minz = -6 - (-2);
2025 for(s16 x=-6; x<=6; x++)
2027 z += -1 + (rand()%3);
2032 for(s16 y=depth+(rand()%2); y<=6; y++)
2034 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2037 v3s16 p2 = p + v3s16(x,y,z-2);
2038 if(is_ground_content(sector->getNode(p2).d))
2039 sector->setNode(p2, n);
2042 v3s16 p2 = p + v3s16(x,y,z-1);
2043 if(is_ground_content(sector->getNode(p2).d))
2044 sector->setNode(p2, n2);
2047 v3s16 p2 = p + v3s16(x,y,z+0);
2048 if(is_ground_content(sector->getNode(p2).d))
2049 sector->setNode(p2, n2);
2052 v3s16 p2 = p + v3s16(x,y,z+1);
2053 if(is_ground_content(sector->getNode(p2).d))
2054 sector->setNode(p2, n);
2057 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2058 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2062 objects_to_remove.push_back(p);
2064 // Lighting has to be recalculated for this one.
2065 sector->getBlocksInArea(p_min, p_max,
2066 lighting_invalidated_blocks);
2071 dstream<<"ServerMap::emergeBlock(): "
2072 "Invalid heightmap object"
2077 catch(InvalidPositionException &e)
2079 dstream<<"WARNING: "<<__FUNCTION_NAME
2080 <<": while inserting object "<<(int)d
2081 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2082 <<" InvalidPositionException.what()="
2083 <<e.what()<<std::endl;
2084 // This is not too fatal and seems to happen sometimes.
2089 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2090 i != objects_to_remove.end(); i++)
2092 objects->remove(*i);
2095 for(core::map<s16, MapBlock*>::Iterator
2096 i = changed_blocks_sector.getIterator();
2097 i.atEnd() == false; i++)
2099 MapBlock *block = i.getNode()->getValue();
2101 changed_blocks.insert(block->getPos(), block);
2107 void ServerMap::createDir(std::string path)
2109 if(fs::CreateDir(path) == false)
2111 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2112 <<"\""<<path<<"\""<<std::endl;
2113 throw BaseException("ServerMap failed to create directory");
2117 std::string ServerMap::getSectorSubDir(v2s16 pos)
2120 snprintf(cc, 9, "%.4x%.4x",
2121 (unsigned int)pos.X&0xffff,
2122 (unsigned int)pos.Y&0xffff);
2124 return std::string(cc);
2127 std::string ServerMap::getSectorDir(v2s16 pos)
2129 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2132 v2s16 ServerMap::getSectorPos(std::string dirname)
2134 if(dirname.size() != 8)
2135 throw InvalidFilenameException("Invalid sector directory name");
2137 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2139 throw InvalidFilenameException("Invalid sector directory name");
2140 v2s16 pos((s16)x, (s16)y);
2144 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2146 v2s16 p2d = getSectorPos(sectordir);
2148 if(blockfile.size() != 4){
2149 throw InvalidFilenameException("Invalid block filename");
2152 int r = sscanf(blockfile.c_str(), "%4x", &y);
2154 throw InvalidFilenameException("Invalid block filename");
2155 return v3s16(p2d.X, y, p2d.Y);
2159 #define ENABLE_SECTOR_SAVING 1
2160 #define ENABLE_SECTOR_LOADING 1
2161 #define ENABLE_BLOCK_SAVING 1
2162 #define ENABLE_BLOCK_LOADING 1
2164 void ServerMap::save(bool only_changed)
2166 DSTACK(__FUNCTION_NAME);
2167 if(m_map_saving_enabled == false)
2169 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2173 if(only_changed == false)
2174 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2177 saveMasterHeightmap();
2179 u32 sector_meta_count = 0;
2180 u32 block_count = 0;
2183 JMutexAutoLock lock(m_sector_mutex);
2185 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2186 for(; i.atEnd() == false; i++)
2188 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2189 assert(sector->getId() == MAPSECTOR_SERVER);
2191 if(ENABLE_SECTOR_SAVING)
2193 if(sector->differs_from_disk || only_changed == false)
2195 saveSectorMeta(sector);
2196 sector_meta_count++;
2199 if(ENABLE_BLOCK_SAVING)
2201 core::list<MapBlock*> blocks;
2202 sector->getBlocks(blocks);
2203 core::list<MapBlock*>::Iterator j;
2204 for(j=blocks.begin(); j!=blocks.end(); j++)
2206 MapBlock *block = *j;
2207 if(block->getChangedFlag() || only_changed == false)
2218 u32 deleted_count = 0;
2219 deleted_count = deleteUnusedSectors
2220 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2223 Only print if something happened or saved whole map
2225 if(only_changed == false || sector_meta_count != 0
2226 || block_count != 0 || deleted_count != 0)
2228 dstream<<DTIME<<"ServerMap: Written: "
2229 <<sector_meta_count<<" sector metadata files, "
2230 <<block_count<<" block files, "
2231 <<deleted_count<<" sectors unloaded from memory."
2236 void ServerMap::loadAll()
2238 DSTACK(__FUNCTION_NAME);
2239 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2241 loadMasterHeightmap();
2243 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2245 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2247 JMutexAutoLock lock(m_sector_mutex);
2250 s32 printed_counter = -100000;
2251 s32 count = list.size();
2253 std::vector<fs::DirListNode>::iterator i;
2254 for(i=list.begin(); i!=list.end(); i++)
2256 if(counter > printed_counter + 10)
2258 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2259 printed_counter = counter;
2263 MapSector *sector = NULL;
2265 // We want directories
2269 sector = loadSectorMeta(i->name);
2271 catch(InvalidFilenameException &e)
2273 // This catches unknown crap in directory
2276 if(ENABLE_BLOCK_LOADING)
2278 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2279 (m_savedir+"/sectors/"+i->name);
2280 std::vector<fs::DirListNode>::iterator i2;
2281 for(i2=list2.begin(); i2!=list2.end(); i2++)
2287 loadBlock(i->name, i2->name, sector);
2289 catch(InvalidFilenameException &e)
2291 // This catches unknown crap in directory
2296 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2299 void ServerMap::saveMasterHeightmap()
2301 DSTACK(__FUNCTION_NAME);
2302 createDir(m_savedir);
2304 std::string fullpath = m_savedir + "/master_heightmap";
2305 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2306 if(o.good() == false)
2307 throw FileNotGoodException("Cannot open master heightmap");
2309 // Format used for writing
2310 u8 version = SER_FMT_VER_HIGHEST;
2313 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2315 [0] u8 serialization version
2316 [1] X master heightmap
2318 u32 fullsize = 1 + hmdata.getSize();
2319 SharedBuffer<u8> data(fullsize);
2322 memcpy(&data[1], *hmdata, hmdata.getSize());
2324 o.write((const char*)*data, fullsize);
2327 m_heightmap->serialize(o, version);
2330 void ServerMap::loadMasterHeightmap()
2332 DSTACK(__FUNCTION_NAME);
2333 std::string fullpath = m_savedir + "/master_heightmap";
2334 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2335 if(is.good() == false)
2336 throw FileNotGoodException("Cannot open master heightmap");
2338 if(m_heightmap != NULL)
2341 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2344 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2346 DSTACK(__FUNCTION_NAME);
2347 // Format used for writing
2348 u8 version = SER_FMT_VER_HIGHEST;
2350 v2s16 pos = sector->getPos();
2351 createDir(m_savedir);
2352 createDir(m_savedir+"/sectors");
2353 std::string dir = getSectorDir(pos);
2356 std::string fullpath = dir + "/heightmap";
2357 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2358 if(o.good() == false)
2359 throw FileNotGoodException("Cannot open master heightmap");
2361 sector->serialize(o, version);
2363 sector->differs_from_disk = false;
2366 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2368 DSTACK(__FUNCTION_NAME);
2370 v2s16 p2d = getSectorPos(dirname);
2371 std::string dir = m_savedir + "/sectors/" + dirname;
2373 std::string fullpath = dir + "/heightmap";
2374 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2375 if(is.good() == false)
2376 throw FileNotGoodException("Cannot open sector heightmap");
2378 ServerMapSector *sector = ServerMapSector::deSerialize
2379 (is, this, p2d, &m_hwrapper, m_sectors);
2381 sector->differs_from_disk = false;
2386 bool ServerMap::loadSectorFull(v2s16 p2d)
2388 DSTACK(__FUNCTION_NAME);
2389 std::string sectorsubdir = getSectorSubDir(p2d);
2391 MapSector *sector = NULL;
2393 JMutexAutoLock lock(m_sector_mutex);
2396 sector = loadSectorMeta(sectorsubdir);
2398 catch(InvalidFilenameException &e)
2402 catch(FileNotGoodException &e)
2406 catch(std::exception &e)
2411 if(ENABLE_BLOCK_LOADING)
2413 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2414 (m_savedir+"/sectors/"+sectorsubdir);
2415 std::vector<fs::DirListNode>::iterator i2;
2416 for(i2=list2.begin(); i2!=list2.end(); i2++)
2422 loadBlock(sectorsubdir, i2->name, sector);
2424 catch(InvalidFilenameException &e)
2426 // This catches unknown crap in directory
2434 bool ServerMap::deFlushSector(v2s16 p2d)
2436 DSTACK(__FUNCTION_NAME);
2437 // See if it already exists in memory
2439 MapSector *sector = getSectorNoGenerate(p2d);
2442 catch(InvalidPositionException &e)
2445 Try to load the sector from disk.
2447 if(loadSectorFull(p2d) == true)
2456 void ServerMap::saveBlock(MapBlock *block)
2458 DSTACK(__FUNCTION_NAME);
2460 Dummy blocks are not written
2462 if(block->isDummy())
2464 /*v3s16 p = block->getPos();
2465 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2466 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2470 // Format used for writing
2471 u8 version = SER_FMT_VER_HIGHEST;
2473 v3s16 p3d = block->getPos();
2474 v2s16 p2d(p3d.X, p3d.Z);
2475 createDir(m_savedir);
2476 createDir(m_savedir+"/sectors");
2477 std::string dir = getSectorDir(p2d);
2480 // Block file is map/sectors/xxxxxxxx/xxxx
2482 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2483 std::string fullpath = dir + "/" + cc;
2484 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2485 if(o.good() == false)
2486 throw FileNotGoodException("Cannot open block data");
2489 [0] u8 serialization version
2492 o.write((char*)&version, 1);
2494 block->serialize(o, version);
2497 Versions up from 9 have block objects.
2501 block->serializeObjects(o, version);
2504 // We just wrote it to the disk
2505 block->resetChangedFlag();
2508 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2510 DSTACK(__FUNCTION_NAME);
2514 // Block file is map/sectors/xxxxxxxx/xxxx
2515 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2516 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2517 if(is.good() == false)
2518 throw FileNotGoodException("Cannot open block file");
2520 v3s16 p3d = getBlockPos(sectordir, blockfile);
2521 v2s16 p2d(p3d.X, p3d.Z);
2523 assert(sector->getPos() == p2d);
2525 u8 version = SER_FMT_VER_INVALID;
2526 is.read((char*)&version, 1);
2528 /*u32 block_size = MapBlock::serializedLength(version);
2529 SharedBuffer<u8> data(block_size);
2530 is.read((char*)*data, block_size);*/
2532 // This will always return a sector because we're the server
2533 //MapSector *sector = emergeSector(p2d);
2535 MapBlock *block = NULL;
2536 bool created_new = false;
2538 block = sector->getBlockNoCreate(p3d.Y);
2540 catch(InvalidPositionException &e)
2542 block = sector->createBlankBlockNoInsert(p3d.Y);
2546 // deserialize block data
2547 block->deSerialize(is, version);
2550 Versions up from 9 have block objects.
2554 block->updateObjects(is, version, NULL);
2558 sector->insertBlock(block);
2561 Convert old formats to new and save
2564 // Save old format blocks in new format
2565 if(version < SER_FMT_VER_HIGHEST)
2570 // We just loaded it from the disk, so it's up-to-date.
2571 block->resetChangedFlag();
2574 catch(SerializationError &e)
2576 dstream<<"WARNING: Invalid block data on disk "
2577 "(SerializationError). Ignoring."
2582 // Gets from master heightmap
2583 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2585 assert(m_heightmap != NULL);
2593 corners[0] = m_heightmap->getGroundHeight
2594 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2595 corners[1] = m_heightmap->getGroundHeight
2596 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2597 corners[2] = m_heightmap->getGroundHeight
2598 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2599 corners[3] = m_heightmap->getGroundHeight
2600 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2603 void ServerMap::PrintInfo(std::ostream &out)
2612 ClientMap::ClientMap(
2614 scene::ISceneNode* parent,
2615 scene::ISceneManager* mgr,
2619 scene::ISceneNode(parent, mgr, id),
2625 /*m_box = core::aabbox3d<f32>(0,0,0,
2626 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2627 /*m_box = core::aabbox3d<f32>(0,0,0,
2628 map->getSizeNodes().X * BS,
2629 map->getSizeNodes().Y * BS,
2630 map->getSizeNodes().Z * BS);*/
2631 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2632 BS*1000000,BS*1000000,BS*1000000);
2634 //setPosition(v3f(BS,BS,BS));
2637 ClientMap::~ClientMap()
2639 JMutexAutoLock lock(mesh_mutex);
2648 MapSector * ClientMap::emergeSector(v2s16 p2d)
2650 DSTACK(__FUNCTION_NAME);
2651 // Check that it doesn't exist already
2653 return getSectorNoGenerate(p2d);
2655 catch(InvalidPositionException &e)
2659 // Create a sector with no heightmaps
2660 ClientMapSector *sector = new ClientMapSector(this, p2d);
2663 JMutexAutoLock lock(m_sector_mutex);
2664 m_sectors.insert(p2d, sector);
2670 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2672 DSTACK(__FUNCTION_NAME);
2673 ClientMapSector *sector = NULL;
2675 JMutexAutoLock lock(m_sector_mutex);
2677 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2681 sector = (ClientMapSector*)n->getValue();
2682 assert(sector->getId() == MAPSECTOR_CLIENT);
2686 sector = new ClientMapSector(this, p2d);
2688 JMutexAutoLock lock(m_sector_mutex);
2689 m_sectors.insert(p2d, sector);
2693 sector->deSerialize(is);
2696 void ClientMap::OnRegisterSceneNode()
2700 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2701 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2704 ISceneNode::OnRegisterSceneNode();
2707 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2709 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2710 DSTACK(__FUNCTION_NAME);
2712 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2715 Get time for measuring timeout.
2717 Measuring time is very useful for long delays when the
2718 machine is swapping a lot.
2720 int time1 = time(0);
2723 Collect all blocks that are in the view range
2725 Should not optimize more here as we want to auto-update
2726 all changed nodes in viewing range at the next step.
2729 s16 viewing_range_nodes;
2730 bool viewing_range_all;
2732 JMutexAutoLock lock(g_range_mutex);
2733 viewing_range_nodes = g_viewing_range_nodes;
2734 viewing_range_all = g_viewing_range_all;
2737 m_camera_mutex.Lock();
2738 v3f camera_position = m_camera_position;
2739 v3f camera_direction = m_camera_direction;
2740 m_camera_mutex.Unlock();
2743 Get all blocks and draw all visible ones
2746 v3s16 cam_pos_nodes(
2747 camera_position.X / BS,
2748 camera_position.Y / BS,
2749 camera_position.Z / BS);
2751 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2753 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2754 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2756 // Take a fair amount as we will be dropping more out later
2758 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2759 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2760 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2762 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2763 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2764 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2766 u32 vertex_count = 0;
2768 // For limiting number of mesh updates per frame
2769 u32 mesh_update_count = 0;
2771 //NOTE: The sectors map should be locked but we're not doing it
2772 // because it'd cause too much delays
2774 core::map<v2s16, MapSector*>::Iterator si;
2775 si = m_sectors.getIterator();
2776 for(; si.atEnd() == false; si++)
2779 static int timecheck_counter = 0;
2780 timecheck_counter++;
2781 if(timecheck_counter > 50)
2783 int time2 = time(0);
2784 if(time2 > time1 + 4)
2786 dstream<<"ClientMap::renderMap(): "
2787 "Rendering takes ages, returning."
2794 MapSector *sector = si.getNode()->getValue();
2795 v2s16 sp = sector->getPos();
2797 if(viewing_range_all == false)
2799 if(sp.X < p_blocks_min.X
2800 || sp.X > p_blocks_max.X
2801 || sp.Y < p_blocks_min.Z
2802 || sp.Y > p_blocks_max.Z)
2806 core::list< MapBlock * > sectorblocks;
2807 sector->getBlocks(sectorblocks);
2813 core::list< MapBlock * >::Iterator i;
2814 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2816 MapBlock *block = *i;
2819 Compare block position to camera position, skip
2820 if not seen on display
2823 v3s16 blockpos_nodes = block->getPosRelative();
2825 // Block center position
2827 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2828 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2829 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2832 // Block position relative to camera
2833 v3f blockpos_relative = blockpos - camera_position;
2835 // Distance in camera direction (+=front, -=back)
2836 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2839 f32 d = blockpos_relative.getLength();
2841 if(viewing_range_all == false)
2843 // If block is far away, don't draw it
2844 if(d > viewing_range_nodes * BS)
2845 // This is nicer when fog is used
2846 //if((dforward+d)/2 > viewing_range_nodes * BS)
2850 // Maximum radius of a block
2851 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2853 // If block is (nearly) touching the camera, don't
2854 // bother validating further (that is, render it anyway)
2855 if(d > block_max_radius * 1.5)
2857 // Cosine of the angle between the camera direction
2858 // and the block direction (camera_direction is an unit vector)
2859 f32 cosangle = dforward / d;
2861 // Compensate for the size of the block
2862 // (as the block has to be shown even if it's a bit off FOV)
2863 // This is an estimate.
2864 cosangle += block_max_radius / dforward;
2866 // If block is not in the field of view, skip it
2867 //if(cosangle < cos(FOV_ANGLE/2))
2868 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2873 Draw the faces of the block
2876 bool mesh_expired = false;
2879 JMutexAutoLock lock(block->mesh_mutex);
2881 mesh_expired = block->getMeshExpired();
2883 // Mesh has not been expired and there is no mesh:
2884 // block has no content
2885 if(block->mesh == NULL && mesh_expired == false)
2890 This has to be done with the mesh_mutex unlocked
2892 if(mesh_expired && mesh_update_count < 1)
2894 mesh_update_count++;
2896 // Mesh has been expired: generate new mesh
2897 block->updateMesh(m_client->getDaylightRatio());
2901 JMutexAutoLock lock(block->mesh_mutex);
2903 if(block->mesh == NULL)
2906 u32 c = block->mesh->getMeshBufferCount();
2908 for(u32 i=0; i<c; i++)
2910 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2911 const video::SMaterial& material = buf->getMaterial();
2912 video::IMaterialRenderer* rnd =
2913 driver->getMaterialRenderer(material.MaterialType);
2914 bool transparent = (rnd && rnd->isTransparent());
2915 // Render transparent on transparent pass and likewise.
2916 if(transparent == is_transparent_pass)
2918 driver->setMaterial(buf->getMaterial());
2919 driver->drawMeshBuffer(buf);
2920 vertex_count += buf->getVertexCount();
2924 } // foreach sectorblocks
2927 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2928 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2931 void ClientMap::updateMesh()
2936 void ClientMap::PrintInfo(std::ostream &out)
2946 MapVoxelManipulator::MapVoxelManipulator(Map *map)
2951 MapVoxelManipulator::~MapVoxelManipulator()
2953 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
2958 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
2960 TimeTaker timer1("emerge", g_device, &emerge_time);
2962 // Units of these are MapBlocks
2963 v3s16 p_min = getNodeBlockPos(a.MinEdge);
2964 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
2966 VoxelArea block_area_nodes
2967 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2969 addArea(block_area_nodes);
2971 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2972 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2973 for(s32 x=p_min.X; x<=p_max.X; x++)
2976 core::map<v3s16, bool>::Node *n;
2977 n = m_loaded_blocks.find(p);
2981 bool block_data_inexistent = false;
2984 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
2986 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
2987 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2990 dstream<<std::endl;*/
2992 MapBlock *block = m_map->getBlockNoCreate(p);
2993 if(block->isDummy())
2994 block_data_inexistent = true;
2996 block->copyTo(*this);
2998 catch(InvalidPositionException &e)
3000 block_data_inexistent = true;
3003 if(block_data_inexistent)
3005 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3006 // Fill with VOXELFLAG_INEXISTENT
3007 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3008 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3010 s32 i = m_area.index(a.MinEdge.X,y,z);
3011 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3015 m_loaded_blocks.insert(p, true);
3018 //dstream<<"emerge done"<<std::endl;
3023 void MapVoxelManipulator::emerge(VoxelArea a)
3025 TimeTaker timer1("emerge", g_device, &emerge_time);
3027 v3s16 size = a.getExtent();
3029 VoxelArea padded = a;
3030 padded.pad(m_area.getExtent() / 4);
3033 for(s16 z=0; z<size.Z; z++)
3034 for(s16 y=0; y<size.Y; y++)
3035 for(s16 x=0; x<size.X; x++)
3038 s32 i = m_area.index(a.MinEdge + p);
3039 // Don't touch nodes that have already been loaded
3040 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3044 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3045 MapNode n = m_map->getNode(a.MinEdge + p);
3049 catch(InvalidPositionException &e)
3051 m_flags[i] = VOXELFLAG_INEXISTENT;
3059 TODO: Add an option to only update eg. water and air nodes.
3060 This will make it interfere less with important stuff if
3063 void MapVoxelManipulator::blitBack
3064 (core::map<v3s16, MapBlock*> & modified_blocks)
3066 if(m_area.getExtent() == v3s16(0,0,0))
3069 //TimeTaker timer1("blitBack", g_device);
3072 Initialize block cache
3074 v3s16 blockpos_last;
3075 MapBlock *block = NULL;
3076 bool block_checked_in_modified = false;
3078 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3079 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3080 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3084 u8 f = m_flags[m_area.index(p)];
3085 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3088 MapNode &n = m_data[m_area.index(p)];
3090 v3s16 blockpos = getNodeBlockPos(p);
3095 if(block == NULL || blockpos != blockpos_last){
3096 block = m_map->getBlockNoCreate(blockpos);
3097 blockpos_last = blockpos;
3098 block_checked_in_modified = false;
3101 // Calculate relative position in block
3102 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3104 // Don't continue if nothing has changed here
3105 if(block->getNode(relpos) == n)
3108 //m_map->setNode(m_area.MinEdge + p, n);
3109 block->setNode(relpos, n);
3112 Make sure block is in modified_blocks
3114 if(block_checked_in_modified == false)
3116 modified_blocks[blockpos] = block;
3117 block_checked_in_modified = true;
3120 catch(InvalidPositionException &e)