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"
33 Map::Map(std::ostream &dout):
35 m_camera_position(0,0,0),
36 m_camera_direction(0,0,1),
40 m_sector_mutex.Init();
41 m_camera_mutex.Init();
42 assert(m_sector_mutex.IsInitialized());
43 assert(m_camera_mutex.IsInitialized());
45 // Get this so that the player can stay on it at first
46 //getSector(v2s16(0,0));
54 /*updater.setRun(false);
55 while(updater.IsRunning())
61 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
62 for(; i.atEnd() == false; i++)
64 MapSector *sector = i.getNode()->getValue();
69 MapSector * Map::getSectorNoGenerate(v2s16 p)
71 JMutexAutoLock lock(m_sector_mutex);
73 if(m_sector_cache != NULL && p == m_sector_cache_p){
74 MapSector * sector = m_sector_cache;
75 // Reset inactivity timer
76 sector->usage_timer = 0.0;
80 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
81 // If sector doesn't exist, throw an exception
84 throw InvalidPositionException();
87 MapSector *sector = n->getValue();
89 // Cache the last result
91 m_sector_cache = sector;
93 //MapSector * ref(sector);
95 // Reset inactivity timer
96 sector->usage_timer = 0.0;
100 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
102 v2s16 p2d(p3d.X, p3d.Z);
103 MapSector * sector = getSectorNoGenerate(p2d);
105 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
110 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
114 v2s16 p2d(p3d.X, p3d.Z);
115 MapSector * sector = getSectorNoGenerate(p2d);
116 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
119 catch(InvalidPositionException &e)
125 f32 Map::getGroundHeight(v2s16 p, bool generate)
128 v2s16 sectorpos = getNodeSectorPos(p);
129 MapSector * sref = getSectorNoGenerate(sectorpos);
130 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
131 f32 y = sref->getGroundHeight(relpos);
134 catch(InvalidPositionException &e)
136 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
140 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
142 /*m_dout<<DTIME<<"Map::setGroundHeight(("
144 <<"), "<<y<<")"<<std::endl;*/
145 v2s16 sectorpos = getNodeSectorPos(p);
146 MapSector * sref = getSectorNoGenerate(sectorpos);
147 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
148 //sref->mutex.Lock();
149 sref->setGroundHeight(relpos, y);
150 //sref->mutex.Unlock();
153 bool Map::isNodeUnderground(v3s16 p)
155 v3s16 blockpos = getNodeBlockPos(p);
157 MapBlock * block = getBlockNoCreate(blockpos);
158 return block->getIsUnderground();
160 catch(InvalidPositionException &e)
167 Goes recursively through the neighbours of the node.
169 Alters only transparent nodes.
171 If the lighting of the neighbour is lower than the lighting of
172 the node was (before changing it to 0 at the step before), the
173 lighting of the neighbour is set to 0 and then the same stuff
174 repeats for the neighbour.
176 The ending nodes of the routine are stored in light_sources.
177 This is useful when a light is removed. In such case, this
178 routine can be called for the light node and then again for
179 light_sources to re-light the area without the removed light.
181 values of from_nodes are lighting values.
183 void Map::unspreadLight(enum LightBank bank,
184 core::map<v3s16, u8> & from_nodes,
185 core::map<v3s16, bool> & light_sources,
186 core::map<v3s16, MapBlock*> & modified_blocks)
189 v3s16(0,0,1), // back
191 v3s16(1,0,0), // right
192 v3s16(0,0,-1), // front
193 v3s16(0,-1,0), // bottom
194 v3s16(-1,0,0), // left
197 if(from_nodes.size() == 0)
200 u32 blockchangecount = 0;
202 core::map<v3s16, u8> unlighted_nodes;
203 core::map<v3s16, u8>::Iterator j;
204 j = from_nodes.getIterator();
207 Initialize block cache
210 MapBlock *block = NULL;
211 // Cache this a bit, too
212 bool block_checked_in_modified = false;
214 for(; j.atEnd() == false; j++)
216 v3s16 pos = j.getNode()->getKey();
217 v3s16 blockpos = getNodeBlockPos(pos);
219 // Only fetch a new block if the block position has changed
221 if(block == NULL || blockpos != blockpos_last){
222 block = getBlockNoCreate(blockpos);
223 blockpos_last = blockpos;
225 block_checked_in_modified = false;
229 catch(InvalidPositionException &e)
237 // Calculate relative position in block
238 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
240 // Get node straight from the block
241 MapNode n = block->getNode(relpos);
243 u8 oldlight = j.getNode()->getValue();
245 // Loop through 6 neighbors
246 for(u16 i=0; i<6; i++)
248 // Get the position of the neighbor node
249 v3s16 n2pos = pos + dirs[i];
251 // Get the block where the node is located
252 v3s16 blockpos = getNodeBlockPos(n2pos);
256 // Only fetch a new block if the block position has changed
258 if(block == NULL || blockpos != blockpos_last){
259 block = getBlockNoCreate(blockpos);
260 blockpos_last = blockpos;
262 block_checked_in_modified = false;
266 catch(InvalidPositionException &e)
271 // Calculate relative position in block
272 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
273 // Get node straight from the block
274 MapNode n2 = block->getNode(relpos);
276 bool changed = false;
278 //TODO: Optimize output by optimizing light_sources?
281 If the neighbor is dimmer than what was specified
282 as oldlight (the light of the previous node)
284 if(n2.getLight(bank) < oldlight)
287 And the neighbor is transparent and it has some light
289 if(n2.light_propagates() && n2.getLight(bank) != 0)
292 Set light to 0 and add to queue
295 u8 current_light = n2.getLight(bank);
296 n2.setLight(bank, 0);
297 block->setNode(relpos, n2);
299 unlighted_nodes.insert(n2pos, current_light);
303 Remove from light_sources if it is there
304 NOTE: This doesn't happen nearly at all
306 /*if(light_sources.find(n2pos))
308 std::cout<<"Removed from light_sources"<<std::endl;
309 light_sources.remove(n2pos);
314 if(light_sources.find(n2pos) != NULL)
315 light_sources.remove(n2pos);*/
318 light_sources.insert(n2pos, true);
321 // Add to modified_blocks
322 if(changed == true && block_checked_in_modified == false)
324 // If the block is not found in modified_blocks, add.
325 if(modified_blocks.find(blockpos) == NULL)
327 modified_blocks.insert(blockpos, block);
329 block_checked_in_modified = true;
332 catch(InvalidPositionException &e)
339 /*dstream<<"unspreadLight(): Changed block "
340 <<blockchangecount<<" times"
341 <<" for "<<from_nodes.size()<<" nodes"
344 if(unlighted_nodes.size() > 0)
345 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
349 A single-node wrapper of the above
351 void Map::unLightNeighbors(enum LightBank bank,
352 v3s16 pos, u8 lightwas,
353 core::map<v3s16, bool> & light_sources,
354 core::map<v3s16, MapBlock*> & modified_blocks)
356 core::map<v3s16, u8> from_nodes;
357 from_nodes.insert(pos, lightwas);
359 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
363 Lights neighbors of from_nodes, collects all them and then
366 void Map::spreadLight(enum LightBank bank,
367 core::map<v3s16, bool> & from_nodes,
368 core::map<v3s16, MapBlock*> & modified_blocks)
370 const v3s16 dirs[6] = {
371 v3s16(0,0,1), // back
373 v3s16(1,0,0), // right
374 v3s16(0,0,-1), // front
375 v3s16(0,-1,0), // bottom
376 v3s16(-1,0,0), // left
379 if(from_nodes.size() == 0)
382 u32 blockchangecount = 0;
384 core::map<v3s16, bool> lighted_nodes;
385 core::map<v3s16, bool>::Iterator j;
386 j = from_nodes.getIterator();
389 Initialize block cache
392 MapBlock *block = NULL;
393 // Cache this a bit, too
394 bool block_checked_in_modified = false;
396 for(; j.atEnd() == false; j++)
397 //for(; j != from_nodes.end(); j++)
399 v3s16 pos = j.getNode()->getKey();
401 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
402 v3s16 blockpos = getNodeBlockPos(pos);
404 // Only fetch a new block if the block position has changed
406 if(block == NULL || blockpos != blockpos_last){
407 block = getBlockNoCreate(blockpos);
408 blockpos_last = blockpos;
410 block_checked_in_modified = false;
414 catch(InvalidPositionException &e)
422 // Calculate relative position in block
423 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
425 // Get node straight from the block
426 MapNode n = block->getNode(relpos);
428 u8 oldlight = n.getLight(bank);
429 u8 newlight = diminish_light(oldlight);
431 // Loop through 6 neighbors
432 for(u16 i=0; i<6; i++){
433 // Get the position of the neighbor node
434 v3s16 n2pos = pos + dirs[i];
436 // Get the block where the node is located
437 v3s16 blockpos = getNodeBlockPos(n2pos);
441 // Only fetch a new block if the block position has changed
443 if(block == NULL || blockpos != blockpos_last){
444 block = getBlockNoCreate(blockpos);
445 blockpos_last = blockpos;
447 block_checked_in_modified = false;
451 catch(InvalidPositionException &e)
456 // Calculate relative position in block
457 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
458 // Get node straight from the block
459 MapNode n2 = block->getNode(relpos);
461 bool changed = false;
463 If the neighbor is brighter than the current node,
464 add to list (it will light up this node on its turn)
466 if(n2.getLight(bank) > undiminish_light(oldlight))
468 lighted_nodes.insert(n2pos, true);
469 //lighted_nodes.push_back(n2pos);
473 If the neighbor is dimmer than how much light this node
474 would spread on it, add to list
476 if(n2.getLight(bank) < newlight)
478 if(n2.light_propagates())
480 n2.setLight(bank, newlight);
481 block->setNode(relpos, n2);
482 lighted_nodes.insert(n2pos, true);
483 //lighted_nodes.push_back(n2pos);
488 // Add to modified_blocks
489 if(changed == true && block_checked_in_modified == false)
491 // If the block is not found in modified_blocks, add.
492 if(modified_blocks.find(blockpos) == NULL)
494 modified_blocks.insert(blockpos, block);
496 block_checked_in_modified = true;
499 catch(InvalidPositionException &e)
506 /*dstream<<"spreadLight(): Changed block "
507 <<blockchangecount<<" times"
508 <<" for "<<from_nodes.size()<<" nodes"
511 if(lighted_nodes.size() > 0)
512 spreadLight(bank, lighted_nodes, modified_blocks);
516 A single-node source variation of the above.
518 void Map::lightNeighbors(enum LightBank bank,
520 core::map<v3s16, MapBlock*> & modified_blocks)
522 core::map<v3s16, bool> from_nodes;
523 from_nodes.insert(pos, true);
524 spreadLight(bank, from_nodes, modified_blocks);
527 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
530 v3s16(0,0,1), // back
532 v3s16(1,0,0), // right
533 v3s16(0,0,-1), // front
534 v3s16(0,-1,0), // bottom
535 v3s16(-1,0,0), // left
538 u8 brightest_light = 0;
539 v3s16 brightest_pos(0,0,0);
540 bool found_something = false;
542 // Loop through 6 neighbors
543 for(u16 i=0; i<6; i++){
544 // Get the position of the neighbor node
545 v3s16 n2pos = p + dirs[i];
550 catch(InvalidPositionException &e)
554 if(n2.getLight(bank) > brightest_light || found_something == false){
555 brightest_light = n2.getLight(bank);
556 brightest_pos = n2pos;
557 found_something = true;
561 if(found_something == false)
562 throw InvalidPositionException();
564 return brightest_pos;
568 Propagates sunlight down from a node.
569 Starting point gets sunlight.
571 Returns the lowest y value of where the sunlight went.
573 Mud is turned into grass in where the sunlight stops.
575 s16 Map::propagateSunlight(v3s16 start,
576 core::map<v3s16, MapBlock*> & modified_blocks)
581 v3s16 pos(start.X, y, start.Z);
583 v3s16 blockpos = getNodeBlockPos(pos);
586 block = getBlockNoCreate(blockpos);
588 catch(InvalidPositionException &e)
593 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
594 MapNode n = block->getNode(relpos);
596 if(n.sunlight_propagates())
598 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
599 block->setNode(relpos, n);
601 modified_blocks.insert(blockpos, block);
605 // Turn mud into grass
606 if(n.d == CONTENT_MUD)
609 block->setNode(relpos, n);
610 modified_blocks.insert(blockpos, block);
613 // Sunlight goes no further
620 void Map::updateLighting(enum LightBank bank,
621 core::map<v3s16, MapBlock*> & a_blocks,
622 core::map<v3s16, MapBlock*> & modified_blocks)
624 /*m_dout<<DTIME<<"Map::updateLighting(): "
625 <<a_blocks.getSize()<<" blocks... ";*/
629 u32 count_was = modified_blocks.size();
631 core::map<v3s16, bool> light_sources;
633 core::map<v3s16, u8> unlight_from;
635 core::map<v3s16, MapBlock*>::Iterator i;
636 i = a_blocks.getIterator();
637 for(; i.atEnd() == false; i++)
639 MapBlock *block = i.getNode()->getValue();
643 // Don't bother with dummy blocks.
647 v3s16 pos = block->getPos();
648 modified_blocks.insert(pos, block);
651 Clear all light from block
653 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
654 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
655 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
660 MapNode n = block->getNode(v3s16(x,y,z));
661 u8 oldlight = n.getLight(bank);
663 block->setNode(v3s16(x,y,z), n);
665 // Collect borders for unlighting
666 if(x==0 || x == MAP_BLOCKSIZE-1
667 || y==0 || y == MAP_BLOCKSIZE-1
668 || z==0 || z == MAP_BLOCKSIZE-1)
670 v3s16 p_map = p + v3s16(
673 MAP_BLOCKSIZE*pos.Z);
674 unlight_from.insert(p_map, oldlight);
677 catch(InvalidPositionException &e)
680 This would happen when dealing with a
684 dstream<<"updateLighting(): InvalidPositionException"
689 if(bank == LIGHTBANK_DAY)
691 bool bottom_valid = block->propagateSunlight(light_sources);
693 // If bottom is valid, we're done.
697 else if(bank == LIGHTBANK_NIGHT)
706 /*dstream<<"Bottom for sunlight-propagated block ("
707 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
710 // Else get the block below and loop to it
714 block = getBlockNoCreate(pos);
716 catch(InvalidPositionException &e)
725 //TimeTaker timer("unspreadLight");
726 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
731 u32 diff = modified_blocks.size() - count_was;
732 count_was = modified_blocks.size();
733 dstream<<"unspreadLight modified "<<diff<<std::endl;
736 // TODO: Spread light from propagated sunlight?
737 // Yes, add it to light_sources... somehow.
738 // It has to be added at somewhere above, in the loop.
740 // NOTE: This actually works fine without doing so
741 // - Find out why it works
744 //TimeTaker timer("spreadLight");
745 spreadLight(bank, light_sources, modified_blocks);
750 u32 diff = modified_blocks.size() - count_was;
751 count_was = modified_blocks.size();
752 dstream<<"spreadLight modified "<<diff<<std::endl;
755 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
758 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
759 core::map<v3s16, MapBlock*> & modified_blocks)
761 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
762 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
765 Update information about whether day and night light differ
767 for(core::map<v3s16, MapBlock*>::Iterator
768 i = modified_blocks.getIterator();
769 i.atEnd() == false; i++)
771 MapBlock *block = i.getNode()->getValue();
772 block->updateDayNightDiff();
777 This is called after changing a node from transparent to opaque.
778 The lighting value of the node should be left as-is after changing
779 other values. This sets the lighting value to 0.
781 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
782 core::map<v3s16, MapBlock*> &modified_blocks)*/
783 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
784 core::map<v3s16, MapBlock*> &modified_blocks)
787 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
788 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
791 From this node to nodes underneath:
792 If lighting is sunlight (1.0), unlight neighbours and
797 v3s16 toppos = p + v3s16(0,1,0);
798 v3s16 bottompos = p + v3s16(0,-1,0);
800 bool node_under_sunlight = true;
801 core::map<v3s16, bool> light_sources;
804 If there is a node at top and it doesn't have sunlight,
805 there has not been any sunlight going down.
807 Otherwise there probably is.
810 MapNode topnode = getNode(toppos);
812 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
813 node_under_sunlight = false;
815 catch(InvalidPositionException &e)
819 if(n.d != CONTENT_TORCH)
822 If there is grass below, change it to mud
825 MapNode bottomnode = getNode(bottompos);
827 if(bottomnode.d == CONTENT_GRASS
828 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
830 bottomnode.d = CONTENT_MUD;
831 setNode(bottompos, bottomnode);
834 catch(InvalidPositionException &e)
839 enum LightBank banks[] =
844 for(s32 i=0; i<2; i++)
846 enum LightBank bank = banks[i];
848 u8 lightwas = getNode(p).getLight(bank);
850 // Add the block of the added node to modified_blocks
851 v3s16 blockpos = getNodeBlockPos(p);
852 MapBlock * block = getBlockNoCreate(blockpos);
853 assert(block != NULL);
854 modified_blocks.insert(blockpos, block);
856 if(isValidPosition(p) == false)
859 // Unlight neighbours of node.
860 // This means setting light of all consequent dimmer nodes
862 // This also collects the nodes at the border which will spread
863 // light again into this.
864 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
872 If node is under sunlight, take all sunlighted nodes under
873 it and clear light from them and from where the light has
875 TODO: This could be optimized by mass-unlighting instead
878 if(node_under_sunlight)
882 //m_dout<<DTIME<<"y="<<y<<std::endl;
883 v3s16 n2pos(p.X, y, p.Z);
889 catch(InvalidPositionException &e)
894 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
896 //m_dout<<DTIME<<"doing"<<std::endl;
897 unLightNeighbors(LIGHTBANK_DAY,
898 n2pos, n2.getLight(LIGHTBANK_DAY),
899 light_sources, modified_blocks);
900 n2.setLight(LIGHTBANK_DAY, 0);
908 for(s32 i=0; i<2; i++)
910 enum LightBank bank = banks[i];
913 Spread light from all nodes that might be capable of doing so
914 TODO: Convert to spreadLight
916 spreadLight(bank, light_sources, modified_blocks);
920 Update information about whether day and night light differ
922 for(core::map<v3s16, MapBlock*>::Iterator
923 i = modified_blocks.getIterator();
924 i.atEnd() == false; i++)
926 MapBlock *block = i.getNode()->getValue();
927 block->updateDayNightDiff();
931 Add neighboring liquid nodes and the node itself if it is
932 liquid (=water node was added) to transform queue.
935 v3s16(0,0,0), // self
936 v3s16(0,0,1), // back
938 v3s16(1,0,0), // right
939 v3s16(0,0,-1), // front
940 v3s16(0,-1,0), // bottom
941 v3s16(-1,0,0), // left
943 for(u16 i=0; i<7; i++)
948 v3s16 p2 = p + dirs[i];
950 MapNode n2 = getNode(p2);
951 if(content_liquid(n2.d))
953 m_transforming_liquid.push_back(p2);
956 }catch(InvalidPositionException &e)
964 void Map::removeNodeAndUpdate(v3s16 p,
965 core::map<v3s16, MapBlock*> &modified_blocks)
968 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
969 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
971 bool node_under_sunlight = true;
973 v3s16 toppos = p + v3s16(0,1,0);
975 // Node will be replaced with this
976 u8 replace_material = CONTENT_AIR;
979 If there is a node at top and it doesn't have sunlight,
980 there will be no sunlight going down.
983 MapNode topnode = getNode(toppos);
985 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
986 node_under_sunlight = false;
988 catch(InvalidPositionException &e)
992 core::map<v3s16, bool> light_sources;
994 enum LightBank banks[] =
999 for(s32 i=0; i<2; i++)
1001 enum LightBank bank = banks[i];
1004 Unlight neighbors (in case the node is a light source)
1006 unLightNeighbors(bank, p,
1007 getNode(p).getLight(bank),
1008 light_sources, modified_blocks);
1013 This also clears the lighting.
1017 n.d = replace_material;
1020 for(s32 i=0; i<2; i++)
1022 enum LightBank bank = banks[i];
1025 Recalculate lighting
1027 spreadLight(bank, light_sources, modified_blocks);
1030 // Add the block of the removed node to modified_blocks
1031 v3s16 blockpos = getNodeBlockPos(p);
1032 MapBlock * block = getBlockNoCreate(blockpos);
1033 assert(block != NULL);
1034 modified_blocks.insert(blockpos, block);
1037 If the removed node was under sunlight, propagate the
1038 sunlight down from it and then light all neighbors
1039 of the propagated blocks.
1041 if(node_under_sunlight)
1043 s16 ybottom = propagateSunlight(p, modified_blocks);
1044 /*m_dout<<DTIME<<"Node was under sunlight. "
1045 "Propagating sunlight";
1046 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1048 for(; y >= ybottom; y--)
1050 v3s16 p2(p.X, y, p.Z);
1051 /*m_dout<<DTIME<<"lighting neighbors of node ("
1052 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1054 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1059 // Set the lighting of this node to 0
1060 // TODO: Is this needed? Lighting is cleared up there already.
1062 MapNode n = getNode(p);
1063 n.setLight(LIGHTBANK_DAY, 0);
1066 catch(InvalidPositionException &e)
1072 for(s32 i=0; i<2; i++)
1074 enum LightBank bank = banks[i];
1076 // Get the brightest neighbour node and propagate light from it
1077 v3s16 n2p = getBrightestNeighbour(bank, p);
1079 MapNode n2 = getNode(n2p);
1080 lightNeighbors(bank, n2p, modified_blocks);
1082 catch(InvalidPositionException &e)
1088 Update information about whether day and night light differ
1090 for(core::map<v3s16, MapBlock*>::Iterator
1091 i = modified_blocks.getIterator();
1092 i.atEnd() == false; i++)
1094 MapBlock *block = i.getNode()->getValue();
1095 block->updateDayNightDiff();
1099 Add neighboring liquid nodes to transform queue.
1102 v3s16(0,0,1), // back
1103 v3s16(0,1,0), // top
1104 v3s16(1,0,0), // right
1105 v3s16(0,0,-1), // front
1106 v3s16(0,-1,0), // bottom
1107 v3s16(-1,0,0), // left
1109 for(u16 i=0; i<6; i++)
1114 v3s16 p2 = p + dirs[i];
1116 MapNode n2 = getNode(p2);
1117 if(content_liquid(n2.d))
1119 m_transforming_liquid.push_back(p2);
1122 }catch(InvalidPositionException &e)
1129 void Map::expireMeshes(bool only_daynight_diffed)
1131 TimeTaker timer("expireMeshes()");
1133 core::map<v2s16, MapSector*>::Iterator si;
1134 si = m_sectors.getIterator();
1135 for(; si.atEnd() == false; si++)
1137 MapSector *sector = si.getNode()->getValue();
1139 core::list< MapBlock * > sectorblocks;
1140 sector->getBlocks(sectorblocks);
1142 core::list< MapBlock * >::Iterator i;
1143 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1145 MapBlock *block = *i;
1147 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1153 JMutexAutoLock lock(block->mesh_mutex);
1154 if(block->mesh != NULL)
1156 /*block->mesh->drop();
1157 block->mesh = NULL;*/
1158 block->setMeshExpired(true);
1165 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1167 assert(mapType() == MAPTYPE_CLIENT);
1170 v3s16 p = blockpos + v3s16(0,0,0);
1171 MapBlock *b = getBlockNoCreate(p);
1172 b->updateMesh(daynight_ratio);
1174 catch(InvalidPositionException &e){}
1177 v3s16 p = blockpos + v3s16(-1,0,0);
1178 MapBlock *b = getBlockNoCreate(p);
1179 b->updateMesh(daynight_ratio);
1181 catch(InvalidPositionException &e){}
1183 v3s16 p = blockpos + v3s16(0,-1,0);
1184 MapBlock *b = getBlockNoCreate(p);
1185 b->updateMesh(daynight_ratio);
1187 catch(InvalidPositionException &e){}
1189 v3s16 p = blockpos + v3s16(0,0,-1);
1190 MapBlock *b = getBlockNoCreate(p);
1191 b->updateMesh(daynight_ratio);
1193 catch(InvalidPositionException &e){}
1196 v3s16 p = blockpos + v3s16(1,0,0);
1197 MapBlock *b = getBlockNoCreate(p);
1198 b->updateMesh(daynight_ratio);
1200 catch(InvalidPositionException &e){}
1202 v3s16 p = blockpos + v3s16(0,1,0);
1203 MapBlock *b = getBlockNoCreate(p);
1204 b->updateMesh(daynight_ratio);
1206 catch(InvalidPositionException &e){}
1208 v3s16 p = blockpos + v3s16(0,0,1);
1209 MapBlock *b = getBlockNoCreate(p);
1210 b->updateMesh(daynight_ratio);
1212 catch(InvalidPositionException &e){}*/
1217 bool Map::dayNightDiffed(v3s16 blockpos)
1220 v3s16 p = blockpos + v3s16(0,0,0);
1221 MapBlock *b = getBlockNoCreate(p);
1222 if(b->dayNightDiffed())
1225 catch(InvalidPositionException &e){}
1228 v3s16 p = blockpos + v3s16(-1,0,0);
1229 MapBlock *b = getBlockNoCreate(p);
1230 if(b->dayNightDiffed())
1233 catch(InvalidPositionException &e){}
1235 v3s16 p = blockpos + v3s16(0,-1,0);
1236 MapBlock *b = getBlockNoCreate(p);
1237 if(b->dayNightDiffed())
1240 catch(InvalidPositionException &e){}
1242 v3s16 p = blockpos + v3s16(0,0,-1);
1243 MapBlock *b = getBlockNoCreate(p);
1244 if(b->dayNightDiffed())
1247 catch(InvalidPositionException &e){}
1250 v3s16 p = blockpos + v3s16(1,0,0);
1251 MapBlock *b = getBlockNoCreate(p);
1252 if(b->dayNightDiffed())
1255 catch(InvalidPositionException &e){}
1257 v3s16 p = blockpos + v3s16(0,1,0);
1258 MapBlock *b = getBlockNoCreate(p);
1259 if(b->dayNightDiffed())
1262 catch(InvalidPositionException &e){}
1264 v3s16 p = blockpos + v3s16(0,0,1);
1265 MapBlock *b = getBlockNoCreate(p);
1266 if(b->dayNightDiffed())
1269 catch(InvalidPositionException &e){}
1275 Updates usage timers
1277 void Map::timerUpdate(float dtime)
1279 JMutexAutoLock lock(m_sector_mutex);
1281 core::map<v2s16, MapSector*>::Iterator si;
1283 si = m_sectors.getIterator();
1284 for(; si.atEnd() == false; si++)
1286 MapSector *sector = si.getNode()->getValue();
1287 sector->usage_timer += dtime;
1291 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1294 Wait for caches to be removed before continuing.
1296 This disables the existence of caches while locked
1298 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1300 core::list<v2s16>::Iterator j;
1301 for(j=list.begin(); j!=list.end(); j++)
1303 MapSector *sector = m_sectors[*j];
1306 sector->deleteBlocks();
1311 If sector is in sector cache, remove it from there
1313 if(m_sector_cache == sector)
1315 m_sector_cache = NULL;
1318 Remove from map and delete
1320 m_sectors.remove(*j);
1326 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1327 core::list<v3s16> *deleted_blocks)
1329 JMutexAutoLock lock(m_sector_mutex);
1331 core::list<v2s16> sector_deletion_queue;
1332 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1333 for(; i.atEnd() == false; i++)
1335 MapSector *sector = i.getNode()->getValue();
1337 Delete sector from memory if it hasn't been used in a long time
1339 if(sector->usage_timer > timeout)
1341 sector_deletion_queue.push_back(i.getNode()->getKey());
1343 if(deleted_blocks != NULL)
1345 // Collect positions of blocks of sector
1346 MapSector *sector = i.getNode()->getValue();
1347 core::list<MapBlock*> blocks;
1348 sector->getBlocks(blocks);
1349 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1350 i != blocks.end(); i++)
1352 deleted_blocks->push_back((*i)->getPos());
1357 deleteSectors(sector_deletion_queue, only_blocks);
1358 return sector_deletion_queue.getSize();
1361 void Map::PrintInfo(std::ostream &out)
1366 #define WATER_DROP_BOOST 4
1368 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1370 DSTACK(__FUNCTION_NAME);
1371 //TimeTaker timer("transformLiquids()");
1374 u32 initial_size = m_transforming_liquid.size();
1376 while(m_transforming_liquid.size() != 0)
1379 Get a queued transforming liquid node
1381 v3s16 p0 = m_transforming_liquid.pop_front();
1383 MapNode n0 = getNode(p0);
1385 // Don't deal with non-liquids
1386 if(content_liquid(n0.d) == false)
1389 bool is_source = !content_flowing_liquid(n0.d);
1391 u8 liquid_level = 8;
1392 if(is_source == false)
1393 liquid_level = n0.param2 & 0x0f;
1395 // Turn possible source into non-source
1396 u8 nonsource_c = make_liquid_flowing(n0.d);
1399 If not source, check that some node flows into this one
1400 and what is the level of liquid in this one
1402 if(is_source == false)
1404 s8 new_liquid_level_max = -1;
1406 v3s16 dirs_from[5] = {
1407 v3s16(0,1,0), // top
1408 v3s16(0,0,1), // back
1409 v3s16(1,0,0), // right
1410 v3s16(0,0,-1), // front
1411 v3s16(-1,0,0), // left
1413 for(u16 i=0; i<5; i++)
1418 bool from_top = (i==0);
1420 v3s16 p2 = p0 + dirs_from[i];
1421 MapNode n2 = getNode(p2);
1423 if(content_liquid(n2.d))
1425 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1426 // Check that the liquids are the same type
1427 if(n2_nonsource_c != nonsource_c)
1429 dstream<<"WARNING: Not handling: different liquids"
1430 " collide"<<std::endl;
1433 bool n2_is_source = !content_flowing_liquid(n2.d);
1434 s8 n2_liquid_level = 8;
1435 if(n2_is_source == false)
1436 n2_liquid_level = n2.param2 & 0x07;
1438 s8 new_liquid_level = -1;
1441 //new_liquid_level = 7;
1442 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1443 new_liquid_level = 7;
1445 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1447 else if(n2_liquid_level > 0)
1449 new_liquid_level = n2_liquid_level - 1;
1452 if(new_liquid_level > new_liquid_level_max)
1453 new_liquid_level_max = new_liquid_level;
1456 }catch(InvalidPositionException &e)
1462 If liquid level should be something else, update it and
1463 add all the neighboring water nodes to the transform queue.
1465 if(new_liquid_level_max != liquid_level)
1467 if(new_liquid_level_max == -1)
1469 // Remove water alltoghether
1476 n0.param2 = new_liquid_level_max;
1480 // Block has been modified
1482 v3s16 blockpos = getNodeBlockPos(p0);
1483 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1485 modified_blocks.insert(blockpos, block);
1489 Add neighboring non-source liquid nodes to transform queue.
1492 v3s16(0,0,1), // back
1493 v3s16(0,1,0), // top
1494 v3s16(1,0,0), // right
1495 v3s16(0,0,-1), // front
1496 v3s16(0,-1,0), // bottom
1497 v3s16(-1,0,0), // left
1499 for(u16 i=0; i<6; i++)
1504 v3s16 p2 = p0 + dirs[i];
1506 MapNode n2 = getNode(p2);
1507 if(content_flowing_liquid(n2.d))
1509 m_transforming_liquid.push_back(p2);
1512 }catch(InvalidPositionException &e)
1519 // Get a new one from queue if the node has turned into non-water
1520 if(content_liquid(n0.d) == false)
1524 Flow water from this node
1526 v3s16 dirs_to[5] = {
1527 v3s16(0,-1,0), // bottom
1528 v3s16(0,0,1), // back
1529 v3s16(1,0,0), // right
1530 v3s16(0,0,-1), // front
1531 v3s16(-1,0,0), // left
1533 for(u16 i=0; i<5; i++)
1538 bool to_bottom = (i == 0);
1540 // If liquid is at lowest possible height, it's not going
1541 // anywhere except down
1542 if(liquid_level == 0 && to_bottom == false)
1545 u8 liquid_next_level = 0;
1546 // If going to bottom
1549 //liquid_next_level = 7;
1550 if(liquid_level >= 7 - WATER_DROP_BOOST)
1551 liquid_next_level = 7;
1553 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1556 liquid_next_level = liquid_level - 1;
1558 bool n2_changed = false;
1559 bool flowed = false;
1561 v3s16 p2 = p0 + dirs_to[i];
1563 MapNode n2 = getNode(p2);
1564 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1566 if(content_liquid(n2.d))
1568 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1569 // Check that the liquids are the same type
1570 if(n2_nonsource_c != nonsource_c)
1572 dstream<<"WARNING: Not handling: different liquids"
1573 " collide"<<std::endl;
1576 bool n2_is_source = !content_flowing_liquid(n2.d);
1577 u8 n2_liquid_level = 8;
1578 if(n2_is_source == false)
1579 n2_liquid_level = n2.param2 & 0x07;
1588 // Just flow into the source, nothing changes.
1589 // n2_changed is not set because destination didn't change
1594 if(liquid_next_level > liquid_level)
1596 n2.param2 = liquid_next_level;
1604 else if(n2.d == CONTENT_AIR)
1607 n2.param2 = liquid_next_level;
1614 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1618 m_transforming_liquid.push_back(p2);
1620 v3s16 blockpos = getNodeBlockPos(p2);
1621 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1623 modified_blocks.insert(blockpos, block);
1626 // If n2_changed to bottom, don't flow anywhere else
1627 if(to_bottom && flowed && !is_source)
1630 }catch(InvalidPositionException &e)
1636 //if(loopcount >= 100000)
1637 if(loopcount >= initial_size * 1)
1640 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1647 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1652 Experimental and debug stuff
1656 dstream<<"Generating map point attribute lists"<<std::endl;
1658 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1659 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1660 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1661 PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1662 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1665 NOTE: BEWARE: Too big amount of these will make map generation
1666 slow. Especially those that are read by every block emerge.
1674 for(u32 i=0; i<5000; i++)
1676 /*u32 lim = MAP_GENERATION_LIMIT;
1680 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1683 -lim + myrand()%(lim*2),
1685 -lim + myrand()%(lim*2)
1687 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1688 plants_amount = pow(plants_amount, 5);
1689 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1691 float plants_amount = 0;
1694 plants_amount = 1.5;
1696 else if(myrand()%4 == 0)
1698 plants_amount = 0.5;
1700 else if(myrand()%2 == 0)
1702 plants_amount = 0.03;
1706 plants_amount = 0.0;
1710 list_plants_amount->addPoint(p, Attribute(plants_amount));
1713 for(u32 i=0; i<1000; i++)
1715 /*u32 lim = MAP_GENERATION_LIMIT;
1719 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000;
1722 -lim + myrand()%(lim*2),
1724 -lim + myrand()%(lim*2)
1727 float caves_amount = 0;
1732 else if(myrand()%3 == 0)
1738 caves_amount = 0.05;
1741 list_caves_amount->addPoint(p, Attribute(caves_amount));
1744 for(u32 i=0; i<5000; i++)
1746 /*u32 lim = MAP_GENERATION_LIMIT;
1750 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1753 -lim + (myrand()%(lim*2)),
1755 -lim + (myrand()%(lim*2))
1758 /*s32 bh_i = (myrand()%200) - 50;
1759 float baseheight = (float)bh_i;
1763 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1764 randmax = pow(randmax, e);
1766 //float randmax = (float)(myrand()%60);
1767 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1769 float baseheight = 0;
1771 float randfactor = 0;
1779 else if(myrand()%6 == 0)
1785 else if(myrand()%4 == 0)
1791 else if(myrand()%3 == 0)
1804 list_baseheight->addPoint(p, Attribute(baseheight));
1805 list_randmax->addPoint(p, Attribute(randmax));
1806 list_randfactor->addPoint(p, Attribute(randfactor));
1809 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
1810 list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
1811 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
1814 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1815 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1816 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1820 Try to load map; if not found, create a new one.
1823 m_savedir = savedir;
1824 m_map_saving_enabled = false;
1828 // If directory exists, check contents and load if possible
1829 if(fs::PathExists(m_savedir))
1831 // If directory is empty, it is safe to save into it.
1832 if(fs::GetDirListing(m_savedir).size() == 0)
1834 dstream<<DTIME<<"Server: Empty save directory is valid."
1836 m_map_saving_enabled = true;
1840 // Load master heightmap
1841 loadMasterHeightmap();
1843 // Load sector (0,0) and throw and exception on fail
1844 if(loadSectorFull(v2s16(0,0)) == false)
1845 throw LoadError("Failed to load sector (0,0)");
1847 dstream<<DTIME<<"Server: Successfully loaded master "
1848 "heightmap and sector (0,0) from "<<savedir<<
1849 ", assuming valid save directory."
1852 m_map_saving_enabled = true;
1853 // Map loaded, not creating new one
1857 // If directory doesn't exist, it is safe to save to it
1859 m_map_saving_enabled = true;
1862 catch(std::exception &e)
1864 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1865 <<", exception: "<<e.what()<<std::endl;
1866 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1867 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1870 dstream<<DTIME<<"Initializing new map."<<std::endl;
1872 // Create master heightmap
1873 /*ValueGenerator *maxgen =
1874 ValueGenerator::deSerialize(hmp.randmax);
1875 ValueGenerator *factorgen =
1876 ValueGenerator::deSerialize(hmp.randfactor);
1877 ValueGenerator *basegen =
1878 ValueGenerator::deSerialize(hmp.base);
1879 m_heightmap = new UnlimitedHeightmap
1880 (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
1882 /*m_heightmap = new UnlimitedHeightmap
1883 (hmp.blocksize, &m_padb);*/
1885 m_heightmap = new UnlimitedHeightmap
1888 // Set map parameters
1891 // Create zero sector
1892 emergeSector(v2s16(0,0));
1894 // Initially write whole map
1898 ServerMap::~ServerMap()
1902 if(m_map_saving_enabled)
1905 // Save only changed parts
1907 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1911 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1914 catch(std::exception &e)
1916 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1917 <<", exception: "<<e.what()<<std::endl;
1920 if(m_heightmap != NULL)
1924 MapSector * ServerMap::emergeSector(v2s16 p2d)
1926 DSTACK("%s: p2d=(%d,%d)",
1929 // Check that it doesn't exist already
1931 return getSectorNoGenerate(p2d);
1933 catch(InvalidPositionException &e)
1938 Try to load the sector from disk.
1940 if(loadSectorFull(p2d) == true)
1942 return getSectorNoGenerate(p2d);
1946 If there is no master heightmap, throw.
1948 if(m_heightmap == NULL)
1950 throw InvalidPositionException("emergeSector(): no heightmap");
1954 Do not generate over-limit
1956 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1957 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1958 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1959 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1960 throw InvalidPositionException("emergeSector(): pos. over limit");
1963 Generate sector and heightmaps
1966 // Number of heightmaps in sector in each direction
1967 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1969 // Heightmap side width
1970 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1972 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1974 // Sector position on map in nodes
1975 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1977 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1978 " heightmaps and objects"<<std::endl;*/
1981 Calculate some information about local properties
1984 v2s16 mhm_p = p2d * hm_split;
1986 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1987 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1988 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1989 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1992 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1993 float avgslope = 0.0;
1994 avgslope += fabs(avgheight - corners[0]);
1995 avgslope += fabs(avgheight - corners[1]);
1996 avgslope += fabs(avgheight - corners[2]);
1997 avgslope += fabs(avgheight - corners[3]);
1999 avgslope /= MAP_BLOCKSIZE;
2000 //dstream<<"avgslope="<<avgslope<<std::endl;
2002 float pitness = 0.0;
2004 a = m_heightmap->getSlope(p2d+v2s16(0,0));
2007 a = m_heightmap->getSlope(p2d+v2s16(0,1));
2010 a = m_heightmap->getSlope(p2d+v2s16(1,1));
2013 a = m_heightmap->getSlope(p2d+v2s16(1,0));
2017 pitness /= MAP_BLOCKSIZE;
2018 //dstream<<"pitness="<<pitness<<std::endl;
2021 Get local attributes
2024 float local_plants_amount = 0.0;
2026 //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
2027 //TimeTaker attrtimer("emergeSector() attribute fetch");
2029 // Get plant amount from attributes
2030 PointAttributeList *palist = m_padb.getList("plants_amount");
2032 /*local_plants_amount =
2033 palist->getNearAttr(nodepos2d).getFloat();*/
2034 local_plants_amount =
2035 palist->getInterpolatedFloat(nodepos2d);
2039 Generate sector heightmap
2042 // Loop through sub-heightmaps
2043 for(s16 y=0; y<hm_split; y++)
2044 for(s16 x=0; x<hm_split; x++)
2046 v2s16 p_in_sector = v2s16(x,y);
2047 v2s16 mhm_p = p2d * hm_split + p_in_sector;
2049 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
2050 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
2051 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
2052 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
2055 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
2056 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
2059 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
2061 sector->setHeightmap(p_in_sector, hm);
2063 //TODO: Make these values configurable
2064 //hm->generateContinued(0.0, 0.0, corners);
2065 //hm->generateContinued(0.25, 0.2, corners);
2066 //hm->generateContinued(0.5, 0.2, corners);
2067 //hm->generateContinued(1.0, 0.2, corners);
2068 //hm->generateContinued(2.0, 0.2, corners);
2069 //hm->generateContinued(2.0 * avgslope, 0.5, corners);
2070 hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners);
2079 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
2080 sector->setObjects(objects);
2082 float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
2085 Plant some trees if there is not much slope
2088 // Avgslope is the derivative of a hill
2089 //float t = avgslope * avgslope;
2091 float a = area/16 * m_params.plants_amount * local_plants_amount;
2093 //float something = 0.17*0.17;
2094 float something = 0.3;
2096 tree_max = a / (t/something);
2100 u32 count = (myrand()%(tree_max+1));
2101 //u32 count = tree_max;
2102 for(u32 i=0; i<count; i++)
2104 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
2105 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
2106 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2109 objects->insert(v3s16(x, y, z),
2110 SECTOR_OBJECT_TREE_1);
2114 Plant some bushes if sector is pit-like
2117 // Pitness usually goes at around -0.5...0.5
2119 u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount;
2121 bush_max = (pitness*a*4);
2124 u32 count = (myrand()%(bush_max+1));
2125 for(u32 i=0; i<count; i++)
2127 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
2128 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
2129 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2132 objects->insert(v3s16(x, y, z),
2133 SECTOR_OBJECT_BUSH_1);
2137 Add ravine (randomly)
2139 if(m_params.ravines_amount > 0.001)
2141 if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
2144 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2145 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2148 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2149 objects->insert(v3s16(x, y, z),
2150 SECTOR_OBJECT_RAVINE);
2157 JMutexAutoLock lock(m_sector_mutex);
2158 m_sectors.insert(p2d, sector);
2163 MapBlock * ServerMap::emergeBlock(
2165 bool only_from_disk,
2166 core::map<v3s16, MapBlock*> &changed_blocks,
2167 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2170 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
2172 p.X, p.Y, p.Z, only_from_disk);
2174 /*dstream<<"ServerMap::emergeBlock(): "
2175 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2176 <<", only_from_disk="<<only_from_disk<<std::endl;*/
2177 v2s16 p2d(p.X, p.Z);
2180 This will create or load a sector if not found in memory.
2181 If block exists on disk, it will be loaded.
2183 NOTE: On old save formats, this will be slow, as it generates
2184 lighting on blocks for them.
2186 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
2187 assert(sector->getId() == MAPSECTOR_SERVER);
2189 // Try to get a block from the sector
2190 MapBlock *block = NULL;
2191 bool not_on_disk = false;
2193 block = sector->getBlockNoCreate(block_y);
2194 if(block->isDummy() == true)
2199 catch(InvalidPositionException &e)
2205 If block was not found on disk and not going to generate a
2206 new one, make sure there is a dummy block in place.
2208 if(not_on_disk && only_from_disk)
2212 // Create dummy block
2213 block = new MapBlock(this, p, true);
2215 // Add block to sector
2216 sector->insertBlock(block);
2222 //dstream<<"Not found on disk, generating."<<std::endl;
2223 //TimeTaker("emergeBlock()", g_irrlicht);
2226 Do not generate over-limit
2228 if(blockpos_over_limit(p))
2229 throw InvalidPositionException("emergeBlock(): pos. over limit");
2234 Go on generating the block.
2236 TODO: If a dungeon gets generated so that it's side gets
2237 revealed to the outside air, the lighting should be
2242 If block doesn't exist, create one.
2243 If it exists, it is a dummy. In that case unDummify() it.
2245 NOTE: This already sets the map as the parent of the block
2249 block = sector->createBlankBlockNoInsert(block_y);
2253 // Remove the block so that nobody can get a half-generated one.
2254 sector->removeBlock(block);
2255 // Allocate the block to contain the generated data
2259 /*u8 water_material = CONTENT_WATER;
2260 if(g_settings.getBool("endless_water"))
2261 water_material = CONTENT_WATERSOURCE;*/
2262 u8 water_material = CONTENT_WATERSOURCE;
2264 s32 lowest_ground_y = 32767;
2265 s32 highest_ground_y = -32768;
2268 //sector->printHeightmaps();
2270 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2271 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2273 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
2275 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
2276 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
2277 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
2279 dstream<<"WARNING: Surface height not found in sector "
2280 "for block that is being emerged"<<std::endl;
2284 s16 surface_y = surface_y_f;
2285 //avg_ground_y += surface_y;
2286 if(surface_y < lowest_ground_y)
2287 lowest_ground_y = surface_y;
2288 if(surface_y > highest_ground_y)
2289 highest_ground_y = surface_y;
2291 s32 surface_depth = 0;
2293 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
2295 //float min_slope = 0.45;
2296 //float max_slope = 0.85;
2297 float min_slope = 0.60;
2298 float max_slope = 1.20;
2299 float min_slope_depth = 5.0;
2300 float max_slope_depth = 0;
2302 if(slope < min_slope)
2303 surface_depth = min_slope_depth;
2304 else if(slope > max_slope)
2305 surface_depth = max_slope_depth;
2307 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2309 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2311 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2316 NOTE: If there are some man-made structures above the
2317 newly created block, they won't be taken into account.
2319 if(real_y > surface_y)
2320 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2326 // If node is over heightmap y, it's air or water
2327 if(real_y > surface_y)
2329 // If under water level, it's water
2330 if(real_y < WATER_LEVEL)
2332 n.d = water_material;
2333 n.setLight(LIGHTBANK_DAY,
2334 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2336 Add to transforming liquid queue (in case it'd
2339 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
2340 m_transforming_liquid.push_back(real_pos);
2346 // Else it's ground or dungeons (air)
2349 // If it's surface_depth under ground, it's stone
2350 if(real_y <= surface_y - surface_depth)
2352 n.d = CONTENT_STONE;
2356 // It is mud if it is under the first ground
2357 // level or under water
2358 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2364 n.d = CONTENT_GRASS;
2367 //n.d = CONTENT_MUD;
2369 /*// If under water level, it's mud
2370 if(real_y < WATER_LEVEL)
2372 // Only the topmost node is grass
2373 else if(real_y <= surface_y - 1)
2376 n.d = CONTENT_GRASS;*/
2380 block->setNode(v3s16(x0,y0,z0), n);
2385 Calculate some helper variables
2388 // Completely underground if the highest part of block is under lowest
2390 // This has to be very sure; it's probably one too strict now but
2391 // that's just better.
2392 bool completely_underground =
2393 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2395 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2397 bool mostly_underwater_surface = false;
2398 if(highest_ground_y < WATER_LEVEL
2399 && some_part_underground && !completely_underground)
2400 mostly_underwater_surface = true;
2403 Get local attributes
2406 //dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
2408 float caves_amount = 0;
2412 NOTE: BEWARE: Too big amount of attribute points slows verything
2414 1 interpolation from 5000 points takes 2-3ms.
2416 //TimeTaker timer("emergeBlock() local attribute retrieval");
2417 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2418 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
2419 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
2422 //dstream<<"emergeBlock(): Done"<<std::endl;
2428 // Initialize temporary table
2429 const s32 ued = MAP_BLOCKSIZE;
2430 bool underground_emptiness[ued*ued*ued];
2431 for(s32 i=0; i<ued*ued*ued; i++)
2433 underground_emptiness[i] = 0;
2440 Initialize orp and ors. Try to find if some neighboring
2441 MapBlock has a tunnel ended in its side
2445 (float)(myrand()%ued)+0.5,
2446 (float)(myrand()%ued)+0.5,
2447 (float)(myrand()%ued)+0.5
2450 bool found_existing = false;
2456 for(s16 y=0; y<ued; y++)
2457 for(s16 x=0; x<ued; x++)
2459 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2460 if(getNode(ap).d == CONTENT_AIR)
2462 orp = v3f(x+1,y+1,0);
2463 found_existing = true;
2464 goto continue_generating;
2468 catch(InvalidPositionException &e){}
2474 for(s16 y=0; y<ued; y++)
2475 for(s16 x=0; x<ued; x++)
2477 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2478 if(getNode(ap).d == CONTENT_AIR)
2480 orp = v3f(x+1,y+1,ued-1);
2481 found_existing = true;
2482 goto continue_generating;
2486 catch(InvalidPositionException &e){}
2492 for(s16 y=0; y<ued; y++)
2493 for(s16 z=0; z<ued; z++)
2495 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2496 if(getNode(ap).d == CONTENT_AIR)
2498 orp = v3f(0,y+1,z+1);
2499 found_existing = true;
2500 goto continue_generating;
2504 catch(InvalidPositionException &e){}
2510 for(s16 y=0; y<ued; y++)
2511 for(s16 z=0; z<ued; z++)
2513 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2514 if(getNode(ap).d == CONTENT_AIR)
2516 orp = v3f(ued-1,y+1,z+1);
2517 found_existing = true;
2518 goto continue_generating;
2522 catch(InvalidPositionException &e){}
2528 for(s16 x=0; x<ued; x++)
2529 for(s16 z=0; z<ued; z++)
2531 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2532 if(getNode(ap).d == CONTENT_AIR)
2534 orp = v3f(x+1,0,z+1);
2535 found_existing = true;
2536 goto continue_generating;
2540 catch(InvalidPositionException &e){}
2546 for(s16 x=0; x<ued; x++)
2547 for(s16 z=0; z<ued; z++)
2549 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2550 if(getNode(ap).d == CONTENT_AIR)
2552 orp = v3f(x+1,ued-1,z+1);
2553 found_existing = true;
2554 goto continue_generating;
2558 catch(InvalidPositionException &e){}
2560 continue_generating:
2563 Choose whether to actually generate dungeon
2565 bool do_generate_dungeons = true;
2566 // Don't generate if no part is underground
2567 if(!some_part_underground)
2569 do_generate_dungeons = false;
2571 // Don't generate if mostly underwater surface
2572 else if(mostly_underwater_surface)
2574 do_generate_dungeons = false;
2576 // Partly underground = cave
2577 else if(!completely_underground)
2579 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2581 // Found existing dungeon underground
2582 else if(found_existing && completely_underground)
2584 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2586 // Underground and no dungeons found
2589 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
2592 if(do_generate_dungeons)
2595 Generate some tunnel starting from orp and ors
2597 for(u16 i=0; i<3; i++)
2600 (float)(myrand()%ued)+0.5,
2601 (float)(myrand()%ued)+0.5,
2602 (float)(myrand()%ued)+0.5
2606 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
2610 for(float f=0; f<1.0; f+=0.04)
2612 v3f fp = orp + vec * f;
2613 v3s16 cp(fp.X, fp.Y, fp.Z);
2615 s16 d1 = d0 + rs - 1;
2616 for(s16 z0=d0; z0<=d1; z0++)
2618 s16 si = rs - abs(z0);
2619 for(s16 x0=-si; x0<=si-1; x0++)
2621 s16 si2 = rs - abs(x0);
2622 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2628 if(isInArea(p, ued) == false)
2630 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2642 // Set to true if has caves.
2643 // Set when some non-air is changed to air when making caves.
2644 bool has_dungeons = false;
2647 Apply temporary cave data to block
2650 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2651 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2653 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2655 MapNode n = block->getNode(v3s16(x0,y0,z0));
2658 if(underground_emptiness[
2659 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2660 +ued*(y0*ued/MAP_BLOCKSIZE)
2661 +(x0*ued/MAP_BLOCKSIZE)])
2663 if(is_ground_content(n.d))
2666 has_dungeons = true;
2672 block->setNode(v3s16(x0,y0,z0), n);
2677 This is used for guessing whether or not the block should
2678 receive sunlight from the top if the top block doesn't exist
2680 block->setIsUnderground(completely_underground);
2683 Force lighting update if some part of block is partly
2684 underground and has caves.
2686 /*if(some_part_underground && !completely_underground && has_dungeons)
2688 //dstream<<"Half-ground caves"<<std::endl;
2689 lighting_invalidated_blocks[block->getPos()] = block;
2692 // DEBUG: Always update lighting
2693 //lighting_invalidated_blocks[block->getPos()] = block;
2699 if(some_part_underground)
2701 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2706 for(s16 i=0; i<underground_level/4 + 1; i++)
2708 if(myrand()%50 == 0)
2711 (myrand()%(MAP_BLOCKSIZE-2))+1,
2712 (myrand()%(MAP_BLOCKSIZE-2))+1,
2713 (myrand()%(MAP_BLOCKSIZE-2))+1
2719 //if(is_ground_content(block->getNode(cp).d))
2720 if(block->getNode(cp).d == CONTENT_STONE)
2722 block->setNode(cp, n);
2724 for(u16 i=0; i<26; i++)
2726 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2727 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2729 block->setNode(cp+g_26dirs[i], n);
2737 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2738 u16 coal_rareness = 60 / coal_amount;
2739 if(coal_rareness == 0)
2741 if(myrand()%coal_rareness == 0)
2743 u16 a = myrand() % 16;
2744 u16 amount = coal_amount * a*a*a / 1000;
2745 for(s16 i=0; i<amount; i++)
2748 (myrand()%(MAP_BLOCKSIZE-2))+1,
2749 (myrand()%(MAP_BLOCKSIZE-2))+1,
2750 (myrand()%(MAP_BLOCKSIZE-2))+1
2754 n.d = CONTENT_COALSTONE;
2756 //dstream<<"Adding coalstone"<<std::endl;
2758 //if(is_ground_content(block->getNode(cp).d))
2759 if(block->getNode(cp).d == CONTENT_STONE)
2761 block->setNode(cp, n);
2763 for(u16 i=0; i<26; i++)
2765 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2766 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2768 block->setNode(cp+g_26dirs[i], n);
2775 Create a few rats in empty blocks underground
2777 if(completely_underground)
2779 //for(u16 i=0; i<2; i++)
2782 (myrand()%(MAP_BLOCKSIZE-2))+1,
2783 (myrand()%(MAP_BLOCKSIZE-2))+1,
2784 (myrand()%(MAP_BLOCKSIZE-2))+1
2787 // Check that the place is empty
2788 //if(!is_ground_content(block->getNode(cp).d))
2791 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2792 block->addObject(obj);
2798 Add block to sector.
2800 sector->insertBlock(block);
2806 // An y-wise container of changed blocks
2807 core::map<s16, MapBlock*> changed_blocks_sector;
2810 Check if any sector's objects can be placed now.
2813 core::map<v3s16, u8> *objects = sector->getObjects();
2814 core::list<v3s16> objects_to_remove;
2815 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2816 i.atEnd() == false; i++)
2818 v3s16 p = i.getNode()->getKey();
2820 u8 d = i.getNode()->getValue();
2822 // Ground level point (user for stuff that is on ground)
2824 bool ground_found = true;
2826 // Search real ground level
2830 MapNode n = sector->getNode(gp);
2832 // If not air, go one up and continue to placing the tree
2833 if(n.d != CONTENT_AIR)
2839 // If air, go one down
2840 gp += v3s16(0,-1,0);
2842 }catch(InvalidPositionException &e)
2844 // Ground not found.
2845 ground_found = false;
2846 // This is most close to ground
2853 if(d == SECTOR_OBJECT_TEST)
2855 if(sector->isValidArea(p + v3s16(0,0,0),
2856 p + v3s16(0,0,0), &changed_blocks_sector))
2859 n.d = CONTENT_TORCH;
2860 sector->setNode(p, n);
2861 objects_to_remove.push_back(p);
2864 else if(d == SECTOR_OBJECT_TREE_1)
2866 if(ground_found == false)
2869 v3s16 p_min = gp + v3s16(-1,0,-1);
2870 v3s16 p_max = gp + v3s16(1,5,1);
2871 if(sector->isValidArea(p_min, p_max,
2872 &changed_blocks_sector))
2876 sector->setNode(gp+v3s16(0,0,0), n);
2877 sector->setNode(gp+v3s16(0,1,0), n);
2878 sector->setNode(gp+v3s16(0,2,0), n);
2879 sector->setNode(gp+v3s16(0,3,0), n);
2881 n.d = CONTENT_LEAVES;
2883 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2885 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2886 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2887 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2888 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2889 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2890 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2891 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2892 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2894 sector->setNode(gp+v3s16(0,4,0), n);
2896 sector->setNode(gp+v3s16(-1,4,0), n);
2897 sector->setNode(gp+v3s16(1,4,0), n);
2898 sector->setNode(gp+v3s16(0,4,-1), n);
2899 sector->setNode(gp+v3s16(0,4,1), n);
2900 sector->setNode(gp+v3s16(1,4,1), n);
2901 sector->setNode(gp+v3s16(-1,4,1), n);
2902 sector->setNode(gp+v3s16(-1,4,-1), n);
2903 sector->setNode(gp+v3s16(1,4,-1), n);
2905 sector->setNode(gp+v3s16(-1,3,0), n);
2906 sector->setNode(gp+v3s16(1,3,0), n);
2907 sector->setNode(gp+v3s16(0,3,-1), n);
2908 sector->setNode(gp+v3s16(0,3,1), n);
2909 sector->setNode(gp+v3s16(1,3,1), n);
2910 sector->setNode(gp+v3s16(-1,3,1), n);
2911 sector->setNode(gp+v3s16(-1,3,-1), n);
2912 sector->setNode(gp+v3s16(1,3,-1), n);
2914 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2915 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2916 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2917 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2918 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2919 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2920 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2921 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2923 // Objects are identified by wanted position
2924 objects_to_remove.push_back(p);
2926 // Lighting has to be recalculated for this one.
2927 sector->getBlocksInArea(p_min, p_max,
2928 lighting_invalidated_blocks);
2931 else if(d == SECTOR_OBJECT_BUSH_1)
2933 if(ground_found == false)
2936 if(sector->isValidArea(gp + v3s16(0,0,0),
2937 gp + v3s16(0,0,0), &changed_blocks_sector))
2940 n.d = CONTENT_LEAVES;
2941 sector->setNode(gp+v3s16(0,0,0), n);
2943 // Objects are identified by wanted position
2944 objects_to_remove.push_back(p);
2947 else if(d == SECTOR_OBJECT_RAVINE)
2950 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2951 v3s16 p_max = p + v3s16(6,6,6);
2952 if(sector->isValidArea(p_min, p_max,
2953 &changed_blocks_sector))
2956 n.d = CONTENT_STONE;
2959 s16 depth = maxdepth + (myrand()%10);
2961 s16 minz = -6 - (-2);
2963 for(s16 x=-6; x<=6; x++)
2965 z += -1 + (myrand()%3);
2970 for(s16 y=depth+(myrand()%2); y<=6; y++)
2972 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2975 v3s16 p2 = p + v3s16(x,y,z-2);
2976 if(is_ground_content(sector->getNode(p2).d)
2977 && !is_mineral(sector->getNode(p2).d))
2978 sector->setNode(p2, n);
2981 v3s16 p2 = p + v3s16(x,y,z-1);
2982 if(is_ground_content(sector->getNode(p2).d)
2983 && !is_mineral(sector->getNode(p2).d))
2984 sector->setNode(p2, n2);
2987 v3s16 p2 = p + v3s16(x,y,z+0);
2988 if(is_ground_content(sector->getNode(p2).d)
2989 && !is_mineral(sector->getNode(p2).d))
2990 sector->setNode(p2, n2);
2993 v3s16 p2 = p + v3s16(x,y,z+1);
2994 if(is_ground_content(sector->getNode(p2).d)
2995 && !is_mineral(sector->getNode(p2).d))
2996 sector->setNode(p2, n);
2999 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
3000 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
3004 objects_to_remove.push_back(p);
3006 // Lighting has to be recalculated for this one.
3007 sector->getBlocksInArea(p_min, p_max,
3008 lighting_invalidated_blocks);
3013 dstream<<"ServerMap::emergeBlock(): "
3014 "Invalid heightmap object"
3019 catch(InvalidPositionException &e)
3021 dstream<<"WARNING: "<<__FUNCTION_NAME
3022 <<": while inserting object "<<(int)d
3023 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
3024 <<" InvalidPositionException.what()="
3025 <<e.what()<<std::endl;
3026 // This is not too fatal and seems to happen sometimes.
3031 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
3032 i != objects_to_remove.end(); i++)
3034 objects->remove(*i);
3038 Initially update sunlight
3042 core::map<v3s16, bool> light_sources;
3043 bool black_air_left = false;
3044 bool bottom_invalid =
3045 block->propagateSunlight(light_sources, true,
3046 &black_air_left, true);
3048 // If sunlight didn't reach everywhere and part of block is
3049 // above ground, lighting has to be properly updated
3050 if(black_air_left && some_part_underground)
3052 lighting_invalidated_blocks[block->getPos()] = block;
3057 lighting_invalidated_blocks[block->getPos()] = block;
3062 Translate sector's changed blocks to global changed blocks
3065 for(core::map<s16, MapBlock*>::Iterator
3066 i = changed_blocks_sector.getIterator();
3067 i.atEnd() == false; i++)
3069 MapBlock *block = i.getNode()->getValue();
3071 changed_blocks.insert(block->getPos(), block);
3080 <<"lighting_invalidated_blocks.size()"
3084 <<" "<<lighting_invalidated_blocks.size()
3085 <<", "<<has_dungeons
3086 <<", "<<completely_underground
3087 <<", "<<some_part_underground
3092 Debug mode operation
3094 bool haxmode = g_settings.getBool("haxmode");
3097 // Don't calculate lighting at all
3098 //lighting_invalidated_blocks.clear();
3104 void ServerMap::createDir(std::string path)
3106 if(fs::CreateDir(path) == false)
3108 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3109 <<"\""<<path<<"\""<<std::endl;
3110 throw BaseException("ServerMap failed to create directory");
3114 std::string ServerMap::getSectorSubDir(v2s16 pos)
3117 snprintf(cc, 9, "%.4x%.4x",
3118 (unsigned int)pos.X&0xffff,
3119 (unsigned int)pos.Y&0xffff);
3121 return std::string(cc);
3124 std::string ServerMap::getSectorDir(v2s16 pos)
3126 return m_savedir + "/sectors/" + getSectorSubDir(pos);
3129 v2s16 ServerMap::getSectorPos(std::string dirname)
3131 if(dirname.size() != 8)
3132 throw InvalidFilenameException("Invalid sector directory name");
3134 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
3136 throw InvalidFilenameException("Invalid sector directory name");
3137 v2s16 pos((s16)x, (s16)y);
3141 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3143 v2s16 p2d = getSectorPos(sectordir);
3145 if(blockfile.size() != 4){
3146 throw InvalidFilenameException("Invalid block filename");
3149 int r = sscanf(blockfile.c_str(), "%4x", &y);
3151 throw InvalidFilenameException("Invalid block filename");
3152 return v3s16(p2d.X, y, p2d.Y);
3156 #define ENABLE_SECTOR_SAVING 1
3157 #define ENABLE_SECTOR_LOADING 1
3158 #define ENABLE_BLOCK_SAVING 1
3159 #define ENABLE_BLOCK_LOADING 1
3161 void ServerMap::save(bool only_changed)
3163 DSTACK(__FUNCTION_NAME);
3164 if(m_map_saving_enabled == false)
3166 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
3170 if(only_changed == false)
3171 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
3174 saveMasterHeightmap();
3176 u32 sector_meta_count = 0;
3177 u32 block_count = 0;
3180 JMutexAutoLock lock(m_sector_mutex);
3182 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3183 for(; i.atEnd() == false; i++)
3185 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3186 assert(sector->getId() == MAPSECTOR_SERVER);
3188 if(ENABLE_SECTOR_SAVING)
3190 if(sector->differs_from_disk || only_changed == false)
3192 saveSectorMeta(sector);
3193 sector_meta_count++;
3196 if(ENABLE_BLOCK_SAVING)
3198 core::list<MapBlock*> blocks;
3199 sector->getBlocks(blocks);
3200 core::list<MapBlock*>::Iterator j;
3201 for(j=blocks.begin(); j!=blocks.end(); j++)
3203 MapBlock *block = *j;
3204 if(block->getChangedFlag() || only_changed == false)
3216 Only print if something happened or saved whole map
3218 if(only_changed == false || sector_meta_count != 0
3219 || block_count != 0)
3221 dstream<<DTIME<<"ServerMap: Written: "
3222 <<sector_meta_count<<" sector metadata files, "
3223 <<block_count<<" block files"
3228 void ServerMap::loadAll()
3230 DSTACK(__FUNCTION_NAME);
3231 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
3233 loadMasterHeightmap();
3235 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
3237 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
3239 JMutexAutoLock lock(m_sector_mutex);
3242 s32 printed_counter = -100000;
3243 s32 count = list.size();
3245 std::vector<fs::DirListNode>::iterator i;
3246 for(i=list.begin(); i!=list.end(); i++)
3248 if(counter > printed_counter + 10)
3250 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
3251 printed_counter = counter;
3255 MapSector *sector = NULL;
3257 // We want directories
3261 sector = loadSectorMeta(i->name);
3263 catch(InvalidFilenameException &e)
3265 // This catches unknown crap in directory
3268 if(ENABLE_BLOCK_LOADING)
3270 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3271 (m_savedir+"/sectors/"+i->name);
3272 std::vector<fs::DirListNode>::iterator i2;
3273 for(i2=list2.begin(); i2!=list2.end(); i2++)
3279 loadBlock(i->name, i2->name, sector);
3281 catch(InvalidFilenameException &e)
3283 // This catches unknown crap in directory
3288 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
3291 void ServerMap::saveMasterHeightmap()
3293 DSTACK(__FUNCTION_NAME);
3294 createDir(m_savedir);
3296 std::string fullpath = m_savedir + "/master_heightmap";
3297 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3298 if(o.good() == false)
3299 throw FileNotGoodException("Cannot open master heightmap");
3301 // Format used for writing
3302 u8 version = SER_FMT_VER_HIGHEST;
3305 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
3307 [0] u8 serialization version
3308 [1] X master heightmap
3310 u32 fullsize = 1 + hmdata.getSize();
3311 SharedBuffer<u8> data(fullsize);
3314 memcpy(&data[1], *hmdata, hmdata.getSize());
3316 o.write((const char*)*data, fullsize);
3319 m_heightmap->serialize(o, version);
3322 void ServerMap::loadMasterHeightmap()
3324 DSTACK(__FUNCTION_NAME);
3325 std::string fullpath = m_savedir + "/master_heightmap";
3326 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3327 if(is.good() == false)
3328 throw FileNotGoodException("Cannot open master heightmap");
3330 if(m_heightmap != NULL)
3333 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
3336 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3338 DSTACK(__FUNCTION_NAME);
3339 // Format used for writing
3340 u8 version = SER_FMT_VER_HIGHEST;
3342 v2s16 pos = sector->getPos();
3343 createDir(m_savedir);
3344 createDir(m_savedir+"/sectors");
3345 std::string dir = getSectorDir(pos);
3348 std::string fullpath = dir + "/heightmap";
3349 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3350 if(o.good() == false)
3351 throw FileNotGoodException("Cannot open master heightmap");
3353 sector->serialize(o, version);
3355 sector->differs_from_disk = false;
3358 MapSector* ServerMap::loadSectorMeta(std::string dirname)
3360 DSTACK(__FUNCTION_NAME);
3362 v2s16 p2d = getSectorPos(dirname);
3363 std::string dir = m_savedir + "/sectors/" + dirname;
3365 std::string fullpath = dir + "/heightmap";
3366 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3367 if(is.good() == false)
3368 throw FileNotGoodException("Cannot open sector heightmap");
3370 ServerMapSector *sector = ServerMapSector::deSerialize
3371 (is, this, p2d, &m_hwrapper, m_sectors);
3373 sector->differs_from_disk = false;
3378 bool ServerMap::loadSectorFull(v2s16 p2d)
3380 DSTACK(__FUNCTION_NAME);
3381 std::string sectorsubdir = getSectorSubDir(p2d);
3383 MapSector *sector = NULL;
3385 JMutexAutoLock lock(m_sector_mutex);
3388 sector = loadSectorMeta(sectorsubdir);
3390 catch(InvalidFilenameException &e)
3394 catch(FileNotGoodException &e)
3398 catch(std::exception &e)
3403 if(ENABLE_BLOCK_LOADING)
3405 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3406 (m_savedir+"/sectors/"+sectorsubdir);
3407 std::vector<fs::DirListNode>::iterator i2;
3408 for(i2=list2.begin(); i2!=list2.end(); i2++)
3414 loadBlock(sectorsubdir, i2->name, sector);
3416 catch(InvalidFilenameException &e)
3418 // This catches unknown crap in directory
3426 bool ServerMap::deFlushSector(v2s16 p2d)
3428 DSTACK(__FUNCTION_NAME);
3429 // See if it already exists in memory
3431 MapSector *sector = getSectorNoGenerate(p2d);
3434 catch(InvalidPositionException &e)
3437 Try to load the sector from disk.
3439 if(loadSectorFull(p2d) == true)
3448 void ServerMap::saveBlock(MapBlock *block)
3450 DSTACK(__FUNCTION_NAME);
3452 Dummy blocks are not written
3454 if(block->isDummy())
3456 /*v3s16 p = block->getPos();
3457 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3458 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3462 // Format used for writing
3463 u8 version = SER_FMT_VER_HIGHEST;
3465 v3s16 p3d = block->getPos();
3466 v2s16 p2d(p3d.X, p3d.Z);
3467 createDir(m_savedir);
3468 createDir(m_savedir+"/sectors");
3469 std::string dir = getSectorDir(p2d);
3472 // Block file is map/sectors/xxxxxxxx/xxxx
3474 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
3475 std::string fullpath = dir + "/" + cc;
3476 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3477 if(o.good() == false)
3478 throw FileNotGoodException("Cannot open block data");
3481 [0] u8 serialization version
3484 o.write((char*)&version, 1);
3486 block->serialize(o, version);
3489 Versions up from 9 have block objects.
3493 block->serializeObjects(o, version);
3496 // We just wrote it to the disk
3497 block->resetChangedFlag();
3500 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
3502 DSTACK(__FUNCTION_NAME);
3506 // Block file is map/sectors/xxxxxxxx/xxxx
3507 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
3508 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3509 if(is.good() == false)
3510 throw FileNotGoodException("Cannot open block file");
3512 v3s16 p3d = getBlockPos(sectordir, blockfile);
3513 v2s16 p2d(p3d.X, p3d.Z);
3515 assert(sector->getPos() == p2d);
3517 u8 version = SER_FMT_VER_INVALID;
3518 is.read((char*)&version, 1);
3520 /*u32 block_size = MapBlock::serializedLength(version);
3521 SharedBuffer<u8> data(block_size);
3522 is.read((char*)*data, block_size);*/
3524 // This will always return a sector because we're the server
3525 //MapSector *sector = emergeSector(p2d);
3527 MapBlock *block = NULL;
3528 bool created_new = false;
3530 block = sector->getBlockNoCreate(p3d.Y);
3532 catch(InvalidPositionException &e)
3534 block = sector->createBlankBlockNoInsert(p3d.Y);
3538 // deserialize block data
3539 block->deSerialize(is, version);
3542 Versions up from 9 have block objects.
3546 block->updateObjects(is, version, NULL, 0);
3550 sector->insertBlock(block);
3553 Convert old formats to new and save
3556 // Save old format blocks in new format
3557 if(version < SER_FMT_VER_HIGHEST)
3562 // We just loaded it from the disk, so it's up-to-date.
3563 block->resetChangedFlag();
3566 catch(SerializationError &e)
3568 dstream<<"WARNING: Invalid block data on disk "
3569 "(SerializationError). Ignoring."
3574 // Gets from master heightmap
3575 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
3577 assert(m_heightmap != NULL);
3585 corners[0] = m_heightmap->getGroundHeight
3586 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
3587 corners[1] = m_heightmap->getGroundHeight
3588 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
3589 corners[2] = m_heightmap->getGroundHeight
3590 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
3591 corners[3] = m_heightmap->getGroundHeight
3592 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
3595 void ServerMap::PrintInfo(std::ostream &out)
3606 ClientMap::ClientMap(
3608 MapDrawControl &control,
3609 scene::ISceneNode* parent,
3610 scene::ISceneManager* mgr,
3614 scene::ISceneNode(parent, mgr, id),
3621 /*m_box = core::aabbox3d<f32>(0,0,0,
3622 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
3623 /*m_box = core::aabbox3d<f32>(0,0,0,
3624 map->getSizeNodes().X * BS,
3625 map->getSizeNodes().Y * BS,
3626 map->getSizeNodes().Z * BS);*/
3627 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3628 BS*1000000,BS*1000000,BS*1000000);
3630 //setPosition(v3f(BS,BS,BS));
3633 ClientMap::~ClientMap()
3635 JMutexAutoLock lock(mesh_mutex);
3644 MapSector * ClientMap::emergeSector(v2s16 p2d)
3646 DSTACK(__FUNCTION_NAME);
3647 // Check that it doesn't exist already
3649 return getSectorNoGenerate(p2d);
3651 catch(InvalidPositionException &e)
3655 // Create a sector with no heightmaps
3656 ClientMapSector *sector = new ClientMapSector(this, p2d);
3659 JMutexAutoLock lock(m_sector_mutex);
3660 m_sectors.insert(p2d, sector);
3666 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3668 DSTACK(__FUNCTION_NAME);
3669 ClientMapSector *sector = NULL;
3671 JMutexAutoLock lock(m_sector_mutex);
3673 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3677 sector = (ClientMapSector*)n->getValue();
3678 assert(sector->getId() == MAPSECTOR_CLIENT);
3682 sector = new ClientMapSector(this, p2d);
3684 JMutexAutoLock lock(m_sector_mutex);
3685 m_sectors.insert(p2d, sector);
3689 sector->deSerialize(is);
3692 void ClientMap::OnRegisterSceneNode()
3696 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3697 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3700 ISceneNode::OnRegisterSceneNode();
3703 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3705 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3706 DSTACK(__FUNCTION_NAME);
3708 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3711 Get time for measuring timeout.
3713 Measuring time is very useful for long delays when the
3714 machine is swapping a lot.
3716 int time1 = time(0);
3718 u32 daynight_ratio = m_client->getDayNightRatio();
3720 m_camera_mutex.Lock();
3721 v3f camera_position = m_camera_position;
3722 v3f camera_direction = m_camera_direction;
3723 m_camera_mutex.Unlock();
3726 Get all blocks and draw all visible ones
3729 v3s16 cam_pos_nodes(
3730 camera_position.X / BS,
3731 camera_position.Y / BS,
3732 camera_position.Z / BS);
3734 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3736 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3737 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3739 // Take a fair amount as we will be dropping more out later
3741 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3742 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3743 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3745 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3746 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3747 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3749 u32 vertex_count = 0;
3751 // For limiting number of mesh updates per frame
3752 u32 mesh_update_count = 0;
3754 u32 blocks_would_have_drawn = 0;
3755 u32 blocks_drawn = 0;
3757 //NOTE: The sectors map should be locked but we're not doing it
3758 // because it'd cause too much delays
3760 int timecheck_counter = 0;
3761 core::map<v2s16, MapSector*>::Iterator si;
3762 si = m_sectors.getIterator();
3763 for(; si.atEnd() == false; si++)
3766 timecheck_counter++;
3767 if(timecheck_counter > 50)
3769 int time2 = time(0);
3770 if(time2 > time1 + 4)
3772 dstream<<"ClientMap::renderMap(): "
3773 "Rendering takes ages, returning."
3780 MapSector *sector = si.getNode()->getValue();
3781 v2s16 sp = sector->getPos();
3783 if(m_control.range_all == false)
3785 if(sp.X < p_blocks_min.X
3786 || sp.X > p_blocks_max.X
3787 || sp.Y < p_blocks_min.Z
3788 || sp.Y > p_blocks_max.Z)
3792 core::list< MapBlock * > sectorblocks;
3793 sector->getBlocks(sectorblocks);
3799 core::list< MapBlock * >::Iterator i;
3800 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3802 MapBlock *block = *i;
3805 Compare block position to camera position, skip
3806 if not seen on display
3809 float range = 100000 * BS;
3810 if(m_control.range_all == false)
3811 range = m_control.wanted_range * BS;
3813 if(isBlockInSight(block->getPos(), camera_position,
3814 camera_direction, range) == false)
3820 v3s16 blockpos_nodes = block->getPosRelative();
3822 // Block center position
3824 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3825 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3826 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3829 // Block position relative to camera
3830 v3f blockpos_relative = blockpos - camera_position;
3832 // Distance in camera direction (+=front, -=back)
3833 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3836 f32 d = blockpos_relative.getLength();
3838 if(m_control.range_all == false)
3840 // If block is far away, don't draw it
3841 if(d > m_control.wanted_range * BS)
3845 // Maximum radius of a block
3846 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3848 // If block is (nearly) touching the camera, don't
3849 // bother validating further (that is, render it anyway)
3850 if(d > block_max_radius * 1.5)
3852 // Cosine of the angle between the camera direction
3853 // and the block direction (camera_direction is an unit vector)
3854 f32 cosangle = dforward / d;
3856 // Compensate for the size of the block
3857 // (as the block has to be shown even if it's a bit off FOV)
3858 // This is an estimate.
3859 cosangle += block_max_radius / dforward;
3861 // If block is not in the field of view, skip it
3862 //if(cosangle < cos(FOV_ANGLE/2))
3863 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3868 v3s16 blockpos_nodes = block->getPosRelative();
3870 // Block center position
3872 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3873 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3874 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3877 // Block position relative to camera
3878 v3f blockpos_relative = blockpos - camera_position;
3881 f32 d = blockpos_relative.getLength();
3888 bool mesh_expired = false;
3891 JMutexAutoLock lock(block->mesh_mutex);
3893 mesh_expired = block->getMeshExpired();
3895 // Mesh has not been expired and there is no mesh:
3896 // block has no content
3897 if(block->mesh == NULL && mesh_expired == false)
3901 f32 faraway = BS*50;
3902 //f32 faraway = m_control.wanted_range * BS;
3905 This has to be done with the mesh_mutex unlocked
3907 // Pretty random but this should work somewhat nicely
3908 if(mesh_expired && (
3909 (mesh_update_count < 3
3910 && (d < faraway || mesh_update_count < 2)
3913 (m_control.range_all && mesh_update_count < 20)
3916 /*if(mesh_expired && mesh_update_count < 6
3917 && (d < faraway || mesh_update_count < 3))*/
3919 mesh_update_count++;
3921 // Mesh has been expired: generate new mesh
3922 //block->updateMeshes(daynight_i);
3923 block->updateMesh(daynight_ratio);
3925 mesh_expired = false;
3929 Don't draw an expired mesh that is far away
3931 /*if(mesh_expired && d >= faraway)
3934 // Instead, delete it
3935 JMutexAutoLock lock(block->mesh_mutex);
3938 block->mesh->drop();
3941 // And continue to next block
3946 Draw the faces of the block
3949 JMutexAutoLock lock(block->mesh_mutex);
3951 scene::SMesh *mesh = block->mesh;
3956 blocks_would_have_drawn++;
3957 if(blocks_drawn >= m_control.wanted_max_blocks
3958 && m_control.range_all == false
3959 && d > m_control.wanted_min_range * BS)
3963 u32 c = mesh->getMeshBufferCount();
3965 for(u32 i=0; i<c; i++)
3967 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3968 const video::SMaterial& material = buf->getMaterial();
3969 video::IMaterialRenderer* rnd =
3970 driver->getMaterialRenderer(material.MaterialType);
3971 bool transparent = (rnd && rnd->isTransparent());
3972 // Render transparent on transparent pass and likewise.
3973 if(transparent == is_transparent_pass)
3975 driver->setMaterial(buf->getMaterial());
3976 driver->drawMeshBuffer(buf);
3977 vertex_count += buf->getVertexCount();
3981 } // foreach sectorblocks
3984 m_control.blocks_drawn = blocks_drawn;
3985 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3987 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3988 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3991 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod, bool *changed)
3994 Add it to all blocks touching it
3997 v3s16(0,0,0), // this
3998 v3s16(0,0,1), // back
3999 v3s16(0,1,0), // top
4000 v3s16(1,0,0), // right
4001 v3s16(0,0,-1), // front
4002 v3s16(0,-1,0), // bottom
4003 v3s16(-1,0,0), // left
4005 for(u16 i=0; i<7; i++)
4007 v3s16 p2 = p + dirs[i];
4008 // Block position of neighbor (or requested) node
4009 v3s16 blockpos = getNodeBlockPos(p2);
4010 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4011 if(blockref == NULL)
4013 // Relative position of requested node
4014 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4015 if(blockref->setTempMod(relpos, mod))
4021 return getNodeBlockPos(p);
4023 v3s16 ClientMap::clearTempMod(v3s16 p, bool *changed)
4026 v3s16(0,0,0), // this
4027 v3s16(0,0,1), // back
4028 v3s16(0,1,0), // top
4029 v3s16(1,0,0), // right
4030 v3s16(0,0,-1), // front
4031 v3s16(0,-1,0), // bottom
4032 v3s16(-1,0,0), // left
4034 for(u16 i=0; i<7; i++)
4036 v3s16 p2 = p + dirs[i];
4037 // Block position of neighbor (or requested) node
4038 v3s16 blockpos = getNodeBlockPos(p2);
4039 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4040 if(blockref == NULL)
4042 // Relative position of requested node
4043 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4044 if(blockref->clearTempMod(relpos))
4050 return getNodeBlockPos(p);
4053 void ClientMap::PrintInfo(std::ostream &out)
4064 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4069 MapVoxelManipulator::~MapVoxelManipulator()
4071 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4076 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4078 TimeTaker timer1("emerge", &emerge_time);
4080 // Units of these are MapBlocks
4081 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4082 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4084 VoxelArea block_area_nodes
4085 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4087 addArea(block_area_nodes);
4089 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4090 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4091 for(s32 x=p_min.X; x<=p_max.X; x++)
4094 core::map<v3s16, bool>::Node *n;
4095 n = m_loaded_blocks.find(p);
4099 bool block_data_inexistent = false;
4102 TimeTaker timer1("emerge load", &emerge_load_time);
4104 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4105 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4108 dstream<<std::endl;*/
4110 MapBlock *block = m_map->getBlockNoCreate(p);
4111 if(block->isDummy())
4112 block_data_inexistent = true;
4114 block->copyTo(*this);
4116 catch(InvalidPositionException &e)
4118 block_data_inexistent = true;
4121 if(block_data_inexistent)
4123 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4124 // Fill with VOXELFLAG_INEXISTENT
4125 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4126 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4128 s32 i = m_area.index(a.MinEdge.X,y,z);
4129 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4133 m_loaded_blocks.insert(p, true);
4136 //dstream<<"emerge done"<<std::endl;
4141 void MapVoxelManipulator::emerge(VoxelArea a)
4143 TimeTaker timer1("emerge", &emerge_time);
4145 v3s16 size = a.getExtent();
4147 VoxelArea padded = a;
4148 padded.pad(m_area.getExtent() / 4);
4151 for(s16 z=0; z<size.Z; z++)
4152 for(s16 y=0; y<size.Y; y++)
4153 for(s16 x=0; x<size.X; x++)
4156 s32 i = m_area.index(a.MinEdge + p);
4157 // Don't touch nodes that have already been loaded
4158 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4162 TimeTaker timer1("emerge load", &emerge_load_time);
4163 MapNode n = m_map->getNode(a.MinEdge + p);
4167 catch(InvalidPositionException &e)
4169 m_flags[i] = VOXELFLAG_INEXISTENT;
4177 TODO: Add an option to only update eg. water and air nodes.
4178 This will make it interfere less with important stuff if
4181 void MapVoxelManipulator::blitBack
4182 (core::map<v3s16, MapBlock*> & modified_blocks)
4184 if(m_area.getExtent() == v3s16(0,0,0))
4187 //TimeTaker timer1("blitBack");
4190 Initialize block cache
4192 v3s16 blockpos_last;
4193 MapBlock *block = NULL;
4194 bool block_checked_in_modified = false;
4196 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4197 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4198 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4202 u8 f = m_flags[m_area.index(p)];
4203 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4206 MapNode &n = m_data[m_area.index(p)];
4208 v3s16 blockpos = getNodeBlockPos(p);
4213 if(block == NULL || blockpos != blockpos_last){
4214 block = m_map->getBlockNoCreate(blockpos);
4215 blockpos_last = blockpos;
4216 block_checked_in_modified = false;
4219 // Calculate relative position in block
4220 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4222 // Don't continue if nothing has changed here
4223 if(block->getNode(relpos) == n)
4226 //m_map->setNode(m_area.MinEdge + p, n);
4227 block->setNode(relpos, n);
4230 Make sure block is in modified_blocks
4232 if(block_checked_in_modified == false)
4234 modified_blocks[blockpos] = block;
4235 block_checked_in_modified = true;
4238 catch(InvalidPositionException &e)