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 Buffer<u8> leaves_d(leaves_a.getVolume());
1917 for(s32 i=0; i<leaves_a.getVolume(); i++)
1920 // Force leaves at near the end of the trunk
1923 for(s16 z=-d; z<=d; z++)
1924 for(s16 y=-d; y<=d; y++)
1925 for(s16 x=-d; x<=d; x++)
1927 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1931 // Add leaves randomly
1932 for(u32 iii=0; iii<7; iii++)
1937 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1938 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1939 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1942 for(s16 z=0; z<=d; z++)
1943 for(s16 y=0; y<=d; y++)
1944 for(s16 x=0; x<=d; x++)
1946 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1950 // Blit leaves to vmanip
1951 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1952 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1953 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1957 if(vmanip.m_area.contains(p) == false)
1959 u32 vi = vmanip.m_area.index(p);
1960 if(vmanip.m_data[vi].d != CONTENT_AIR)
1962 u32 i = leaves_a.index(x,y,z);
1963 if(leaves_d[i] == 1)
1964 vmanip.m_data[vi] = leavesnode;
1969 Noise functions. Make sure seed is mangled differently in each one.
1972 // Amount of trees per area in nodes
1973 double tree_amount_2d(u64 seed, v2s16 p)
1975 double noise = noise2d_perlin(
1976 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1978 double zeroval = -0.3;
1982 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1985 /*double base_rock_level_2d(u64 seed, v2s16 p)
1987 return WATER_LEVEL - 6.0 + 25. * noise2d_perlin(
1988 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1992 /*double highlands_level_2d(u64 seed, v2s16 p)
1994 double a = noise2d_perlin(
1995 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2000 return WATER_LEVEL + 25;
2001 return WATER_LEVEL + 55. * noise2d_perlin(
2002 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2003 seed+85039, 6, 0.69);
2009 double base_rock_level_2d(u64 seed, v2s16 p)
2011 // The base ground level
2012 double base = WATER_LEVEL - 4.0 + 25. * noise2d_perlin(
2013 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2014 (seed>>32)+654879876, 6, 0.6);
2015 /*// A bit hillier one
2016 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2017 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2018 (seed>>27)+90340, 6, 0.69);
2022 // Higher ground level
2023 double higher = WATER_LEVEL + 13. + 50. * noise2d_perlin(
2024 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2025 seed+85039, 6, 0.69);
2026 //higher = 30; // For debugging
2028 // Limit higher to at least base
2032 // Steepness factor of cliffs
2033 double b = 1.0 + 1.0 * noise2d_perlin(
2034 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2036 b = rangelim(b, 0.0, 1000.0);
2039 b = rangelim(b, 3.0, 1000.0);
2040 //dstream<<"b="<<b<<std::endl;
2043 // Offset to more low
2044 double a_off = -0.3;
2045 // High/low selector
2046 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2047 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2048 seed-359, 6, 0.7));*/
2049 double a = 0.5 + b * (a_off + noise2d_perlin(
2050 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2051 seed-359, 5, 0.60));
2053 a = rangelim(a, 0.0, 1.0);
2055 //dstream<<"a="<<a<<std::endl;
2057 double h = base*(1.0-a) + higher*a;
2065 This is the main map generation method
2068 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2069 core::map<v3s16, MapBlock*> &changed_blocks)
2071 DSTACK(__FUNCTION_NAME);
2074 Don't generate if already fully generated
2077 MapChunk *chunk = getChunk(chunkpos);
2078 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2080 dstream<<"generateChunkRaw(): Chunk "
2081 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2082 <<" already generated"<<std::endl;
2087 dstream<<"generateChunkRaw(): Generating chunk "
2088 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2091 TimeTaker timer("generateChunkRaw()");
2093 // The distance how far into the neighbors the generator is allowed to go.
2094 s16 max_spread_amount_sectors = 2;
2095 assert(max_spread_amount_sectors <= m_chunksize);
2096 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2098 // Minimum amount of space left on sides for mud to fall in
2099 //s16 min_mud_fall_space = 2;
2101 // Maximum diameter of stone obstacles in X and Z
2102 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2103 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2105 s16 y_blocks_min = -4;
2106 s16 y_blocks_max = 3;
2107 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2108 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2109 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2111 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2112 s16 sectorpos_base_size = m_chunksize;
2114 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2115 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2116 v2s16 sectorpos_bigbase =
2117 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2118 s16 sectorpos_bigbase_size =
2119 sectorpos_base_size + 2 * max_spread_amount_sectors;
2121 v3s16 bigarea_blocks_min(
2122 sectorpos_bigbase.X,
2127 v3s16 bigarea_blocks_max(
2128 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2130 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2133 // Relative values to control amount of stuff in one chunk
2134 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2135 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2136 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2137 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2138 *(u32)h_blocks*MAP_BLOCKSIZE;
2141 The limiting edges of the lighting update, inclusive.
2143 s16 lighting_min_d = 0-max_spread_amount;
2144 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2147 Create the whole area of this and the neighboring chunks
2150 TimeTaker timer("generateChunkRaw() create area");
2152 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2153 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2155 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2156 ServerMapSector *sector = createSector(sectorpos);
2159 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2161 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2162 MapBlock *block = createBlock(blockpos);
2164 // Lighting won't be calculated
2165 //block->setLightingExpired(true);
2166 // Lighting will be calculated
2167 block->setLightingExpired(false);
2170 Block gets sunlight if this is true.
2172 This should be set to true when the top side of a block
2173 is completely exposed to the sky.
2175 Actually this doesn't matter now because the
2176 initial lighting is done here.
2178 block->setIsUnderground(y != y_blocks_max);
2184 Now we have a big empty area.
2186 Make a ManualMapVoxelManipulator that contains this and the
2190 ManualMapVoxelManipulator vmanip(this);
2191 // Add the area we just generated
2193 TimeTaker timer("generateChunkRaw() initialEmerge");
2194 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2197 TimeTaker timer_generate("generateChunkRaw() generate");
2199 // Maximum height of the stone surface and obstacles.
2200 // This is used to disable dungeon generation from going too high.
2201 s16 stone_surface_max_y = 0;
2204 Generate general ground level to full area
2209 //TimeTaker timer1("ground level");
2211 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2212 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2215 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2218 Skip of already generated
2221 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2222 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2226 // Ground height at this point
2227 float surface_y_f = 0.0;
2229 // Use perlin noise for ground height
2230 surface_y_f = base_rock_level_2d(m_seed, p2d);
2232 /*// Experimental stuff
2234 float a = highlands_level_2d(m_seed, p2d);
2239 // Convert to integer
2240 s16 surface_y = (s16)surface_y_f;
2243 if(surface_y > stone_surface_max_y)
2244 stone_surface_max_y = surface_y;
2247 Fill ground with stone
2250 // Use fast index incrementing
2251 v3s16 em = vmanip.m_area.getExtent();
2252 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2253 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2255 vmanip.m_data[i].d = CONTENT_STONE;
2257 vmanip.m_area.add_y(em, i, 1);
2265 Randomize some parameters
2268 s32 stone_obstacle_count = 0;
2269 /*s32 stone_obstacle_count =
2270 rangelim((1.0+noise2d(m_seed+897,
2271 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2273 s16 stone_obstacle_max_height = 0;
2274 /*s16 stone_obstacle_max_height =
2275 rangelim((1.0+noise2d(m_seed+5902,
2276 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2279 Loop this part, it will make stuff look older and newer nicely
2281 //for(u32 i_age=0; i_age<1; i_age++)
2282 for(u32 i_age=0; i_age<2; i_age++)
2287 //TimeTaker timer1("stone obstacles");
2290 Add some random stone obstacles
2293 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2295 // Randomize max height so usually stuff will be quite low
2296 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2298 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2299 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2302 myrand_range(5, stone_obstacle_max_size),
2303 myrand_range(0, maxheight_randomized),
2304 myrand_range(5, stone_obstacle_max_size)
2307 // Don't make stupid small rectangle bumps
2312 myrand_range(1+ob_size.X/2+2,
2313 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2314 myrand_range(1+ob_size.Z/2+2,
2315 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2318 // Minimum space left on top of the obstacle
2319 s16 min_head_space = 12;
2321 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2322 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2324 // Node position in 2d
2325 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2327 // Find stone ground level
2328 // (ignore everything else than mud in already generated chunks)
2329 // and mud amount over the stone level
2333 v3s16 em = vmanip.m_area.getExtent();
2334 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2336 // Go to ground level
2337 for(y=y_nodes_max; y>=y_nodes_min; y--)
2339 MapNode *n = &vmanip.m_data[i];
2340 /*if(content_walkable(n.d)
2341 && n.d != CONTENT_MUD
2342 && n.d != CONTENT_GRASS)
2344 if(n->d == CONTENT_STONE)
2347 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2351 Change to mud because otherwise we might
2352 be throwing mud on grass at the next
2358 vmanip.m_area.add_y(em, i, -1);
2360 if(y >= y_nodes_min)
2363 surface_y = y_nodes_min;
2371 v3s16 em = vmanip.m_area.getExtent();
2372 s16 y_start = surface_y+1;
2373 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2377 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2379 MapNode &n = vmanip.m_data[i];
2380 n.d = CONTENT_STONE;
2382 if(y > stone_surface_max_y)
2383 stone_surface_max_y = y;
2386 if(count >= ob_size.Y)
2389 vmanip.m_area.add_y(em, i, 1);
2393 for(; y<=y_nodes_max - min_head_space; y++)
2395 MapNode &n = vmanip.m_data[i];
2398 if(count >= mud_amount)
2401 vmanip.m_area.add_y(em, i, 1);
2411 //TimeTaker timer1("dungeons");
2416 //u32 dungeons_count = relative_volume / 600000;
2417 /*u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2418 if(stone_surface_max_y < WATER_LEVEL)
2419 bruises_count = 0;*/
2420 u32 dungeons_count = 0;
2421 u32 bruises_count = 0;
2422 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2424 s16 min_tunnel_diameter = 2;
2425 s16 max_tunnel_diameter = 6;
2426 u16 tunnel_routepoints = 15;
2428 bool bruise_surface = (jj < bruises_count);
2432 min_tunnel_diameter = 5;
2433 max_tunnel_diameter = myrand_range(10, 20);
2434 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2435 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2437 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2438 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2440 tunnel_routepoints = 5;
2443 // Allowed route area size in nodes
2445 sectorpos_base_size*MAP_BLOCKSIZE,
2446 h_blocks*MAP_BLOCKSIZE,
2447 sectorpos_base_size*MAP_BLOCKSIZE
2450 // Area starting point in nodes
2452 sectorpos_base.X*MAP_BLOCKSIZE,
2453 y_blocks_min*MAP_BLOCKSIZE,
2454 sectorpos_base.Y*MAP_BLOCKSIZE
2458 //(this should be more than the maximum radius of the tunnel)
2459 //s16 insure = 5; // Didn't work with max_d = 20
2461 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2462 ar += v3s16(1,0,1) * more * 2;
2463 of -= v3s16(1,0,1) * more;
2465 s16 route_y_min = 0;
2466 //s16 route_y_max = ar.Y-1;
2467 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
2469 if(bruise_surface == false)
2471 // Don't go through surface too often
2472 route_y_max -= myrand_range(0, max_tunnel_diameter*2);
2474 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2478 /*// Minimum is at y=0
2479 route_y_min = -of.Y - 0;*/
2480 // Minimum is at y=max_tunnel_diameter/4
2481 //route_y_min = -of.Y + max_tunnel_diameter/4;
2482 //s16 min = -of.Y + max_tunnel_diameter/4;
2483 s16 min = -of.Y + 0;
2484 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2485 route_y_min = rangelim(route_y_min, 0, route_y_max);
2488 /*dstream<<"route_y_min = "<<route_y_min
2489 <<", route_y_max = "<<route_y_max<<std::endl;*/
2491 // Randomize starting position
2493 (float)(myrand()%ar.X)+0.5,
2494 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2495 (float)(myrand()%ar.Z)+0.5
2498 MapNode airnode(CONTENT_AIR);
2501 Generate some tunnel starting from orp
2504 for(u16 j=0; j<tunnel_routepoints; j++)
2507 s16 min_d = min_tunnel_diameter;
2508 s16 max_d = max_tunnel_diameter;
2509 s16 rs = myrand_range(min_d, max_d);
2511 v3s16 maxlen(15, 5, 15);
2515 maxlen = v3s16(rs*7,rs*7,rs*7);
2519 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2520 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2521 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2526 else if(rp.X >= ar.X)
2528 if(rp.Y < route_y_min)
2530 else if(rp.Y >= route_y_max)
2531 rp.Y = route_y_max-1;
2534 else if(rp.Z >= ar.Z)
2538 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2540 v3f fp = orp + vec * f;
2541 v3s16 cp(fp.X, fp.Y, fp.Z);
2544 s16 d1 = d0 + rs - 1;
2545 for(s16 z0=d0; z0<=d1; z0++)
2547 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2548 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2549 for(s16 x0=-si; x0<=si-1; x0++)
2551 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2552 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2553 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2554 //s16 si2 = rs - abs(x0);
2555 for(s16 y0=-si2+1+1; y0<=si2-1; y0++)
2561 /*if(isInArea(p, ar) == false)
2563 // Check only height
2564 if(y < 0 || y >= ar.Y)
2568 //assert(vmanip.m_area.contains(p));
2569 if(vmanip.m_area.contains(p) == false)
2571 dstream<<"WARNING: "<<__FUNCTION_NAME
2572 <<":"<<__LINE__<<": "
2573 <<"point not in area"
2578 // Just set it to air, it will be changed to
2580 u32 i = vmanip.m_area.index(p);
2581 vmanip.m_data[i] = airnode;
2595 //TimeTaker timer1("ore veins");
2600 for(u32 jj=0; jj<relative_volume/2000; jj++)
2602 s16 max_vein_diameter = 3;
2604 // Allowed route area size in nodes
2606 sectorpos_base_size*MAP_BLOCKSIZE,
2607 h_blocks*MAP_BLOCKSIZE,
2608 sectorpos_base_size*MAP_BLOCKSIZE
2611 // Area starting point in nodes
2613 sectorpos_base.X*MAP_BLOCKSIZE,
2614 y_blocks_min*MAP_BLOCKSIZE,
2615 sectorpos_base.Y*MAP_BLOCKSIZE
2619 //(this should be more than the maximum radius of the tunnel)
2621 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2622 ar += v3s16(1,0,1) * more * 2;
2623 of -= v3s16(1,0,1) * more;
2625 // Randomize starting position
2627 (float)(myrand()%ar.X)+0.5,
2628 (float)(myrand()%ar.Y)+0.5,
2629 (float)(myrand()%ar.Z)+0.5
2632 // Randomize mineral
2633 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2636 Generate some vein starting from orp
2639 for(u16 j=0; j<2; j++)
2642 (float)(myrand()%ar.X)+0.5,
2643 (float)(myrand()%ar.Y)+0.5,
2644 (float)(myrand()%ar.Z)+0.5
2646 v3f vec = rp - orp;*/
2648 v3s16 maxlen(10, 10, 10);
2650 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2651 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2652 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2657 else if(rp.X >= ar.X)
2661 else if(rp.Y >= ar.Y)
2665 else if(rp.Z >= ar.Z)
2671 s16 max_d = max_vein_diameter;
2672 s16 rs = myrand_range(min_d, max_d);
2674 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2676 v3f fp = orp + vec * f;
2677 v3s16 cp(fp.X, fp.Y, fp.Z);
2679 s16 d1 = d0 + rs - 1;
2680 for(s16 z0=d0; z0<=d1; z0++)
2682 s16 si = rs - abs(z0);
2683 for(s16 x0=-si; x0<=si-1; x0++)
2685 s16 si2 = rs - abs(x0);
2686 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2688 // Don't put mineral to every place
2696 /*if(isInArea(p, ar) == false)
2698 // Check only height
2699 if(y < 0 || y >= ar.Y)
2703 assert(vmanip.m_area.contains(p));
2705 // Just set it to air, it will be changed to
2707 u32 i = vmanip.m_area.index(p);
2708 MapNode *n = &vmanip.m_data[i];
2709 if(n->d == CONTENT_STONE)
2724 //TimeTaker timer1("add mud");
2727 Add mud to the central chunk
2730 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2731 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2733 // Node position in 2d
2734 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2736 // Randomize mud amount
2737 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2738 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2739 m_seed+1, 3, 0.55));
2741 // Find ground level
2742 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2745 If topmost node is grass, change it to mud.
2746 It might be if it was flown to there from a neighboring
2747 chunk and then converted.
2750 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2751 MapNode *n = &vmanip.m_data[i];
2752 if(n->d == CONTENT_GRASS)
2761 v3s16 em = vmanip.m_area.getExtent();
2762 s16 y_start = surface_y+1;
2763 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2764 for(s16 y=y_start; y<=y_nodes_max; y++)
2766 if(mudcount >= mud_add_amount)
2769 MapNode &n = vmanip.m_data[i];
2773 vmanip.m_area.add_y(em, i, 1);
2782 TimeTaker timer1("flow mud");
2785 Flow mud away from steep edges
2788 // Limit area by 1 because mud is flown into neighbors.
2789 s16 mudflow_minpos = 0-max_spread_amount+1;
2790 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2792 // Iterate a few times
2793 for(s16 k=0; k<3; k++)
2796 for(s16 x=mudflow_minpos;
2799 for(s16 z=mudflow_minpos;
2803 // Invert coordinates every 2nd iteration
2806 x = mudflow_maxpos - (x-mudflow_minpos);
2807 z = mudflow_maxpos - (z-mudflow_minpos);
2810 // Node position in 2d
2811 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2813 v3s16 em = vmanip.m_area.getExtent();
2814 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2821 for(; y>=y_nodes_min; y--)
2823 n = &vmanip.m_data[i];
2824 //if(content_walkable(n->d))
2826 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2829 vmanip.m_area.add_y(em, i, -1);
2832 // Stop if out of area
2833 //if(vmanip.m_area.contains(i) == false)
2837 /*// If not mud, do nothing to it
2838 MapNode *n = &vmanip.m_data[i];
2839 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2843 Don't flow it if the stuff under it is not mud
2847 vmanip.m_area.add_y(em, i2, -1);
2848 // Cancel if out of area
2849 if(vmanip.m_area.contains(i2) == false)
2851 MapNode *n2 = &vmanip.m_data[i2];
2852 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2856 // Make it exactly mud
2859 /*s16 recurse_count = 0;
2863 v3s16(0,0,1), // back
2864 v3s16(1,0,0), // right
2865 v3s16(0,0,-1), // front
2866 v3s16(-1,0,0), // left
2869 // Theck that upper is air or doesn't exist.
2870 // Cancel dropping if upper keeps it in place
2872 vmanip.m_area.add_y(em, i3, 1);
2873 if(vmanip.m_area.contains(i3) == true
2874 && content_walkable(vmanip.m_data[i3].d) == true)
2881 for(u32 di=0; di<4; di++)
2883 v3s16 dirp = dirs4[di];
2886 vmanip.m_area.add_p(em, i2, dirp);
2887 // Fail if out of area
2888 if(vmanip.m_area.contains(i2) == false)
2890 // Check that side is air
2891 MapNode *n2 = &vmanip.m_data[i2];
2892 if(content_walkable(n2->d))
2894 // Check that under side is air
2895 vmanip.m_area.add_y(em, i2, -1);
2896 if(vmanip.m_area.contains(i2) == false)
2898 n2 = &vmanip.m_data[i2];
2899 if(content_walkable(n2->d))
2901 /*// Check that under that is air (need a drop of 2)
2902 vmanip.m_area.add_y(em, i2, -1);
2903 if(vmanip.m_area.contains(i2) == false)
2905 n2 = &vmanip.m_data[i2];
2906 if(content_walkable(n2->d))
2908 // Loop further down until not air
2910 vmanip.m_area.add_y(em, i2, -1);
2911 // Fail if out of area
2912 if(vmanip.m_area.contains(i2) == false)
2914 n2 = &vmanip.m_data[i2];
2915 }while(content_walkable(n2->d) == false);
2916 // Loop one up so that we're in air
2917 vmanip.m_area.add_y(em, i2, 1);
2918 n2 = &vmanip.m_data[i2];
2920 // Move mud to new place
2922 // Set old place to be air
2923 *n = MapNode(CONTENT_AIR);
2936 //TimeTaker timer1("add water");
2939 Add water to the central chunk (and a bit more)
2942 for(s16 x=0-max_spread_amount;
2943 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2945 for(s16 z=0-max_spread_amount;
2946 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2949 // Node position in 2d
2950 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2952 // Find ground level
2953 //s16 surface_y = find_ground_level(vmanip, p2d);
2956 If ground level is over water level, skip.
2957 NOTE: This leaves caves near water without water,
2958 which looks especially crappy when the nearby water
2959 won't start flowing either for some reason
2961 /*if(surface_y > WATER_LEVEL)
2968 v3s16 em = vmanip.m_area.getExtent();
2969 u8 light = LIGHT_MAX;
2970 // Start at global water surface level
2971 s16 y_start = WATER_LEVEL;
2972 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2973 MapNode *n = &vmanip.m_data[i];
2975 /*// Add first one to transforming liquid queue, if water
2976 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2978 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2979 m_transforming_liquid.push_back(p);
2982 for(s16 y=y_start; y>=y_nodes_min; y--)
2984 n = &vmanip.m_data[i];
2986 // Stop when there is no water and no air
2987 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2988 && n->d != CONTENT_WATER)
2990 /*// Add bottom one to transforming liquid queue
2991 vmanip.m_area.add_y(em, i, 1);
2992 n = &vmanip.m_data[i];
2993 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2995 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2996 m_transforming_liquid.push_back(p);
3002 n->d = CONTENT_WATERSOURCE;
3003 n->setLight(LIGHTBANK_DAY, light);
3005 // Add to transforming liquid queue (in case it'd
3007 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3008 m_transforming_liquid.push_back(p);
3011 vmanip.m_area.add_y(em, i, -1);
3024 //TimeTaker timer1("convert mud to sand");
3030 //s16 mud_add_amount = myrand_range(2, 4);
3031 //s16 mud_add_amount = 0;
3033 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3034 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3035 for(s16 x=0-max_spread_amount+1;
3036 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3038 for(s16 z=0-max_spread_amount+1;
3039 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3042 // Node position in 2d
3043 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3045 // Determine whether to have sand here
3046 double sandnoise = noise2d_perlin(
3047 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3048 m_seed+59420, 3, 0.50);
3050 bool have_sand = (sandnoise > -0.15);
3052 if(have_sand == false)
3055 // Find ground level
3056 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3058 if(surface_y > WATER_LEVEL + 2)
3062 v3s16 em = vmanip.m_area.getExtent();
3063 s16 y_start = surface_y;
3064 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3065 u32 not_sand_counter = 0;
3066 for(s16 y=y_start; y>=y_nodes_min; y--)
3068 MapNode *n = &vmanip.m_data[i];
3069 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3071 n->d = CONTENT_SAND;
3076 if(not_sand_counter > 3)
3080 vmanip.m_area.add_y(em, i, -1);
3089 //TimeTaker timer1("generate trees");
3095 // Divide area into parts
3097 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3098 double area = sidelen * sidelen;
3099 for(s16 x0=0; x0<div; x0++)
3100 for(s16 z0=0; z0<div; z0++)
3102 // Center position of part of division
3104 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3105 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3107 // Minimum edge of part of division
3109 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3110 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3112 // Maximum edge of part of division
3114 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3115 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3118 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3119 // Put trees in random places on part of division
3120 for(u32 i=0; i<tree_count; i++)
3122 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3123 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3124 s16 y = find_ground_level(vmanip, v2s16(x,z));
3125 // Don't make a tree under water level
3130 Trees grow only on mud and grass
3133 u32 i = vmanip.m_area.index(v3s16(p));
3134 MapNode *n = &vmanip.m_data[i];
3135 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3140 make_tree(vmanip, p);
3143 /*u32 tree_max = relative_area / 60;
3144 //u32 count = myrand_range(0, tree_max);
3145 for(u32 i=0; i<count; i++)
3147 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3148 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3149 x += sectorpos_base.X*MAP_BLOCKSIZE;
3150 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3151 s16 y = find_ground_level(vmanip, v2s16(x,z));
3152 // Don't make a tree under water level
3157 make_tree(vmanip, p);
3165 //TimeTaker timer1("grow grass");
3171 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3172 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3173 for(s16 x=0-max_spread_amount;
3174 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3176 for(s16 z=0-max_spread_amount;
3177 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3180 // Node position in 2d
3181 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3184 Find the lowest surface to which enough light ends up
3187 Basically just wait until not air and not leaves.
3191 v3s16 em = vmanip.m_area.getExtent();
3192 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3194 // Go to ground level
3195 for(y=y_nodes_max; y>=y_nodes_min; y--)
3197 MapNode &n = vmanip.m_data[i];
3198 if(n.d != CONTENT_AIR
3199 && n.d != CONTENT_LEAVES)
3201 vmanip.m_area.add_y(em, i, -1);
3203 if(y >= y_nodes_min)
3206 surface_y = y_nodes_min;
3209 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3210 MapNode *n = &vmanip.m_data[i];
3211 if(n->d == CONTENT_MUD)
3212 n->d = CONTENT_GRASS;
3218 Initial lighting (sunlight)
3221 core::map<v3s16, bool> light_sources;
3224 // 750ms @cs=8, can't optimize more
3225 TimeTaker timer1("initial lighting");
3229 Go through the edges and add all nodes that have light to light_sources
3233 for(s16 i=0; i<4; i++)
3235 for(s16 j=lighting_min_d;
3242 if(i == 0 || i == 1)
3244 x = (i==0) ? lighting_min_d : lighting_max_d;
3253 z = (i==0) ? lighting_min_d : lighting_max_d;
3260 // Node position in 2d
3261 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3264 v3s16 em = vmanip.m_area.getExtent();
3265 s16 y_start = y_nodes_max;
3266 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3267 for(s16 y=y_start; y>=y_nodes_min; y--)
3269 MapNode *n = &vmanip.m_data[i];
3270 if(n->getLight(LIGHTBANK_DAY) != 0)
3272 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3274 //NOTE: This is broken, at least the index has to
3283 Go through the edges and apply sunlight to them, not caring
3288 for(s16 i=0; i<4; i++)
3290 for(s16 j=lighting_min_d;
3297 if(i == 0 || i == 1)
3299 x = (i==0) ? lighting_min_d : lighting_max_d;
3308 z = (i==0) ? lighting_min_d : lighting_max_d;
3315 // Node position in 2d
3316 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3318 // Loop from top to down
3320 u8 light = LIGHT_SUN;
3321 v3s16 em = vmanip.m_area.getExtent();
3322 s16 y_start = y_nodes_max;
3323 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3324 for(s16 y=y_start; y>=y_nodes_min; y--)
3326 MapNode *n = &vmanip.m_data[i];
3327 if(light_propagates_content(n->d) == false)
3331 else if(light != LIGHT_SUN
3332 || sunlight_propagates_content(n->d) == false)
3338 n->setLight(LIGHTBANK_DAY, light);
3339 n->setLight(LIGHTBANK_NIGHT, 0);
3343 // Insert light source
3344 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3347 // Increment index by y
3348 vmanip.m_area.add_y(em, i, -1);
3354 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3355 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3356 /*for(s16 x=0-max_spread_amount+1;
3357 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3359 for(s16 z=0-max_spread_amount+1;
3360 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3364 This has to be 1 smaller than the actual area, because
3365 neighboring nodes are checked.
3367 for(s16 x=lighting_min_d+1;
3368 x<=lighting_max_d-1;
3370 for(s16 z=lighting_min_d+1;
3371 z<=lighting_max_d-1;
3374 // Node position in 2d
3375 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3378 Apply initial sunlight
3381 u8 light = LIGHT_SUN;
3382 bool add_to_sources = false;
3383 v3s16 em = vmanip.m_area.getExtent();
3384 s16 y_start = y_nodes_max;
3385 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3386 for(s16 y=y_start; y>=y_nodes_min; y--)
3388 MapNode *n = &vmanip.m_data[i];
3390 if(light_propagates_content(n->d) == false)
3394 else if(light != LIGHT_SUN
3395 || sunlight_propagates_content(n->d) == false)
3401 // This doesn't take much time
3402 if(add_to_sources == false)
3405 Check sides. If side is not air or water, start
3406 adding to light_sources.
3409 v3s16(0,0,1), // back
3410 v3s16(1,0,0), // right
3411 v3s16(0,0,-1), // front
3412 v3s16(-1,0,0), // left
3414 for(u32 di=0; di<4; di++)
3416 v3s16 dirp = dirs4[di];
3418 vmanip.m_area.add_p(em, i2, dirp);
3419 MapNode *n2 = &vmanip.m_data[i2];
3421 n2->d != CONTENT_AIR
3422 && n2->d != CONTENT_WATERSOURCE
3423 && n2->d != CONTENT_WATER
3425 add_to_sources = true;
3431 n->setLight(LIGHTBANK_DAY, light);
3432 n->setLight(LIGHTBANK_NIGHT, 0);
3434 // This doesn't take much time
3435 if(light != 0 && add_to_sources)
3437 // Insert light source
3438 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3441 // Increment index by y
3442 vmanip.m_area.add_y(em, i, -1);
3449 for(s16 x=lighting_min_d+1;
3450 x<=lighting_max_d-1;
3452 for(s16 z=lighting_min_d+1;
3453 z<=lighting_max_d-1;
3456 // Node position in 2d
3457 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3460 Apply initial sunlight
3463 u8 light = LIGHT_SUN;
3464 v3s16 em = vmanip.m_area.getExtent();
3465 s16 y_start = y_nodes_max;
3466 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3467 for(s16 y=y_start; y>=y_nodes_min; y--)
3469 MapNode *n = &vmanip.m_data[i];
3471 if(light_propagates_content(n->d) == false)
3475 else if(light != LIGHT_SUN
3476 || sunlight_propagates_content(n->d) == false)
3482 n->setLight(LIGHTBANK_DAY, light);
3483 n->setLight(LIGHTBANK_NIGHT, 0);
3485 // This doesn't take much time
3488 // Insert light source
3489 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3492 // Increment index by y
3493 vmanip.m_area.add_y(em, i, -1);
3501 // Spread light around
3503 TimeTaker timer("generateChunkRaw() spreadLight");
3504 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3511 timer_generate.stop();
3514 Blit generated stuff to map
3518 //TimeTaker timer("generateChunkRaw() blitBackAll");
3519 vmanip.blitBackAll(&changed_blocks);
3522 Update day/night difference cache of the MapBlocks
3525 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3526 i.atEnd() == false; i++)
3528 MapBlock *block = i.getNode()->getValue();
3529 block->updateDayNightDiff();
3535 Create chunk metadata
3538 for(s16 x=-1; x<=1; x++)
3539 for(s16 y=-1; y<=1; y++)
3541 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3542 // Add chunk meta information
3543 MapChunk *chunk = getChunk(chunkpos0);
3546 chunk = new MapChunk();
3547 m_chunks.insert(chunkpos0, chunk);
3549 //chunk->setIsVolatile(true);
3550 if(chunk->getGenLevel() > GENERATED_PARTLY)
3551 chunk->setGenLevel(GENERATED_PARTLY);
3555 Set central chunk non-volatile and return it
3557 MapChunk *chunk = getChunk(chunkpos);
3560 //chunk->setIsVolatile(false);
3561 chunk->setGenLevel(GENERATED_FULLY);
3566 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3567 core::map<v3s16, MapBlock*> &changed_blocks)
3569 dstream<<"generateChunk(): Generating chunk "
3570 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3573 /*for(s16 x=-1; x<=1; x++)
3574 for(s16 y=-1; y<=1; y++)*/
3575 for(s16 x=-0; x<=0; x++)
3576 for(s16 y=-0; y<=0; y++)
3578 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3579 MapChunk *chunk = getChunk(chunkpos0);
3580 // Skip if already generated
3581 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3583 generateChunkRaw(chunkpos0, changed_blocks);
3586 assert(chunkNonVolatile(chunkpos1));
3588 MapChunk *chunk = getChunk(chunkpos1);
3592 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3594 DSTACK("%s: p2d=(%d,%d)",
3599 Check if it exists already in memory
3601 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3606 Try to load it from disk (with blocks)
3608 if(loadSectorFull(p2d) == true)
3610 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3613 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3614 throw InvalidPositionException("");
3620 Do not create over-limit
3622 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3623 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3624 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3625 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3626 throw InvalidPositionException("createSector(): pos. over limit");
3629 Generate blank sector
3632 sector = new ServerMapSector(this, p2d);
3634 // Sector position on map in nodes
3635 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3640 m_sectors.insert(p2d, sector);
3645 MapSector * ServerMap::emergeSector(v2s16 p2d,
3646 core::map<v3s16, MapBlock*> &changed_blocks)
3648 DSTACK("%s: p2d=(%d,%d)",
3655 v2s16 chunkpos = sector_to_chunk(p2d);
3656 /*bool chunk_nonvolatile = false;
3657 MapChunk *chunk = getChunk(chunkpos);
3658 if(chunk && chunk->getIsVolatile() == false)
3659 chunk_nonvolatile = true;*/
3660 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3663 If chunk is not fully generated, generate chunk
3665 if(chunk_nonvolatile == false)
3667 // Generate chunk and neighbors
3668 generateChunk(chunkpos, changed_blocks);
3672 Return sector if it exists now
3674 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3679 Try to load it from disk
3681 if(loadSectorFull(p2d) == true)
3683 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3686 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3687 throw InvalidPositionException("");
3693 generateChunk should have generated the sector
3700 //return generateSector();
3704 NOTE: This is not used for main map generation, only for blocks
3705 that are very high or low
3707 MapBlock * ServerMap::generateBlock(
3709 MapBlock *original_dummy,
3710 ServerMapSector *sector,
3711 core::map<v3s16, MapBlock*> &changed_blocks,
3712 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3715 DSTACK("%s: p=(%d,%d,%d)",
3719 /*dstream<<"generateBlock(): "
3720 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3723 MapBlock *block = original_dummy;
3725 v2s16 p2d(p.X, p.Z);
3729 Do not generate over-limit
3731 if(blockpos_over_limit(p))
3733 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3734 throw InvalidPositionException("generateBlock(): pos. over limit");
3738 If block doesn't exist, create one.
3739 If it exists, it is a dummy. In that case unDummify() it.
3741 NOTE: This already sets the map as the parent of the block
3745 block = sector->createBlankBlockNoInsert(block_y);
3749 // Remove the block so that nobody can get a half-generated one.
3750 sector->removeBlock(block);
3751 // Allocate the block to contain the generated data
3755 u8 water_material = CONTENT_WATERSOURCE;
3757 s32 lowest_ground_y = 32767;
3758 s32 highest_ground_y = -32768;
3760 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3761 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3763 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3767 if(surface_y < lowest_ground_y)
3768 lowest_ground_y = surface_y;
3769 if(surface_y > highest_ground_y)
3770 highest_ground_y = surface_y;
3772 s32 surface_depth = 2;
3774 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3776 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3781 NOTE: If there are some man-made structures above the
3782 newly created block, they won't be taken into account.
3784 if(real_y > surface_y)
3785 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3791 // If node is over heightmap y, it's air or water
3792 if(real_y > surface_y)
3794 // If under water level, it's water
3795 if(real_y < WATER_LEVEL)
3797 n.d = water_material;
3798 n.setLight(LIGHTBANK_DAY,
3799 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3801 Add to transforming liquid queue (in case it'd
3804 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3805 m_transforming_liquid.push_back(real_pos);
3811 // Else it's ground or dungeons (air)
3814 // If it's surface_depth under ground, it's stone
3815 if(real_y <= surface_y - surface_depth)
3817 n.d = CONTENT_STONE;
3821 // It is mud if it is under the first ground
3822 // level or under water
3823 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3829 n.d = CONTENT_GRASS;
3832 //n.d = CONTENT_MUD;
3834 /*// If under water level, it's mud
3835 if(real_y < WATER_LEVEL)
3837 // Only the topmost node is grass
3838 else if(real_y <= surface_y - 1)
3841 n.d = CONTENT_GRASS;*/
3845 block->setNode(v3s16(x0,y0,z0), n);
3850 Calculate some helper variables
3853 // Completely underground if the highest part of block is under lowest
3855 // This has to be very sure; it's probably one too strict now but
3856 // that's just better.
3857 bool completely_underground =
3858 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3860 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3862 bool mostly_underwater_surface = false;
3863 if(highest_ground_y < WATER_LEVEL
3864 && some_part_underground && !completely_underground)
3865 mostly_underwater_surface = true;
3868 Get local attributes
3871 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3873 float caves_amount = 0.5;
3878 NOTE: BEWARE: Too big amount of attribute points slows verything
3880 1 interpolation from 5000 points takes 2-3ms.
3882 //TimeTaker timer("generateBlock() local attribute retrieval");
3883 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3884 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3885 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3889 //dstream<<"generateBlock(): Done"<<std::endl;
3895 // Initialize temporary table
3896 const s32 ued = MAP_BLOCKSIZE;
3897 bool underground_emptiness[ued*ued*ued];
3898 for(s32 i=0; i<ued*ued*ued; i++)
3900 underground_emptiness[i] = 0;
3907 Initialize orp and ors. Try to find if some neighboring
3908 MapBlock has a tunnel ended in its side
3912 (float)(myrand()%ued)+0.5,
3913 (float)(myrand()%ued)+0.5,
3914 (float)(myrand()%ued)+0.5
3917 bool found_existing = false;
3923 for(s16 y=0; y<ued; y++)
3924 for(s16 x=0; x<ued; x++)
3926 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3927 if(getNode(ap).d == CONTENT_AIR)
3929 orp = v3f(x+1,y+1,0);
3930 found_existing = true;
3931 goto continue_generating;
3935 catch(InvalidPositionException &e){}
3941 for(s16 y=0; y<ued; y++)
3942 for(s16 x=0; x<ued; x++)
3944 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3945 if(getNode(ap).d == CONTENT_AIR)
3947 orp = v3f(x+1,y+1,ued-1);
3948 found_existing = true;
3949 goto continue_generating;
3953 catch(InvalidPositionException &e){}
3959 for(s16 y=0; y<ued; y++)
3960 for(s16 z=0; z<ued; z++)
3962 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3963 if(getNode(ap).d == CONTENT_AIR)
3965 orp = v3f(0,y+1,z+1);
3966 found_existing = true;
3967 goto continue_generating;
3971 catch(InvalidPositionException &e){}
3977 for(s16 y=0; y<ued; y++)
3978 for(s16 z=0; z<ued; z++)
3980 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3981 if(getNode(ap).d == CONTENT_AIR)
3983 orp = v3f(ued-1,y+1,z+1);
3984 found_existing = true;
3985 goto continue_generating;
3989 catch(InvalidPositionException &e){}
3995 for(s16 x=0; x<ued; x++)
3996 for(s16 z=0; z<ued; z++)
3998 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3999 if(getNode(ap).d == CONTENT_AIR)
4001 orp = v3f(x+1,0,z+1);
4002 found_existing = true;
4003 goto continue_generating;
4007 catch(InvalidPositionException &e){}
4013 for(s16 x=0; x<ued; x++)
4014 for(s16 z=0; z<ued; z++)
4016 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4017 if(getNode(ap).d == CONTENT_AIR)
4019 orp = v3f(x+1,ued-1,z+1);
4020 found_existing = true;
4021 goto continue_generating;
4025 catch(InvalidPositionException &e){}
4027 continue_generating:
4030 Choose whether to actually generate dungeon
4032 bool do_generate_dungeons = true;
4033 // Don't generate if no part is underground
4034 if(!some_part_underground)
4036 do_generate_dungeons = false;
4038 // Don't generate if mostly underwater surface
4039 /*else if(mostly_underwater_surface)
4041 do_generate_dungeons = false;
4043 // Partly underground = cave
4044 else if(!completely_underground)
4046 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4048 // Found existing dungeon underground
4049 else if(found_existing && completely_underground)
4051 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4053 // Underground and no dungeons found
4056 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4059 if(do_generate_dungeons)
4062 Generate some tunnel starting from orp and ors
4064 for(u16 i=0; i<3; i++)
4067 (float)(myrand()%ued)+0.5,
4068 (float)(myrand()%ued)+0.5,
4069 (float)(myrand()%ued)+0.5
4073 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4077 for(float f=0; f<1.0; f+=0.04)
4079 v3f fp = orp + vec * f;
4080 v3s16 cp(fp.X, fp.Y, fp.Z);
4082 s16 d1 = d0 + rs - 1;
4083 for(s16 z0=d0; z0<=d1; z0++)
4085 s16 si = rs - abs(z0);
4086 for(s16 x0=-si; x0<=si-1; x0++)
4088 s16 si2 = rs - abs(x0);
4089 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4095 if(isInArea(p, ued) == false)
4097 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4109 // Set to true if has caves.
4110 // Set when some non-air is changed to air when making caves.
4111 bool has_dungeons = false;
4114 Apply temporary cave data to block
4117 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4118 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4120 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4122 MapNode n = block->getNode(v3s16(x0,y0,z0));
4125 if(underground_emptiness[
4126 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4127 +ued*(y0*ued/MAP_BLOCKSIZE)
4128 +(x0*ued/MAP_BLOCKSIZE)])
4130 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4133 has_dungeons = true;
4139 block->setNode(v3s16(x0,y0,z0), n);
4144 This is used for guessing whether or not the block should
4145 receive sunlight from the top if the block above doesn't exist
4147 block->setIsUnderground(completely_underground);
4150 Force lighting update if some part of block is partly
4151 underground and has caves.
4153 /*if(some_part_underground && !completely_underground && has_dungeons)
4155 //dstream<<"Half-ground caves"<<std::endl;
4156 lighting_invalidated_blocks[block->getPos()] = block;
4159 // DEBUG: Always update lighting
4160 //lighting_invalidated_blocks[block->getPos()] = block;
4166 if(some_part_underground)
4168 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4173 for(s16 i=0; i<underground_level/4 + 1; i++)
4175 if(myrand()%50 == 0)
4178 (myrand()%(MAP_BLOCKSIZE-2))+1,
4179 (myrand()%(MAP_BLOCKSIZE-2))+1,
4180 (myrand()%(MAP_BLOCKSIZE-2))+1
4186 for(u16 i=0; i<27; i++)
4188 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4190 block->setNode(cp+g_27dirs[i], n);
4198 u16 coal_amount = 30;
4199 u16 coal_rareness = 60 / coal_amount;
4200 if(coal_rareness == 0)
4202 if(myrand()%coal_rareness == 0)
4204 u16 a = myrand() % 16;
4205 u16 amount = coal_amount * a*a*a / 1000;
4206 for(s16 i=0; i<amount; i++)
4209 (myrand()%(MAP_BLOCKSIZE-2))+1,
4210 (myrand()%(MAP_BLOCKSIZE-2))+1,
4211 (myrand()%(MAP_BLOCKSIZE-2))+1
4215 n.d = CONTENT_STONE;
4216 n.param = MINERAL_COAL;
4218 for(u16 i=0; i<27; i++)
4220 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4222 block->setNode(cp+g_27dirs[i], n);
4230 //TODO: change to iron_amount or whatever
4231 u16 iron_amount = 15;
4232 u16 iron_rareness = 60 / iron_amount;
4233 if(iron_rareness == 0)
4235 if(myrand()%iron_rareness == 0)
4237 u16 a = myrand() % 16;
4238 u16 amount = iron_amount * a*a*a / 1000;
4239 for(s16 i=0; i<amount; i++)
4242 (myrand()%(MAP_BLOCKSIZE-2))+1,
4243 (myrand()%(MAP_BLOCKSIZE-2))+1,
4244 (myrand()%(MAP_BLOCKSIZE-2))+1
4248 n.d = CONTENT_STONE;
4249 n.param = MINERAL_IRON;
4251 for(u16 i=0; i<27; i++)
4253 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4255 block->setNode(cp+g_27dirs[i], n);
4262 Create a few rats in empty blocks underground
4264 if(completely_underground)
4266 //for(u16 i=0; i<2; i++)
4269 (myrand()%(MAP_BLOCKSIZE-2))+1,
4270 (myrand()%(MAP_BLOCKSIZE-2))+1,
4271 (myrand()%(MAP_BLOCKSIZE-2))+1
4274 // Check that the place is empty
4275 //if(!is_ground_content(block->getNode(cp).d))
4278 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4279 block->addObject(obj);
4285 Add block to sector.
4287 sector->insertBlock(block);
4289 // Lighting is invalid after generation.
4290 block->setLightingExpired(true);
4297 <<"lighting_invalidated_blocks.size()"
4301 <<" "<<lighting_invalidated_blocks.size()
4302 <<", "<<has_dungeons
4303 <<", "<<completely_underground
4304 <<", "<<some_part_underground
4311 MapBlock * ServerMap::createBlock(v3s16 p)
4313 DSTACK("%s: p=(%d,%d,%d)",
4314 __FUNCTION_NAME, p.X, p.Y, p.Z);
4316 v2s16 p2d(p.X, p.Z);
4319 This will create or load a sector if not found in memory.
4320 If block exists on disk, it will be loaded.
4322 NOTE: On old save formats, this will be slow, as it generates
4323 lighting on blocks for them.
4325 ServerMapSector *sector;
4327 sector = (ServerMapSector*)createSector(p2d);
4328 assert(sector->getId() == MAPSECTOR_SERVER);
4330 /*catch(InvalidPositionException &e)
4332 dstream<<"createBlock: createSector() failed"<<std::endl;
4335 catch(std::exception &e)
4337 dstream<<"createBlock: createSector() failed: "
4338 <<e.what()<<std::endl;
4343 Try to get a block from the sector
4346 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4350 block = sector->createBlankBlock(block_y);
4354 MapBlock * ServerMap::emergeBlock(
4356 bool only_from_disk,
4357 core::map<v3s16, MapBlock*> &changed_blocks,
4358 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4361 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4363 p.X, p.Y, p.Z, only_from_disk);
4365 v2s16 p2d(p.X, p.Z);
4368 This will create or load a sector if not found in memory.
4369 If block exists on disk, it will be loaded.
4371 NOTE: On old save formats, this will be slow, as it generates
4372 lighting on blocks for them.
4374 ServerMapSector *sector;
4376 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4377 assert(sector->getId() == MAPSECTOR_SERVER);
4379 catch(std::exception &e)
4381 dstream<<"emergeBlock: emergeSector() failed: "
4382 <<e.what()<<std::endl;
4387 Try to get a block from the sector
4390 bool does_not_exist = false;
4391 bool lighting_expired = false;
4392 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4396 does_not_exist = true;
4398 else if(block->isDummy() == true)
4400 does_not_exist = true;
4402 else if(block->getLightingExpired())
4404 lighting_expired = true;
4409 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4414 If block was not found on disk and not going to generate a
4415 new one, make sure there is a dummy block in place.
4417 if(only_from_disk && (does_not_exist || lighting_expired))
4419 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4423 // Create dummy block
4424 block = new MapBlock(this, p, true);
4426 // Add block to sector
4427 sector->insertBlock(block);
4433 //dstream<<"Not found on disk, generating."<<std::endl;
4435 //TimeTaker("emergeBlock() generate");
4437 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4440 If the block doesn't exist, generate the block.
4444 block = generateBlock(p, block, sector, changed_blocks,
4445 lighting_invalidated_blocks);
4448 if(lighting_expired)
4450 lighting_invalidated_blocks.insert(p, block);
4454 Initially update sunlight
4458 core::map<v3s16, bool> light_sources;
4459 bool black_air_left = false;
4460 bool bottom_invalid =
4461 block->propagateSunlight(light_sources, true,
4462 &black_air_left, true);
4464 // If sunlight didn't reach everywhere and part of block is
4465 // above ground, lighting has to be properly updated
4466 //if(black_air_left && some_part_underground)
4469 lighting_invalidated_blocks[block->getPos()] = block;
4474 lighting_invalidated_blocks[block->getPos()] = block;
4481 void ServerMap::createDir(std::string path)
4483 if(fs::CreateDir(path) == false)
4485 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4486 <<"\""<<path<<"\""<<std::endl;
4487 throw BaseException("ServerMap failed to create directory");
4491 std::string ServerMap::getSectorSubDir(v2s16 pos)
4494 snprintf(cc, 9, "%.4x%.4x",
4495 (unsigned int)pos.X&0xffff,
4496 (unsigned int)pos.Y&0xffff);
4498 return std::string(cc);
4501 std::string ServerMap::getSectorDir(v2s16 pos)
4503 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4506 v2s16 ServerMap::getSectorPos(std::string dirname)
4508 if(dirname.size() != 8)
4509 throw InvalidFilenameException("Invalid sector directory name");
4511 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4513 throw InvalidFilenameException("Invalid sector directory name");
4514 v2s16 pos((s16)x, (s16)y);
4518 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4520 v2s16 p2d = getSectorPos(sectordir);
4522 if(blockfile.size() != 4){
4523 throw InvalidFilenameException("Invalid block filename");
4526 int r = sscanf(blockfile.c_str(), "%4x", &y);
4528 throw InvalidFilenameException("Invalid block filename");
4529 return v3s16(p2d.X, y, p2d.Y);
4532 void ServerMap::save(bool only_changed)
4534 DSTACK(__FUNCTION_NAME);
4535 if(m_map_saving_enabled == false)
4537 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4541 if(only_changed == false)
4542 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4545 saveMasterHeightmap();
4547 u32 sector_meta_count = 0;
4548 u32 block_count = 0;
4551 JMutexAutoLock lock(m_sector_mutex);
4553 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4554 for(; i.atEnd() == false; i++)
4556 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4557 assert(sector->getId() == MAPSECTOR_SERVER);
4559 if(sector->differs_from_disk || only_changed == false)
4561 saveSectorMeta(sector);
4562 sector_meta_count++;
4564 core::list<MapBlock*> blocks;
4565 sector->getBlocks(blocks);
4566 core::list<MapBlock*>::Iterator j;
4567 for(j=blocks.begin(); j!=blocks.end(); j++)
4569 MapBlock *block = *j;
4570 if(block->getChangedFlag() || only_changed == false)
4575 /*dstream<<"ServerMap: Written block ("
4576 <<block->getPos().X<<","
4577 <<block->getPos().Y<<","
4578 <<block->getPos().Z<<")"
4587 Only print if something happened or saved whole map
4589 if(only_changed == false || sector_meta_count != 0
4590 || block_count != 0)
4592 dstream<<DTIME<<"ServerMap: Written: "
4593 <<sector_meta_count<<" sector metadata files, "
4594 <<block_count<<" block files"
4599 void ServerMap::loadAll()
4601 DSTACK(__FUNCTION_NAME);
4602 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4604 loadMasterHeightmap();
4606 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4608 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4610 JMutexAutoLock lock(m_sector_mutex);
4613 s32 printed_counter = -100000;
4614 s32 count = list.size();
4616 std::vector<fs::DirListNode>::iterator i;
4617 for(i=list.begin(); i!=list.end(); i++)
4619 if(counter > printed_counter + 10)
4621 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4622 printed_counter = counter;
4626 MapSector *sector = NULL;
4628 // We want directories
4632 sector = loadSectorMeta(i->name);
4634 catch(InvalidFilenameException &e)
4636 // This catches unknown crap in directory
4639 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4640 (m_savedir+"/sectors/"+i->name);
4641 std::vector<fs::DirListNode>::iterator i2;
4642 for(i2=list2.begin(); i2!=list2.end(); i2++)
4648 loadBlock(i->name, i2->name, sector);
4650 catch(InvalidFilenameException &e)
4652 // This catches unknown crap in directory
4656 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4659 void ServerMap::saveMasterHeightmap()
4661 DSTACK(__FUNCTION_NAME);
4663 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4665 createDir(m_savedir);
4667 /*std::string fullpath = m_savedir + "/master_heightmap";
4668 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4669 if(o.good() == false)
4670 throw FileNotGoodException("Cannot open master heightmap");*/
4672 // Format used for writing
4673 //u8 version = SER_FMT_VER_HIGHEST;
4676 void ServerMap::loadMasterHeightmap()
4678 DSTACK(__FUNCTION_NAME);
4680 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4682 /*std::string fullpath = m_savedir + "/master_heightmap";
4683 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4684 if(is.good() == false)
4685 throw FileNotGoodException("Cannot open master heightmap");*/
4688 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4690 DSTACK(__FUNCTION_NAME);
4691 // Format used for writing
4692 u8 version = SER_FMT_VER_HIGHEST;
4694 v2s16 pos = sector->getPos();
4695 createDir(m_savedir);
4696 createDir(m_savedir+"/sectors");
4697 std::string dir = getSectorDir(pos);
4700 std::string fullpath = dir + "/heightmap";
4701 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4702 if(o.good() == false)
4703 throw FileNotGoodException("Cannot open master heightmap");
4705 sector->serialize(o, version);
4707 sector->differs_from_disk = false;
4710 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4712 DSTACK(__FUNCTION_NAME);
4714 v2s16 p2d = getSectorPos(dirname);
4715 std::string dir = m_savedir + "/sectors/" + dirname;
4717 std::string fullpath = dir + "/heightmap";
4718 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4719 if(is.good() == false)
4720 throw FileNotGoodException("Cannot open sector heightmap");
4722 ServerMapSector *sector = ServerMapSector::deSerialize
4723 (is, this, p2d, m_sectors);
4725 sector->differs_from_disk = false;
4730 bool ServerMap::loadSectorFull(v2s16 p2d)
4732 DSTACK(__FUNCTION_NAME);
4733 std::string sectorsubdir = getSectorSubDir(p2d);
4735 MapSector *sector = NULL;
4737 JMutexAutoLock lock(m_sector_mutex);
4740 sector = loadSectorMeta(sectorsubdir);
4742 catch(InvalidFilenameException &e)
4746 catch(FileNotGoodException &e)
4750 catch(std::exception &e)
4755 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4756 (m_savedir+"/sectors/"+sectorsubdir);
4757 std::vector<fs::DirListNode>::iterator i2;
4758 for(i2=list2.begin(); i2!=list2.end(); i2++)
4764 loadBlock(sectorsubdir, i2->name, sector);
4766 catch(InvalidFilenameException &e)
4768 // This catches unknown crap in directory
4775 bool ServerMap::deFlushSector(v2s16 p2d)
4777 DSTACK(__FUNCTION_NAME);
4778 // See if it already exists in memory
4780 MapSector *sector = getSectorNoGenerate(p2d);
4783 catch(InvalidPositionException &e)
4786 Try to load the sector from disk.
4788 if(loadSectorFull(p2d) == true)
4797 void ServerMap::saveBlock(MapBlock *block)
4799 DSTACK(__FUNCTION_NAME);
4801 Dummy blocks are not written
4803 if(block->isDummy())
4805 /*v3s16 p = block->getPos();
4806 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4807 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4811 // Format used for writing
4812 u8 version = SER_FMT_VER_HIGHEST;
4814 v3s16 p3d = block->getPos();
4815 v2s16 p2d(p3d.X, p3d.Z);
4816 createDir(m_savedir);
4817 createDir(m_savedir+"/sectors");
4818 std::string dir = getSectorDir(p2d);
4821 // Block file is map/sectors/xxxxxxxx/xxxx
4823 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4824 std::string fullpath = dir + "/" + cc;
4825 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4826 if(o.good() == false)
4827 throw FileNotGoodException("Cannot open block data");
4830 [0] u8 serialization version
4833 o.write((char*)&version, 1);
4835 block->serialize(o, version);
4838 Versions up from 9 have block objects.
4842 block->serializeObjects(o, version);
4845 // We just wrote it to the disk
4846 block->resetChangedFlag();
4849 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4851 DSTACK(__FUNCTION_NAME);
4855 // Block file is map/sectors/xxxxxxxx/xxxx
4856 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4857 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4858 if(is.good() == false)
4859 throw FileNotGoodException("Cannot open block file");
4861 v3s16 p3d = getBlockPos(sectordir, blockfile);
4862 v2s16 p2d(p3d.X, p3d.Z);
4864 assert(sector->getPos() == p2d);
4866 u8 version = SER_FMT_VER_INVALID;
4867 is.read((char*)&version, 1);
4869 /*u32 block_size = MapBlock::serializedLength(version);
4870 SharedBuffer<u8> data(block_size);
4871 is.read((char*)*data, block_size);*/
4873 // This will always return a sector because we're the server
4874 //MapSector *sector = emergeSector(p2d);
4876 MapBlock *block = NULL;
4877 bool created_new = false;
4879 block = sector->getBlockNoCreate(p3d.Y);
4881 catch(InvalidPositionException &e)
4883 block = sector->createBlankBlockNoInsert(p3d.Y);
4887 // deserialize block data
4888 block->deSerialize(is, version);
4891 Versions up from 9 have block objects.
4895 block->updateObjects(is, version, NULL, 0);
4899 sector->insertBlock(block);
4902 Convert old formats to new and save
4905 // Save old format blocks in new format
4906 if(version < SER_FMT_VER_HIGHEST)
4911 // We just loaded it from the disk, so it's up-to-date.
4912 block->resetChangedFlag();
4915 catch(SerializationError &e)
4917 dstream<<"WARNING: Invalid block data on disk "
4918 "(SerializationError). Ignoring."
4923 // Gets from master heightmap
4924 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4926 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4927 //assert(m_heightmap != NULL);
4935 /*corners[0] = m_heightmap->getGroundHeight
4936 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4937 corners[1] = m_heightmap->getGroundHeight
4938 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4939 corners[2] = m_heightmap->getGroundHeight
4940 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4941 corners[3] = m_heightmap->getGroundHeight
4942 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
4945 void ServerMap::PrintInfo(std::ostream &out)
4956 ClientMap::ClientMap(
4958 MapDrawControl &control,
4959 scene::ISceneNode* parent,
4960 scene::ISceneManager* mgr,
4964 scene::ISceneNode(parent, mgr, id),
4968 //mesh_mutex.Init();
4970 /*m_box = core::aabbox3d<f32>(0,0,0,
4971 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4972 /*m_box = core::aabbox3d<f32>(0,0,0,
4973 map->getSizeNodes().X * BS,
4974 map->getSizeNodes().Y * BS,
4975 map->getSizeNodes().Z * BS);*/
4976 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4977 BS*1000000,BS*1000000,BS*1000000);
4979 //setPosition(v3f(BS,BS,BS));
4982 ClientMap::~ClientMap()
4984 /*JMutexAutoLock lock(mesh_mutex);
4993 MapSector * ClientMap::emergeSector(v2s16 p2d)
4995 DSTACK(__FUNCTION_NAME);
4996 // Check that it doesn't exist already
4998 return getSectorNoGenerate(p2d);
5000 catch(InvalidPositionException &e)
5004 // Create a sector with no heightmaps
5005 ClientMapSector *sector = new ClientMapSector(this, p2d);
5008 JMutexAutoLock lock(m_sector_mutex);
5009 m_sectors.insert(p2d, sector);
5015 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5017 DSTACK(__FUNCTION_NAME);
5018 ClientMapSector *sector = NULL;
5020 JMutexAutoLock lock(m_sector_mutex);
5022 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5026 sector = (ClientMapSector*)n->getValue();
5027 assert(sector->getId() == MAPSECTOR_CLIENT);
5031 sector = new ClientMapSector(this, p2d);
5033 JMutexAutoLock lock(m_sector_mutex);
5034 m_sectors.insert(p2d, sector);
5038 sector->deSerialize(is);
5041 void ClientMap::OnRegisterSceneNode()
5045 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5046 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5049 ISceneNode::OnRegisterSceneNode();
5052 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5054 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5055 DSTACK(__FUNCTION_NAME);
5057 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5060 Get time for measuring timeout.
5062 Measuring time is very useful for long delays when the
5063 machine is swapping a lot.
5065 int time1 = time(0);
5067 u32 daynight_ratio = m_client->getDayNightRatio();
5069 m_camera_mutex.Lock();
5070 v3f camera_position = m_camera_position;
5071 v3f camera_direction = m_camera_direction;
5072 m_camera_mutex.Unlock();
5075 Get all blocks and draw all visible ones
5078 v3s16 cam_pos_nodes(
5079 camera_position.X / BS,
5080 camera_position.Y / BS,
5081 camera_position.Z / BS);
5083 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5085 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5086 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5088 // Take a fair amount as we will be dropping more out later
5090 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5091 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5092 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5094 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5095 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5096 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5098 u32 vertex_count = 0;
5100 // For limiting number of mesh updates per frame
5101 u32 mesh_update_count = 0;
5103 u32 blocks_would_have_drawn = 0;
5104 u32 blocks_drawn = 0;
5106 //NOTE: The sectors map should be locked but we're not doing it
5107 // because it'd cause too much delays
5109 int timecheck_counter = 0;
5110 core::map<v2s16, MapSector*>::Iterator si;
5111 si = m_sectors.getIterator();
5112 for(; si.atEnd() == false; si++)
5115 timecheck_counter++;
5116 if(timecheck_counter > 50)
5118 int time2 = time(0);
5119 if(time2 > time1 + 4)
5121 dstream<<"ClientMap::renderMap(): "
5122 "Rendering takes ages, returning."
5129 MapSector *sector = si.getNode()->getValue();
5130 v2s16 sp = sector->getPos();
5132 if(m_control.range_all == false)
5134 if(sp.X < p_blocks_min.X
5135 || sp.X > p_blocks_max.X
5136 || sp.Y < p_blocks_min.Z
5137 || sp.Y > p_blocks_max.Z)
5141 core::list< MapBlock * > sectorblocks;
5142 sector->getBlocks(sectorblocks);
5148 core::list< MapBlock * >::Iterator i;
5149 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5151 MapBlock *block = *i;
5154 Compare block position to camera position, skip
5155 if not seen on display
5158 float range = 100000 * BS;
5159 if(m_control.range_all == false)
5160 range = m_control.wanted_range * BS;
5163 if(isBlockInSight(block->getPos(), camera_position,
5164 camera_direction, range, &d) == false)
5170 v3s16 blockpos_nodes = block->getPosRelative();
5172 // Block center position
5174 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5175 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5176 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5179 // Block position relative to camera
5180 v3f blockpos_relative = blockpos - camera_position;
5182 // Distance in camera direction (+=front, -=back)
5183 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5186 f32 d = blockpos_relative.getLength();
5188 if(m_control.range_all == false)
5190 // If block is far away, don't draw it
5191 if(d > m_control.wanted_range * BS)
5195 // Maximum radius of a block
5196 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5198 // If block is (nearly) touching the camera, don't
5199 // bother validating further (that is, render it anyway)
5200 if(d > block_max_radius * 1.5)
5202 // Cosine of the angle between the camera direction
5203 // and the block direction (camera_direction is an unit vector)
5204 f32 cosangle = dforward / d;
5206 // Compensate for the size of the block
5207 // (as the block has to be shown even if it's a bit off FOV)
5208 // This is an estimate.
5209 cosangle += block_max_radius / dforward;
5211 // If block is not in the field of view, skip it
5212 //if(cosangle < cos(FOV_ANGLE/2))
5213 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5218 v3s16 blockpos_nodes = block->getPosRelative();
5220 // Block center position
5222 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5223 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5224 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5227 // Block position relative to camera
5228 v3f blockpos_relative = blockpos - camera_position;
5231 f32 d = blockpos_relative.getLength();
5239 bool mesh_expired = false;
5242 JMutexAutoLock lock(block->mesh_mutex);
5244 mesh_expired = block->getMeshExpired();
5246 // Mesh has not been expired and there is no mesh:
5247 // block has no content
5248 if(block->mesh == NULL && mesh_expired == false)
5252 f32 faraway = BS*50;
5253 //f32 faraway = m_control.wanted_range * BS;
5256 This has to be done with the mesh_mutex unlocked
5258 // Pretty random but this should work somewhat nicely
5259 if(mesh_expired && (
5260 (mesh_update_count < 3
5261 && (d < faraway || mesh_update_count < 2)
5264 (m_control.range_all && mesh_update_count < 20)
5267 /*if(mesh_expired && mesh_update_count < 6
5268 && (d < faraway || mesh_update_count < 3))*/
5270 mesh_update_count++;
5272 // Mesh has been expired: generate new mesh
5273 //block->updateMeshes(daynight_i);
5274 block->updateMesh(daynight_ratio);
5276 mesh_expired = false;
5280 Don't draw an expired mesh that is far away
5282 /*if(mesh_expired && d >= faraway)
5285 // Instead, delete it
5286 JMutexAutoLock lock(block->mesh_mutex);
5289 block->mesh->drop();
5292 // And continue to next block
5297 Draw the faces of the block
5300 JMutexAutoLock lock(block->mesh_mutex);
5302 scene::SMesh *mesh = block->mesh;
5307 blocks_would_have_drawn++;
5308 if(blocks_drawn >= m_control.wanted_max_blocks
5309 && m_control.range_all == false
5310 && d > m_control.wanted_min_range * BS)
5314 u32 c = mesh->getMeshBufferCount();
5316 for(u32 i=0; i<c; i++)
5318 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5319 const video::SMaterial& material = buf->getMaterial();
5320 video::IMaterialRenderer* rnd =
5321 driver->getMaterialRenderer(material.MaterialType);
5322 bool transparent = (rnd && rnd->isTransparent());
5323 // Render transparent on transparent pass and likewise.
5324 if(transparent == is_transparent_pass)
5326 driver->setMaterial(buf->getMaterial());
5327 driver->drawMeshBuffer(buf);
5328 vertex_count += buf->getVertexCount();
5332 } // foreach sectorblocks
5335 m_control.blocks_drawn = blocks_drawn;
5336 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5338 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5339 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5342 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5343 core::map<v3s16, MapBlock*> *affected_blocks)
5345 bool changed = false;
5347 Add it to all blocks touching it
5350 v3s16(0,0,0), // this
5351 v3s16(0,0,1), // back
5352 v3s16(0,1,0), // top
5353 v3s16(1,0,0), // right
5354 v3s16(0,0,-1), // front
5355 v3s16(0,-1,0), // bottom
5356 v3s16(-1,0,0), // left
5358 for(u16 i=0; i<7; i++)
5360 v3s16 p2 = p + dirs[i];
5361 // Block position of neighbor (or requested) node
5362 v3s16 blockpos = getNodeBlockPos(p2);
5363 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5364 if(blockref == NULL)
5366 // Relative position of requested node
5367 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5368 if(blockref->setTempMod(relpos, mod))
5373 if(changed && affected_blocks!=NULL)
5375 for(u16 i=0; i<7; i++)
5377 v3s16 p2 = p + dirs[i];
5378 // Block position of neighbor (or requested) node
5379 v3s16 blockpos = getNodeBlockPos(p2);
5380 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5381 if(blockref == NULL)
5383 affected_blocks->insert(blockpos, blockref);
5389 bool ClientMap::clearTempMod(v3s16 p,
5390 core::map<v3s16, MapBlock*> *affected_blocks)
5392 bool changed = false;
5394 v3s16(0,0,0), // this
5395 v3s16(0,0,1), // back
5396 v3s16(0,1,0), // top
5397 v3s16(1,0,0), // right
5398 v3s16(0,0,-1), // front
5399 v3s16(0,-1,0), // bottom
5400 v3s16(-1,0,0), // left
5402 for(u16 i=0; i<7; i++)
5404 v3s16 p2 = p + dirs[i];
5405 // Block position of neighbor (or requested) node
5406 v3s16 blockpos = getNodeBlockPos(p2);
5407 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5408 if(blockref == NULL)
5410 // Relative position of requested node
5411 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5412 if(blockref->clearTempMod(relpos))
5417 if(changed && affected_blocks!=NULL)
5419 for(u16 i=0; i<7; i++)
5421 v3s16 p2 = p + dirs[i];
5422 // Block position of neighbor (or requested) node
5423 v3s16 blockpos = getNodeBlockPos(p2);
5424 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5425 if(blockref == NULL)
5427 affected_blocks->insert(blockpos, blockref);
5433 void ClientMap::PrintInfo(std::ostream &out)
5444 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5449 MapVoxelManipulator::~MapVoxelManipulator()
5451 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5455 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5457 TimeTaker timer1("emerge", &emerge_time);
5459 // Units of these are MapBlocks
5460 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5461 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5463 VoxelArea block_area_nodes
5464 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5466 addArea(block_area_nodes);
5468 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5469 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5470 for(s32 x=p_min.X; x<=p_max.X; x++)
5473 core::map<v3s16, bool>::Node *n;
5474 n = m_loaded_blocks.find(p);
5478 bool block_data_inexistent = false;
5481 TimeTaker timer1("emerge load", &emerge_load_time);
5483 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5484 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5487 dstream<<std::endl;*/
5489 MapBlock *block = m_map->getBlockNoCreate(p);
5490 if(block->isDummy())
5491 block_data_inexistent = true;
5493 block->copyTo(*this);
5495 catch(InvalidPositionException &e)
5497 block_data_inexistent = true;
5500 if(block_data_inexistent)
5502 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5503 // Fill with VOXELFLAG_INEXISTENT
5504 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5505 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5507 s32 i = m_area.index(a.MinEdge.X,y,z);
5508 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5512 m_loaded_blocks.insert(p, !block_data_inexistent);
5515 //dstream<<"emerge done"<<std::endl;
5519 SUGG: Add an option to only update eg. water and air nodes.
5520 This will make it interfere less with important stuff if
5523 void MapVoxelManipulator::blitBack
5524 (core::map<v3s16, MapBlock*> & modified_blocks)
5526 if(m_area.getExtent() == v3s16(0,0,0))
5529 //TimeTaker timer1("blitBack");
5531 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5532 <<m_loaded_blocks.size()<<std::endl;*/
5535 Initialize block cache
5537 v3s16 blockpos_last;
5538 MapBlock *block = NULL;
5539 bool block_checked_in_modified = false;
5541 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5542 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5543 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5547 u8 f = m_flags[m_area.index(p)];
5548 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5551 MapNode &n = m_data[m_area.index(p)];
5553 v3s16 blockpos = getNodeBlockPos(p);
5558 if(block == NULL || blockpos != blockpos_last){
5559 block = m_map->getBlockNoCreate(blockpos);
5560 blockpos_last = blockpos;
5561 block_checked_in_modified = false;
5564 // Calculate relative position in block
5565 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5567 // Don't continue if nothing has changed here
5568 if(block->getNode(relpos) == n)
5571 //m_map->setNode(m_area.MinEdge + p, n);
5572 block->setNode(relpos, n);
5575 Make sure block is in modified_blocks
5577 if(block_checked_in_modified == false)
5579 modified_blocks[blockpos] = block;
5580 block_checked_in_modified = true;
5583 catch(InvalidPositionException &e)
5589 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5590 MapVoxelManipulator(map)
5594 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5598 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5600 // Just create the area so that it can be pointed to
5601 VoxelManipulator::emerge(a, caller_id);
5604 void ManualMapVoxelManipulator::initialEmerge(
5605 v3s16 blockpos_min, v3s16 blockpos_max)
5607 TimeTaker timer1("initialEmerge", &emerge_time);
5609 // Units of these are MapBlocks
5610 v3s16 p_min = blockpos_min;
5611 v3s16 p_max = blockpos_max;
5613 VoxelArea block_area_nodes
5614 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5616 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5619 dstream<<"initialEmerge: area: ";
5620 block_area_nodes.print(dstream);
5621 dstream<<" ("<<size_MB<<"MB)";
5625 addArea(block_area_nodes);
5627 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5628 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5629 for(s32 x=p_min.X; x<=p_max.X; x++)
5632 core::map<v3s16, bool>::Node *n;
5633 n = m_loaded_blocks.find(p);
5637 bool block_data_inexistent = false;
5640 TimeTaker timer1("emerge load", &emerge_load_time);
5642 MapBlock *block = m_map->getBlockNoCreate(p);
5643 if(block->isDummy())
5644 block_data_inexistent = true;
5646 block->copyTo(*this);
5648 catch(InvalidPositionException &e)
5650 block_data_inexistent = true;
5653 if(block_data_inexistent)
5656 Mark area inexistent
5658 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5659 // Fill with VOXELFLAG_INEXISTENT
5660 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5661 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5663 s32 i = m_area.index(a.MinEdge.X,y,z);
5664 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5668 m_loaded_blocks.insert(p, !block_data_inexistent);
5672 void ManualMapVoxelManipulator::blitBackAll(
5673 core::map<v3s16, MapBlock*> * modified_blocks)
5675 if(m_area.getExtent() == v3s16(0,0,0))
5679 Copy data of all blocks
5681 for(core::map<v3s16, bool>::Iterator
5682 i = m_loaded_blocks.getIterator();
5683 i.atEnd() == false; i++)
5685 bool existed = i.getNode()->getValue();
5686 if(existed == false)
5688 v3s16 p = i.getNode()->getKey();
5689 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5692 dstream<<"WARNING: "<<__FUNCTION_NAME
5693 <<": got NULL block "
5694 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5699 block->copyFrom(*this);
5702 modified_blocks->insert(p, block);