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"
35 Map::Map(std::ostream &dout):
37 m_camera_position(0,0,0),
38 m_camera_direction(0,0,1),
41 m_sector_mutex.Init();
42 m_camera_mutex.Init();
43 assert(m_sector_mutex.IsInitialized());
44 assert(m_camera_mutex.IsInitialized());
46 // Get this so that the player can stay on it at first
47 //getSector(v2s16(0,0));
55 /*updater.setRun(false);
56 while(updater.IsRunning())
62 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
63 for(; i.atEnd() == false; i++)
65 MapSector *sector = i.getNode()->getValue();
70 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
72 if(m_sector_cache != NULL && p == m_sector_cache_p){
73 MapSector * sector = m_sector_cache;
74 // Reset inactivity timer
75 sector->usage_timer = 0.0;
79 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
84 MapSector *sector = n->getValue();
86 // Cache the last result
88 m_sector_cache = sector;
90 // Reset inactivity timer
91 sector->usage_timer = 0.0;
95 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
97 JMutexAutoLock lock(m_sector_mutex);
99 return getSectorNoGenerateNoExNoLock(p);
102 MapSector * Map::getSectorNoGenerate(v2s16 p)
104 MapSector *sector = getSectorNoGenerateNoEx(p);
106 throw InvalidPositionException();
111 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
113 v2s16 p2d(p3d.X, p3d.Z);
114 MapSector * sector = getSectorNoGenerate(p2d);
116 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
121 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerate(p2d);
127 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 catch(InvalidPositionException &e)
136 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
138 v2s16 p2d(p3d.X, p3d.Z);
139 MapSector * sector = getSectorCreate(p2d);
141 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
144 block = sector->createBlankBlock(p3d.Y);
148 f32 Map::getGroundHeight(v2s16 p, bool generate)
151 v2s16 sectorpos = getNodeSectorPos(p);
152 MapSector * sref = getSectorNoGenerate(sectorpos);
153 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
154 f32 y = sref->getGroundHeight(relpos);
157 catch(InvalidPositionException &e)
159 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
163 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
165 /*m_dout<<DTIME<<"Map::setGroundHeight(("
167 <<"), "<<y<<")"<<std::endl;*/
168 v2s16 sectorpos = getNodeSectorPos(p);
169 MapSector * sref = getSectorNoGenerate(sectorpos);
170 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
171 //sref->mutex.Lock();
172 sref->setGroundHeight(relpos, y);
173 //sref->mutex.Unlock();
176 bool Map::isNodeUnderground(v3s16 p)
178 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock * block = getBlockNoCreate(blockpos);
181 return block->getIsUnderground();
183 catch(InvalidPositionException &e)
190 Goes recursively through the neighbours of the node.
192 Alters only transparent nodes.
194 If the lighting of the neighbour is lower than the lighting of
195 the node was (before changing it to 0 at the step before), the
196 lighting of the neighbour is set to 0 and then the same stuff
197 repeats for the neighbour.
199 The ending nodes of the routine are stored in light_sources.
200 This is useful when a light is removed. In such case, this
201 routine can be called for the light node and then again for
202 light_sources to re-light the area without the removed light.
204 values of from_nodes are lighting values.
206 void Map::unspreadLight(enum LightBank bank,
207 core::map<v3s16, u8> & from_nodes,
208 core::map<v3s16, bool> & light_sources,
209 core::map<v3s16, MapBlock*> & modified_blocks)
212 v3s16(0,0,1), // back
214 v3s16(1,0,0), // right
215 v3s16(0,0,-1), // front
216 v3s16(0,-1,0), // bottom
217 v3s16(-1,0,0), // left
220 if(from_nodes.size() == 0)
223 u32 blockchangecount = 0;
225 core::map<v3s16, u8> unlighted_nodes;
226 core::map<v3s16, u8>::Iterator j;
227 j = from_nodes.getIterator();
230 Initialize block cache
233 MapBlock *block = NULL;
234 // Cache this a bit, too
235 bool block_checked_in_modified = false;
237 for(; j.atEnd() == false; j++)
239 v3s16 pos = j.getNode()->getKey();
240 v3s16 blockpos = getNodeBlockPos(pos);
242 // Only fetch a new block if the block position has changed
244 if(block == NULL || blockpos != blockpos_last){
245 block = getBlockNoCreate(blockpos);
246 blockpos_last = blockpos;
248 block_checked_in_modified = false;
252 catch(InvalidPositionException &e)
260 // Calculate relative position in block
261 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
263 // Get node straight from the block
264 MapNode n = block->getNode(relpos);
266 u8 oldlight = j.getNode()->getValue();
268 // Loop through 6 neighbors
269 for(u16 i=0; i<6; i++)
271 // Get the position of the neighbor node
272 v3s16 n2pos = pos + dirs[i];
274 // Get the block where the node is located
275 v3s16 blockpos = getNodeBlockPos(n2pos);
279 // Only fetch a new block if the block position has changed
281 if(block == NULL || blockpos != blockpos_last){
282 block = getBlockNoCreate(blockpos);
283 blockpos_last = blockpos;
285 block_checked_in_modified = false;
289 catch(InvalidPositionException &e)
294 // Calculate relative position in block
295 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
296 // Get node straight from the block
297 MapNode n2 = block->getNode(relpos);
299 bool changed = false;
301 //TODO: Optimize output by optimizing light_sources?
304 If the neighbor is dimmer than what was specified
305 as oldlight (the light of the previous node)
307 if(n2.getLight(bank) < oldlight)
310 And the neighbor is transparent and it has some light
312 if(n2.light_propagates() && n2.getLight(bank) != 0)
315 Set light to 0 and add to queue
318 u8 current_light = n2.getLight(bank);
319 n2.setLight(bank, 0);
320 block->setNode(relpos, n2);
322 unlighted_nodes.insert(n2pos, current_light);
326 Remove from light_sources if it is there
327 NOTE: This doesn't happen nearly at all
329 /*if(light_sources.find(n2pos))
331 std::cout<<"Removed from light_sources"<<std::endl;
332 light_sources.remove(n2pos);
337 if(light_sources.find(n2pos) != NULL)
338 light_sources.remove(n2pos);*/
341 light_sources.insert(n2pos, true);
344 // Add to modified_blocks
345 if(changed == true && block_checked_in_modified == false)
347 // If the block is not found in modified_blocks, add.
348 if(modified_blocks.find(blockpos) == NULL)
350 modified_blocks.insert(blockpos, block);
352 block_checked_in_modified = true;
355 catch(InvalidPositionException &e)
362 /*dstream<<"unspreadLight(): Changed block "
363 <<blockchangecount<<" times"
364 <<" for "<<from_nodes.size()<<" nodes"
367 if(unlighted_nodes.size() > 0)
368 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
372 A single-node wrapper of the above
374 void Map::unLightNeighbors(enum LightBank bank,
375 v3s16 pos, u8 lightwas,
376 core::map<v3s16, bool> & light_sources,
377 core::map<v3s16, MapBlock*> & modified_blocks)
379 core::map<v3s16, u8> from_nodes;
380 from_nodes.insert(pos, lightwas);
382 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
386 Lights neighbors of from_nodes, collects all them and then
389 void Map::spreadLight(enum LightBank bank,
390 core::map<v3s16, bool> & from_nodes,
391 core::map<v3s16, MapBlock*> & modified_blocks)
393 const v3s16 dirs[6] = {
394 v3s16(0,0,1), // back
396 v3s16(1,0,0), // right
397 v3s16(0,0,-1), // front
398 v3s16(0,-1,0), // bottom
399 v3s16(-1,0,0), // left
402 if(from_nodes.size() == 0)
405 u32 blockchangecount = 0;
407 core::map<v3s16, bool> lighted_nodes;
408 core::map<v3s16, bool>::Iterator j;
409 j = from_nodes.getIterator();
412 Initialize block cache
415 MapBlock *block = NULL;
416 // Cache this a bit, too
417 bool block_checked_in_modified = false;
419 for(; j.atEnd() == false; j++)
420 //for(; j != from_nodes.end(); j++)
422 v3s16 pos = j.getNode()->getKey();
424 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
425 v3s16 blockpos = getNodeBlockPos(pos);
427 // Only fetch a new block if the block position has changed
429 if(block == NULL || blockpos != blockpos_last){
430 block = getBlockNoCreate(blockpos);
431 blockpos_last = blockpos;
433 block_checked_in_modified = false;
437 catch(InvalidPositionException &e)
445 // Calculate relative position in block
446 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
448 // Get node straight from the block
449 MapNode n = block->getNode(relpos);
451 u8 oldlight = n.getLight(bank);
452 u8 newlight = diminish_light(oldlight);
454 // Loop through 6 neighbors
455 for(u16 i=0; i<6; i++){
456 // Get the position of the neighbor node
457 v3s16 n2pos = pos + dirs[i];
459 // Get the block where the node is located
460 v3s16 blockpos = getNodeBlockPos(n2pos);
464 // Only fetch a new block if the block position has changed
466 if(block == NULL || blockpos != blockpos_last){
467 block = getBlockNoCreate(blockpos);
468 blockpos_last = blockpos;
470 block_checked_in_modified = false;
474 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
481 // Get node straight from the block
482 MapNode n2 = block->getNode(relpos);
484 bool changed = false;
486 If the neighbor is brighter than the current node,
487 add to list (it will light up this node on its turn)
489 if(n2.getLight(bank) > undiminish_light(oldlight))
491 lighted_nodes.insert(n2pos, true);
492 //lighted_nodes.push_back(n2pos);
496 If the neighbor is dimmer than how much light this node
497 would spread on it, add to list
499 if(n2.getLight(bank) < newlight)
501 if(n2.light_propagates())
503 n2.setLight(bank, newlight);
504 block->setNode(relpos, n2);
505 lighted_nodes.insert(n2pos, true);
506 //lighted_nodes.push_back(n2pos);
511 // Add to modified_blocks
512 if(changed == true && block_checked_in_modified == false)
514 // If the block is not found in modified_blocks, add.
515 if(modified_blocks.find(blockpos) == NULL)
517 modified_blocks.insert(blockpos, block);
519 block_checked_in_modified = true;
522 catch(InvalidPositionException &e)
529 /*dstream<<"spreadLight(): Changed block "
530 <<blockchangecount<<" times"
531 <<" for "<<from_nodes.size()<<" nodes"
534 if(lighted_nodes.size() > 0)
535 spreadLight(bank, lighted_nodes, modified_blocks);
539 A single-node source variation of the above.
541 void Map::lightNeighbors(enum LightBank bank,
543 core::map<v3s16, MapBlock*> & modified_blocks)
545 core::map<v3s16, bool> from_nodes;
546 from_nodes.insert(pos, true);
547 spreadLight(bank, from_nodes, modified_blocks);
550 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
553 v3s16(0,0,1), // back
555 v3s16(1,0,0), // right
556 v3s16(0,0,-1), // front
557 v3s16(0,-1,0), // bottom
558 v3s16(-1,0,0), // left
561 u8 brightest_light = 0;
562 v3s16 brightest_pos(0,0,0);
563 bool found_something = false;
565 // Loop through 6 neighbors
566 for(u16 i=0; i<6; i++){
567 // Get the position of the neighbor node
568 v3s16 n2pos = p + dirs[i];
573 catch(InvalidPositionException &e)
577 if(n2.getLight(bank) > brightest_light || found_something == false){
578 brightest_light = n2.getLight(bank);
579 brightest_pos = n2pos;
580 found_something = true;
584 if(found_something == false)
585 throw InvalidPositionException();
587 return brightest_pos;
591 Propagates sunlight down from a node.
592 Starting point gets sunlight.
594 Returns the lowest y value of where the sunlight went.
596 Mud is turned into grass in where the sunlight stops.
598 s16 Map::propagateSunlight(v3s16 start,
599 core::map<v3s16, MapBlock*> & modified_blocks)
604 v3s16 pos(start.X, y, start.Z);
606 v3s16 blockpos = getNodeBlockPos(pos);
609 block = getBlockNoCreate(blockpos);
611 catch(InvalidPositionException &e)
616 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
617 MapNode n = block->getNode(relpos);
619 if(n.sunlight_propagates())
621 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
622 block->setNode(relpos, n);
624 modified_blocks.insert(blockpos, block);
628 // Turn mud into grass
629 if(n.d == CONTENT_MUD)
632 block->setNode(relpos, n);
633 modified_blocks.insert(blockpos, block);
636 // Sunlight goes no further
643 void Map::updateLighting(enum LightBank bank,
644 core::map<v3s16, MapBlock*> & a_blocks,
645 core::map<v3s16, MapBlock*> & modified_blocks)
647 /*m_dout<<DTIME<<"Map::updateLighting(): "
648 <<a_blocks.size()<<" blocks."<<std::endl;*/
650 //TimeTaker timer("updateLighting");
654 //u32 count_was = modified_blocks.size();
656 core::map<v3s16, MapBlock*> blocks_to_update;
658 core::map<v3s16, bool> light_sources;
660 core::map<v3s16, u8> unlight_from;
662 core::map<v3s16, MapBlock*>::Iterator i;
663 i = a_blocks.getIterator();
664 for(; i.atEnd() == false; i++)
666 MapBlock *block = i.getNode()->getValue();
670 // Don't bother with dummy blocks.
674 v3s16 pos = block->getPos();
675 modified_blocks.insert(pos, block);
677 blocks_to_update.insert(pos, block);
680 Clear all light from block
682 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
683 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
684 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
689 MapNode n = block->getNode(v3s16(x,y,z));
690 u8 oldlight = n.getLight(bank);
692 block->setNode(v3s16(x,y,z), n);
694 // Collect borders for unlighting
695 if(x==0 || x == MAP_BLOCKSIZE-1
696 || y==0 || y == MAP_BLOCKSIZE-1
697 || z==0 || z == MAP_BLOCKSIZE-1)
699 v3s16 p_map = p + v3s16(
702 MAP_BLOCKSIZE*pos.Z);
703 unlight_from.insert(p_map, oldlight);
706 catch(InvalidPositionException &e)
709 This would happen when dealing with a
713 dstream<<"updateLighting(): InvalidPositionException"
718 if(bank == LIGHTBANK_DAY)
720 bool bottom_valid = block->propagateSunlight(light_sources);
722 // If bottom is valid, we're done.
726 else if(bank == LIGHTBANK_NIGHT)
728 // For night lighting, sunlight is not propagated
733 // Invalid lighting bank
737 /*dstream<<"Bottom for sunlight-propagated block ("
738 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
741 // Bottom sunlight is not valid; get the block and loop to it
745 block = getBlockNoCreate(pos);
747 catch(InvalidPositionException &e)
757 TimeTaker timer("unspreadLight");
758 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
763 u32 diff = modified_blocks.size() - count_was;
764 count_was = modified_blocks.size();
765 dstream<<"unspreadLight modified "<<diff<<std::endl;
769 TimeTaker timer("spreadLight");
770 spreadLight(bank, light_sources, modified_blocks);
775 u32 diff = modified_blocks.size() - count_was;
776 count_was = modified_blocks.size();
777 dstream<<"spreadLight modified "<<diff<<std::endl;
782 //MapVoxelManipulator vmanip(this);
784 // Make a manual voxel manipulator and load all the blocks
785 // that touch the requested blocks
786 ManualMapVoxelManipulator vmanip(this);
787 core::map<v3s16, MapBlock*>::Iterator i;
788 i = blocks_to_update.getIterator();
789 for(; i.atEnd() == false; i++)
791 MapBlock *block = i.getNode()->getValue();
792 v3s16 p = block->getPos();
794 // Add all surrounding blocks
795 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
798 Add all surrounding blocks that have up-to-date lighting
799 NOTE: This doesn't quite do the job (not everything
800 appropriate is lighted)
802 /*for(s16 z=-1; z<=1; z++)
803 for(s16 y=-1; y<=1; y++)
804 for(s16 x=-1; x<=1; x++)
807 MapBlock *block = getBlockNoCreateNoEx(p);
812 if(block->getLightingExpired())
814 vmanip.initialEmerge(p, p);
817 // Lighting of block will be updated completely
818 block->setLightingExpired(false);
822 //TimeTaker timer("unSpreadLight");
823 vmanip.unspreadLight(bank, unlight_from, light_sources);
826 //TimeTaker timer("spreadLight");
827 vmanip.spreadLight(bank, light_sources);
830 //TimeTaker timer("blitBack");
831 vmanip.blitBack(modified_blocks);
833 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
837 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
840 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
841 core::map<v3s16, MapBlock*> & modified_blocks)
843 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
844 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
847 Update information about whether day and night light differ
849 for(core::map<v3s16, MapBlock*>::Iterator
850 i = modified_blocks.getIterator();
851 i.atEnd() == false; i++)
853 MapBlock *block = i.getNode()->getValue();
854 block->updateDayNightDiff();
859 This is called after changing a node from transparent to opaque.
860 The lighting value of the node should be left as-is after changing
861 other values. This sets the lighting value to 0.
863 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
864 core::map<v3s16, MapBlock*> &modified_blocks)*/
865 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
866 core::map<v3s16, MapBlock*> &modified_blocks)
869 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
870 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
873 From this node to nodes underneath:
874 If lighting is sunlight (1.0), unlight neighbours and
879 v3s16 toppos = p + v3s16(0,1,0);
880 v3s16 bottompos = p + v3s16(0,-1,0);
882 bool node_under_sunlight = true;
883 core::map<v3s16, bool> light_sources;
886 If there is a node at top and it doesn't have sunlight,
887 there has not been any sunlight going down.
889 Otherwise there probably is.
892 MapNode topnode = getNode(toppos);
894 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
895 node_under_sunlight = false;
897 catch(InvalidPositionException &e)
901 if(n.d != CONTENT_TORCH)
904 If there is grass below, change it to mud
907 MapNode bottomnode = getNode(bottompos);
909 if(bottomnode.d == CONTENT_GRASS
910 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
912 bottomnode.d = CONTENT_MUD;
913 setNode(bottompos, bottomnode);
916 catch(InvalidPositionException &e)
921 enum LightBank banks[] =
926 for(s32 i=0; i<2; i++)
928 enum LightBank bank = banks[i];
930 u8 lightwas = getNode(p).getLight(bank);
932 // Add the block of the added node to modified_blocks
933 v3s16 blockpos = getNodeBlockPos(p);
934 MapBlock * block = getBlockNoCreate(blockpos);
935 assert(block != NULL);
936 modified_blocks.insert(blockpos, block);
938 if(isValidPosition(p) == false)
941 // Unlight neighbours of node.
942 // This means setting light of all consequent dimmer nodes
944 // This also collects the nodes at the border which will spread
945 // light again into this.
946 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
954 If node is under sunlight, take all sunlighted nodes under
955 it and clear light from them and from where the light has
957 TODO: This could be optimized by mass-unlighting instead
960 if(node_under_sunlight)
964 //m_dout<<DTIME<<"y="<<y<<std::endl;
965 v3s16 n2pos(p.X, y, p.Z);
971 catch(InvalidPositionException &e)
976 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
978 //m_dout<<DTIME<<"doing"<<std::endl;
979 unLightNeighbors(LIGHTBANK_DAY,
980 n2pos, n2.getLight(LIGHTBANK_DAY),
981 light_sources, modified_blocks);
982 n2.setLight(LIGHTBANK_DAY, 0);
990 for(s32 i=0; i<2; i++)
992 enum LightBank bank = banks[i];
995 Spread light from all nodes that might be capable of doing so
996 TODO: Convert to spreadLight
998 spreadLight(bank, light_sources, modified_blocks);
1002 Update information about whether day and night light differ
1004 for(core::map<v3s16, MapBlock*>::Iterator
1005 i = modified_blocks.getIterator();
1006 i.atEnd() == false; i++)
1008 MapBlock *block = i.getNode()->getValue();
1009 block->updateDayNightDiff();
1013 Add neighboring liquid nodes and the node itself if it is
1014 liquid (=water node was added) to transform queue.
1017 v3s16(0,0,0), // self
1018 v3s16(0,0,1), // back
1019 v3s16(0,1,0), // top
1020 v3s16(1,0,0), // right
1021 v3s16(0,0,-1), // front
1022 v3s16(0,-1,0), // bottom
1023 v3s16(-1,0,0), // left
1025 for(u16 i=0; i<7; i++)
1030 v3s16 p2 = p + dirs[i];
1032 MapNode n2 = getNode(p2);
1033 if(content_liquid(n2.d))
1035 m_transforming_liquid.push_back(p2);
1038 }catch(InvalidPositionException &e)
1046 void Map::removeNodeAndUpdate(v3s16 p,
1047 core::map<v3s16, MapBlock*> &modified_blocks)
1049 /*PrintInfo(m_dout);
1050 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1051 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1053 bool node_under_sunlight = true;
1055 v3s16 toppos = p + v3s16(0,1,0);
1057 // Node will be replaced with this
1058 u8 replace_material = CONTENT_AIR;
1061 If there is a node at top and it doesn't have sunlight,
1062 there will be no sunlight going down.
1065 MapNode topnode = getNode(toppos);
1067 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1068 node_under_sunlight = false;
1070 catch(InvalidPositionException &e)
1074 core::map<v3s16, bool> light_sources;
1076 enum LightBank banks[] =
1081 for(s32 i=0; i<2; i++)
1083 enum LightBank bank = banks[i];
1086 Unlight neighbors (in case the node is a light source)
1088 unLightNeighbors(bank, p,
1089 getNode(p).getLight(bank),
1090 light_sources, modified_blocks);
1095 This also clears the lighting.
1099 n.d = replace_material;
1102 for(s32 i=0; i<2; i++)
1104 enum LightBank bank = banks[i];
1107 Recalculate lighting
1109 spreadLight(bank, light_sources, modified_blocks);
1112 // Add the block of the removed node to modified_blocks
1113 v3s16 blockpos = getNodeBlockPos(p);
1114 MapBlock * block = getBlockNoCreate(blockpos);
1115 assert(block != NULL);
1116 modified_blocks.insert(blockpos, block);
1119 If the removed node was under sunlight, propagate the
1120 sunlight down from it and then light all neighbors
1121 of the propagated blocks.
1123 if(node_under_sunlight)
1125 s16 ybottom = propagateSunlight(p, modified_blocks);
1126 /*m_dout<<DTIME<<"Node was under sunlight. "
1127 "Propagating sunlight";
1128 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1130 for(; y >= ybottom; y--)
1132 v3s16 p2(p.X, y, p.Z);
1133 /*m_dout<<DTIME<<"lighting neighbors of node ("
1134 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1136 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1141 // Set the lighting of this node to 0
1142 // TODO: Is this needed? Lighting is cleared up there already.
1144 MapNode n = getNode(p);
1145 n.setLight(LIGHTBANK_DAY, 0);
1148 catch(InvalidPositionException &e)
1154 for(s32 i=0; i<2; i++)
1156 enum LightBank bank = banks[i];
1158 // Get the brightest neighbour node and propagate light from it
1159 v3s16 n2p = getBrightestNeighbour(bank, p);
1161 MapNode n2 = getNode(n2p);
1162 lightNeighbors(bank, n2p, modified_blocks);
1164 catch(InvalidPositionException &e)
1170 Update information about whether day and night light differ
1172 for(core::map<v3s16, MapBlock*>::Iterator
1173 i = modified_blocks.getIterator();
1174 i.atEnd() == false; i++)
1176 MapBlock *block = i.getNode()->getValue();
1177 block->updateDayNightDiff();
1181 Add neighboring liquid nodes to transform queue.
1184 v3s16(0,0,1), // back
1185 v3s16(0,1,0), // top
1186 v3s16(1,0,0), // right
1187 v3s16(0,0,-1), // front
1188 v3s16(0,-1,0), // bottom
1189 v3s16(-1,0,0), // left
1191 for(u16 i=0; i<6; i++)
1196 v3s16 p2 = p + dirs[i];
1198 MapNode n2 = getNode(p2);
1199 if(content_liquid(n2.d))
1201 m_transforming_liquid.push_back(p2);
1204 }catch(InvalidPositionException &e)
1211 void Map::expireMeshes(bool only_daynight_diffed)
1213 TimeTaker timer("expireMeshes()");
1215 core::map<v2s16, MapSector*>::Iterator si;
1216 si = m_sectors.getIterator();
1217 for(; si.atEnd() == false; si++)
1219 MapSector *sector = si.getNode()->getValue();
1221 core::list< MapBlock * > sectorblocks;
1222 sector->getBlocks(sectorblocks);
1224 core::list< MapBlock * >::Iterator i;
1225 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1227 MapBlock *block = *i;
1229 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1235 JMutexAutoLock lock(block->mesh_mutex);
1236 if(block->mesh != NULL)
1238 /*block->mesh->drop();
1239 block->mesh = NULL;*/
1240 block->setMeshExpired(true);
1247 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1249 assert(mapType() == MAPTYPE_CLIENT);
1252 v3s16 p = blockpos + v3s16(0,0,0);
1253 MapBlock *b = getBlockNoCreate(p);
1254 b->updateMesh(daynight_ratio);
1256 catch(InvalidPositionException &e){}
1259 v3s16 p = blockpos + v3s16(-1,0,0);
1260 MapBlock *b = getBlockNoCreate(p);
1261 b->updateMesh(daynight_ratio);
1263 catch(InvalidPositionException &e){}
1265 v3s16 p = blockpos + v3s16(0,-1,0);
1266 MapBlock *b = getBlockNoCreate(p);
1267 b->updateMesh(daynight_ratio);
1269 catch(InvalidPositionException &e){}
1271 v3s16 p = blockpos + v3s16(0,0,-1);
1272 MapBlock *b = getBlockNoCreate(p);
1273 b->updateMesh(daynight_ratio);
1275 catch(InvalidPositionException &e){}
1278 v3s16 p = blockpos + v3s16(1,0,0);
1279 MapBlock *b = getBlockNoCreate(p);
1280 b->updateMesh(daynight_ratio);
1282 catch(InvalidPositionException &e){}
1284 v3s16 p = blockpos + v3s16(0,1,0);
1285 MapBlock *b = getBlockNoCreate(p);
1286 b->updateMesh(daynight_ratio);
1288 catch(InvalidPositionException &e){}
1290 v3s16 p = blockpos + v3s16(0,0,1);
1291 MapBlock *b = getBlockNoCreate(p);
1292 b->updateMesh(daynight_ratio);
1294 catch(InvalidPositionException &e){}*/
1299 bool Map::dayNightDiffed(v3s16 blockpos)
1302 v3s16 p = blockpos + v3s16(0,0,0);
1303 MapBlock *b = getBlockNoCreate(p);
1304 if(b->dayNightDiffed())
1307 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(-1,0,0);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1317 v3s16 p = blockpos + v3s16(0,-1,0);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1324 v3s16 p = blockpos + v3s16(0,0,-1);
1325 MapBlock *b = getBlockNoCreate(p);
1326 if(b->dayNightDiffed())
1329 catch(InvalidPositionException &e){}
1332 v3s16 p = blockpos + v3s16(1,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1339 v3s16 p = blockpos + v3s16(0,1,0);
1340 MapBlock *b = getBlockNoCreate(p);
1341 if(b->dayNightDiffed())
1344 catch(InvalidPositionException &e){}
1346 v3s16 p = blockpos + v3s16(0,0,1);
1347 MapBlock *b = getBlockNoCreate(p);
1348 if(b->dayNightDiffed())
1351 catch(InvalidPositionException &e){}
1357 Updates usage timers
1359 void Map::timerUpdate(float dtime)
1361 JMutexAutoLock lock(m_sector_mutex);
1363 core::map<v2s16, MapSector*>::Iterator si;
1365 si = m_sectors.getIterator();
1366 for(; si.atEnd() == false; si++)
1368 MapSector *sector = si.getNode()->getValue();
1369 sector->usage_timer += dtime;
1373 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1376 Wait for caches to be removed before continuing.
1378 This disables the existence of caches while locked
1380 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1382 core::list<v2s16>::Iterator j;
1383 for(j=list.begin(); j!=list.end(); j++)
1385 MapSector *sector = m_sectors[*j];
1388 sector->deleteBlocks();
1393 If sector is in sector cache, remove it from there
1395 if(m_sector_cache == sector)
1397 m_sector_cache = NULL;
1400 Remove from map and delete
1402 m_sectors.remove(*j);
1408 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1409 core::list<v3s16> *deleted_blocks)
1411 JMutexAutoLock lock(m_sector_mutex);
1413 core::list<v2s16> sector_deletion_queue;
1414 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1415 for(; i.atEnd() == false; i++)
1417 MapSector *sector = i.getNode()->getValue();
1419 Delete sector from memory if it hasn't been used in a long time
1421 if(sector->usage_timer > timeout)
1423 sector_deletion_queue.push_back(i.getNode()->getKey());
1425 if(deleted_blocks != NULL)
1427 // Collect positions of blocks of sector
1428 MapSector *sector = i.getNode()->getValue();
1429 core::list<MapBlock*> blocks;
1430 sector->getBlocks(blocks);
1431 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1432 i != blocks.end(); i++)
1434 deleted_blocks->push_back((*i)->getPos());
1439 deleteSectors(sector_deletion_queue, only_blocks);
1440 return sector_deletion_queue.getSize();
1443 void Map::PrintInfo(std::ostream &out)
1448 #define WATER_DROP_BOOST 4
1450 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1452 DSTACK(__FUNCTION_NAME);
1453 //TimeTaker timer("transformLiquids()");
1456 u32 initial_size = m_transforming_liquid.size();
1458 if(initial_size != 0)
1459 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1461 while(m_transforming_liquid.size() != 0)
1464 Get a queued transforming liquid node
1466 v3s16 p0 = m_transforming_liquid.pop_front();
1468 MapNode n0 = getNode(p0);
1470 // Don't deal with non-liquids
1471 if(content_liquid(n0.d) == false)
1474 bool is_source = !content_flowing_liquid(n0.d);
1476 u8 liquid_level = 8;
1477 if(is_source == false)
1478 liquid_level = n0.param2 & 0x0f;
1480 // Turn possible source into non-source
1481 u8 nonsource_c = make_liquid_flowing(n0.d);
1484 If not source, check that some node flows into this one
1485 and what is the level of liquid in this one
1487 if(is_source == false)
1489 s8 new_liquid_level_max = -1;
1491 v3s16 dirs_from[5] = {
1492 v3s16(0,1,0), // top
1493 v3s16(0,0,1), // back
1494 v3s16(1,0,0), // right
1495 v3s16(0,0,-1), // front
1496 v3s16(-1,0,0), // left
1498 for(u16 i=0; i<5; i++)
1503 bool from_top = (i==0);
1505 v3s16 p2 = p0 + dirs_from[i];
1506 MapNode n2 = getNode(p2);
1508 if(content_liquid(n2.d))
1510 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1511 // Check that the liquids are the same type
1512 if(n2_nonsource_c != nonsource_c)
1514 dstream<<"WARNING: Not handling: different liquids"
1515 " collide"<<std::endl;
1518 bool n2_is_source = !content_flowing_liquid(n2.d);
1519 s8 n2_liquid_level = 8;
1520 if(n2_is_source == false)
1521 n2_liquid_level = n2.param2 & 0x07;
1523 s8 new_liquid_level = -1;
1526 //new_liquid_level = 7;
1527 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1528 new_liquid_level = 7;
1530 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1532 else if(n2_liquid_level > 0)
1534 new_liquid_level = n2_liquid_level - 1;
1537 if(new_liquid_level > new_liquid_level_max)
1538 new_liquid_level_max = new_liquid_level;
1541 }catch(InvalidPositionException &e)
1547 If liquid level should be something else, update it and
1548 add all the neighboring water nodes to the transform queue.
1550 if(new_liquid_level_max != liquid_level)
1552 if(new_liquid_level_max == -1)
1554 // Remove water alltoghether
1561 n0.param2 = new_liquid_level_max;
1565 // Block has been modified
1567 v3s16 blockpos = getNodeBlockPos(p0);
1568 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1570 modified_blocks.insert(blockpos, block);
1574 Add neighboring non-source liquid nodes to transform queue.
1577 v3s16(0,0,1), // back
1578 v3s16(0,1,0), // top
1579 v3s16(1,0,0), // right
1580 v3s16(0,0,-1), // front
1581 v3s16(0,-1,0), // bottom
1582 v3s16(-1,0,0), // left
1584 for(u16 i=0; i<6; i++)
1589 v3s16 p2 = p0 + dirs[i];
1591 MapNode n2 = getNode(p2);
1592 if(content_flowing_liquid(n2.d))
1594 m_transforming_liquid.push_back(p2);
1597 }catch(InvalidPositionException &e)
1604 // Get a new one from queue if the node has turned into non-water
1605 if(content_liquid(n0.d) == false)
1609 Flow water from this node
1611 v3s16 dirs_to[5] = {
1612 v3s16(0,-1,0), // bottom
1613 v3s16(0,0,1), // back
1614 v3s16(1,0,0), // right
1615 v3s16(0,0,-1), // front
1616 v3s16(-1,0,0), // left
1618 for(u16 i=0; i<5; i++)
1623 bool to_bottom = (i == 0);
1625 // If liquid is at lowest possible height, it's not going
1626 // anywhere except down
1627 if(liquid_level == 0 && to_bottom == false)
1630 u8 liquid_next_level = 0;
1631 // If going to bottom
1634 //liquid_next_level = 7;
1635 if(liquid_level >= 7 - WATER_DROP_BOOST)
1636 liquid_next_level = 7;
1638 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1641 liquid_next_level = liquid_level - 1;
1643 bool n2_changed = false;
1644 bool flowed = false;
1646 v3s16 p2 = p0 + dirs_to[i];
1648 MapNode n2 = getNode(p2);
1649 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1651 if(content_liquid(n2.d))
1653 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1654 // Check that the liquids are the same type
1655 if(n2_nonsource_c != nonsource_c)
1657 dstream<<"WARNING: Not handling: different liquids"
1658 " collide"<<std::endl;
1661 bool n2_is_source = !content_flowing_liquid(n2.d);
1662 u8 n2_liquid_level = 8;
1663 if(n2_is_source == false)
1664 n2_liquid_level = n2.param2 & 0x07;
1673 // Just flow into the source, nothing changes.
1674 // n2_changed is not set because destination didn't change
1679 if(liquid_next_level > liquid_level)
1681 n2.param2 = liquid_next_level;
1689 else if(n2.d == CONTENT_AIR)
1692 n2.param2 = liquid_next_level;
1699 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1703 m_transforming_liquid.push_back(p2);
1705 v3s16 blockpos = getNodeBlockPos(p2);
1706 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1708 modified_blocks.insert(blockpos, block);
1711 // If n2_changed to bottom, don't flow anywhere else
1712 if(to_bottom && flowed && !is_source)
1715 }catch(InvalidPositionException &e)
1721 //if(loopcount >= 100000)
1722 if(loopcount >= initial_size * 1)
1725 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1732 ServerMap::ServerMap(std::string savedir):
1738 //m_chunksize = 16; // Too slow
1739 m_chunksize = 8; // Takes a few seconds
1743 // TODO: Save to and load from a file
1744 m_seed = (((u64)myrand()<<0)%0x7fff)
1745 + (((u64)myrand()<<16)%0x7fff)
1746 + (((u64)myrand()<<32)%0x7fff)
1747 + (((u64)myrand()<<48)%0x7fff);
1750 Experimental and debug stuff
1757 Try to load map; if not found, create a new one.
1760 m_savedir = savedir;
1761 m_map_saving_enabled = false;
1765 // If directory exists, check contents and load if possible
1766 if(fs::PathExists(m_savedir))
1768 // If directory is empty, it is safe to save into it.
1769 if(fs::GetDirListing(m_savedir).size() == 0)
1771 dstream<<DTIME<<"Server: Empty save directory is valid."
1773 m_map_saving_enabled = true;
1777 // Load master heightmap
1778 loadMasterHeightmap();
1780 // Load sector (0,0) and throw and exception on fail
1781 if(loadSectorFull(v2s16(0,0)) == false)
1782 throw LoadError("Failed to load sector (0,0)");
1784 dstream<<DTIME<<"Server: Successfully loaded master "
1785 "heightmap and sector (0,0) from "<<savedir<<
1786 ", assuming valid save directory."
1789 m_map_saving_enabled = true;
1790 // Map loaded, not creating new one
1794 // If directory doesn't exist, it is safe to save to it
1796 m_map_saving_enabled = true;
1799 catch(std::exception &e)
1801 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1802 <<", exception: "<<e.what()<<std::endl;
1803 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1804 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1807 dstream<<DTIME<<"Initializing new map."<<std::endl;
1809 // Create zero sector
1810 emergeSector(v2s16(0,0));
1812 // Initially write whole map
1816 ServerMap::~ServerMap()
1820 if(m_map_saving_enabled)
1823 // Save only changed parts
1825 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1829 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1832 catch(std::exception &e)
1834 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1835 <<", exception: "<<e.what()<<std::endl;
1841 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1842 for(; i.atEnd() == false; i++)
1844 MapChunk *chunk = i.getNode()->getValue();
1850 Some helper functions for the map generator
1853 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1855 v3s16 em = vmanip.m_area.getExtent();
1856 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1857 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1858 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1860 for(y=y_nodes_max; y>=y_nodes_min; y--)
1862 MapNode &n = vmanip.m_data[i];
1863 if(content_walkable(n.d))
1866 vmanip.m_area.add_y(em, i, -1);
1868 if(y >= y_nodes_min)
1874 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1876 v3s16 em = vmanip.m_area.getExtent();
1877 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1878 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1879 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1881 for(y=y_nodes_max; y>=y_nodes_min; y--)
1883 MapNode &n = vmanip.m_data[i];
1884 if(content_walkable(n.d)
1885 && n.d != CONTENT_TREE
1886 && n.d != CONTENT_LEAVES)
1889 vmanip.m_area.add_y(em, i, -1);
1891 if(y >= y_nodes_min)
1897 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1899 MapNode treenode(CONTENT_TREE);
1900 MapNode leavesnode(CONTENT_LEAVES);
1902 s16 trunk_h = myrand_range(3, 6);
1904 for(s16 ii=0; ii<trunk_h; ii++)
1906 if(vmanip.m_area.contains(p1))
1907 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1911 // p1 is now the last piece of the trunk
1914 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1915 SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1916 for(s32 i=0; i<leaves_a.getVolume(); i++)
1919 // Force leaves at near the end of the trunk
1922 for(s16 z=-d; z<=d; z++)
1923 for(s16 y=-d; y<=d; y++)
1924 for(s16 x=-d; x<=d; x++)
1926 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1930 // Add leaves randomly
1931 for(u32 iii=0; iii<7; iii++)
1936 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1937 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1938 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1941 for(s16 z=0; z<=d; z++)
1942 for(s16 y=0; y<=d; y++)
1943 for(s16 x=0; x<=d; x++)
1945 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1949 // Blit leaves to vmanip
1950 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1951 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1952 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1956 if(vmanip.m_area.contains(p) == false)
1958 u32 vi = vmanip.m_area.index(p);
1959 if(vmanip.m_data[vi].d != CONTENT_AIR)
1961 u32 i = leaves_a.index(x,y,z);
1962 if(leaves_d[i] == 1)
1963 vmanip.m_data[vi] = leavesnode;
1968 Noise functions. Make sure seed is mangled differently in each one.
1971 // Amount of trees per area in nodes
1972 double tree_amount_2d(u64 seed, v2s16 p)
1974 double noise = noise2d_perlin(
1975 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1977 double zeroval = -0.4;
1981 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1984 /*double base_rock_level_2d(u64 seed, v2s16 p)
1986 return WATER_LEVEL - 6.0 + 25. * noise2d_perlin(
1987 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1991 /*double highlands_level_2d(u64 seed, v2s16 p)
1993 double a = noise2d_perlin(
1994 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
1999 return WATER_LEVEL + 25;
2000 return WATER_LEVEL + 55. * noise2d_perlin(
2001 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2002 seed+85039, 6, 0.69);
2008 double base_rock_level_2d(u64 seed, v2s16 p)
2010 // The base ground level
2011 double base = WATER_LEVEL - 4.0 + 25. * noise2d_perlin(
2012 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2015 // Higher ground level
2016 double higher = WATER_LEVEL + 23. + 30. * noise2d_perlin(
2017 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2018 seed+85039, 6, 0.69);
2019 //higher = 30; // For debugging
2021 // Limit higher to at least base
2025 // Steepness factor of cliffs
2026 double b = 1.0 + 1.0 * noise2d_perlin(
2027 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2029 b = rangelim(b, 0.0, 1000.0);
2030 // Make steep stuff very steep and non-steep stuff very non-steep
2035 // High/low selector
2036 double a = 0.5 + b * noise2d_perlin(
2037 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2039 a = rangelim(a, 0.0, 1.0);
2041 //dstream<<"a="<<a<<std::endl;
2043 double h = base*(1.0-a) + higher*a;
2049 This is the main map generation method
2052 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2053 core::map<v3s16, MapBlock*> &changed_blocks)
2055 DSTACK(__FUNCTION_NAME);
2058 Don't generate if already fully generated
2061 MapChunk *chunk = getChunk(chunkpos);
2062 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2064 dstream<<"generateChunkRaw(): Chunk "
2065 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2066 <<" already generated"<<std::endl;
2071 dstream<<"generateChunkRaw(): Generating chunk "
2072 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2075 TimeTaker timer("generateChunkRaw()");
2077 // The distance how far into the neighbors the generator is allowed to go.
2078 s16 max_spread_amount_sectors = 2;
2079 assert(max_spread_amount_sectors <= m_chunksize);
2080 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2082 // Minimum amount of space left on sides for mud to fall in
2083 //s16 min_mud_fall_space = 2;
2085 // Maximum diameter of stone obstacles in X and Z
2086 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2087 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2089 s16 y_blocks_min = -4;
2090 s16 y_blocks_max = 3;
2091 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2092 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2093 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2095 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2096 s16 sectorpos_base_size = m_chunksize;
2098 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2099 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2100 v2s16 sectorpos_bigbase =
2101 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2102 s16 sectorpos_bigbase_size =
2103 sectorpos_base_size + 2 * max_spread_amount_sectors;
2105 v3s16 bigarea_blocks_min(
2106 sectorpos_bigbase.X,
2111 v3s16 bigarea_blocks_max(
2112 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2114 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2117 // Relative values to control amount of stuff in one chunk
2118 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2119 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2120 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2121 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2122 *(u32)h_blocks*MAP_BLOCKSIZE;
2125 The limiting edges of the lighting update, inclusive.
2127 s16 lighting_min_d = 0-max_spread_amount;
2128 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2131 Create the whole area of this and the neighboring chunks
2134 TimeTaker timer("generateChunkRaw() create area");
2136 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2137 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2139 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2140 ServerMapSector *sector = createSector(sectorpos);
2143 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2145 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2146 MapBlock *block = createBlock(blockpos);
2148 // Lighting won't be calculated
2149 //block->setLightingExpired(true);
2150 // Lighting will be calculated
2151 block->setLightingExpired(false);
2154 Block gets sunlight if this is true.
2156 This should be set to true when the top side of a block
2157 is completely exposed to the sky.
2159 Actually this doesn't matter now because the
2160 initial lighting is done here.
2162 block->setIsUnderground(y != y_blocks_max);
2168 Now we have a big empty area.
2170 Make a ManualMapVoxelManipulator that contains this and the
2174 ManualMapVoxelManipulator vmanip(this);
2175 // Add the area we just generated
2177 TimeTaker timer("generateChunkRaw() initialEmerge");
2178 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2181 TimeTaker timer_generate("generateChunkRaw() generate");
2183 // Maximum height of the stone surface and obstacles.
2184 // This is used to disable dungeon generation from going too high.
2185 s16 stone_surface_max_y = 0;
2188 Generate general ground level to full area
2193 //TimeTaker timer1("ground level");
2195 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2196 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2199 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2202 Skip of already generated
2205 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2206 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2210 // Ground height at this point
2211 float surface_y_f = 0.0;
2213 // Use perlin noise for ground height
2214 surface_y_f = base_rock_level_2d(m_seed, p2d);
2216 /*// Experimental stuff
2218 float a = highlands_level_2d(m_seed, p2d);
2223 // Convert to integer
2224 s16 surface_y = (s16)surface_y_f;
2227 if(surface_y > stone_surface_max_y)
2228 stone_surface_max_y = surface_y;
2231 Fill ground with stone
2234 // Use fast index incrementing
2235 v3s16 em = vmanip.m_area.getExtent();
2236 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2237 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2239 vmanip.m_data[i].d = CONTENT_STONE;
2241 vmanip.m_area.add_y(em, i, 1);
2249 Randomize some parameters
2252 s32 stone_obstacle_count = 0;
2253 /*s32 stone_obstacle_count =
2254 rangelim((1.0+noise2d(m_seed+897,
2255 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2257 s16 stone_obstacle_max_height = 0;
2258 /*s16 stone_obstacle_max_height =
2259 rangelim((1.0+noise2d(m_seed+5902,
2260 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2263 Loop this part, it will make stuff look older and newer nicely
2265 //for(u32 i_age=0; i_age<1; i_age++)
2266 for(u32 i_age=0; i_age<2; i_age++)
2271 //TimeTaker timer1("stone obstacles");
2274 Add some random stone obstacles
2277 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2279 // Randomize max height so usually stuff will be quite low
2280 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2282 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2283 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2286 myrand_range(5, stone_obstacle_max_size),
2287 myrand_range(0, maxheight_randomized),
2288 myrand_range(5, stone_obstacle_max_size)
2291 // Don't make stupid small rectangle bumps
2296 myrand_range(1+ob_size.X/2+2,
2297 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2298 myrand_range(1+ob_size.Z/2+2,
2299 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2302 // Minimum space left on top of the obstacle
2303 s16 min_head_space = 12;
2305 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2306 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2308 // Node position in 2d
2309 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2311 // Find stone ground level
2312 // (ignore everything else than mud in already generated chunks)
2313 // and mud amount over the stone level
2317 v3s16 em = vmanip.m_area.getExtent();
2318 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2320 // Go to ground level
2321 for(y=y_nodes_max; y>=y_nodes_min; y--)
2323 MapNode *n = &vmanip.m_data[i];
2324 /*if(content_walkable(n.d)
2325 && n.d != CONTENT_MUD
2326 && n.d != CONTENT_GRASS)
2328 if(n->d == CONTENT_STONE)
2331 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2335 Change to mud because otherwise we might
2336 be throwing mud on grass at the next
2342 vmanip.m_area.add_y(em, i, -1);
2344 if(y >= y_nodes_min)
2347 surface_y = y_nodes_min;
2355 v3s16 em = vmanip.m_area.getExtent();
2356 s16 y_start = surface_y+1;
2357 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2361 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2363 MapNode &n = vmanip.m_data[i];
2364 n.d = CONTENT_STONE;
2366 if(y > stone_surface_max_y)
2367 stone_surface_max_y = y;
2370 if(count >= ob_size.Y)
2373 vmanip.m_area.add_y(em, i, 1);
2377 for(; y<=y_nodes_max - min_head_space; y++)
2379 MapNode &n = vmanip.m_data[i];
2382 if(count >= mud_amount)
2385 vmanip.m_area.add_y(em, i, 1);
2395 //TimeTaker timer1("dungeons");
2400 u32 dungeons_count = relative_volume / 600000;
2401 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2402 if(stone_surface_max_y < WATER_LEVEL)
2404 //dungeons_count = 0;
2405 //bruises_count = 0;
2406 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2408 s16 min_tunnel_diameter = 2;
2409 s16 max_tunnel_diameter = 6;
2410 u16 tunnel_routepoints = 15;
2412 bool bruise_surface = (jj < bruises_count);
2416 min_tunnel_diameter = 5;
2417 max_tunnel_diameter = myrand_range(10, 20);
2418 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2419 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2421 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2422 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2424 tunnel_routepoints = 5;
2427 // Allowed route area size in nodes
2429 sectorpos_base_size*MAP_BLOCKSIZE,
2430 h_blocks*MAP_BLOCKSIZE,
2431 sectorpos_base_size*MAP_BLOCKSIZE
2434 // Area starting point in nodes
2436 sectorpos_base.X*MAP_BLOCKSIZE,
2437 y_blocks_min*MAP_BLOCKSIZE,
2438 sectorpos_base.Y*MAP_BLOCKSIZE
2442 //(this should be more than the maximum radius of the tunnel)
2443 //s16 insure = 5; // Didn't work with max_d = 20
2445 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2446 ar += v3s16(1,0,1) * more * 2;
2447 of -= v3s16(1,0,1) * more;
2449 s16 route_y_min = 0;
2450 //s16 route_y_max = ar.Y-1;
2451 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
2453 if(bruise_surface == false)
2455 // Don't go through surface too often
2456 route_y_max -= myrand_range(0, max_tunnel_diameter*2);
2458 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2462 /*// Minimum is at y=0
2463 route_y_min = -of.Y - 0;*/
2464 // Minimum is at y=max_tunnel_diameter/4
2465 //route_y_min = -of.Y + max_tunnel_diameter/4;
2466 //s16 min = -of.Y + max_tunnel_diameter/4;
2467 s16 min = -of.Y + 0;
2468 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2469 route_y_min = rangelim(route_y_min, 0, route_y_max);
2472 /*dstream<<"route_y_min = "<<route_y_min
2473 <<", route_y_max = "<<route_y_max<<std::endl;*/
2475 // Randomize starting position
2477 (float)(myrand()%ar.X)+0.5,
2478 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2479 (float)(myrand()%ar.Z)+0.5
2482 MapNode airnode(CONTENT_AIR);
2485 Generate some tunnel starting from orp
2488 for(u16 j=0; j<tunnel_routepoints; j++)
2491 s16 min_d = min_tunnel_diameter;
2492 s16 max_d = max_tunnel_diameter;
2493 s16 rs = myrand_range(min_d, max_d);
2495 v3s16 maxlen(15, 5, 15);
2499 maxlen = v3s16(rs*7,rs*7,rs*7);
2503 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2504 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2505 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2510 else if(rp.X >= ar.X)
2512 if(rp.Y < route_y_min)
2514 else if(rp.Y >= route_y_max)
2515 rp.Y = route_y_max-1;
2518 else if(rp.Z >= ar.Z)
2522 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2524 v3f fp = orp + vec * f;
2525 v3s16 cp(fp.X, fp.Y, fp.Z);
2528 s16 d1 = d0 + rs - 1;
2529 for(s16 z0=d0; z0<=d1; z0++)
2531 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2532 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2533 for(s16 x0=-si; x0<=si-1; x0++)
2535 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2536 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2537 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2538 //s16 si2 = rs - abs(x0);
2539 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2545 /*if(isInArea(p, ar) == false)
2547 // Check only height
2548 if(y < 0 || y >= ar.Y)
2552 //assert(vmanip.m_area.contains(p));
2553 if(vmanip.m_area.contains(p) == false)
2555 dstream<<"WARNING: "<<__FUNCTION_NAME
2556 <<":"<<__LINE__<<": "
2557 <<"point not in area"
2562 // Just set it to air, it will be changed to
2564 u32 i = vmanip.m_area.index(p);
2565 vmanip.m_data[i] = airnode;
2579 //TimeTaker timer1("ore veins");
2584 for(u32 jj=0; jj<relative_volume/2000; jj++)
2586 s16 max_vein_diameter = 3;
2588 // Allowed route area size in nodes
2590 sectorpos_base_size*MAP_BLOCKSIZE,
2591 h_blocks*MAP_BLOCKSIZE,
2592 sectorpos_base_size*MAP_BLOCKSIZE
2595 // Area starting point in nodes
2597 sectorpos_base.X*MAP_BLOCKSIZE,
2598 y_blocks_min*MAP_BLOCKSIZE,
2599 sectorpos_base.Y*MAP_BLOCKSIZE
2603 //(this should be more than the maximum radius of the tunnel)
2605 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2606 ar += v3s16(1,0,1) * more * 2;
2607 of -= v3s16(1,0,1) * more;
2609 // Randomize starting position
2611 (float)(myrand()%ar.X)+0.5,
2612 (float)(myrand()%ar.Y)+0.5,
2613 (float)(myrand()%ar.Z)+0.5
2616 // Randomize mineral
2617 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2620 Generate some vein starting from orp
2623 for(u16 j=0; j<2; j++)
2626 (float)(myrand()%ar.X)+0.5,
2627 (float)(myrand()%ar.Y)+0.5,
2628 (float)(myrand()%ar.Z)+0.5
2630 v3f vec = rp - orp;*/
2632 v3s16 maxlen(10, 10, 10);
2634 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2635 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2636 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2641 else if(rp.X >= ar.X)
2645 else if(rp.Y >= ar.Y)
2649 else if(rp.Z >= ar.Z)
2655 s16 max_d = max_vein_diameter;
2656 s16 rs = myrand_range(min_d, max_d);
2658 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2660 v3f fp = orp + vec * f;
2661 v3s16 cp(fp.X, fp.Y, fp.Z);
2663 s16 d1 = d0 + rs - 1;
2664 for(s16 z0=d0; z0<=d1; z0++)
2666 s16 si = rs - abs(z0);
2667 for(s16 x0=-si; x0<=si-1; x0++)
2669 s16 si2 = rs - abs(x0);
2670 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2672 // Don't put mineral to every place
2680 /*if(isInArea(p, ar) == false)
2682 // Check only height
2683 if(y < 0 || y >= ar.Y)
2687 assert(vmanip.m_area.contains(p));
2689 // Just set it to air, it will be changed to
2691 u32 i = vmanip.m_area.index(p);
2692 MapNode *n = &vmanip.m_data[i];
2693 if(n->d == CONTENT_STONE)
2708 //TimeTaker timer1("add mud");
2711 Add mud to the central chunk
2714 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2715 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2717 // Node position in 2d
2718 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2720 // Randomize mud amount
2721 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2722 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2723 m_seed+1, 3, 0.55));
2725 // Find ground level
2726 s16 surface_y = find_ground_level(vmanip, p2d);
2729 If topmost node is grass, change it to mud.
2730 It might be if it was flown to there from a neighboring
2731 chunk and then converted.
2734 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2735 MapNode *n = &vmanip.m_data[i];
2736 if(n->d == CONTENT_GRASS)
2745 v3s16 em = vmanip.m_area.getExtent();
2746 s16 y_start = surface_y+1;
2747 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2748 for(s16 y=y_start; y<=y_nodes_max; y++)
2750 if(mudcount >= mud_add_amount)
2753 MapNode &n = vmanip.m_data[i];
2757 vmanip.m_area.add_y(em, i, 1);
2766 TimeTaker timer1("flow mud");
2769 Flow mud away from steep edges
2772 // Limit area by 1 because mud is flown into neighbors.
2773 s16 mudflow_minpos = 0-max_spread_amount+1;
2774 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2776 // Iterate a few times
2777 for(s16 k=0; k<3; k++)
2780 for(s16 x=mudflow_minpos;
2783 for(s16 z=mudflow_minpos;
2787 // Invert coordinates every 2nd iteration
2790 x = mudflow_maxpos - (x-mudflow_minpos);
2791 z = mudflow_maxpos - (z-mudflow_minpos);
2794 // Node position in 2d
2795 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2797 v3s16 em = vmanip.m_area.getExtent();
2798 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2805 for(; y>=y_nodes_min; y--)
2807 n = &vmanip.m_data[i];
2808 //if(content_walkable(n->d))
2810 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2813 vmanip.m_area.add_y(em, i, -1);
2816 // Stop if out of area
2817 //if(vmanip.m_area.contains(i) == false)
2821 /*// If not mud, do nothing to it
2822 MapNode *n = &vmanip.m_data[i];
2823 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2827 Don't flow it if the stuff under it is not mud
2831 vmanip.m_area.add_y(em, i2, -1);
2832 // Cancel if out of area
2833 if(vmanip.m_area.contains(i2) == false)
2835 MapNode *n2 = &vmanip.m_data[i2];
2836 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2840 // Make it exactly mud
2843 /*s16 recurse_count = 0;
2847 v3s16(0,0,1), // back
2848 v3s16(1,0,0), // right
2849 v3s16(0,0,-1), // front
2850 v3s16(-1,0,0), // left
2853 // Theck that upper is air or doesn't exist.
2854 // Cancel dropping if upper keeps it in place
2856 vmanip.m_area.add_y(em, i3, 1);
2857 if(vmanip.m_area.contains(i3) == true
2858 && content_walkable(vmanip.m_data[i3].d) == true)
2865 for(u32 di=0; di<4; di++)
2867 v3s16 dirp = dirs4[di];
2870 vmanip.m_area.add_p(em, i2, dirp);
2871 // Fail if out of area
2872 if(vmanip.m_area.contains(i2) == false)
2874 // Check that side is air
2875 MapNode *n2 = &vmanip.m_data[i2];
2876 if(content_walkable(n2->d))
2878 // Check that under side is air
2879 vmanip.m_area.add_y(em, i2, -1);
2880 if(vmanip.m_area.contains(i2) == false)
2882 n2 = &vmanip.m_data[i2];
2883 if(content_walkable(n2->d))
2885 /*// Check that under that is air (need a drop of 2)
2886 vmanip.m_area.add_y(em, i2, -1);
2887 if(vmanip.m_area.contains(i2) == false)
2889 n2 = &vmanip.m_data[i2];
2890 if(content_walkable(n2->d))
2892 // Loop further down until not air
2894 vmanip.m_area.add_y(em, i2, -1);
2895 // Fail if out of area
2896 if(vmanip.m_area.contains(i2) == false)
2898 n2 = &vmanip.m_data[i2];
2899 }while(content_walkable(n2->d) == false);
2900 // Loop one up so that we're in air
2901 vmanip.m_area.add_y(em, i2, 1);
2902 n2 = &vmanip.m_data[i2];
2904 // Move mud to new place
2906 // Set old place to be air
2907 *n = MapNode(CONTENT_AIR);
2920 //TimeTaker timer1("add water");
2923 Add water to the central chunk (and a bit more)
2926 for(s16 x=0-max_spread_amount;
2927 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2929 for(s16 z=0-max_spread_amount;
2930 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2933 // Node position in 2d
2934 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2936 // Find ground level
2937 //s16 surface_y = find_ground_level(vmanip, p2d);
2940 If ground level is over water level, skip.
2941 NOTE: This leaves caves near water without water,
2942 which looks especially crappy when the nearby water
2943 won't start flowing either for some reason
2945 /*if(surface_y > WATER_LEVEL)
2952 v3s16 em = vmanip.m_area.getExtent();
2953 u8 light = LIGHT_MAX;
2954 // Start at global water surface level
2955 s16 y_start = WATER_LEVEL;
2956 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2957 MapNode *n = &vmanip.m_data[i];
2959 /*// Add first one to transforming liquid queue, if water
2960 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2962 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2963 m_transforming_liquid.push_back(p);
2966 for(s16 y=y_start; y>=y_nodes_min; y--)
2968 n = &vmanip.m_data[i];
2970 // Stop when there is no water and no air
2971 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2972 && n->d != CONTENT_WATER)
2974 /*// Add bottom one to transforming liquid queue
2975 vmanip.m_area.add_y(em, i, 1);
2976 n = &vmanip.m_data[i];
2977 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2979 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2980 m_transforming_liquid.push_back(p);
2986 n->d = CONTENT_WATERSOURCE;
2987 n->setLight(LIGHTBANK_DAY, light);
2989 // Add to transforming liquid queue (in case it'd
2991 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2992 m_transforming_liquid.push_back(p);
2995 vmanip.m_area.add_y(em, i, -1);
3008 //TimeTaker timer1("convert mud to sand");
3014 //s16 mud_add_amount = myrand_range(2, 4);
3015 //s16 mud_add_amount = 0;
3017 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3018 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3019 for(s16 x=0-max_spread_amount+1;
3020 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3022 for(s16 z=0-max_spread_amount+1;
3023 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3026 // Node position in 2d
3027 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3029 // Determine whether to have sand here
3030 double sandnoise = noise2d_perlin(
3031 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3032 m_seed+59420, 3, 0.50);
3034 bool have_sand = (sandnoise > 0.0);
3036 if(have_sand == false)
3039 // Find ground level
3040 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3042 if(surface_y > WATER_LEVEL + 2)
3046 v3s16 em = vmanip.m_area.getExtent();
3047 s16 y_start = surface_y;
3048 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3049 u32 not_sand_counter = 0;
3050 for(s16 y=y_start; y>=y_nodes_min; y--)
3052 MapNode *n = &vmanip.m_data[i];
3053 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3055 n->d = CONTENT_SAND;
3060 if(not_sand_counter > 3)
3064 vmanip.m_area.add_y(em, i, -1);
3073 //TimeTaker timer1("generate trees");
3079 // Divide area into parts
3081 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3082 double area = sidelen * sidelen;
3083 for(s16 x0=0; x0<div; x0++)
3084 for(s16 z0=0; z0<div; z0++)
3086 // Center position of part of division
3088 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3089 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3091 // Minimum edge of part of division
3093 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3094 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3096 // Maximum edge of part of division
3098 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3099 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3102 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3103 // Put trees in random places on part of division
3104 for(u32 i=0; i<tree_count; i++)
3106 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3107 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3108 s16 y = find_ground_level(vmanip, v2s16(x,z));
3109 // Don't make a tree under water level
3114 Trees grow only on mud and grass
3117 u32 i = vmanip.m_area.index(v3s16(p));
3118 MapNode *n = &vmanip.m_data[i];
3119 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3124 make_tree(vmanip, p);
3127 /*u32 tree_max = relative_area / 60;
3128 //u32 count = myrand_range(0, tree_max);
3129 for(u32 i=0; i<count; i++)
3131 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3132 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3133 x += sectorpos_base.X*MAP_BLOCKSIZE;
3134 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3135 s16 y = find_ground_level(vmanip, v2s16(x,z));
3136 // Don't make a tree under water level
3141 make_tree(vmanip, p);
3149 //TimeTaker timer1("grow grass");
3155 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3156 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3157 for(s16 x=0-max_spread_amount;
3158 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3160 for(s16 z=0-max_spread_amount;
3161 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3164 // Node position in 2d
3165 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3168 Find the lowest surface to which enough light ends up
3171 Basically just wait until not air and not leaves.
3175 v3s16 em = vmanip.m_area.getExtent();
3176 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3178 // Go to ground level
3179 for(y=y_nodes_max; y>=y_nodes_min; y--)
3181 MapNode &n = vmanip.m_data[i];
3182 if(n.d != CONTENT_AIR
3183 && n.d != CONTENT_LEAVES)
3185 vmanip.m_area.add_y(em, i, -1);
3187 if(y >= y_nodes_min)
3190 surface_y = y_nodes_min;
3193 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3194 MapNode *n = &vmanip.m_data[i];
3195 if(n->d == CONTENT_MUD)
3196 n->d = CONTENT_GRASS;
3202 Initial lighting (sunlight)
3205 core::map<v3s16, bool> light_sources;
3208 // 750ms @cs=8, can't optimize more
3209 TimeTaker timer1("initial lighting");
3213 Go through the edges and add all nodes that have light to light_sources
3217 for(s16 i=0; i<4; i++)
3219 for(s16 j=lighting_min_d;
3226 if(i == 0 || i == 1)
3228 x = (i==0) ? lighting_min_d : lighting_max_d;
3237 z = (i==0) ? lighting_min_d : lighting_max_d;
3244 // Node position in 2d
3245 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3248 v3s16 em = vmanip.m_area.getExtent();
3249 s16 y_start = y_nodes_max;
3250 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3251 for(s16 y=y_start; y>=y_nodes_min; y--)
3253 MapNode *n = &vmanip.m_data[i];
3254 if(n->getLight(LIGHTBANK_DAY) != 0)
3256 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3258 //NOTE: This is broken, at least the index has to
3267 Go through the edges and apply sunlight to them, not caring
3272 for(s16 i=0; i<4; i++)
3274 for(s16 j=lighting_min_d;
3281 if(i == 0 || i == 1)
3283 x = (i==0) ? lighting_min_d : lighting_max_d;
3292 z = (i==0) ? lighting_min_d : lighting_max_d;
3299 // Node position in 2d
3300 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3302 // Loop from top to down
3304 u8 light = LIGHT_SUN;
3305 v3s16 em = vmanip.m_area.getExtent();
3306 s16 y_start = y_nodes_max;
3307 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3308 for(s16 y=y_start; y>=y_nodes_min; y--)
3310 MapNode *n = &vmanip.m_data[i];
3311 if(light_propagates_content(n->d) == false)
3315 else if(light != LIGHT_SUN
3316 || sunlight_propagates_content(n->d) == false)
3322 n->setLight(LIGHTBANK_DAY, light);
3323 n->setLight(LIGHTBANK_NIGHT, 0);
3327 // Insert light source
3328 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3331 // Increment index by y
3332 vmanip.m_area.add_y(em, i, -1);
3338 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3339 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3340 /*for(s16 x=0-max_spread_amount+1;
3341 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3343 for(s16 z=0-max_spread_amount+1;
3344 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3348 This has to be 1 smaller than the actual area, because
3349 neighboring nodes are checked.
3351 for(s16 x=lighting_min_d+1;
3352 x<=lighting_max_d-1;
3354 for(s16 z=lighting_min_d+1;
3355 z<=lighting_max_d-1;
3358 // Node position in 2d
3359 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3362 Apply initial sunlight
3365 u8 light = LIGHT_SUN;
3366 bool add_to_sources = false;
3367 v3s16 em = vmanip.m_area.getExtent();
3368 s16 y_start = y_nodes_max;
3369 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3370 for(s16 y=y_start; y>=y_nodes_min; y--)
3372 MapNode *n = &vmanip.m_data[i];
3374 if(light_propagates_content(n->d) == false)
3378 else if(light != LIGHT_SUN
3379 || sunlight_propagates_content(n->d) == false)
3385 // This doesn't take much time
3386 if(add_to_sources == false)
3389 Check sides. If side is not air or water, start
3390 adding to light_sources.
3393 v3s16(0,0,1), // back
3394 v3s16(1,0,0), // right
3395 v3s16(0,0,-1), // front
3396 v3s16(-1,0,0), // left
3398 for(u32 di=0; di<4; di++)
3400 v3s16 dirp = dirs4[di];
3402 vmanip.m_area.add_p(em, i2, dirp);
3403 MapNode *n2 = &vmanip.m_data[i2];
3405 n2->d != CONTENT_AIR
3406 && n2->d != CONTENT_WATERSOURCE
3407 && n2->d != CONTENT_WATER
3409 add_to_sources = true;
3415 n->setLight(LIGHTBANK_DAY, light);
3416 n->setLight(LIGHTBANK_NIGHT, 0);
3418 // This doesn't take much time
3419 if(light != 0 && add_to_sources)
3421 // Insert light source
3422 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3425 // Increment index by y
3426 vmanip.m_area.add_y(em, i, -1);
3433 for(s16 x=lighting_min_d+1;
3434 x<=lighting_max_d-1;
3436 for(s16 z=lighting_min_d+1;
3437 z<=lighting_max_d-1;
3440 // Node position in 2d
3441 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3444 Apply initial sunlight
3447 u8 light = LIGHT_SUN;
3448 v3s16 em = vmanip.m_area.getExtent();
3449 s16 y_start = y_nodes_max;
3450 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3451 for(s16 y=y_start; y>=y_nodes_min; y--)
3453 MapNode *n = &vmanip.m_data[i];
3455 if(light_propagates_content(n->d) == false)
3459 else if(light != LIGHT_SUN
3460 || sunlight_propagates_content(n->d) == false)
3466 n->setLight(LIGHTBANK_DAY, light);
3467 n->setLight(LIGHTBANK_NIGHT, 0);
3469 // This doesn't take much time
3472 // Insert light source
3473 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3476 // Increment index by y
3477 vmanip.m_area.add_y(em, i, -1);
3485 // Spread light around
3487 TimeTaker timer("generateChunkRaw() spreadLight");
3488 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3495 timer_generate.stop();
3498 Blit generated stuff to map
3502 //TimeTaker timer("generateChunkRaw() blitBackAll");
3503 vmanip.blitBackAll(&changed_blocks);
3506 Update day/night difference cache of the MapBlocks
3509 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3510 i.atEnd() == false; i++)
3512 MapBlock *block = i.getNode()->getValue();
3513 block->updateDayNightDiff();
3519 Create chunk metadata
3522 for(s16 x=-1; x<=1; x++)
3523 for(s16 y=-1; y<=1; y++)
3525 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3526 // Add chunk meta information
3527 MapChunk *chunk = getChunk(chunkpos0);
3530 chunk = new MapChunk();
3531 m_chunks.insert(chunkpos0, chunk);
3533 //chunk->setIsVolatile(true);
3534 if(chunk->getGenLevel() > GENERATED_PARTLY)
3535 chunk->setGenLevel(GENERATED_PARTLY);
3539 Set central chunk non-volatile and return it
3541 MapChunk *chunk = getChunk(chunkpos);
3544 //chunk->setIsVolatile(false);
3545 chunk->setGenLevel(GENERATED_FULLY);
3550 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3551 core::map<v3s16, MapBlock*> &changed_blocks)
3553 dstream<<"generateChunk(): Generating chunk "
3554 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3557 /*for(s16 x=-1; x<=1; x++)
3558 for(s16 y=-1; y<=1; y++)*/
3559 for(s16 x=-0; x<=0; x++)
3560 for(s16 y=-0; y<=0; y++)
3562 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3563 MapChunk *chunk = getChunk(chunkpos0);
3564 // Skip if already generated
3565 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3567 generateChunkRaw(chunkpos0, changed_blocks);
3570 assert(chunkNonVolatile(chunkpos1));
3572 MapChunk *chunk = getChunk(chunkpos1);
3576 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3578 DSTACK("%s: p2d=(%d,%d)",
3583 Check if it exists already in memory
3585 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3590 Try to load it from disk (with blocks)
3592 if(loadSectorFull(p2d) == true)
3594 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3597 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3598 throw InvalidPositionException("");
3604 Do not create over-limit
3606 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3607 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3608 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3609 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3610 throw InvalidPositionException("createSector(): pos. over limit");
3613 Generate blank sector
3616 sector = new ServerMapSector(this, p2d);
3618 // Sector position on map in nodes
3619 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3624 m_sectors.insert(p2d, sector);
3629 MapSector * ServerMap::emergeSector(v2s16 p2d,
3630 core::map<v3s16, MapBlock*> &changed_blocks)
3632 DSTACK("%s: p2d=(%d,%d)",
3639 v2s16 chunkpos = sector_to_chunk(p2d);
3640 /*bool chunk_nonvolatile = false;
3641 MapChunk *chunk = getChunk(chunkpos);
3642 if(chunk && chunk->getIsVolatile() == false)
3643 chunk_nonvolatile = true;*/
3644 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3647 If chunk is not fully generated, generate chunk
3649 if(chunk_nonvolatile == false)
3651 // Generate chunk and neighbors
3652 generateChunk(chunkpos, changed_blocks);
3656 Return sector if it exists now
3658 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3663 Try to load it from disk
3665 if(loadSectorFull(p2d) == true)
3667 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3670 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3671 throw InvalidPositionException("");
3677 generateChunk should have generated the sector
3684 //return generateSector();
3688 NOTE: This is not used for main map generation, only for blocks
3689 that are very high or low
3691 MapBlock * ServerMap::generateBlock(
3693 MapBlock *original_dummy,
3694 ServerMapSector *sector,
3695 core::map<v3s16, MapBlock*> &changed_blocks,
3696 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3699 DSTACK("%s: p=(%d,%d,%d)",
3703 /*dstream<<"generateBlock(): "
3704 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3707 MapBlock *block = original_dummy;
3709 v2s16 p2d(p.X, p.Z);
3713 Do not generate over-limit
3715 if(blockpos_over_limit(p))
3717 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3718 throw InvalidPositionException("generateBlock(): pos. over limit");
3722 If block doesn't exist, create one.
3723 If it exists, it is a dummy. In that case unDummify() it.
3725 NOTE: This already sets the map as the parent of the block
3729 block = sector->createBlankBlockNoInsert(block_y);
3733 // Remove the block so that nobody can get a half-generated one.
3734 sector->removeBlock(block);
3735 // Allocate the block to contain the generated data
3739 u8 water_material = CONTENT_WATERSOURCE;
3741 s32 lowest_ground_y = 32767;
3742 s32 highest_ground_y = -32768;
3744 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3745 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3747 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3751 if(surface_y < lowest_ground_y)
3752 lowest_ground_y = surface_y;
3753 if(surface_y > highest_ground_y)
3754 highest_ground_y = surface_y;
3756 s32 surface_depth = 2;
3758 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3760 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3765 NOTE: If there are some man-made structures above the
3766 newly created block, they won't be taken into account.
3768 if(real_y > surface_y)
3769 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3775 // If node is over heightmap y, it's air or water
3776 if(real_y > surface_y)
3778 // If under water level, it's water
3779 if(real_y < WATER_LEVEL)
3781 n.d = water_material;
3782 n.setLight(LIGHTBANK_DAY,
3783 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3785 Add to transforming liquid queue (in case it'd
3788 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3789 m_transforming_liquid.push_back(real_pos);
3795 // Else it's ground or dungeons (air)
3798 // If it's surface_depth under ground, it's stone
3799 if(real_y <= surface_y - surface_depth)
3801 n.d = CONTENT_STONE;
3805 // It is mud if it is under the first ground
3806 // level or under water
3807 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3813 n.d = CONTENT_GRASS;
3816 //n.d = CONTENT_MUD;
3818 /*// If under water level, it's mud
3819 if(real_y < WATER_LEVEL)
3821 // Only the topmost node is grass
3822 else if(real_y <= surface_y - 1)
3825 n.d = CONTENT_GRASS;*/
3829 block->setNode(v3s16(x0,y0,z0), n);
3834 Calculate some helper variables
3837 // Completely underground if the highest part of block is under lowest
3839 // This has to be very sure; it's probably one too strict now but
3840 // that's just better.
3841 bool completely_underground =
3842 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3844 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3846 bool mostly_underwater_surface = false;
3847 if(highest_ground_y < WATER_LEVEL
3848 && some_part_underground && !completely_underground)
3849 mostly_underwater_surface = true;
3852 Get local attributes
3855 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3857 float caves_amount = 0.5;
3862 NOTE: BEWARE: Too big amount of attribute points slows verything
3864 1 interpolation from 5000 points takes 2-3ms.
3866 //TimeTaker timer("generateBlock() local attribute retrieval");
3867 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3868 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3869 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3873 //dstream<<"generateBlock(): Done"<<std::endl;
3879 // Initialize temporary table
3880 const s32 ued = MAP_BLOCKSIZE;
3881 bool underground_emptiness[ued*ued*ued];
3882 for(s32 i=0; i<ued*ued*ued; i++)
3884 underground_emptiness[i] = 0;
3891 Initialize orp and ors. Try to find if some neighboring
3892 MapBlock has a tunnel ended in its side
3896 (float)(myrand()%ued)+0.5,
3897 (float)(myrand()%ued)+0.5,
3898 (float)(myrand()%ued)+0.5
3901 bool found_existing = false;
3907 for(s16 y=0; y<ued; y++)
3908 for(s16 x=0; x<ued; x++)
3910 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3911 if(getNode(ap).d == CONTENT_AIR)
3913 orp = v3f(x+1,y+1,0);
3914 found_existing = true;
3915 goto continue_generating;
3919 catch(InvalidPositionException &e){}
3925 for(s16 y=0; y<ued; y++)
3926 for(s16 x=0; x<ued; x++)
3928 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3929 if(getNode(ap).d == CONTENT_AIR)
3931 orp = v3f(x+1,y+1,ued-1);
3932 found_existing = true;
3933 goto continue_generating;
3937 catch(InvalidPositionException &e){}
3943 for(s16 y=0; y<ued; y++)
3944 for(s16 z=0; z<ued; z++)
3946 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3947 if(getNode(ap).d == CONTENT_AIR)
3949 orp = v3f(0,y+1,z+1);
3950 found_existing = true;
3951 goto continue_generating;
3955 catch(InvalidPositionException &e){}
3961 for(s16 y=0; y<ued; y++)
3962 for(s16 z=0; z<ued; z++)
3964 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3965 if(getNode(ap).d == CONTENT_AIR)
3967 orp = v3f(ued-1,y+1,z+1);
3968 found_existing = true;
3969 goto continue_generating;
3973 catch(InvalidPositionException &e){}
3979 for(s16 x=0; x<ued; x++)
3980 for(s16 z=0; z<ued; z++)
3982 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3983 if(getNode(ap).d == CONTENT_AIR)
3985 orp = v3f(x+1,0,z+1);
3986 found_existing = true;
3987 goto continue_generating;
3991 catch(InvalidPositionException &e){}
3997 for(s16 x=0; x<ued; x++)
3998 for(s16 z=0; z<ued; z++)
4000 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4001 if(getNode(ap).d == CONTENT_AIR)
4003 orp = v3f(x+1,ued-1,z+1);
4004 found_existing = true;
4005 goto continue_generating;
4009 catch(InvalidPositionException &e){}
4011 continue_generating:
4014 Choose whether to actually generate dungeon
4016 bool do_generate_dungeons = true;
4017 // Don't generate if no part is underground
4018 if(!some_part_underground)
4020 do_generate_dungeons = false;
4022 // Don't generate if mostly underwater surface
4023 /*else if(mostly_underwater_surface)
4025 do_generate_dungeons = false;
4027 // Partly underground = cave
4028 else if(!completely_underground)
4030 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4032 // Found existing dungeon underground
4033 else if(found_existing && completely_underground)
4035 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4037 // Underground and no dungeons found
4040 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4043 if(do_generate_dungeons)
4046 Generate some tunnel starting from orp and ors
4048 for(u16 i=0; i<3; i++)
4051 (float)(myrand()%ued)+0.5,
4052 (float)(myrand()%ued)+0.5,
4053 (float)(myrand()%ued)+0.5
4057 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4061 for(float f=0; f<1.0; f+=0.04)
4063 v3f fp = orp + vec * f;
4064 v3s16 cp(fp.X, fp.Y, fp.Z);
4066 s16 d1 = d0 + rs - 1;
4067 for(s16 z0=d0; z0<=d1; z0++)
4069 s16 si = rs - abs(z0);
4070 for(s16 x0=-si; x0<=si-1; x0++)
4072 s16 si2 = rs - abs(x0);
4073 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4079 if(isInArea(p, ued) == false)
4081 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4093 // Set to true if has caves.
4094 // Set when some non-air is changed to air when making caves.
4095 bool has_dungeons = false;
4098 Apply temporary cave data to block
4101 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4102 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4104 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4106 MapNode n = block->getNode(v3s16(x0,y0,z0));
4109 if(underground_emptiness[
4110 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4111 +ued*(y0*ued/MAP_BLOCKSIZE)
4112 +(x0*ued/MAP_BLOCKSIZE)])
4114 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4117 has_dungeons = true;
4123 block->setNode(v3s16(x0,y0,z0), n);
4128 This is used for guessing whether or not the block should
4129 receive sunlight from the top if the block above doesn't exist
4131 block->setIsUnderground(completely_underground);
4134 Force lighting update if some part of block is partly
4135 underground and has caves.
4137 /*if(some_part_underground && !completely_underground && has_dungeons)
4139 //dstream<<"Half-ground caves"<<std::endl;
4140 lighting_invalidated_blocks[block->getPos()] = block;
4143 // DEBUG: Always update lighting
4144 //lighting_invalidated_blocks[block->getPos()] = block;
4150 if(some_part_underground)
4152 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4157 for(s16 i=0; i<underground_level/4 + 1; i++)
4159 if(myrand()%50 == 0)
4162 (myrand()%(MAP_BLOCKSIZE-2))+1,
4163 (myrand()%(MAP_BLOCKSIZE-2))+1,
4164 (myrand()%(MAP_BLOCKSIZE-2))+1
4170 for(u16 i=0; i<27; i++)
4172 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4174 block->setNode(cp+g_27dirs[i], n);
4182 u16 coal_amount = 30;
4183 u16 coal_rareness = 60 / coal_amount;
4184 if(coal_rareness == 0)
4186 if(myrand()%coal_rareness == 0)
4188 u16 a = myrand() % 16;
4189 u16 amount = coal_amount * a*a*a / 1000;
4190 for(s16 i=0; i<amount; i++)
4193 (myrand()%(MAP_BLOCKSIZE-2))+1,
4194 (myrand()%(MAP_BLOCKSIZE-2))+1,
4195 (myrand()%(MAP_BLOCKSIZE-2))+1
4199 n.d = CONTENT_STONE;
4200 n.param = MINERAL_COAL;
4202 for(u16 i=0; i<27; i++)
4204 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4206 block->setNode(cp+g_27dirs[i], n);
4214 //TODO: change to iron_amount or whatever
4215 u16 iron_amount = 15;
4216 u16 iron_rareness = 60 / iron_amount;
4217 if(iron_rareness == 0)
4219 if(myrand()%iron_rareness == 0)
4221 u16 a = myrand() % 16;
4222 u16 amount = iron_amount * a*a*a / 1000;
4223 for(s16 i=0; i<amount; i++)
4226 (myrand()%(MAP_BLOCKSIZE-2))+1,
4227 (myrand()%(MAP_BLOCKSIZE-2))+1,
4228 (myrand()%(MAP_BLOCKSIZE-2))+1
4232 n.d = CONTENT_STONE;
4233 n.param = MINERAL_IRON;
4235 for(u16 i=0; i<27; i++)
4237 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4239 block->setNode(cp+g_27dirs[i], n);
4246 Create a few rats in empty blocks underground
4248 if(completely_underground)
4250 //for(u16 i=0; i<2; i++)
4253 (myrand()%(MAP_BLOCKSIZE-2))+1,
4254 (myrand()%(MAP_BLOCKSIZE-2))+1,
4255 (myrand()%(MAP_BLOCKSIZE-2))+1
4258 // Check that the place is empty
4259 //if(!is_ground_content(block->getNode(cp).d))
4262 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4263 block->addObject(obj);
4269 Add block to sector.
4271 sector->insertBlock(block);
4273 // Lighting is invalid after generation.
4274 block->setLightingExpired(true);
4281 <<"lighting_invalidated_blocks.size()"
4285 <<" "<<lighting_invalidated_blocks.size()
4286 <<", "<<has_dungeons
4287 <<", "<<completely_underground
4288 <<", "<<some_part_underground
4295 MapBlock * ServerMap::createBlock(v3s16 p)
4297 DSTACK("%s: p=(%d,%d,%d)",
4298 __FUNCTION_NAME, p.X, p.Y, p.Z);
4300 v2s16 p2d(p.X, p.Z);
4303 This will create or load a sector if not found in memory.
4304 If block exists on disk, it will be loaded.
4306 NOTE: On old save formats, this will be slow, as it generates
4307 lighting on blocks for them.
4309 ServerMapSector *sector;
4311 sector = (ServerMapSector*)createSector(p2d);
4312 assert(sector->getId() == MAPSECTOR_SERVER);
4314 /*catch(InvalidPositionException &e)
4316 dstream<<"createBlock: createSector() failed"<<std::endl;
4319 catch(std::exception &e)
4321 dstream<<"createBlock: createSector() failed: "
4322 <<e.what()<<std::endl;
4327 Try to get a block from the sector
4330 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4334 block = sector->createBlankBlock(block_y);
4338 MapBlock * ServerMap::emergeBlock(
4340 bool only_from_disk,
4341 core::map<v3s16, MapBlock*> &changed_blocks,
4342 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4345 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4347 p.X, p.Y, p.Z, only_from_disk);
4349 v2s16 p2d(p.X, p.Z);
4352 This will create or load a sector if not found in memory.
4353 If block exists on disk, it will be loaded.
4355 NOTE: On old save formats, this will be slow, as it generates
4356 lighting on blocks for them.
4358 ServerMapSector *sector;
4360 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4361 assert(sector->getId() == MAPSECTOR_SERVER);
4363 catch(std::exception &e)
4365 dstream<<"emergeBlock: emergeSector() failed: "
4366 <<e.what()<<std::endl;
4371 Try to get a block from the sector
4374 bool does_not_exist = false;
4375 bool lighting_expired = false;
4376 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4380 does_not_exist = true;
4382 else if(block->isDummy() == true)
4384 does_not_exist = true;
4386 else if(block->getLightingExpired())
4388 lighting_expired = true;
4393 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4398 If block was not found on disk and not going to generate a
4399 new one, make sure there is a dummy block in place.
4401 if(only_from_disk && (does_not_exist || lighting_expired))
4403 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4407 // Create dummy block
4408 block = new MapBlock(this, p, true);
4410 // Add block to sector
4411 sector->insertBlock(block);
4417 //dstream<<"Not found on disk, generating."<<std::endl;
4419 //TimeTaker("emergeBlock() generate");
4421 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4424 If the block doesn't exist, generate the block.
4428 block = generateBlock(p, block, sector, changed_blocks,
4429 lighting_invalidated_blocks);
4432 if(lighting_expired)
4434 lighting_invalidated_blocks.insert(p, block);
4438 Initially update sunlight
4442 core::map<v3s16, bool> light_sources;
4443 bool black_air_left = false;
4444 bool bottom_invalid =
4445 block->propagateSunlight(light_sources, true,
4446 &black_air_left, true);
4448 // If sunlight didn't reach everywhere and part of block is
4449 // above ground, lighting has to be properly updated
4450 //if(black_air_left && some_part_underground)
4453 lighting_invalidated_blocks[block->getPos()] = block;
4458 lighting_invalidated_blocks[block->getPos()] = block;
4465 void ServerMap::createDir(std::string path)
4467 if(fs::CreateDir(path) == false)
4469 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4470 <<"\""<<path<<"\""<<std::endl;
4471 throw BaseException("ServerMap failed to create directory");
4475 std::string ServerMap::getSectorSubDir(v2s16 pos)
4478 snprintf(cc, 9, "%.4x%.4x",
4479 (unsigned int)pos.X&0xffff,
4480 (unsigned int)pos.Y&0xffff);
4482 return std::string(cc);
4485 std::string ServerMap::getSectorDir(v2s16 pos)
4487 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4490 v2s16 ServerMap::getSectorPos(std::string dirname)
4492 if(dirname.size() != 8)
4493 throw InvalidFilenameException("Invalid sector directory name");
4495 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4497 throw InvalidFilenameException("Invalid sector directory name");
4498 v2s16 pos((s16)x, (s16)y);
4502 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4504 v2s16 p2d = getSectorPos(sectordir);
4506 if(blockfile.size() != 4){
4507 throw InvalidFilenameException("Invalid block filename");
4510 int r = sscanf(blockfile.c_str(), "%4x", &y);
4512 throw InvalidFilenameException("Invalid block filename");
4513 return v3s16(p2d.X, y, p2d.Y);
4516 void ServerMap::save(bool only_changed)
4518 DSTACK(__FUNCTION_NAME);
4519 if(m_map_saving_enabled == false)
4521 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4525 if(only_changed == false)
4526 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4529 saveMasterHeightmap();
4531 u32 sector_meta_count = 0;
4532 u32 block_count = 0;
4535 JMutexAutoLock lock(m_sector_mutex);
4537 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4538 for(; i.atEnd() == false; i++)
4540 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4541 assert(sector->getId() == MAPSECTOR_SERVER);
4543 if(sector->differs_from_disk || only_changed == false)
4545 saveSectorMeta(sector);
4546 sector_meta_count++;
4548 core::list<MapBlock*> blocks;
4549 sector->getBlocks(blocks);
4550 core::list<MapBlock*>::Iterator j;
4551 for(j=blocks.begin(); j!=blocks.end(); j++)
4553 MapBlock *block = *j;
4554 if(block->getChangedFlag() || only_changed == false)
4559 /*dstream<<"ServerMap: Written block ("
4560 <<block->getPos().X<<","
4561 <<block->getPos().Y<<","
4562 <<block->getPos().Z<<")"
4571 Only print if something happened or saved whole map
4573 if(only_changed == false || sector_meta_count != 0
4574 || block_count != 0)
4576 dstream<<DTIME<<"ServerMap: Written: "
4577 <<sector_meta_count<<" sector metadata files, "
4578 <<block_count<<" block files"
4583 void ServerMap::loadAll()
4585 DSTACK(__FUNCTION_NAME);
4586 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4588 loadMasterHeightmap();
4590 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4592 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4594 JMutexAutoLock lock(m_sector_mutex);
4597 s32 printed_counter = -100000;
4598 s32 count = list.size();
4600 std::vector<fs::DirListNode>::iterator i;
4601 for(i=list.begin(); i!=list.end(); i++)
4603 if(counter > printed_counter + 10)
4605 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4606 printed_counter = counter;
4610 MapSector *sector = NULL;
4612 // We want directories
4616 sector = loadSectorMeta(i->name);
4618 catch(InvalidFilenameException &e)
4620 // This catches unknown crap in directory
4623 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4624 (m_savedir+"/sectors/"+i->name);
4625 std::vector<fs::DirListNode>::iterator i2;
4626 for(i2=list2.begin(); i2!=list2.end(); i2++)
4632 loadBlock(i->name, i2->name, sector);
4634 catch(InvalidFilenameException &e)
4636 // This catches unknown crap in directory
4640 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4643 void ServerMap::saveMasterHeightmap()
4645 DSTACK(__FUNCTION_NAME);
4647 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4649 createDir(m_savedir);
4651 /*std::string fullpath = m_savedir + "/master_heightmap";
4652 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4653 if(o.good() == false)
4654 throw FileNotGoodException("Cannot open master heightmap");*/
4656 // Format used for writing
4657 //u8 version = SER_FMT_VER_HIGHEST;
4660 void ServerMap::loadMasterHeightmap()
4662 DSTACK(__FUNCTION_NAME);
4664 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4666 /*std::string fullpath = m_savedir + "/master_heightmap";
4667 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4668 if(is.good() == false)
4669 throw FileNotGoodException("Cannot open master heightmap");*/
4672 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4674 DSTACK(__FUNCTION_NAME);
4675 // Format used for writing
4676 u8 version = SER_FMT_VER_HIGHEST;
4678 v2s16 pos = sector->getPos();
4679 createDir(m_savedir);
4680 createDir(m_savedir+"/sectors");
4681 std::string dir = getSectorDir(pos);
4684 std::string fullpath = dir + "/heightmap";
4685 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4686 if(o.good() == false)
4687 throw FileNotGoodException("Cannot open master heightmap");
4689 sector->serialize(o, version);
4691 sector->differs_from_disk = false;
4694 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4696 DSTACK(__FUNCTION_NAME);
4698 v2s16 p2d = getSectorPos(dirname);
4699 std::string dir = m_savedir + "/sectors/" + dirname;
4701 std::string fullpath = dir + "/heightmap";
4702 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4703 if(is.good() == false)
4704 throw FileNotGoodException("Cannot open sector heightmap");
4706 ServerMapSector *sector = ServerMapSector::deSerialize
4707 (is, this, p2d, m_sectors);
4709 sector->differs_from_disk = false;
4714 bool ServerMap::loadSectorFull(v2s16 p2d)
4716 DSTACK(__FUNCTION_NAME);
4717 std::string sectorsubdir = getSectorSubDir(p2d);
4719 MapSector *sector = NULL;
4721 JMutexAutoLock lock(m_sector_mutex);
4724 sector = loadSectorMeta(sectorsubdir);
4726 catch(InvalidFilenameException &e)
4730 catch(FileNotGoodException &e)
4734 catch(std::exception &e)
4739 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4740 (m_savedir+"/sectors/"+sectorsubdir);
4741 std::vector<fs::DirListNode>::iterator i2;
4742 for(i2=list2.begin(); i2!=list2.end(); i2++)
4748 loadBlock(sectorsubdir, i2->name, sector);
4750 catch(InvalidFilenameException &e)
4752 // This catches unknown crap in directory
4759 bool ServerMap::deFlushSector(v2s16 p2d)
4761 DSTACK(__FUNCTION_NAME);
4762 // See if it already exists in memory
4764 MapSector *sector = getSectorNoGenerate(p2d);
4767 catch(InvalidPositionException &e)
4770 Try to load the sector from disk.
4772 if(loadSectorFull(p2d) == true)
4781 void ServerMap::saveBlock(MapBlock *block)
4783 DSTACK(__FUNCTION_NAME);
4785 Dummy blocks are not written
4787 if(block->isDummy())
4789 /*v3s16 p = block->getPos();
4790 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4791 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4795 // Format used for writing
4796 u8 version = SER_FMT_VER_HIGHEST;
4798 v3s16 p3d = block->getPos();
4799 v2s16 p2d(p3d.X, p3d.Z);
4800 createDir(m_savedir);
4801 createDir(m_savedir+"/sectors");
4802 std::string dir = getSectorDir(p2d);
4805 // Block file is map/sectors/xxxxxxxx/xxxx
4807 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4808 std::string fullpath = dir + "/" + cc;
4809 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4810 if(o.good() == false)
4811 throw FileNotGoodException("Cannot open block data");
4814 [0] u8 serialization version
4817 o.write((char*)&version, 1);
4819 block->serialize(o, version);
4822 Versions up from 9 have block objects.
4826 block->serializeObjects(o, version);
4829 // We just wrote it to the disk
4830 block->resetChangedFlag();
4833 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4835 DSTACK(__FUNCTION_NAME);
4839 // Block file is map/sectors/xxxxxxxx/xxxx
4840 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4841 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4842 if(is.good() == false)
4843 throw FileNotGoodException("Cannot open block file");
4845 v3s16 p3d = getBlockPos(sectordir, blockfile);
4846 v2s16 p2d(p3d.X, p3d.Z);
4848 assert(sector->getPos() == p2d);
4850 u8 version = SER_FMT_VER_INVALID;
4851 is.read((char*)&version, 1);
4853 /*u32 block_size = MapBlock::serializedLength(version);
4854 SharedBuffer<u8> data(block_size);
4855 is.read((char*)*data, block_size);*/
4857 // This will always return a sector because we're the server
4858 //MapSector *sector = emergeSector(p2d);
4860 MapBlock *block = NULL;
4861 bool created_new = false;
4863 block = sector->getBlockNoCreate(p3d.Y);
4865 catch(InvalidPositionException &e)
4867 block = sector->createBlankBlockNoInsert(p3d.Y);
4871 // deserialize block data
4872 block->deSerialize(is, version);
4875 Versions up from 9 have block objects.
4879 block->updateObjects(is, version, NULL, 0);
4883 sector->insertBlock(block);
4886 Convert old formats to new and save
4889 // Save old format blocks in new format
4890 if(version < SER_FMT_VER_HIGHEST)
4895 // We just loaded it from the disk, so it's up-to-date.
4896 block->resetChangedFlag();
4899 catch(SerializationError &e)
4901 dstream<<"WARNING: Invalid block data on disk "
4902 "(SerializationError). Ignoring."
4907 // Gets from master heightmap
4908 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4910 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4911 //assert(m_heightmap != NULL);
4919 /*corners[0] = m_heightmap->getGroundHeight
4920 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4921 corners[1] = m_heightmap->getGroundHeight
4922 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4923 corners[2] = m_heightmap->getGroundHeight
4924 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4925 corners[3] = m_heightmap->getGroundHeight
4926 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
4929 void ServerMap::PrintInfo(std::ostream &out)
4940 ClientMap::ClientMap(
4942 MapDrawControl &control,
4943 scene::ISceneNode* parent,
4944 scene::ISceneManager* mgr,
4948 scene::ISceneNode(parent, mgr, id),
4952 //mesh_mutex.Init();
4954 /*m_box = core::aabbox3d<f32>(0,0,0,
4955 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4956 /*m_box = core::aabbox3d<f32>(0,0,0,
4957 map->getSizeNodes().X * BS,
4958 map->getSizeNodes().Y * BS,
4959 map->getSizeNodes().Z * BS);*/
4960 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4961 BS*1000000,BS*1000000,BS*1000000);
4963 //setPosition(v3f(BS,BS,BS));
4966 ClientMap::~ClientMap()
4968 /*JMutexAutoLock lock(mesh_mutex);
4977 MapSector * ClientMap::emergeSector(v2s16 p2d)
4979 DSTACK(__FUNCTION_NAME);
4980 // Check that it doesn't exist already
4982 return getSectorNoGenerate(p2d);
4984 catch(InvalidPositionException &e)
4988 // Create a sector with no heightmaps
4989 ClientMapSector *sector = new ClientMapSector(this, p2d);
4992 JMutexAutoLock lock(m_sector_mutex);
4993 m_sectors.insert(p2d, sector);
4999 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5001 DSTACK(__FUNCTION_NAME);
5002 ClientMapSector *sector = NULL;
5004 JMutexAutoLock lock(m_sector_mutex);
5006 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5010 sector = (ClientMapSector*)n->getValue();
5011 assert(sector->getId() == MAPSECTOR_CLIENT);
5015 sector = new ClientMapSector(this, p2d);
5017 JMutexAutoLock lock(m_sector_mutex);
5018 m_sectors.insert(p2d, sector);
5022 sector->deSerialize(is);
5025 void ClientMap::OnRegisterSceneNode()
5029 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5030 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5033 ISceneNode::OnRegisterSceneNode();
5036 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5038 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5039 DSTACK(__FUNCTION_NAME);
5041 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5044 Get time for measuring timeout.
5046 Measuring time is very useful for long delays when the
5047 machine is swapping a lot.
5049 int time1 = time(0);
5051 u32 daynight_ratio = m_client->getDayNightRatio();
5053 m_camera_mutex.Lock();
5054 v3f camera_position = m_camera_position;
5055 v3f camera_direction = m_camera_direction;
5056 m_camera_mutex.Unlock();
5059 Get all blocks and draw all visible ones
5062 v3s16 cam_pos_nodes(
5063 camera_position.X / BS,
5064 camera_position.Y / BS,
5065 camera_position.Z / BS);
5067 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5069 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5070 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5072 // Take a fair amount as we will be dropping more out later
5074 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5075 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5076 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5078 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5079 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5080 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5082 u32 vertex_count = 0;
5084 // For limiting number of mesh updates per frame
5085 u32 mesh_update_count = 0;
5087 u32 blocks_would_have_drawn = 0;
5088 u32 blocks_drawn = 0;
5090 //NOTE: The sectors map should be locked but we're not doing it
5091 // because it'd cause too much delays
5093 int timecheck_counter = 0;
5094 core::map<v2s16, MapSector*>::Iterator si;
5095 si = m_sectors.getIterator();
5096 for(; si.atEnd() == false; si++)
5099 timecheck_counter++;
5100 if(timecheck_counter > 50)
5102 int time2 = time(0);
5103 if(time2 > time1 + 4)
5105 dstream<<"ClientMap::renderMap(): "
5106 "Rendering takes ages, returning."
5113 MapSector *sector = si.getNode()->getValue();
5114 v2s16 sp = sector->getPos();
5116 if(m_control.range_all == false)
5118 if(sp.X < p_blocks_min.X
5119 || sp.X > p_blocks_max.X
5120 || sp.Y < p_blocks_min.Z
5121 || sp.Y > p_blocks_max.Z)
5125 core::list< MapBlock * > sectorblocks;
5126 sector->getBlocks(sectorblocks);
5132 core::list< MapBlock * >::Iterator i;
5133 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5135 MapBlock *block = *i;
5138 Compare block position to camera position, skip
5139 if not seen on display
5142 float range = 100000 * BS;
5143 if(m_control.range_all == false)
5144 range = m_control.wanted_range * BS;
5147 if(isBlockInSight(block->getPos(), camera_position,
5148 camera_direction, range, &d) == false)
5154 v3s16 blockpos_nodes = block->getPosRelative();
5156 // Block center position
5158 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5159 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5160 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5163 // Block position relative to camera
5164 v3f blockpos_relative = blockpos - camera_position;
5166 // Distance in camera direction (+=front, -=back)
5167 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5170 f32 d = blockpos_relative.getLength();
5172 if(m_control.range_all == false)
5174 // If block is far away, don't draw it
5175 if(d > m_control.wanted_range * BS)
5179 // Maximum radius of a block
5180 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5182 // If block is (nearly) touching the camera, don't
5183 // bother validating further (that is, render it anyway)
5184 if(d > block_max_radius * 1.5)
5186 // Cosine of the angle between the camera direction
5187 // and the block direction (camera_direction is an unit vector)
5188 f32 cosangle = dforward / d;
5190 // Compensate for the size of the block
5191 // (as the block has to be shown even if it's a bit off FOV)
5192 // This is an estimate.
5193 cosangle += block_max_radius / dforward;
5195 // If block is not in the field of view, skip it
5196 //if(cosangle < cos(FOV_ANGLE/2))
5197 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5202 v3s16 blockpos_nodes = block->getPosRelative();
5204 // Block center position
5206 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5207 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5208 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5211 // Block position relative to camera
5212 v3f blockpos_relative = blockpos - camera_position;
5215 f32 d = blockpos_relative.getLength();
5223 bool mesh_expired = false;
5226 JMutexAutoLock lock(block->mesh_mutex);
5228 mesh_expired = block->getMeshExpired();
5230 // Mesh has not been expired and there is no mesh:
5231 // block has no content
5232 if(block->mesh == NULL && mesh_expired == false)
5236 f32 faraway = BS*50;
5237 //f32 faraway = m_control.wanted_range * BS;
5240 This has to be done with the mesh_mutex unlocked
5242 // Pretty random but this should work somewhat nicely
5243 if(mesh_expired && (
5244 (mesh_update_count < 3
5245 && (d < faraway || mesh_update_count < 2)
5248 (m_control.range_all && mesh_update_count < 20)
5251 /*if(mesh_expired && mesh_update_count < 6
5252 && (d < faraway || mesh_update_count < 3))*/
5254 mesh_update_count++;
5256 // Mesh has been expired: generate new mesh
5257 //block->updateMeshes(daynight_i);
5258 block->updateMesh(daynight_ratio);
5260 mesh_expired = false;
5264 Don't draw an expired mesh that is far away
5266 /*if(mesh_expired && d >= faraway)
5269 // Instead, delete it
5270 JMutexAutoLock lock(block->mesh_mutex);
5273 block->mesh->drop();
5276 // And continue to next block
5281 Draw the faces of the block
5284 JMutexAutoLock lock(block->mesh_mutex);
5286 scene::SMesh *mesh = block->mesh;
5291 blocks_would_have_drawn++;
5292 if(blocks_drawn >= m_control.wanted_max_blocks
5293 && m_control.range_all == false
5294 && d > m_control.wanted_min_range * BS)
5298 u32 c = mesh->getMeshBufferCount();
5300 for(u32 i=0; i<c; i++)
5302 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5303 const video::SMaterial& material = buf->getMaterial();
5304 video::IMaterialRenderer* rnd =
5305 driver->getMaterialRenderer(material.MaterialType);
5306 bool transparent = (rnd && rnd->isTransparent());
5307 // Render transparent on transparent pass and likewise.
5308 if(transparent == is_transparent_pass)
5310 driver->setMaterial(buf->getMaterial());
5311 driver->drawMeshBuffer(buf);
5312 vertex_count += buf->getVertexCount();
5316 } // foreach sectorblocks
5319 m_control.blocks_drawn = blocks_drawn;
5320 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5322 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5323 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5326 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5327 core::map<v3s16, MapBlock*> *affected_blocks)
5329 bool changed = false;
5331 Add it to all blocks touching it
5334 v3s16(0,0,0), // this
5335 v3s16(0,0,1), // back
5336 v3s16(0,1,0), // top
5337 v3s16(1,0,0), // right
5338 v3s16(0,0,-1), // front
5339 v3s16(0,-1,0), // bottom
5340 v3s16(-1,0,0), // left
5342 for(u16 i=0; i<7; i++)
5344 v3s16 p2 = p + dirs[i];
5345 // Block position of neighbor (or requested) node
5346 v3s16 blockpos = getNodeBlockPos(p2);
5347 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5348 if(blockref == NULL)
5350 // Relative position of requested node
5351 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5352 if(blockref->setTempMod(relpos, mod))
5357 if(changed && affected_blocks!=NULL)
5359 for(u16 i=0; i<7; i++)
5361 v3s16 p2 = p + dirs[i];
5362 // Block position of neighbor (or requested) node
5363 v3s16 blockpos = getNodeBlockPos(p2);
5364 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5365 if(blockref == NULL)
5367 affected_blocks->insert(blockpos, blockref);
5373 bool ClientMap::clearTempMod(v3s16 p,
5374 core::map<v3s16, MapBlock*> *affected_blocks)
5376 bool changed = false;
5378 v3s16(0,0,0), // this
5379 v3s16(0,0,1), // back
5380 v3s16(0,1,0), // top
5381 v3s16(1,0,0), // right
5382 v3s16(0,0,-1), // front
5383 v3s16(0,-1,0), // bottom
5384 v3s16(-1,0,0), // left
5386 for(u16 i=0; i<7; i++)
5388 v3s16 p2 = p + dirs[i];
5389 // Block position of neighbor (or requested) node
5390 v3s16 blockpos = getNodeBlockPos(p2);
5391 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5392 if(blockref == NULL)
5394 // Relative position of requested node
5395 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5396 if(blockref->clearTempMod(relpos))
5401 if(changed && affected_blocks!=NULL)
5403 for(u16 i=0; i<7; i++)
5405 v3s16 p2 = p + dirs[i];
5406 // Block position of neighbor (or requested) node
5407 v3s16 blockpos = getNodeBlockPos(p2);
5408 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5409 if(blockref == NULL)
5411 affected_blocks->insert(blockpos, blockref);
5417 void ClientMap::PrintInfo(std::ostream &out)
5428 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5433 MapVoxelManipulator::~MapVoxelManipulator()
5435 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5439 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5441 TimeTaker timer1("emerge", &emerge_time);
5443 // Units of these are MapBlocks
5444 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5445 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5447 VoxelArea block_area_nodes
5448 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5450 addArea(block_area_nodes);
5452 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5453 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5454 for(s32 x=p_min.X; x<=p_max.X; x++)
5457 core::map<v3s16, bool>::Node *n;
5458 n = m_loaded_blocks.find(p);
5462 bool block_data_inexistent = false;
5465 TimeTaker timer1("emerge load", &emerge_load_time);
5467 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5468 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5471 dstream<<std::endl;*/
5473 MapBlock *block = m_map->getBlockNoCreate(p);
5474 if(block->isDummy())
5475 block_data_inexistent = true;
5477 block->copyTo(*this);
5479 catch(InvalidPositionException &e)
5481 block_data_inexistent = true;
5484 if(block_data_inexistent)
5486 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5487 // Fill with VOXELFLAG_INEXISTENT
5488 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5489 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5491 s32 i = m_area.index(a.MinEdge.X,y,z);
5492 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5496 m_loaded_blocks.insert(p, !block_data_inexistent);
5499 //dstream<<"emerge done"<<std::endl;
5503 SUGG: Add an option to only update eg. water and air nodes.
5504 This will make it interfere less with important stuff if
5507 void MapVoxelManipulator::blitBack
5508 (core::map<v3s16, MapBlock*> & modified_blocks)
5510 if(m_area.getExtent() == v3s16(0,0,0))
5513 //TimeTaker timer1("blitBack");
5515 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5516 <<m_loaded_blocks.size()<<std::endl;*/
5519 Initialize block cache
5521 v3s16 blockpos_last;
5522 MapBlock *block = NULL;
5523 bool block_checked_in_modified = false;
5525 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5526 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5527 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5531 u8 f = m_flags[m_area.index(p)];
5532 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5535 MapNode &n = m_data[m_area.index(p)];
5537 v3s16 blockpos = getNodeBlockPos(p);
5542 if(block == NULL || blockpos != blockpos_last){
5543 block = m_map->getBlockNoCreate(blockpos);
5544 blockpos_last = blockpos;
5545 block_checked_in_modified = false;
5548 // Calculate relative position in block
5549 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5551 // Don't continue if nothing has changed here
5552 if(block->getNode(relpos) == n)
5555 //m_map->setNode(m_area.MinEdge + p, n);
5556 block->setNode(relpos, n);
5559 Make sure block is in modified_blocks
5561 if(block_checked_in_modified == false)
5563 modified_blocks[blockpos] = block;
5564 block_checked_in_modified = true;
5567 catch(InvalidPositionException &e)
5573 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5574 MapVoxelManipulator(map)
5578 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5582 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5584 // Just create the area so that it can be pointed to
5585 VoxelManipulator::emerge(a, caller_id);
5588 void ManualMapVoxelManipulator::initialEmerge(
5589 v3s16 blockpos_min, v3s16 blockpos_max)
5591 TimeTaker timer1("initialEmerge", &emerge_time);
5593 // Units of these are MapBlocks
5594 v3s16 p_min = blockpos_min;
5595 v3s16 p_max = blockpos_max;
5597 VoxelArea block_area_nodes
5598 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5600 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5603 dstream<<"initialEmerge: area: ";
5604 block_area_nodes.print(dstream);
5605 dstream<<" ("<<size_MB<<"MB)";
5609 addArea(block_area_nodes);
5611 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5612 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5613 for(s32 x=p_min.X; x<=p_max.X; x++)
5616 core::map<v3s16, bool>::Node *n;
5617 n = m_loaded_blocks.find(p);
5621 bool block_data_inexistent = false;
5624 TimeTaker timer1("emerge load", &emerge_load_time);
5626 MapBlock *block = m_map->getBlockNoCreate(p);
5627 if(block->isDummy())
5628 block_data_inexistent = true;
5630 block->copyTo(*this);
5632 catch(InvalidPositionException &e)
5634 block_data_inexistent = true;
5637 if(block_data_inexistent)
5640 Mark area inexistent
5642 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5643 // Fill with VOXELFLAG_INEXISTENT
5644 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5645 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5647 s32 i = m_area.index(a.MinEdge.X,y,z);
5648 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5652 m_loaded_blocks.insert(p, !block_data_inexistent);
5656 void ManualMapVoxelManipulator::blitBackAll(
5657 core::map<v3s16, MapBlock*> * modified_blocks)
5659 if(m_area.getExtent() == v3s16(0,0,0))
5663 Copy data of all blocks
5665 for(core::map<v3s16, bool>::Iterator
5666 i = m_loaded_blocks.getIterator();
5667 i.atEnd() == false; i++)
5669 bool existed = i.getNode()->getValue();
5670 if(existed == false)
5672 v3s16 p = i.getNode()->getKey();
5673 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5676 dstream<<"WARNING: "<<__FUNCTION_NAME
5677 <<": got NULL block "
5678 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5683 block->copyFrom(*this);
5686 modified_blocks->insert(p, block);