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.size()<<" blocks."<<std::endl;*/
627 //TimeTaker timer("updateLighting");
632 u32 count_was = modified_blocks.size();
634 core::map<v3s16, bool> light_sources;
636 core::map<v3s16, u8> unlight_from;
638 core::map<v3s16, MapBlock*>::Iterator i;
639 i = a_blocks.getIterator();
640 for(; i.atEnd() == false; i++)
642 MapBlock *block = i.getNode()->getValue();
646 // Don't bother with dummy blocks.
650 v3s16 pos = block->getPos();
651 modified_blocks.insert(pos, block);
654 Clear all light from block
656 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
657 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
658 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
663 MapNode n = block->getNode(v3s16(x,y,z));
664 u8 oldlight = n.getLight(bank);
666 block->setNode(v3s16(x,y,z), n);
668 // Collect borders for unlighting
669 if(x==0 || x == MAP_BLOCKSIZE-1
670 || y==0 || y == MAP_BLOCKSIZE-1
671 || z==0 || z == MAP_BLOCKSIZE-1)
673 v3s16 p_map = p + v3s16(
676 MAP_BLOCKSIZE*pos.Z);
677 unlight_from.insert(p_map, oldlight);
680 catch(InvalidPositionException &e)
683 This would happen when dealing with a
687 dstream<<"updateLighting(): InvalidPositionException"
692 if(bank == LIGHTBANK_DAY)
694 bool bottom_valid = block->propagateSunlight(light_sources);
696 // If bottom is valid, we're done.
700 else if(bank == LIGHTBANK_NIGHT)
709 /*dstream<<"Bottom for sunlight-propagated block ("
710 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
713 // Else get the block below and loop to it
717 block = getBlockNoCreate(pos);
719 catch(InvalidPositionException &e)
729 TimeTaker timer("unspreadLight");
730 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
735 u32 diff = modified_blocks.size() - count_was;
736 count_was = modified_blocks.size();
737 dstream<<"unspreadLight modified "<<diff<<std::endl;
740 // TODO: Spread light from propagated sunlight?
741 // Yes, add it to light_sources... somehow.
742 // It has to be added at somewhere above, in the loop.
744 // NOTE: This actually works fine without doing so
745 // - Find out why it works
748 TimeTaker timer("spreadLight");
749 spreadLight(bank, light_sources, modified_blocks);
754 u32 diff = modified_blocks.size() - count_was;
755 count_was = modified_blocks.size();
756 dstream<<"spreadLight modified "<<diff<<std::endl;
761 //MapVoxelManipulator vmanip(this);
763 ManualMapVoxelManipulator vmanip(this);
765 core::map<v3s16, MapBlock*>::Iterator i;
766 i = a_blocks.getIterator();
767 for(; i.atEnd() == false; i++)
769 MapBlock *block = i.getNode()->getValue();
770 v3s16 p = block->getPos();
771 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
774 //TimeTaker timer("unSpreadLight");
775 vmanip.unspreadLight(bank, unlight_from, light_sources);
778 //TimeTaker timer("spreadLight");
779 vmanip.spreadLight(bank, light_sources);
782 //TimeTaker timer("blitBack");
783 vmanip.blitBack(modified_blocks);
785 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
789 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
792 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
793 core::map<v3s16, MapBlock*> & modified_blocks)
795 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
796 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
799 Update information about whether day and night light differ
801 for(core::map<v3s16, MapBlock*>::Iterator
802 i = modified_blocks.getIterator();
803 i.atEnd() == false; i++)
805 MapBlock *block = i.getNode()->getValue();
806 block->updateDayNightDiff();
811 This is called after changing a node from transparent to opaque.
812 The lighting value of the node should be left as-is after changing
813 other values. This sets the lighting value to 0.
815 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
816 core::map<v3s16, MapBlock*> &modified_blocks)*/
817 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
818 core::map<v3s16, MapBlock*> &modified_blocks)
821 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
822 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
825 From this node to nodes underneath:
826 If lighting is sunlight (1.0), unlight neighbours and
831 v3s16 toppos = p + v3s16(0,1,0);
832 v3s16 bottompos = p + v3s16(0,-1,0);
834 bool node_under_sunlight = true;
835 core::map<v3s16, bool> light_sources;
838 If there is a node at top and it doesn't have sunlight,
839 there has not been any sunlight going down.
841 Otherwise there probably is.
844 MapNode topnode = getNode(toppos);
846 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
847 node_under_sunlight = false;
849 catch(InvalidPositionException &e)
853 if(n.d != CONTENT_TORCH)
856 If there is grass below, change it to mud
859 MapNode bottomnode = getNode(bottompos);
861 if(bottomnode.d == CONTENT_GRASS
862 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
864 bottomnode.d = CONTENT_MUD;
865 setNode(bottompos, bottomnode);
868 catch(InvalidPositionException &e)
873 enum LightBank banks[] =
878 for(s32 i=0; i<2; i++)
880 enum LightBank bank = banks[i];
882 u8 lightwas = getNode(p).getLight(bank);
884 // Add the block of the added node to modified_blocks
885 v3s16 blockpos = getNodeBlockPos(p);
886 MapBlock * block = getBlockNoCreate(blockpos);
887 assert(block != NULL);
888 modified_blocks.insert(blockpos, block);
890 if(isValidPosition(p) == false)
893 // Unlight neighbours of node.
894 // This means setting light of all consequent dimmer nodes
896 // This also collects the nodes at the border which will spread
897 // light again into this.
898 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
906 If node is under sunlight, take all sunlighted nodes under
907 it and clear light from them and from where the light has
909 TODO: This could be optimized by mass-unlighting instead
912 if(node_under_sunlight)
916 //m_dout<<DTIME<<"y="<<y<<std::endl;
917 v3s16 n2pos(p.X, y, p.Z);
923 catch(InvalidPositionException &e)
928 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
930 //m_dout<<DTIME<<"doing"<<std::endl;
931 unLightNeighbors(LIGHTBANK_DAY,
932 n2pos, n2.getLight(LIGHTBANK_DAY),
933 light_sources, modified_blocks);
934 n2.setLight(LIGHTBANK_DAY, 0);
942 for(s32 i=0; i<2; i++)
944 enum LightBank bank = banks[i];
947 Spread light from all nodes that might be capable of doing so
948 TODO: Convert to spreadLight
950 spreadLight(bank, light_sources, modified_blocks);
954 Update information about whether day and night light differ
956 for(core::map<v3s16, MapBlock*>::Iterator
957 i = modified_blocks.getIterator();
958 i.atEnd() == false; i++)
960 MapBlock *block = i.getNode()->getValue();
961 block->updateDayNightDiff();
965 Add neighboring liquid nodes and the node itself if it is
966 liquid (=water node was added) to transform queue.
969 v3s16(0,0,0), // self
970 v3s16(0,0,1), // back
972 v3s16(1,0,0), // right
973 v3s16(0,0,-1), // front
974 v3s16(0,-1,0), // bottom
975 v3s16(-1,0,0), // left
977 for(u16 i=0; i<7; i++)
982 v3s16 p2 = p + dirs[i];
984 MapNode n2 = getNode(p2);
985 if(content_liquid(n2.d))
987 m_transforming_liquid.push_back(p2);
990 }catch(InvalidPositionException &e)
998 void Map::removeNodeAndUpdate(v3s16 p,
999 core::map<v3s16, MapBlock*> &modified_blocks)
1001 /*PrintInfo(m_dout);
1002 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1003 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1005 bool node_under_sunlight = true;
1007 v3s16 toppos = p + v3s16(0,1,0);
1009 // Node will be replaced with this
1010 u8 replace_material = CONTENT_AIR;
1013 If there is a node at top and it doesn't have sunlight,
1014 there will be no sunlight going down.
1017 MapNode topnode = getNode(toppos);
1019 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1020 node_under_sunlight = false;
1022 catch(InvalidPositionException &e)
1026 core::map<v3s16, bool> light_sources;
1028 enum LightBank banks[] =
1033 for(s32 i=0; i<2; i++)
1035 enum LightBank bank = banks[i];
1038 Unlight neighbors (in case the node is a light source)
1040 unLightNeighbors(bank, p,
1041 getNode(p).getLight(bank),
1042 light_sources, modified_blocks);
1047 This also clears the lighting.
1051 n.d = replace_material;
1054 for(s32 i=0; i<2; i++)
1056 enum LightBank bank = banks[i];
1059 Recalculate lighting
1061 spreadLight(bank, light_sources, modified_blocks);
1064 // Add the block of the removed node to modified_blocks
1065 v3s16 blockpos = getNodeBlockPos(p);
1066 MapBlock * block = getBlockNoCreate(blockpos);
1067 assert(block != NULL);
1068 modified_blocks.insert(blockpos, block);
1071 If the removed node was under sunlight, propagate the
1072 sunlight down from it and then light all neighbors
1073 of the propagated blocks.
1075 if(node_under_sunlight)
1077 s16 ybottom = propagateSunlight(p, modified_blocks);
1078 /*m_dout<<DTIME<<"Node was under sunlight. "
1079 "Propagating sunlight";
1080 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1082 for(; y >= ybottom; y--)
1084 v3s16 p2(p.X, y, p.Z);
1085 /*m_dout<<DTIME<<"lighting neighbors of node ("
1086 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1088 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1093 // Set the lighting of this node to 0
1094 // TODO: Is this needed? Lighting is cleared up there already.
1096 MapNode n = getNode(p);
1097 n.setLight(LIGHTBANK_DAY, 0);
1100 catch(InvalidPositionException &e)
1106 for(s32 i=0; i<2; i++)
1108 enum LightBank bank = banks[i];
1110 // Get the brightest neighbour node and propagate light from it
1111 v3s16 n2p = getBrightestNeighbour(bank, p);
1113 MapNode n2 = getNode(n2p);
1114 lightNeighbors(bank, n2p, modified_blocks);
1116 catch(InvalidPositionException &e)
1122 Update information about whether day and night light differ
1124 for(core::map<v3s16, MapBlock*>::Iterator
1125 i = modified_blocks.getIterator();
1126 i.atEnd() == false; i++)
1128 MapBlock *block = i.getNode()->getValue();
1129 block->updateDayNightDiff();
1133 Add neighboring liquid nodes to transform queue.
1136 v3s16(0,0,1), // back
1137 v3s16(0,1,0), // top
1138 v3s16(1,0,0), // right
1139 v3s16(0,0,-1), // front
1140 v3s16(0,-1,0), // bottom
1141 v3s16(-1,0,0), // left
1143 for(u16 i=0; i<6; i++)
1148 v3s16 p2 = p + dirs[i];
1150 MapNode n2 = getNode(p2);
1151 if(content_liquid(n2.d))
1153 m_transforming_liquid.push_back(p2);
1156 }catch(InvalidPositionException &e)
1163 void Map::expireMeshes(bool only_daynight_diffed)
1165 TimeTaker timer("expireMeshes()");
1167 core::map<v2s16, MapSector*>::Iterator si;
1168 si = m_sectors.getIterator();
1169 for(; si.atEnd() == false; si++)
1171 MapSector *sector = si.getNode()->getValue();
1173 core::list< MapBlock * > sectorblocks;
1174 sector->getBlocks(sectorblocks);
1176 core::list< MapBlock * >::Iterator i;
1177 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1179 MapBlock *block = *i;
1181 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1187 JMutexAutoLock lock(block->mesh_mutex);
1188 if(block->mesh != NULL)
1190 /*block->mesh->drop();
1191 block->mesh = NULL;*/
1192 block->setMeshExpired(true);
1199 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1201 assert(mapType() == MAPTYPE_CLIENT);
1204 v3s16 p = blockpos + v3s16(0,0,0);
1205 MapBlock *b = getBlockNoCreate(p);
1206 b->updateMesh(daynight_ratio);
1208 catch(InvalidPositionException &e){}
1211 v3s16 p = blockpos + v3s16(-1,0,0);
1212 MapBlock *b = getBlockNoCreate(p);
1213 b->updateMesh(daynight_ratio);
1215 catch(InvalidPositionException &e){}
1217 v3s16 p = blockpos + v3s16(0,-1,0);
1218 MapBlock *b = getBlockNoCreate(p);
1219 b->updateMesh(daynight_ratio);
1221 catch(InvalidPositionException &e){}
1223 v3s16 p = blockpos + v3s16(0,0,-1);
1224 MapBlock *b = getBlockNoCreate(p);
1225 b->updateMesh(daynight_ratio);
1227 catch(InvalidPositionException &e){}
1230 v3s16 p = blockpos + v3s16(1,0,0);
1231 MapBlock *b = getBlockNoCreate(p);
1232 b->updateMesh(daynight_ratio);
1234 catch(InvalidPositionException &e){}
1236 v3s16 p = blockpos + v3s16(0,1,0);
1237 MapBlock *b = getBlockNoCreate(p);
1238 b->updateMesh(daynight_ratio);
1240 catch(InvalidPositionException &e){}
1242 v3s16 p = blockpos + v3s16(0,0,1);
1243 MapBlock *b = getBlockNoCreate(p);
1244 b->updateMesh(daynight_ratio);
1246 catch(InvalidPositionException &e){}*/
1251 bool Map::dayNightDiffed(v3s16 blockpos)
1254 v3s16 p = blockpos + v3s16(0,0,0);
1255 MapBlock *b = getBlockNoCreate(p);
1256 if(b->dayNightDiffed())
1259 catch(InvalidPositionException &e){}
1262 v3s16 p = blockpos + v3s16(-1,0,0);
1263 MapBlock *b = getBlockNoCreate(p);
1264 if(b->dayNightDiffed())
1267 catch(InvalidPositionException &e){}
1269 v3s16 p = blockpos + v3s16(0,-1,0);
1270 MapBlock *b = getBlockNoCreate(p);
1271 if(b->dayNightDiffed())
1274 catch(InvalidPositionException &e){}
1276 v3s16 p = blockpos + v3s16(0,0,-1);
1277 MapBlock *b = getBlockNoCreate(p);
1278 if(b->dayNightDiffed())
1281 catch(InvalidPositionException &e){}
1284 v3s16 p = blockpos + v3s16(1,0,0);
1285 MapBlock *b = getBlockNoCreate(p);
1286 if(b->dayNightDiffed())
1289 catch(InvalidPositionException &e){}
1291 v3s16 p = blockpos + v3s16(0,1,0);
1292 MapBlock *b = getBlockNoCreate(p);
1293 if(b->dayNightDiffed())
1296 catch(InvalidPositionException &e){}
1298 v3s16 p = blockpos + v3s16(0,0,1);
1299 MapBlock *b = getBlockNoCreate(p);
1300 if(b->dayNightDiffed())
1303 catch(InvalidPositionException &e){}
1309 Updates usage timers
1311 void Map::timerUpdate(float dtime)
1313 JMutexAutoLock lock(m_sector_mutex);
1315 core::map<v2s16, MapSector*>::Iterator si;
1317 si = m_sectors.getIterator();
1318 for(; si.atEnd() == false; si++)
1320 MapSector *sector = si.getNode()->getValue();
1321 sector->usage_timer += dtime;
1325 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1328 Wait for caches to be removed before continuing.
1330 This disables the existence of caches while locked
1332 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1334 core::list<v2s16>::Iterator j;
1335 for(j=list.begin(); j!=list.end(); j++)
1337 MapSector *sector = m_sectors[*j];
1340 sector->deleteBlocks();
1345 If sector is in sector cache, remove it from there
1347 if(m_sector_cache == sector)
1349 m_sector_cache = NULL;
1352 Remove from map and delete
1354 m_sectors.remove(*j);
1360 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1361 core::list<v3s16> *deleted_blocks)
1363 JMutexAutoLock lock(m_sector_mutex);
1365 core::list<v2s16> sector_deletion_queue;
1366 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1367 for(; i.atEnd() == false; i++)
1369 MapSector *sector = i.getNode()->getValue();
1371 Delete sector from memory if it hasn't been used in a long time
1373 if(sector->usage_timer > timeout)
1375 sector_deletion_queue.push_back(i.getNode()->getKey());
1377 if(deleted_blocks != NULL)
1379 // Collect positions of blocks of sector
1380 MapSector *sector = i.getNode()->getValue();
1381 core::list<MapBlock*> blocks;
1382 sector->getBlocks(blocks);
1383 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1384 i != blocks.end(); i++)
1386 deleted_blocks->push_back((*i)->getPos());
1391 deleteSectors(sector_deletion_queue, only_blocks);
1392 return sector_deletion_queue.getSize();
1395 void Map::PrintInfo(std::ostream &out)
1400 #define WATER_DROP_BOOST 4
1402 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1404 DSTACK(__FUNCTION_NAME);
1405 //TimeTaker timer("transformLiquids()");
1408 u32 initial_size = m_transforming_liquid.size();
1410 while(m_transforming_liquid.size() != 0)
1413 Get a queued transforming liquid node
1415 v3s16 p0 = m_transforming_liquid.pop_front();
1417 MapNode n0 = getNode(p0);
1419 // Don't deal with non-liquids
1420 if(content_liquid(n0.d) == false)
1423 bool is_source = !content_flowing_liquid(n0.d);
1425 u8 liquid_level = 8;
1426 if(is_source == false)
1427 liquid_level = n0.param2 & 0x0f;
1429 // Turn possible source into non-source
1430 u8 nonsource_c = make_liquid_flowing(n0.d);
1433 If not source, check that some node flows into this one
1434 and what is the level of liquid in this one
1436 if(is_source == false)
1438 s8 new_liquid_level_max = -1;
1440 v3s16 dirs_from[5] = {
1441 v3s16(0,1,0), // top
1442 v3s16(0,0,1), // back
1443 v3s16(1,0,0), // right
1444 v3s16(0,0,-1), // front
1445 v3s16(-1,0,0), // left
1447 for(u16 i=0; i<5; i++)
1452 bool from_top = (i==0);
1454 v3s16 p2 = p0 + dirs_from[i];
1455 MapNode n2 = getNode(p2);
1457 if(content_liquid(n2.d))
1459 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1460 // Check that the liquids are the same type
1461 if(n2_nonsource_c != nonsource_c)
1463 dstream<<"WARNING: Not handling: different liquids"
1464 " collide"<<std::endl;
1467 bool n2_is_source = !content_flowing_liquid(n2.d);
1468 s8 n2_liquid_level = 8;
1469 if(n2_is_source == false)
1470 n2_liquid_level = n2.param2 & 0x07;
1472 s8 new_liquid_level = -1;
1475 //new_liquid_level = 7;
1476 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1477 new_liquid_level = 7;
1479 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1481 else if(n2_liquid_level > 0)
1483 new_liquid_level = n2_liquid_level - 1;
1486 if(new_liquid_level > new_liquid_level_max)
1487 new_liquid_level_max = new_liquid_level;
1490 }catch(InvalidPositionException &e)
1496 If liquid level should be something else, update it and
1497 add all the neighboring water nodes to the transform queue.
1499 if(new_liquid_level_max != liquid_level)
1501 if(new_liquid_level_max == -1)
1503 // Remove water alltoghether
1510 n0.param2 = new_liquid_level_max;
1514 // Block has been modified
1516 v3s16 blockpos = getNodeBlockPos(p0);
1517 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1519 modified_blocks.insert(blockpos, block);
1523 Add neighboring non-source liquid nodes to transform queue.
1526 v3s16(0,0,1), // back
1527 v3s16(0,1,0), // top
1528 v3s16(1,0,0), // right
1529 v3s16(0,0,-1), // front
1530 v3s16(0,-1,0), // bottom
1531 v3s16(-1,0,0), // left
1533 for(u16 i=0; i<6; i++)
1538 v3s16 p2 = p0 + dirs[i];
1540 MapNode n2 = getNode(p2);
1541 if(content_flowing_liquid(n2.d))
1543 m_transforming_liquid.push_back(p2);
1546 }catch(InvalidPositionException &e)
1553 // Get a new one from queue if the node has turned into non-water
1554 if(content_liquid(n0.d) == false)
1558 Flow water from this node
1560 v3s16 dirs_to[5] = {
1561 v3s16(0,-1,0), // bottom
1562 v3s16(0,0,1), // back
1563 v3s16(1,0,0), // right
1564 v3s16(0,0,-1), // front
1565 v3s16(-1,0,0), // left
1567 for(u16 i=0; i<5; i++)
1572 bool to_bottom = (i == 0);
1574 // If liquid is at lowest possible height, it's not going
1575 // anywhere except down
1576 if(liquid_level == 0 && to_bottom == false)
1579 u8 liquid_next_level = 0;
1580 // If going to bottom
1583 //liquid_next_level = 7;
1584 if(liquid_level >= 7 - WATER_DROP_BOOST)
1585 liquid_next_level = 7;
1587 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1590 liquid_next_level = liquid_level - 1;
1592 bool n2_changed = false;
1593 bool flowed = false;
1595 v3s16 p2 = p0 + dirs_to[i];
1597 MapNode n2 = getNode(p2);
1598 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1600 if(content_liquid(n2.d))
1602 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1603 // Check that the liquids are the same type
1604 if(n2_nonsource_c != nonsource_c)
1606 dstream<<"WARNING: Not handling: different liquids"
1607 " collide"<<std::endl;
1610 bool n2_is_source = !content_flowing_liquid(n2.d);
1611 u8 n2_liquid_level = 8;
1612 if(n2_is_source == false)
1613 n2_liquid_level = n2.param2 & 0x07;
1622 // Just flow into the source, nothing changes.
1623 // n2_changed is not set because destination didn't change
1628 if(liquid_next_level > liquid_level)
1630 n2.param2 = liquid_next_level;
1638 else if(n2.d == CONTENT_AIR)
1641 n2.param2 = liquid_next_level;
1648 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1652 m_transforming_liquid.push_back(p2);
1654 v3s16 blockpos = getNodeBlockPos(p2);
1655 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1657 modified_blocks.insert(blockpos, block);
1660 // If n2_changed to bottom, don't flow anywhere else
1661 if(to_bottom && flowed && !is_source)
1664 }catch(InvalidPositionException &e)
1670 //if(loopcount >= 100000)
1671 if(loopcount >= initial_size * 1)
1674 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1681 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1686 Experimental and debug stuff
1690 dstream<<"Generating map point attribute lists"<<std::endl;
1692 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1693 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1694 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1695 PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1696 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1699 NOTE: BEWARE: Too big amount of these will make map generation
1700 slow. Especially those that are read by every block emerge.
1708 for(u32 i=0; i<5000; i++)
1710 /*u32 lim = MAP_GENERATION_LIMIT;
1714 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1717 -lim + myrand()%(lim*2),
1719 -lim + myrand()%(lim*2)
1721 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1722 plants_amount = pow(plants_amount, 5);
1723 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1725 float plants_amount = 0;
1728 plants_amount = 1.5;
1730 else if(myrand()%4 == 0)
1732 plants_amount = 0.5;
1734 else if(myrand()%2 == 0)
1736 plants_amount = 0.03;
1740 plants_amount = 0.0;
1744 list_plants_amount->addPoint(p, Attribute(plants_amount));
1747 for(u32 i=0; i<1000; i++)
1749 /*u32 lim = MAP_GENERATION_LIMIT;
1753 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000;
1756 -lim + myrand()%(lim*2),
1758 -lim + myrand()%(lim*2)
1761 float caves_amount = 0;
1766 else if(myrand()%3 == 0)
1772 caves_amount = 0.05;
1775 list_caves_amount->addPoint(p, Attribute(caves_amount));
1778 for(u32 i=0; i<5000; i++)
1780 /*u32 lim = MAP_GENERATION_LIMIT;
1784 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1787 -lim + (myrand()%(lim*2)),
1789 -lim + (myrand()%(lim*2))
1792 /*s32 bh_i = (myrand()%200) - 50;
1793 float baseheight = (float)bh_i;
1797 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1798 randmax = pow(randmax, e);
1800 //float randmax = (float)(myrand()%60);
1801 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1803 float baseheight = 0;
1805 float randfactor = 0;
1813 else if(myrand()%6 == 0)
1819 else if(myrand()%4 == 0)
1825 else if(myrand()%3 == 0)
1838 list_baseheight->addPoint(p, Attribute(baseheight));
1839 list_randmax->addPoint(p, Attribute(randmax));
1840 list_randfactor->addPoint(p, Attribute(randfactor));
1843 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
1844 list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
1845 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
1848 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1849 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1850 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1854 Try to load map; if not found, create a new one.
1857 m_savedir = savedir;
1858 m_map_saving_enabled = false;
1862 // If directory exists, check contents and load if possible
1863 if(fs::PathExists(m_savedir))
1865 // If directory is empty, it is safe to save into it.
1866 if(fs::GetDirListing(m_savedir).size() == 0)
1868 dstream<<DTIME<<"Server: Empty save directory is valid."
1870 m_map_saving_enabled = true;
1874 // Load master heightmap
1875 loadMasterHeightmap();
1877 // Load sector (0,0) and throw and exception on fail
1878 if(loadSectorFull(v2s16(0,0)) == false)
1879 throw LoadError("Failed to load sector (0,0)");
1881 dstream<<DTIME<<"Server: Successfully loaded master "
1882 "heightmap and sector (0,0) from "<<savedir<<
1883 ", assuming valid save directory."
1886 m_map_saving_enabled = true;
1887 // Map loaded, not creating new one
1891 // If directory doesn't exist, it is safe to save to it
1893 m_map_saving_enabled = true;
1896 catch(std::exception &e)
1898 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1899 <<", exception: "<<e.what()<<std::endl;
1900 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1901 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1904 dstream<<DTIME<<"Initializing new map."<<std::endl;
1906 // Create master heightmap
1907 /*ValueGenerator *maxgen =
1908 ValueGenerator::deSerialize(hmp.randmax);
1909 ValueGenerator *factorgen =
1910 ValueGenerator::deSerialize(hmp.randfactor);
1911 ValueGenerator *basegen =
1912 ValueGenerator::deSerialize(hmp.base);
1913 m_heightmap = new UnlimitedHeightmap
1914 (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
1916 /*m_heightmap = new UnlimitedHeightmap
1917 (hmp.blocksize, &m_padb);*/
1919 m_heightmap = new UnlimitedHeightmap
1922 // Set map parameters
1925 // Create zero sector
1926 emergeSector(v2s16(0,0));
1928 // Initially write whole map
1932 ServerMap::~ServerMap()
1936 if(m_map_saving_enabled)
1939 // Save only changed parts
1941 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1945 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1948 catch(std::exception &e)
1950 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1951 <<", exception: "<<e.what()<<std::endl;
1954 if(m_heightmap != NULL)
1958 MapSector * ServerMap::emergeSector(v2s16 p2d)
1960 DSTACK("%s: p2d=(%d,%d)",
1963 // Check that it doesn't exist already
1965 return getSectorNoGenerate(p2d);
1967 catch(InvalidPositionException &e)
1972 Try to load the sector from disk.
1974 if(loadSectorFull(p2d) == true)
1976 return getSectorNoGenerate(p2d);
1980 If there is no master heightmap, throw.
1982 if(m_heightmap == NULL)
1984 throw InvalidPositionException("emergeSector(): no heightmap");
1988 Do not generate over-limit
1990 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1991 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1992 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1993 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1994 throw InvalidPositionException("emergeSector(): pos. over limit");
1997 Generate sector and heightmaps
2000 // Number of heightmaps in sector in each direction
2001 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
2003 // Heightmap side width
2004 s16 hm_d = MAP_BLOCKSIZE / hm_split;
2006 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
2008 // Sector position on map in nodes
2009 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2011 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
2012 " heightmaps and objects"<<std::endl;*/
2015 Calculate some information about local properties
2018 v2s16 mhm_p = p2d * hm_split;
2020 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
2021 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
2022 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
2023 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
2026 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
2027 float avgslope = 0.0;
2028 avgslope += fabs(avgheight - corners[0]);
2029 avgslope += fabs(avgheight - corners[1]);
2030 avgslope += fabs(avgheight - corners[2]);
2031 avgslope += fabs(avgheight - corners[3]);
2033 avgslope /= MAP_BLOCKSIZE;
2034 //dstream<<"avgslope="<<avgslope<<std::endl;
2036 float pitness = 0.0;
2038 a = m_heightmap->getSlope(p2d+v2s16(0,0));
2041 a = m_heightmap->getSlope(p2d+v2s16(0,1));
2044 a = m_heightmap->getSlope(p2d+v2s16(1,1));
2047 a = m_heightmap->getSlope(p2d+v2s16(1,0));
2051 pitness /= MAP_BLOCKSIZE;
2052 //dstream<<"pitness="<<pitness<<std::endl;
2055 Get local attributes
2058 float local_plants_amount = 0.0;
2060 //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
2061 //TimeTaker attrtimer("emergeSector() attribute fetch");
2063 // Get plant amount from attributes
2064 PointAttributeList *palist = m_padb.getList("plants_amount");
2066 /*local_plants_amount =
2067 palist->getNearAttr(nodepos2d).getFloat();*/
2068 local_plants_amount =
2069 palist->getInterpolatedFloat(nodepos2d);
2073 Generate sector heightmap
2076 // Loop through sub-heightmaps
2077 for(s16 y=0; y<hm_split; y++)
2078 for(s16 x=0; x<hm_split; x++)
2080 v2s16 p_in_sector = v2s16(x,y);
2081 v2s16 mhm_p = p2d * hm_split + p_in_sector;
2083 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
2084 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
2085 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
2086 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
2089 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
2090 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
2093 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
2095 sector->setHeightmap(p_in_sector, hm);
2097 //TODO: Make these values configurable
2098 //hm->generateContinued(0.0, 0.0, corners);
2099 //hm->generateContinued(0.25, 0.2, corners);
2100 //hm->generateContinued(0.5, 0.2, corners);
2101 //hm->generateContinued(1.0, 0.2, corners);
2102 //hm->generateContinued(2.0, 0.2, corners);
2103 //hm->generateContinued(2.0 * avgslope, 0.5, corners);
2104 hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners);
2113 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
2114 sector->setObjects(objects);
2116 float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
2119 Plant some trees if there is not much slope
2122 // Avgslope is the derivative of a hill
2123 //float t = avgslope * avgslope;
2125 float a = area/16 * m_params.plants_amount * local_plants_amount;
2127 //float something = 0.17*0.17;
2128 float something = 0.3;
2130 tree_max = a / (t/something);
2134 u32 count = (myrand()%(tree_max+1));
2135 //u32 count = tree_max;
2136 for(u32 i=0; i<count; i++)
2138 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
2139 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
2140 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2143 objects->insert(v3s16(x, y, z),
2144 SECTOR_OBJECT_TREE_1);
2148 Plant some bushes if sector is pit-like
2151 // Pitness usually goes at around -0.5...0.5
2153 u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount;
2155 bush_max = (pitness*a*4);
2158 u32 count = (myrand()%(bush_max+1));
2159 for(u32 i=0; i<count; i++)
2161 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
2162 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
2163 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2166 objects->insert(v3s16(x, y, z),
2167 SECTOR_OBJECT_BUSH_1);
2171 Add ravine (randomly)
2173 if(m_params.ravines_amount > 0.001)
2175 if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
2178 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2179 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2182 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2183 objects->insert(v3s16(x, y, z),
2184 SECTOR_OBJECT_RAVINE);
2191 JMutexAutoLock lock(m_sector_mutex);
2192 m_sectors.insert(p2d, sector);
2197 MapBlock * ServerMap::emergeBlock(
2199 bool only_from_disk,
2200 core::map<v3s16, MapBlock*> &changed_blocks,
2201 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2204 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
2206 p.X, p.Y, p.Z, only_from_disk);
2208 /*dstream<<"ServerMap::emergeBlock(): "
2209 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2210 <<", only_from_disk="<<only_from_disk<<std::endl;*/
2211 v2s16 p2d(p.X, p.Z);
2214 This will create or load a sector if not found in memory.
2215 If block exists on disk, it will be loaded.
2217 NOTE: On old save formats, this will be slow, as it generates
2218 lighting on blocks for them.
2220 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
2221 assert(sector->getId() == MAPSECTOR_SERVER);
2223 // Try to get a block from the sector
2224 MapBlock *block = NULL;
2225 bool not_on_disk = false;
2227 block = sector->getBlockNoCreate(block_y);
2228 if(block->isDummy() == true)
2233 catch(InvalidPositionException &e)
2239 If block was not found on disk and not going to generate a
2240 new one, make sure there is a dummy block in place.
2242 if(not_on_disk && only_from_disk)
2246 // Create dummy block
2247 block = new MapBlock(this, p, true);
2249 // Add block to sector
2250 sector->insertBlock(block);
2256 //dstream<<"Not found on disk, generating."<<std::endl;
2258 //TimeTaker("emergeBlock() generate");
2261 Do not generate over-limit
2263 if(blockpos_over_limit(p))
2264 throw InvalidPositionException("emergeBlock(): pos. over limit");
2269 Go on generating the block.
2271 TODO: If a dungeon gets generated so that it's side gets
2272 revealed to the outside air, the lighting should be
2277 If block doesn't exist, create one.
2278 If it exists, it is a dummy. In that case unDummify() it.
2280 NOTE: This already sets the map as the parent of the block
2284 block = sector->createBlankBlockNoInsert(block_y);
2288 // Remove the block so that nobody can get a half-generated one.
2289 sector->removeBlock(block);
2290 // Allocate the block to contain the generated data
2294 /*u8 water_material = CONTENT_WATER;
2295 if(g_settings.getBool("endless_water"))
2296 water_material = CONTENT_WATERSOURCE;*/
2297 u8 water_material = CONTENT_WATERSOURCE;
2299 s32 lowest_ground_y = 32767;
2300 s32 highest_ground_y = -32768;
2303 //sector->printHeightmaps();
2305 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2306 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2308 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
2310 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
2311 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
2312 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
2314 dstream<<"WARNING: Surface height not found in sector "
2315 "for block that is being emerged"<<std::endl;
2319 s16 surface_y = surface_y_f;
2320 //avg_ground_y += surface_y;
2321 if(surface_y < lowest_ground_y)
2322 lowest_ground_y = surface_y;
2323 if(surface_y > highest_ground_y)
2324 highest_ground_y = surface_y;
2326 s32 surface_depth = 0;
2328 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
2330 //float min_slope = 0.45;
2331 //float max_slope = 0.85;
2332 float min_slope = 0.60;
2333 float max_slope = 1.20;
2334 float min_slope_depth = 5.0;
2335 float max_slope_depth = 0;
2337 if(slope < min_slope)
2338 surface_depth = min_slope_depth;
2339 else if(slope > max_slope)
2340 surface_depth = max_slope_depth;
2342 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2344 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2346 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2351 NOTE: If there are some man-made structures above the
2352 newly created block, they won't be taken into account.
2354 if(real_y > surface_y)
2355 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2361 // If node is over heightmap y, it's air or water
2362 if(real_y > surface_y)
2364 // If under water level, it's water
2365 if(real_y < WATER_LEVEL)
2367 n.d = water_material;
2368 n.setLight(LIGHTBANK_DAY,
2369 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2371 Add to transforming liquid queue (in case it'd
2374 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
2375 m_transforming_liquid.push_back(real_pos);
2381 // Else it's ground or dungeons (air)
2384 // If it's surface_depth under ground, it's stone
2385 if(real_y <= surface_y - surface_depth)
2387 n.d = CONTENT_STONE;
2391 // It is mud if it is under the first ground
2392 // level or under water
2393 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2399 n.d = CONTENT_GRASS;
2402 //n.d = CONTENT_MUD;
2404 /*// If under water level, it's mud
2405 if(real_y < WATER_LEVEL)
2407 // Only the topmost node is grass
2408 else if(real_y <= surface_y - 1)
2411 n.d = CONTENT_GRASS;*/
2415 block->setNode(v3s16(x0,y0,z0), n);
2420 Calculate some helper variables
2423 // Completely underground if the highest part of block is under lowest
2425 // This has to be very sure; it's probably one too strict now but
2426 // that's just better.
2427 bool completely_underground =
2428 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2430 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2432 bool mostly_underwater_surface = false;
2433 if(highest_ground_y < WATER_LEVEL
2434 && some_part_underground && !completely_underground)
2435 mostly_underwater_surface = true;
2438 Get local attributes
2441 //dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
2443 float caves_amount = 0;
2447 NOTE: BEWARE: Too big amount of attribute points slows verything
2449 1 interpolation from 5000 points takes 2-3ms.
2451 //TimeTaker timer("emergeBlock() local attribute retrieval");
2452 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2453 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
2454 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
2457 //dstream<<"emergeBlock(): Done"<<std::endl;
2463 // Initialize temporary table
2464 const s32 ued = MAP_BLOCKSIZE;
2465 bool underground_emptiness[ued*ued*ued];
2466 for(s32 i=0; i<ued*ued*ued; i++)
2468 underground_emptiness[i] = 0;
2475 Initialize orp and ors. Try to find if some neighboring
2476 MapBlock has a tunnel ended in its side
2480 (float)(myrand()%ued)+0.5,
2481 (float)(myrand()%ued)+0.5,
2482 (float)(myrand()%ued)+0.5
2485 bool found_existing = false;
2491 for(s16 y=0; y<ued; y++)
2492 for(s16 x=0; x<ued; x++)
2494 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2495 if(getNode(ap).d == CONTENT_AIR)
2497 orp = v3f(x+1,y+1,0);
2498 found_existing = true;
2499 goto continue_generating;
2503 catch(InvalidPositionException &e){}
2509 for(s16 y=0; y<ued; y++)
2510 for(s16 x=0; x<ued; x++)
2512 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2513 if(getNode(ap).d == CONTENT_AIR)
2515 orp = v3f(x+1,y+1,ued-1);
2516 found_existing = true;
2517 goto continue_generating;
2521 catch(InvalidPositionException &e){}
2527 for(s16 y=0; y<ued; y++)
2528 for(s16 z=0; z<ued; z++)
2530 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2531 if(getNode(ap).d == CONTENT_AIR)
2533 orp = v3f(0,y+1,z+1);
2534 found_existing = true;
2535 goto continue_generating;
2539 catch(InvalidPositionException &e){}
2545 for(s16 y=0; y<ued; y++)
2546 for(s16 z=0; z<ued; z++)
2548 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2549 if(getNode(ap).d == CONTENT_AIR)
2551 orp = v3f(ued-1,y+1,z+1);
2552 found_existing = true;
2553 goto continue_generating;
2557 catch(InvalidPositionException &e){}
2563 for(s16 x=0; x<ued; x++)
2564 for(s16 z=0; z<ued; z++)
2566 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2567 if(getNode(ap).d == CONTENT_AIR)
2569 orp = v3f(x+1,0,z+1);
2570 found_existing = true;
2571 goto continue_generating;
2575 catch(InvalidPositionException &e){}
2581 for(s16 x=0; x<ued; x++)
2582 for(s16 z=0; z<ued; z++)
2584 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2585 if(getNode(ap).d == CONTENT_AIR)
2587 orp = v3f(x+1,ued-1,z+1);
2588 found_existing = true;
2589 goto continue_generating;
2593 catch(InvalidPositionException &e){}
2595 continue_generating:
2598 Choose whether to actually generate dungeon
2600 bool do_generate_dungeons = true;
2601 // Don't generate if no part is underground
2602 if(!some_part_underground)
2604 do_generate_dungeons = false;
2606 // Don't generate if mostly underwater surface
2607 else if(mostly_underwater_surface)
2609 do_generate_dungeons = false;
2611 // Partly underground = cave
2612 else if(!completely_underground)
2614 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2616 // Found existing dungeon underground
2617 else if(found_existing && completely_underground)
2619 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2621 // Underground and no dungeons found
2624 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
2627 if(do_generate_dungeons)
2630 Generate some tunnel starting from orp and ors
2632 for(u16 i=0; i<3; i++)
2635 (float)(myrand()%ued)+0.5,
2636 (float)(myrand()%ued)+0.5,
2637 (float)(myrand()%ued)+0.5
2641 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
2645 for(float f=0; f<1.0; f+=0.04)
2647 v3f fp = orp + vec * f;
2648 v3s16 cp(fp.X, fp.Y, fp.Z);
2650 s16 d1 = d0 + rs - 1;
2651 for(s16 z0=d0; z0<=d1; z0++)
2653 s16 si = rs - abs(z0);
2654 for(s16 x0=-si; x0<=si-1; x0++)
2656 s16 si2 = rs - abs(x0);
2657 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2663 if(isInArea(p, ued) == false)
2665 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2677 // Set to true if has caves.
2678 // Set when some non-air is changed to air when making caves.
2679 bool has_dungeons = false;
2682 Apply temporary cave data to block
2685 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2686 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2688 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2690 MapNode n = block->getNode(v3s16(x0,y0,z0));
2693 if(underground_emptiness[
2694 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2695 +ued*(y0*ued/MAP_BLOCKSIZE)
2696 +(x0*ued/MAP_BLOCKSIZE)])
2698 if(is_ground_content(n.d))
2701 has_dungeons = true;
2707 block->setNode(v3s16(x0,y0,z0), n);
2712 This is used for guessing whether or not the block should
2713 receive sunlight from the top if the top block doesn't exist
2715 block->setIsUnderground(completely_underground);
2718 Force lighting update if some part of block is partly
2719 underground and has caves.
2721 /*if(some_part_underground && !completely_underground && has_dungeons)
2723 //dstream<<"Half-ground caves"<<std::endl;
2724 lighting_invalidated_blocks[block->getPos()] = block;
2727 // DEBUG: Always update lighting
2728 //lighting_invalidated_blocks[block->getPos()] = block;
2734 if(some_part_underground)
2736 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2741 for(s16 i=0; i<underground_level/4 + 1; i++)
2743 if(myrand()%50 == 0)
2746 (myrand()%(MAP_BLOCKSIZE-2))+1,
2747 (myrand()%(MAP_BLOCKSIZE-2))+1,
2748 (myrand()%(MAP_BLOCKSIZE-2))+1
2754 //if(is_ground_content(block->getNode(cp).d))
2755 if(block->getNode(cp).d == CONTENT_STONE)
2757 block->setNode(cp, n);
2759 for(u16 i=0; i<26; i++)
2761 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2762 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2764 block->setNode(cp+g_26dirs[i], n);
2772 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2773 u16 coal_rareness = 60 / coal_amount;
2774 if(coal_rareness == 0)
2776 if(myrand()%coal_rareness == 0)
2778 u16 a = myrand() % 16;
2779 u16 amount = coal_amount * a*a*a / 1000;
2780 for(s16 i=0; i<amount; i++)
2783 (myrand()%(MAP_BLOCKSIZE-2))+1,
2784 (myrand()%(MAP_BLOCKSIZE-2))+1,
2785 (myrand()%(MAP_BLOCKSIZE-2))+1
2789 n.d = CONTENT_COALSTONE;
2791 //dstream<<"Adding coalstone"<<std::endl;
2793 //if(is_ground_content(block->getNode(cp).d))
2794 if(block->getNode(cp).d == CONTENT_STONE)
2796 block->setNode(cp, n);
2798 for(u16 i=0; i<26; i++)
2800 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2801 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2803 block->setNode(cp+g_26dirs[i], n);
2810 Create a few rats in empty blocks underground
2812 if(completely_underground)
2814 //for(u16 i=0; i<2; i++)
2817 (myrand()%(MAP_BLOCKSIZE-2))+1,
2818 (myrand()%(MAP_BLOCKSIZE-2))+1,
2819 (myrand()%(MAP_BLOCKSIZE-2))+1
2822 // Check that the place is empty
2823 //if(!is_ground_content(block->getNode(cp).d))
2826 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2827 block->addObject(obj);
2833 Add block to sector.
2835 sector->insertBlock(block);
2841 // An y-wise container of changed blocks
2842 core::map<s16, MapBlock*> changed_blocks_sector;
2845 Check if any sector's objects can be placed now.
2848 core::map<v3s16, u8> *objects = sector->getObjects();
2849 core::list<v3s16> objects_to_remove;
2850 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2851 i.atEnd() == false; i++)
2853 v3s16 p = i.getNode()->getKey();
2855 u8 d = i.getNode()->getValue();
2857 // Ground level point (user for stuff that is on ground)
2859 bool ground_found = true;
2861 // Search real ground level
2865 MapNode n = sector->getNode(gp);
2867 // If not air, go one up and continue to placing the tree
2868 if(n.d != CONTENT_AIR)
2874 // If air, go one down
2875 gp += v3s16(0,-1,0);
2877 }catch(InvalidPositionException &e)
2879 // Ground not found.
2880 ground_found = false;
2881 // This is most close to ground
2888 if(d == SECTOR_OBJECT_TEST)
2890 if(sector->isValidArea(p + v3s16(0,0,0),
2891 p + v3s16(0,0,0), &changed_blocks_sector))
2894 n.d = CONTENT_TORCH;
2895 sector->setNode(p, n);
2896 objects_to_remove.push_back(p);
2899 else if(d == SECTOR_OBJECT_TREE_1)
2901 if(ground_found == false)
2904 v3s16 p_min = gp + v3s16(-1,0,-1);
2905 v3s16 p_max = gp + v3s16(1,5,1);
2906 if(sector->isValidArea(p_min, p_max,
2907 &changed_blocks_sector))
2911 sector->setNode(gp+v3s16(0,0,0), n);
2912 sector->setNode(gp+v3s16(0,1,0), n);
2913 sector->setNode(gp+v3s16(0,2,0), n);
2914 sector->setNode(gp+v3s16(0,3,0), n);
2916 n.d = CONTENT_LEAVES;
2918 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2920 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2921 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2922 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2923 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2924 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2925 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2926 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2927 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2929 sector->setNode(gp+v3s16(0,4,0), n);
2931 sector->setNode(gp+v3s16(-1,4,0), n);
2932 sector->setNode(gp+v3s16(1,4,0), n);
2933 sector->setNode(gp+v3s16(0,4,-1), n);
2934 sector->setNode(gp+v3s16(0,4,1), n);
2935 sector->setNode(gp+v3s16(1,4,1), n);
2936 sector->setNode(gp+v3s16(-1,4,1), n);
2937 sector->setNode(gp+v3s16(-1,4,-1), n);
2938 sector->setNode(gp+v3s16(1,4,-1), n);
2940 sector->setNode(gp+v3s16(-1,3,0), n);
2941 sector->setNode(gp+v3s16(1,3,0), n);
2942 sector->setNode(gp+v3s16(0,3,-1), n);
2943 sector->setNode(gp+v3s16(0,3,1), n);
2944 sector->setNode(gp+v3s16(1,3,1), n);
2945 sector->setNode(gp+v3s16(-1,3,1), n);
2946 sector->setNode(gp+v3s16(-1,3,-1), n);
2947 sector->setNode(gp+v3s16(1,3,-1), n);
2949 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2950 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2951 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2952 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2953 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2954 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2955 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2956 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2958 // Objects are identified by wanted position
2959 objects_to_remove.push_back(p);
2961 // Lighting has to be recalculated for this one.
2962 sector->getBlocksInArea(p_min, p_max,
2963 lighting_invalidated_blocks);
2966 else if(d == SECTOR_OBJECT_BUSH_1)
2968 if(ground_found == false)
2971 if(sector->isValidArea(gp + v3s16(0,0,0),
2972 gp + v3s16(0,0,0), &changed_blocks_sector))
2975 n.d = CONTENT_LEAVES;
2976 sector->setNode(gp+v3s16(0,0,0), n);
2978 // Objects are identified by wanted position
2979 objects_to_remove.push_back(p);
2982 else if(d == SECTOR_OBJECT_RAVINE)
2985 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2986 v3s16 p_max = p + v3s16(6,6,6);
2987 if(sector->isValidArea(p_min, p_max,
2988 &changed_blocks_sector))
2991 n.d = CONTENT_STONE;
2994 s16 depth = maxdepth + (myrand()%10);
2996 s16 minz = -6 - (-2);
2998 for(s16 x=-6; x<=6; x++)
3000 z += -1 + (myrand()%3);
3005 for(s16 y=depth+(myrand()%2); y<=6; y++)
3007 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
3010 v3s16 p2 = p + v3s16(x,y,z-2);
3011 if(is_ground_content(sector->getNode(p2).d)
3012 && !is_mineral(sector->getNode(p2).d))
3013 sector->setNode(p2, n);
3016 v3s16 p2 = p + v3s16(x,y,z-1);
3017 if(is_ground_content(sector->getNode(p2).d)
3018 && !is_mineral(sector->getNode(p2).d))
3019 sector->setNode(p2, n2);
3022 v3s16 p2 = p + v3s16(x,y,z+0);
3023 if(is_ground_content(sector->getNode(p2).d)
3024 && !is_mineral(sector->getNode(p2).d))
3025 sector->setNode(p2, n2);
3028 v3s16 p2 = p + v3s16(x,y,z+1);
3029 if(is_ground_content(sector->getNode(p2).d)
3030 && !is_mineral(sector->getNode(p2).d))
3031 sector->setNode(p2, n);
3034 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
3035 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
3039 objects_to_remove.push_back(p);
3041 // Lighting has to be recalculated for this one.
3042 sector->getBlocksInArea(p_min, p_max,
3043 lighting_invalidated_blocks);
3048 dstream<<"ServerMap::emergeBlock(): "
3049 "Invalid heightmap object"
3054 catch(InvalidPositionException &e)
3056 dstream<<"WARNING: "<<__FUNCTION_NAME
3057 <<": while inserting object "<<(int)d
3058 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
3059 <<" InvalidPositionException.what()="
3060 <<e.what()<<std::endl;
3061 // This is not too fatal and seems to happen sometimes.
3066 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
3067 i != objects_to_remove.end(); i++)
3069 objects->remove(*i);
3073 Initially update sunlight
3077 core::map<v3s16, bool> light_sources;
3078 bool black_air_left = false;
3079 bool bottom_invalid =
3080 block->propagateSunlight(light_sources, true,
3081 &black_air_left, true);
3083 // If sunlight didn't reach everywhere and part of block is
3084 // above ground, lighting has to be properly updated
3085 if(black_air_left && some_part_underground)
3087 lighting_invalidated_blocks[block->getPos()] = block;
3092 lighting_invalidated_blocks[block->getPos()] = block;
3097 Translate sector's changed blocks to global changed blocks
3100 for(core::map<s16, MapBlock*>::Iterator
3101 i = changed_blocks_sector.getIterator();
3102 i.atEnd() == false; i++)
3104 MapBlock *block = i.getNode()->getValue();
3106 changed_blocks.insert(block->getPos(), block);
3115 <<"lighting_invalidated_blocks.size()"
3119 <<" "<<lighting_invalidated_blocks.size()
3120 <<", "<<has_dungeons
3121 <<", "<<completely_underground
3122 <<", "<<some_part_underground
3127 Debug mode operation
3129 bool haxmode = g_settings.getBool("haxmode");
3132 // Don't calculate lighting at all
3133 //lighting_invalidated_blocks.clear();
3139 void ServerMap::createDir(std::string path)
3141 if(fs::CreateDir(path) == false)
3143 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3144 <<"\""<<path<<"\""<<std::endl;
3145 throw BaseException("ServerMap failed to create directory");
3149 std::string ServerMap::getSectorSubDir(v2s16 pos)
3152 snprintf(cc, 9, "%.4x%.4x",
3153 (unsigned int)pos.X&0xffff,
3154 (unsigned int)pos.Y&0xffff);
3156 return std::string(cc);
3159 std::string ServerMap::getSectorDir(v2s16 pos)
3161 return m_savedir + "/sectors/" + getSectorSubDir(pos);
3164 v2s16 ServerMap::getSectorPos(std::string dirname)
3166 if(dirname.size() != 8)
3167 throw InvalidFilenameException("Invalid sector directory name");
3169 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
3171 throw InvalidFilenameException("Invalid sector directory name");
3172 v2s16 pos((s16)x, (s16)y);
3176 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3178 v2s16 p2d = getSectorPos(sectordir);
3180 if(blockfile.size() != 4){
3181 throw InvalidFilenameException("Invalid block filename");
3184 int r = sscanf(blockfile.c_str(), "%4x", &y);
3186 throw InvalidFilenameException("Invalid block filename");
3187 return v3s16(p2d.X, y, p2d.Y);
3191 #define ENABLE_SECTOR_SAVING 1
3192 #define ENABLE_SECTOR_LOADING 1
3193 #define ENABLE_BLOCK_SAVING 1
3194 #define ENABLE_BLOCK_LOADING 1
3196 void ServerMap::save(bool only_changed)
3198 DSTACK(__FUNCTION_NAME);
3199 if(m_map_saving_enabled == false)
3201 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
3205 if(only_changed == false)
3206 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
3209 saveMasterHeightmap();
3211 u32 sector_meta_count = 0;
3212 u32 block_count = 0;
3215 JMutexAutoLock lock(m_sector_mutex);
3217 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3218 for(; i.atEnd() == false; i++)
3220 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3221 assert(sector->getId() == MAPSECTOR_SERVER);
3223 if(ENABLE_SECTOR_SAVING)
3225 if(sector->differs_from_disk || only_changed == false)
3227 saveSectorMeta(sector);
3228 sector_meta_count++;
3231 if(ENABLE_BLOCK_SAVING)
3233 core::list<MapBlock*> blocks;
3234 sector->getBlocks(blocks);
3235 core::list<MapBlock*>::Iterator j;
3236 for(j=blocks.begin(); j!=blocks.end(); j++)
3238 MapBlock *block = *j;
3239 if(block->getChangedFlag() || only_changed == false)
3251 Only print if something happened or saved whole map
3253 if(only_changed == false || sector_meta_count != 0
3254 || block_count != 0)
3256 dstream<<DTIME<<"ServerMap: Written: "
3257 <<sector_meta_count<<" sector metadata files, "
3258 <<block_count<<" block files"
3263 void ServerMap::loadAll()
3265 DSTACK(__FUNCTION_NAME);
3266 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
3268 loadMasterHeightmap();
3270 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
3272 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
3274 JMutexAutoLock lock(m_sector_mutex);
3277 s32 printed_counter = -100000;
3278 s32 count = list.size();
3280 std::vector<fs::DirListNode>::iterator i;
3281 for(i=list.begin(); i!=list.end(); i++)
3283 if(counter > printed_counter + 10)
3285 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
3286 printed_counter = counter;
3290 MapSector *sector = NULL;
3292 // We want directories
3296 sector = loadSectorMeta(i->name);
3298 catch(InvalidFilenameException &e)
3300 // This catches unknown crap in directory
3303 if(ENABLE_BLOCK_LOADING)
3305 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3306 (m_savedir+"/sectors/"+i->name);
3307 std::vector<fs::DirListNode>::iterator i2;
3308 for(i2=list2.begin(); i2!=list2.end(); i2++)
3314 loadBlock(i->name, i2->name, sector);
3316 catch(InvalidFilenameException &e)
3318 // This catches unknown crap in directory
3323 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
3326 void ServerMap::saveMasterHeightmap()
3328 DSTACK(__FUNCTION_NAME);
3329 createDir(m_savedir);
3331 std::string fullpath = m_savedir + "/master_heightmap";
3332 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3333 if(o.good() == false)
3334 throw FileNotGoodException("Cannot open master heightmap");
3336 // Format used for writing
3337 u8 version = SER_FMT_VER_HIGHEST;
3340 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
3342 [0] u8 serialization version
3343 [1] X master heightmap
3345 u32 fullsize = 1 + hmdata.getSize();
3346 SharedBuffer<u8> data(fullsize);
3349 memcpy(&data[1], *hmdata, hmdata.getSize());
3351 o.write((const char*)*data, fullsize);
3354 m_heightmap->serialize(o, version);
3357 void ServerMap::loadMasterHeightmap()
3359 DSTACK(__FUNCTION_NAME);
3360 std::string fullpath = m_savedir + "/master_heightmap";
3361 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3362 if(is.good() == false)
3363 throw FileNotGoodException("Cannot open master heightmap");
3365 if(m_heightmap != NULL)
3368 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
3371 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3373 DSTACK(__FUNCTION_NAME);
3374 // Format used for writing
3375 u8 version = SER_FMT_VER_HIGHEST;
3377 v2s16 pos = sector->getPos();
3378 createDir(m_savedir);
3379 createDir(m_savedir+"/sectors");
3380 std::string dir = getSectorDir(pos);
3383 std::string fullpath = dir + "/heightmap";
3384 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3385 if(o.good() == false)
3386 throw FileNotGoodException("Cannot open master heightmap");
3388 sector->serialize(o, version);
3390 sector->differs_from_disk = false;
3393 MapSector* ServerMap::loadSectorMeta(std::string dirname)
3395 DSTACK(__FUNCTION_NAME);
3397 v2s16 p2d = getSectorPos(dirname);
3398 std::string dir = m_savedir + "/sectors/" + dirname;
3400 std::string fullpath = dir + "/heightmap";
3401 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3402 if(is.good() == false)
3403 throw FileNotGoodException("Cannot open sector heightmap");
3405 ServerMapSector *sector = ServerMapSector::deSerialize
3406 (is, this, p2d, &m_hwrapper, m_sectors);
3408 sector->differs_from_disk = false;
3413 bool ServerMap::loadSectorFull(v2s16 p2d)
3415 DSTACK(__FUNCTION_NAME);
3416 std::string sectorsubdir = getSectorSubDir(p2d);
3418 MapSector *sector = NULL;
3420 JMutexAutoLock lock(m_sector_mutex);
3423 sector = loadSectorMeta(sectorsubdir);
3425 catch(InvalidFilenameException &e)
3429 catch(FileNotGoodException &e)
3433 catch(std::exception &e)
3438 if(ENABLE_BLOCK_LOADING)
3440 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3441 (m_savedir+"/sectors/"+sectorsubdir);
3442 std::vector<fs::DirListNode>::iterator i2;
3443 for(i2=list2.begin(); i2!=list2.end(); i2++)
3449 loadBlock(sectorsubdir, i2->name, sector);
3451 catch(InvalidFilenameException &e)
3453 // This catches unknown crap in directory
3461 bool ServerMap::deFlushSector(v2s16 p2d)
3463 DSTACK(__FUNCTION_NAME);
3464 // See if it already exists in memory
3466 MapSector *sector = getSectorNoGenerate(p2d);
3469 catch(InvalidPositionException &e)
3472 Try to load the sector from disk.
3474 if(loadSectorFull(p2d) == true)
3483 void ServerMap::saveBlock(MapBlock *block)
3485 DSTACK(__FUNCTION_NAME);
3487 Dummy blocks are not written
3489 if(block->isDummy())
3491 /*v3s16 p = block->getPos();
3492 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3493 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3497 // Format used for writing
3498 u8 version = SER_FMT_VER_HIGHEST;
3500 v3s16 p3d = block->getPos();
3501 v2s16 p2d(p3d.X, p3d.Z);
3502 createDir(m_savedir);
3503 createDir(m_savedir+"/sectors");
3504 std::string dir = getSectorDir(p2d);
3507 // Block file is map/sectors/xxxxxxxx/xxxx
3509 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
3510 std::string fullpath = dir + "/" + cc;
3511 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3512 if(o.good() == false)
3513 throw FileNotGoodException("Cannot open block data");
3516 [0] u8 serialization version
3519 o.write((char*)&version, 1);
3521 block->serialize(o, version);
3524 Versions up from 9 have block objects.
3528 block->serializeObjects(o, version);
3531 // We just wrote it to the disk
3532 block->resetChangedFlag();
3535 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
3537 DSTACK(__FUNCTION_NAME);
3541 // Block file is map/sectors/xxxxxxxx/xxxx
3542 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
3543 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3544 if(is.good() == false)
3545 throw FileNotGoodException("Cannot open block file");
3547 v3s16 p3d = getBlockPos(sectordir, blockfile);
3548 v2s16 p2d(p3d.X, p3d.Z);
3550 assert(sector->getPos() == p2d);
3552 u8 version = SER_FMT_VER_INVALID;
3553 is.read((char*)&version, 1);
3555 /*u32 block_size = MapBlock::serializedLength(version);
3556 SharedBuffer<u8> data(block_size);
3557 is.read((char*)*data, block_size);*/
3559 // This will always return a sector because we're the server
3560 //MapSector *sector = emergeSector(p2d);
3562 MapBlock *block = NULL;
3563 bool created_new = false;
3565 block = sector->getBlockNoCreate(p3d.Y);
3567 catch(InvalidPositionException &e)
3569 block = sector->createBlankBlockNoInsert(p3d.Y);
3573 // deserialize block data
3574 block->deSerialize(is, version);
3577 Versions up from 9 have block objects.
3581 block->updateObjects(is, version, NULL, 0);
3585 sector->insertBlock(block);
3588 Convert old formats to new and save
3591 // Save old format blocks in new format
3592 if(version < SER_FMT_VER_HIGHEST)
3597 // We just loaded it from the disk, so it's up-to-date.
3598 block->resetChangedFlag();
3601 catch(SerializationError &e)
3603 dstream<<"WARNING: Invalid block data on disk "
3604 "(SerializationError). Ignoring."
3609 // Gets from master heightmap
3610 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
3612 assert(m_heightmap != NULL);
3620 corners[0] = m_heightmap->getGroundHeight
3621 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
3622 corners[1] = m_heightmap->getGroundHeight
3623 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
3624 corners[2] = m_heightmap->getGroundHeight
3625 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
3626 corners[3] = m_heightmap->getGroundHeight
3627 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
3630 void ServerMap::PrintInfo(std::ostream &out)
3641 ClientMap::ClientMap(
3643 MapDrawControl &control,
3644 scene::ISceneNode* parent,
3645 scene::ISceneManager* mgr,
3649 scene::ISceneNode(parent, mgr, id),
3656 /*m_box = core::aabbox3d<f32>(0,0,0,
3657 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
3658 /*m_box = core::aabbox3d<f32>(0,0,0,
3659 map->getSizeNodes().X * BS,
3660 map->getSizeNodes().Y * BS,
3661 map->getSizeNodes().Z * BS);*/
3662 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3663 BS*1000000,BS*1000000,BS*1000000);
3665 //setPosition(v3f(BS,BS,BS));
3668 ClientMap::~ClientMap()
3670 JMutexAutoLock lock(mesh_mutex);
3679 MapSector * ClientMap::emergeSector(v2s16 p2d)
3681 DSTACK(__FUNCTION_NAME);
3682 // Check that it doesn't exist already
3684 return getSectorNoGenerate(p2d);
3686 catch(InvalidPositionException &e)
3690 // Create a sector with no heightmaps
3691 ClientMapSector *sector = new ClientMapSector(this, p2d);
3694 JMutexAutoLock lock(m_sector_mutex);
3695 m_sectors.insert(p2d, sector);
3701 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3703 DSTACK(__FUNCTION_NAME);
3704 ClientMapSector *sector = NULL;
3706 JMutexAutoLock lock(m_sector_mutex);
3708 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3712 sector = (ClientMapSector*)n->getValue();
3713 assert(sector->getId() == MAPSECTOR_CLIENT);
3717 sector = new ClientMapSector(this, p2d);
3719 JMutexAutoLock lock(m_sector_mutex);
3720 m_sectors.insert(p2d, sector);
3724 sector->deSerialize(is);
3727 void ClientMap::OnRegisterSceneNode()
3731 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3732 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3735 ISceneNode::OnRegisterSceneNode();
3738 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3740 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3741 DSTACK(__FUNCTION_NAME);
3743 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3746 Get time for measuring timeout.
3748 Measuring time is very useful for long delays when the
3749 machine is swapping a lot.
3751 int time1 = time(0);
3753 u32 daynight_ratio = m_client->getDayNightRatio();
3755 m_camera_mutex.Lock();
3756 v3f camera_position = m_camera_position;
3757 v3f camera_direction = m_camera_direction;
3758 m_camera_mutex.Unlock();
3761 Get all blocks and draw all visible ones
3764 v3s16 cam_pos_nodes(
3765 camera_position.X / BS,
3766 camera_position.Y / BS,
3767 camera_position.Z / BS);
3769 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3771 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3772 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3774 // Take a fair amount as we will be dropping more out later
3776 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3777 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3778 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3780 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3781 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3782 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3784 u32 vertex_count = 0;
3786 // For limiting number of mesh updates per frame
3787 u32 mesh_update_count = 0;
3789 u32 blocks_would_have_drawn = 0;
3790 u32 blocks_drawn = 0;
3792 //NOTE: The sectors map should be locked but we're not doing it
3793 // because it'd cause too much delays
3795 int timecheck_counter = 0;
3796 core::map<v2s16, MapSector*>::Iterator si;
3797 si = m_sectors.getIterator();
3798 for(; si.atEnd() == false; si++)
3801 timecheck_counter++;
3802 if(timecheck_counter > 50)
3804 int time2 = time(0);
3805 if(time2 > time1 + 4)
3807 dstream<<"ClientMap::renderMap(): "
3808 "Rendering takes ages, returning."
3815 MapSector *sector = si.getNode()->getValue();
3816 v2s16 sp = sector->getPos();
3818 if(m_control.range_all == false)
3820 if(sp.X < p_blocks_min.X
3821 || sp.X > p_blocks_max.X
3822 || sp.Y < p_blocks_min.Z
3823 || sp.Y > p_blocks_max.Z)
3827 core::list< MapBlock * > sectorblocks;
3828 sector->getBlocks(sectorblocks);
3834 core::list< MapBlock * >::Iterator i;
3835 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3837 MapBlock *block = *i;
3840 Compare block position to camera position, skip
3841 if not seen on display
3844 float range = 100000 * BS;
3845 if(m_control.range_all == false)
3846 range = m_control.wanted_range * BS;
3848 if(isBlockInSight(block->getPos(), camera_position,
3849 camera_direction, range) == false)
3855 v3s16 blockpos_nodes = block->getPosRelative();
3857 // Block center position
3859 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3860 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3861 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3864 // Block position relative to camera
3865 v3f blockpos_relative = blockpos - camera_position;
3867 // Distance in camera direction (+=front, -=back)
3868 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3871 f32 d = blockpos_relative.getLength();
3873 if(m_control.range_all == false)
3875 // If block is far away, don't draw it
3876 if(d > m_control.wanted_range * BS)
3880 // Maximum radius of a block
3881 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3883 // If block is (nearly) touching the camera, don't
3884 // bother validating further (that is, render it anyway)
3885 if(d > block_max_radius * 1.5)
3887 // Cosine of the angle between the camera direction
3888 // and the block direction (camera_direction is an unit vector)
3889 f32 cosangle = dforward / d;
3891 // Compensate for the size of the block
3892 // (as the block has to be shown even if it's a bit off FOV)
3893 // This is an estimate.
3894 cosangle += block_max_radius / dforward;
3896 // If block is not in the field of view, skip it
3897 //if(cosangle < cos(FOV_ANGLE/2))
3898 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3903 v3s16 blockpos_nodes = block->getPosRelative();
3905 // Block center position
3907 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3908 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3909 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3912 // Block position relative to camera
3913 v3f blockpos_relative = blockpos - camera_position;
3916 f32 d = blockpos_relative.getLength();
3923 bool mesh_expired = false;
3926 JMutexAutoLock lock(block->mesh_mutex);
3928 mesh_expired = block->getMeshExpired();
3930 // Mesh has not been expired and there is no mesh:
3931 // block has no content
3932 if(block->mesh == NULL && mesh_expired == false)
3936 f32 faraway = BS*50;
3937 //f32 faraway = m_control.wanted_range * BS;
3940 This has to be done with the mesh_mutex unlocked
3942 // Pretty random but this should work somewhat nicely
3943 if(mesh_expired && (
3944 (mesh_update_count < 3
3945 && (d < faraway || mesh_update_count < 2)
3948 (m_control.range_all && mesh_update_count < 20)
3951 /*if(mesh_expired && mesh_update_count < 6
3952 && (d < faraway || mesh_update_count < 3))*/
3954 mesh_update_count++;
3956 // Mesh has been expired: generate new mesh
3957 //block->updateMeshes(daynight_i);
3958 block->updateMesh(daynight_ratio);
3960 mesh_expired = false;
3964 Don't draw an expired mesh that is far away
3966 /*if(mesh_expired && d >= faraway)
3969 // Instead, delete it
3970 JMutexAutoLock lock(block->mesh_mutex);
3973 block->mesh->drop();
3976 // And continue to next block
3981 Draw the faces of the block
3984 JMutexAutoLock lock(block->mesh_mutex);
3986 scene::SMesh *mesh = block->mesh;
3991 blocks_would_have_drawn++;
3992 if(blocks_drawn >= m_control.wanted_max_blocks
3993 && m_control.range_all == false
3994 && d > m_control.wanted_min_range * BS)
3998 u32 c = mesh->getMeshBufferCount();
4000 for(u32 i=0; i<c; i++)
4002 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
4003 const video::SMaterial& material = buf->getMaterial();
4004 video::IMaterialRenderer* rnd =
4005 driver->getMaterialRenderer(material.MaterialType);
4006 bool transparent = (rnd && rnd->isTransparent());
4007 // Render transparent on transparent pass and likewise.
4008 if(transparent == is_transparent_pass)
4010 driver->setMaterial(buf->getMaterial());
4011 driver->drawMeshBuffer(buf);
4012 vertex_count += buf->getVertexCount();
4016 } // foreach sectorblocks
4019 m_control.blocks_drawn = blocks_drawn;
4020 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4022 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4023 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4026 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod, bool *changed)
4029 Add it to all blocks touching it
4032 v3s16(0,0,0), // this
4033 v3s16(0,0,1), // back
4034 v3s16(0,1,0), // top
4035 v3s16(1,0,0), // right
4036 v3s16(0,0,-1), // front
4037 v3s16(0,-1,0), // bottom
4038 v3s16(-1,0,0), // left
4040 for(u16 i=0; i<7; i++)
4042 v3s16 p2 = p + dirs[i];
4043 // Block position of neighbor (or requested) node
4044 v3s16 blockpos = getNodeBlockPos(p2);
4045 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4046 if(blockref == NULL)
4048 // Relative position of requested node
4049 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4050 if(blockref->setTempMod(relpos, mod))
4056 return getNodeBlockPos(p);
4058 v3s16 ClientMap::clearTempMod(v3s16 p, bool *changed)
4061 v3s16(0,0,0), // this
4062 v3s16(0,0,1), // back
4063 v3s16(0,1,0), // top
4064 v3s16(1,0,0), // right
4065 v3s16(0,0,-1), // front
4066 v3s16(0,-1,0), // bottom
4067 v3s16(-1,0,0), // left
4069 for(u16 i=0; i<7; i++)
4071 v3s16 p2 = p + dirs[i];
4072 // Block position of neighbor (or requested) node
4073 v3s16 blockpos = getNodeBlockPos(p2);
4074 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4075 if(blockref == NULL)
4077 // Relative position of requested node
4078 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4079 if(blockref->clearTempMod(relpos))
4085 return getNodeBlockPos(p);
4088 void ClientMap::PrintInfo(std::ostream &out)
4099 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4104 MapVoxelManipulator::~MapVoxelManipulator()
4106 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4111 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4113 TimeTaker timer1("emerge", &emerge_time);
4115 // Units of these are MapBlocks
4116 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4117 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4119 VoxelArea block_area_nodes
4120 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4122 addArea(block_area_nodes);
4124 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4125 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4126 for(s32 x=p_min.X; x<=p_max.X; x++)
4129 core::map<v3s16, bool>::Node *n;
4130 n = m_loaded_blocks.find(p);
4134 bool block_data_inexistent = false;
4137 TimeTaker timer1("emerge load", &emerge_load_time);
4139 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4140 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4143 dstream<<std::endl;*/
4145 MapBlock *block = m_map->getBlockNoCreate(p);
4146 if(block->isDummy())
4147 block_data_inexistent = true;
4149 block->copyTo(*this);
4151 catch(InvalidPositionException &e)
4153 block_data_inexistent = true;
4156 if(block_data_inexistent)
4158 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4159 // Fill with VOXELFLAG_INEXISTENT
4160 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4161 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4163 s32 i = m_area.index(a.MinEdge.X,y,z);
4164 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4168 m_loaded_blocks.insert(p, true);
4171 //dstream<<"emerge done"<<std::endl;
4179 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4181 TimeTaker timer1("emerge", &emerge_time);
4183 v3s16 size = a.getExtent();
4185 VoxelArea padded = a;
4186 padded.pad(m_area.getExtent() / 4);
4189 for(s16 z=0; z<size.Z; z++)
4190 for(s16 y=0; y<size.Y; y++)
4191 for(s16 x=0; x<size.X; x++)
4194 s32 i = m_area.index(a.MinEdge + p);
4195 // Don't touch nodes that have already been loaded
4196 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4200 TimeTaker timer1("emerge load", &emerge_load_time);
4201 MapNode n = m_map->getNode(a.MinEdge + p);
4205 catch(InvalidPositionException &e)
4207 m_flags[i] = VOXELFLAG_INEXISTENT;
4215 TODO: Add an option to only update eg. water and air nodes.
4216 This will make it interfere less with important stuff if
4219 void MapVoxelManipulator::blitBack
4220 (core::map<v3s16, MapBlock*> & modified_blocks)
4222 if(m_area.getExtent() == v3s16(0,0,0))
4225 //TimeTaker timer1("blitBack");
4227 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4228 <<m_loaded_blocks.size()<<std::endl;*/
4231 Initialize block cache
4233 v3s16 blockpos_last;
4234 MapBlock *block = NULL;
4235 bool block_checked_in_modified = false;
4237 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4238 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4239 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4243 u8 f = m_flags[m_area.index(p)];
4244 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4247 MapNode &n = m_data[m_area.index(p)];
4249 v3s16 blockpos = getNodeBlockPos(p);
4254 if(block == NULL || blockpos != blockpos_last){
4255 block = m_map->getBlockNoCreate(blockpos);
4256 blockpos_last = blockpos;
4257 block_checked_in_modified = false;
4260 // Calculate relative position in block
4261 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4263 // Don't continue if nothing has changed here
4264 if(block->getNode(relpos) == n)
4267 //m_map->setNode(m_area.MinEdge + p, n);
4268 block->setNode(relpos, n);
4271 Make sure block is in modified_blocks
4273 if(block_checked_in_modified == false)
4275 modified_blocks[blockpos] = block;
4276 block_checked_in_modified = true;
4279 catch(InvalidPositionException &e)
4285 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4286 MapVoxelManipulator(map)
4290 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4294 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4296 // Just create the area to avoid segfaults
4297 VoxelManipulator::emerge(a, caller_id);
4300 Just create the area to avoid segfaults
4303 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4304 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4305 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
4307 s32 i = m_area.index(x,y,z);
4308 // Don't touch nodes that have already been loaded
4309 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4311 m_flags[i] = VOXELFLAG_INEXISTENT;
4315 void ManualMapVoxelManipulator::initialEmerge(
4316 v3s16 blockpos_min, v3s16 blockpos_max)
4318 TimeTaker timer1("emerge", &emerge_time);
4320 // Units of these are MapBlocks
4321 v3s16 p_min = blockpos_min;
4322 v3s16 p_max = blockpos_max;
4324 VoxelArea block_area_nodes
4325 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4327 addArea(block_area_nodes);
4329 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4330 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4331 for(s32 x=p_min.X; x<=p_max.X; x++)
4334 core::map<v3s16, bool>::Node *n;
4335 n = m_loaded_blocks.find(p);
4339 bool block_data_inexistent = false;
4342 TimeTaker timer1("emerge load", &emerge_load_time);
4344 MapBlock *block = m_map->getBlockNoCreate(p);
4345 if(block->isDummy())
4346 block_data_inexistent = true;
4348 block->copyTo(*this);
4350 catch(InvalidPositionException &e)
4352 block_data_inexistent = true;
4355 if(block_data_inexistent)
4357 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4358 // Fill with VOXELFLAG_INEXISTENT
4359 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4360 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4362 s32 i = m_area.index(a.MinEdge.X,y,z);
4363 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4367 m_loaded_blocks.insert(p, true);