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"
34 Map::Map(std::ostream &dout):
36 m_camera_position(0,0,0),
37 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 //dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1460 while(m_transforming_liquid.size() != 0)
1463 Get a queued transforming liquid node
1465 v3s16 p0 = m_transforming_liquid.pop_front();
1467 MapNode n0 = getNode(p0);
1469 // Don't deal with non-liquids
1470 if(content_liquid(n0.d) == false)
1473 bool is_source = !content_flowing_liquid(n0.d);
1475 u8 liquid_level = 8;
1476 if(is_source == false)
1477 liquid_level = n0.param2 & 0x0f;
1479 // Turn possible source into non-source
1480 u8 nonsource_c = make_liquid_flowing(n0.d);
1483 If not source, check that some node flows into this one
1484 and what is the level of liquid in this one
1486 if(is_source == false)
1488 s8 new_liquid_level_max = -1;
1490 v3s16 dirs_from[5] = {
1491 v3s16(0,1,0), // top
1492 v3s16(0,0,1), // back
1493 v3s16(1,0,0), // right
1494 v3s16(0,0,-1), // front
1495 v3s16(-1,0,0), // left
1497 for(u16 i=0; i<5; i++)
1502 bool from_top = (i==0);
1504 v3s16 p2 = p0 + dirs_from[i];
1505 MapNode n2 = getNode(p2);
1507 if(content_liquid(n2.d))
1509 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1510 // Check that the liquids are the same type
1511 if(n2_nonsource_c != nonsource_c)
1513 dstream<<"WARNING: Not handling: different liquids"
1514 " collide"<<std::endl;
1517 bool n2_is_source = !content_flowing_liquid(n2.d);
1518 s8 n2_liquid_level = 8;
1519 if(n2_is_source == false)
1520 n2_liquid_level = n2.param2 & 0x07;
1522 s8 new_liquid_level = -1;
1525 //new_liquid_level = 7;
1526 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1527 new_liquid_level = 7;
1529 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1531 else if(n2_liquid_level > 0)
1533 new_liquid_level = n2_liquid_level - 1;
1536 if(new_liquid_level > new_liquid_level_max)
1537 new_liquid_level_max = new_liquid_level;
1540 }catch(InvalidPositionException &e)
1546 If liquid level should be something else, update it and
1547 add all the neighboring water nodes to the transform queue.
1549 if(new_liquid_level_max != liquid_level)
1551 if(new_liquid_level_max == -1)
1553 // Remove water alltoghether
1560 n0.param2 = new_liquid_level_max;
1564 // Block has been modified
1566 v3s16 blockpos = getNodeBlockPos(p0);
1567 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1569 modified_blocks.insert(blockpos, block);
1573 Add neighboring non-source liquid nodes to transform queue.
1576 v3s16(0,0,1), // back
1577 v3s16(0,1,0), // top
1578 v3s16(1,0,0), // right
1579 v3s16(0,0,-1), // front
1580 v3s16(0,-1,0), // bottom
1581 v3s16(-1,0,0), // left
1583 for(u16 i=0; i<6; i++)
1588 v3s16 p2 = p0 + dirs[i];
1590 MapNode n2 = getNode(p2);
1591 if(content_flowing_liquid(n2.d))
1593 m_transforming_liquid.push_back(p2);
1596 }catch(InvalidPositionException &e)
1603 // Get a new one from queue if the node has turned into non-water
1604 if(content_liquid(n0.d) == false)
1608 Flow water from this node
1610 v3s16 dirs_to[5] = {
1611 v3s16(0,-1,0), // bottom
1612 v3s16(0,0,1), // back
1613 v3s16(1,0,0), // right
1614 v3s16(0,0,-1), // front
1615 v3s16(-1,0,0), // left
1617 for(u16 i=0; i<5; i++)
1622 bool to_bottom = (i == 0);
1624 // If liquid is at lowest possible height, it's not going
1625 // anywhere except down
1626 if(liquid_level == 0 && to_bottom == false)
1629 u8 liquid_next_level = 0;
1630 // If going to bottom
1633 //liquid_next_level = 7;
1634 if(liquid_level >= 7 - WATER_DROP_BOOST)
1635 liquid_next_level = 7;
1637 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1640 liquid_next_level = liquid_level - 1;
1642 bool n2_changed = false;
1643 bool flowed = false;
1645 v3s16 p2 = p0 + dirs_to[i];
1647 MapNode n2 = getNode(p2);
1648 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1650 if(content_liquid(n2.d))
1652 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1653 // Check that the liquids are the same type
1654 if(n2_nonsource_c != nonsource_c)
1656 dstream<<"WARNING: Not handling: different liquids"
1657 " collide"<<std::endl;
1660 bool n2_is_source = !content_flowing_liquid(n2.d);
1661 u8 n2_liquid_level = 8;
1662 if(n2_is_source == false)
1663 n2_liquid_level = n2.param2 & 0x07;
1672 // Just flow into the source, nothing changes.
1673 // n2_changed is not set because destination didn't change
1678 if(liquid_next_level > liquid_level)
1680 n2.param2 = liquid_next_level;
1688 else if(n2.d == CONTENT_AIR)
1691 n2.param2 = liquid_next_level;
1698 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1702 m_transforming_liquid.push_back(p2);
1704 v3s16 blockpos = getNodeBlockPos(p2);
1705 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1707 modified_blocks.insert(blockpos, block);
1710 // If n2_changed to bottom, don't flow anywhere else
1711 if(to_bottom && flowed && !is_source)
1714 }catch(InvalidPositionException &e)
1720 //if(loopcount >= 100000)
1721 if(loopcount >= initial_size * 1)
1724 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1731 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1743 Experimental and debug stuff
1747 dstream<<"Generating map point attribute lists"<<std::endl;
1749 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1750 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1751 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1752 //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1753 //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1757 NOTE: BEWARE: Too big amount of these will make map generation
1758 slow. Especially those that are read by every block emerge.
1766 for(u32 i=0; i<500; i++)
1768 /*u32 lim = MAP_GENERATION_LIMIT;
1772 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1775 -lim + myrand()%(lim*2),
1777 -lim + myrand()%(lim*2)
1779 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1780 plants_amount = pow(plants_amount, 5);
1781 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1783 float plants_amount = 0;
1786 plants_amount = 1.5;
1788 else if(myrand()%4 == 0)
1790 plants_amount = 0.5;
1792 else if(myrand()%2 == 0)
1794 plants_amount = 0.03;
1798 plants_amount = 0.0;
1802 list_plants_amount->addPoint(p, Attribute(plants_amount));
1805 for(u32 i=0; i<500; i++)
1807 /*u32 lim = MAP_GENERATION_LIMIT;
1811 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1814 -lim + myrand()%(lim*2),
1816 -lim + myrand()%(lim*2)
1819 float caves_amount = 0;
1824 else if(myrand()%3 == 0)
1830 caves_amount = 0.05;
1833 list_caves_amount->addPoint(p, Attribute(caves_amount));
1836 for(u32 i=0; i<500; i++)
1838 /*u32 lim = MAP_GENERATION_LIMIT;
1842 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1845 -lim + (myrand()%(lim*2)),
1847 -lim + (myrand()%(lim*2))
1850 /*s32 bh_i = (myrand()%200) - 50;
1851 float baseheight = (float)bh_i;
1855 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1856 randmax = pow(randmax, e);
1858 //float randmax = (float)(myrand()%60);
1859 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1861 float baseheight = 0;
1863 float randfactor = 0;
1865 /*if(myrand()%5 == 0)
1871 else if(myrand()%6 == 0)
1877 else if(myrand()%4 == 0)
1883 else if(myrand()%3 == 0)
1909 list_baseheight->addPoint(p, Attribute(baseheight));
1910 list_randmax->addPoint(p, Attribute(randmax));
1911 list_randfactor->addPoint(p, Attribute(randfactor));
1915 // Add only one entry
1916 list_baseheight->addPoint(v3s16(0,0,0), Attribute(-4));
1917 list_randmax->addPoint(v3s16(0,0,0), Attribute(22));
1918 //list_randmax->addPoint(v3s16(0,0,0), Attribute(0));
1919 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1922 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1923 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1924 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1928 Try to load map; if not found, create a new one.
1931 m_savedir = savedir;
1932 m_map_saving_enabled = false;
1936 // If directory exists, check contents and load if possible
1937 if(fs::PathExists(m_savedir))
1939 // If directory is empty, it is safe to save into it.
1940 if(fs::GetDirListing(m_savedir).size() == 0)
1942 dstream<<DTIME<<"Server: Empty save directory is valid."
1944 m_map_saving_enabled = true;
1948 // Load master heightmap
1949 loadMasterHeightmap();
1951 // Load sector (0,0) and throw and exception on fail
1952 if(loadSectorFull(v2s16(0,0)) == false)
1953 throw LoadError("Failed to load sector (0,0)");
1955 dstream<<DTIME<<"Server: Successfully loaded master "
1956 "heightmap and sector (0,0) from "<<savedir<<
1957 ", assuming valid save directory."
1960 m_map_saving_enabled = true;
1961 // Map loaded, not creating new one
1965 // If directory doesn't exist, it is safe to save to it
1967 m_map_saving_enabled = true;
1970 catch(std::exception &e)
1972 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1973 <<", exception: "<<e.what()<<std::endl;
1974 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1975 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1978 dstream<<DTIME<<"Initializing new map."<<std::endl;
1980 // Create master heightmap
1981 m_heightmap = new UnlimitedHeightmap
1984 // Set map parameters
1987 // Create zero sector
1988 emergeSector(v2s16(0,0));
1990 // Initially write whole map
1994 ServerMap::~ServerMap()
1998 if(m_map_saving_enabled)
2001 // Save only changed parts
2003 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2007 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2010 catch(std::exception &e)
2012 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2013 <<", exception: "<<e.what()<<std::endl;
2016 if(m_heightmap != NULL)
2022 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2023 for(; i.atEnd() == false; i++)
2025 MapChunk *chunk = i.getNode()->getValue();
2031 Some helper functions
2034 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2036 v3s16 em = vmanip.m_area.getExtent();
2037 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2038 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2039 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2041 for(y=y_nodes_max; y>=y_nodes_min; y--)
2043 MapNode &n = vmanip.m_data[i];
2044 if(content_walkable(n.d))
2047 vmanip.m_area.add_y(em, i, -1);
2049 if(y >= y_nodes_min)
2055 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2057 MapNode treenode(CONTENT_TREE);
2058 MapNode leavesnode(CONTENT_LEAVES);
2060 s16 trunk_h = myrand_range(2, 6);
2062 for(s16 ii=0; ii<trunk_h; ii++)
2064 if(vmanip.m_area.contains(p1))
2065 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2069 // p1 is now the last piece of the trunk
2072 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2073 SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2074 for(s32 i=0; i<leaves_a.getVolume(); i++)
2077 // Force leaves at near the end of the trunk
2080 for(s16 z=-d; z<=d; z++)
2081 for(s16 y=-d; y<=d; y++)
2082 for(s16 x=-d; x<=d; x++)
2084 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2088 // Add leaves randomly
2089 for(u32 iii=0; iii<7; iii++)
2094 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2095 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2096 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2099 for(s16 z=0; z<=d; z++)
2100 for(s16 y=0; y<=d; y++)
2101 for(s16 x=0; x<=d; x++)
2103 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2107 // Blit leaves to vmanip
2108 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2109 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2110 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2114 if(vmanip.m_area.contains(p) == false)
2116 u32 vi = vmanip.m_area.index(p);
2117 if(vmanip.m_data[vi].d != CONTENT_AIR)
2119 u32 i = leaves_a.index(x,y,z);
2120 if(leaves_d[i] == 1)
2121 vmanip.m_data[vi] = leavesnode;
2125 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2126 core::map<v3s16, MapBlock*> &changed_blocks)
2129 Don't generate if already fully generated
2132 MapChunk *chunk = getChunk(chunkpos);
2133 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2135 dstream<<"generateChunkRaw(): Chunk "
2136 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2137 <<" already generated"<<std::endl;
2142 dstream<<"generateChunkRaw(): Generating chunk "
2143 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2146 TimeTaker timer("generateChunkRaw()");
2148 // The distance how far into the neighbors the generator is allowed to go
2149 s16 max_spread_amount_sectors = 2;
2150 assert(max_spread_amount_sectors <= m_chunksize);
2151 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2152 // Minimum amount of space left on sides for mud to fall in
2153 s16 min_mud_fall_space = 2;
2154 // Maximum diameter of stone obstacles in X and Z
2155 s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2156 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2158 s16 y_blocks_min = -4;
2159 s16 y_blocks_max = 3;
2160 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2161 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2162 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2164 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2165 s16 sectorpos_base_size = m_chunksize;
2167 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2168 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2169 v2s16 sectorpos_bigbase =
2170 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2171 s16 sectorpos_bigbase_size =
2172 sectorpos_base_size + 2 * max_spread_amount_sectors;
2174 v3s16 bigarea_blocks_min(
2175 sectorpos_bigbase.X,
2180 v3s16 bigarea_blocks_max(
2181 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2183 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2186 // Relative values to control amount of stuff in one chunk
2187 u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2188 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;
2189 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2190 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2191 *(u32)h_blocks*MAP_BLOCKSIZE;
2194 Create the whole area of this and the neighboring chunks
2197 TimeTaker timer("generateChunkRaw() create area");
2199 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2200 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2202 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2203 ServerMapSector *sector = createSector(sectorpos);
2206 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2208 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2209 MapBlock *block = createBlock(blockpos);
2211 // Lighting won't be calculated
2212 //block->setLightingExpired(true);
2213 // Lighting will be calculated
2214 block->setLightingExpired(false);
2217 Block gets sunlight if this is true.
2219 This should be set to true when the top side of a block
2220 is completely exposed to the sky.
2222 Actually this doesn't matter now because the
2223 initial lighting is done here.
2225 block->setIsUnderground(y != y_blocks_max);
2231 Now we have a big empty area.
2233 Make a ManualMapVoxelManipulator that contains this and the
2237 ManualMapVoxelManipulator vmanip(this);
2238 // Add the area we just generated
2240 TimeTaker timer("generateChunkRaw() initialEmerge");
2241 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2244 TimeTaker timer_generate("generateChunkRaw() generate");
2247 Generate general ground level to full area
2252 //TimeTaker timer1("ground level");
2254 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2255 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2258 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2261 Skip of already generated
2264 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2265 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2269 // Ground height at this point
2270 float surface_y_f = 0.0;
2272 A hack to get the ground height from the sector.
2276 v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
2277 v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
2278 MapSector *sector = getSectorNoGenerate(sectorpos);
2280 float h = sector->getGroundHeight(sector_relpos);
2281 if(h > GROUNDHEIGHT_VALID_MINVALUE)
2284 dstream<<"WARNING: "<<__FUNCTION_NAME
2285 <<": sector->getGroundHeight returned bad height"<<std::endl;
2287 // Convert to integer
2288 s16 surface_y = (s16)surface_y_f;
2291 Fill ground with stone
2294 // Use fast index incrementing
2295 v3s16 em = vmanip.m_area.getExtent();
2296 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2297 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2299 vmanip.m_data[i].d = CONTENT_STONE;
2301 vmanip.m_area.add_y(em, i, 1);
2309 Randomize some parameters
2312 u32 stone_obstacle_amount =
2313 myrand_range(0, myrand_range(20, myrand_range(80,150)));
2316 Loop this part, it will make stuff look older and newer nicely
2319 for(u32 i_age=0; i_age<2; i_age++)
2322 // This is set during the next operation.
2323 // Maximum height of the stone surface and obstacles.
2324 // This is used to disable dungeon generation from going too high.
2325 s16 stone_surface_max_y = 0;
2329 //TimeTaker timer1("stone obstacles");
2332 Add some random stone obstacles
2335 for(u32 ri=0; ri<stone_obstacle_amount/3; ri++)
2336 //for(u32 ri=0; ri<7; ri++)
2339 // Randomize max height so usually stuff will be quite low
2340 //s16 maxheight_randomized = myrand_range(0, 25);
2341 s16 maxheight_randomized = myrand_range(0, stone_obstacle_amount/3);
2343 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2345 myrand_range(5, stone_obstacle_max_size),
2346 myrand_range(0, maxheight_randomized),
2347 myrand_range(5, stone_obstacle_max_size)
2350 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2351 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2354 // Minimum space left on top of the obstacle
2355 s16 min_head_space = 10;
2357 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2358 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2360 // Node position in 2d
2361 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2363 // Find stone ground level
2364 // (ignore everything else than mud in already generated chunks)
2365 // and mud amount over the stone level
2369 v3s16 em = vmanip.m_area.getExtent();
2370 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2372 // Go to ground level
2373 for(y=y_nodes_max; y>=y_nodes_min; y--)
2375 MapNode *n = &vmanip.m_data[i];
2376 /*if(content_walkable(n.d)
2377 && n.d != CONTENT_MUD
2378 && n.d != CONTENT_GRASS)
2380 if(n->d == CONTENT_STONE)
2383 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2387 Change to mud because otherwise we might
2388 be throwing mud on grass at the next
2394 vmanip.m_area.add_y(em, i, -1);
2396 if(y >= y_nodes_min)
2399 surface_y = y_nodes_min;
2407 v3s16 em = vmanip.m_area.getExtent();
2408 s16 y_start = surface_y+1;
2409 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2413 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2415 MapNode &n = vmanip.m_data[i];
2416 n.d = CONTENT_STONE;
2418 if(y > stone_surface_max_y)
2419 stone_surface_max_y = y;
2422 if(count >= ob_size.Y)
2425 vmanip.m_area.add_y(em, i, 1);
2429 for(; y<=y_nodes_max; y++)
2431 MapNode &n = vmanip.m_data[i];
2434 if(count >= mud_amount)
2437 vmanip.m_area.add_y(em, i, 1);
2447 //TimeTaker timer1("dungeons");
2452 u32 dungeons_count = relative_volume / 200000;
2453 u32 bruises_count = relative_volume * stone_surface_max_y / 15000000;
2454 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2456 s16 min_tunnel_diameter = 1;
2457 s16 max_tunnel_diameter = 5;
2458 u16 tunnel_routepoints = 10;
2460 bool bruise_surface = (jj < bruises_count);
2464 min_tunnel_diameter = 5;
2465 max_tunnel_diameter = myrand_range(8, 20);
2466 tunnel_routepoints = 7;
2469 // Allowed route area size in nodes
2471 sectorpos_base_size*MAP_BLOCKSIZE,
2472 h_blocks*MAP_BLOCKSIZE,
2473 sectorpos_base_size*MAP_BLOCKSIZE
2476 // Area starting point in nodes
2478 sectorpos_base.X*MAP_BLOCKSIZE,
2479 y_blocks_min*MAP_BLOCKSIZE,
2480 sectorpos_base.Y*MAP_BLOCKSIZE
2484 //(this should be more than the maximum radius of the tunnel)
2485 //s16 insure = 5; // Didn't work with max_d = 20
2487 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2488 ar += v3s16(1,0,1) * more * 2;
2489 of -= v3s16(1,0,1) * more;
2491 s16 route_y_min = 0;
2492 //s16 route_y_max = ar.Y-1;
2493 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
2494 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2498 /*// Minimum is at y=0
2499 route_y_min = -of.Y - 0;*/
2500 // Minimum is at y=max_tunnel_diameter/4
2501 //route_y_min = -of.Y + max_tunnel_diameter/4;
2502 //s16 min = -of.Y + max_tunnel_diameter/4;
2503 s16 min = -of.Y + 0;
2504 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2505 route_y_min = rangelim(route_y_min, 0, route_y_max);
2508 /*dstream<<"route_y_min = "<<route_y_min
2509 <<", route_y_max = "<<route_y_max<<std::endl;*/
2511 // Randomize starting position
2513 (float)(myrand()%ar.X)+0.5,
2514 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2515 (float)(myrand()%ar.Z)+0.5
2518 MapNode airnode(CONTENT_AIR);
2521 Generate some tunnel starting from orp
2524 for(u16 j=0; j<tunnel_routepoints; j++)
2526 v3s16 maxlen(20, 10, 20);
2530 maxlen = v3s16(60,60,60);
2534 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2535 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2536 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2541 else if(rp.X >= ar.X)
2543 if(rp.Y < route_y_min)
2545 else if(rp.Y >= route_y_max)
2546 rp.Y = route_y_max-1;
2549 else if(rp.Z >= ar.Z)
2554 s16 min_d = min_tunnel_diameter;
2555 s16 max_d = max_tunnel_diameter;
2556 s16 rs = myrand_range(min_d, max_d);
2558 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2560 v3f fp = orp + vec * f;
2561 v3s16 cp(fp.X, fp.Y, fp.Z);
2564 s16 d1 = d0 + rs - 1;
2565 for(s16 z0=d0; z0<=d1; z0++)
2567 s16 si = rs - abs(z0);
2568 for(s16 x0=-si; x0<=si-1; x0++)
2570 s16 maxabsxz = abs(x0)>abs(z0)?abs(x0):abs(z0);
2571 s16 si2 = rs - maxabsxz;
2572 //s16 si2 = rs - abs(x0);
2573 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2579 /*if(isInArea(p, ar) == false)
2581 // Check only height
2582 if(y < 0 || y >= ar.Y)
2586 //assert(vmanip.m_area.contains(p));
2587 if(vmanip.m_area.contains(p) == false)
2589 dstream<<"WARNING: "<<__FUNCTION_NAME
2590 <<":"<<__LINE__<<": "
2591 <<"point not in area"
2596 // Just set it to air, it will be changed to
2598 u32 i = vmanip.m_area.index(p);
2599 vmanip.m_data[i] = airnode;
2613 //TimeTaker timer1("ore veins");
2618 for(u32 jj=0; jj<relative_volume/524; jj++)
2620 s16 max_vein_diameter = 3;
2622 // Allowed route area size in nodes
2624 sectorpos_base_size*MAP_BLOCKSIZE,
2625 h_blocks*MAP_BLOCKSIZE,
2626 sectorpos_base_size*MAP_BLOCKSIZE
2629 // Area starting point in nodes
2631 sectorpos_base.X*MAP_BLOCKSIZE,
2632 y_blocks_min*MAP_BLOCKSIZE,
2633 sectorpos_base.Y*MAP_BLOCKSIZE
2637 //(this should be more than the maximum radius of the tunnel)
2639 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2640 ar += v3s16(1,0,1) * more * 2;
2641 of -= v3s16(1,0,1) * more;
2643 // Randomize starting position
2645 (float)(myrand()%ar.X)+0.5,
2646 (float)(myrand()%ar.Y)+0.5,
2647 (float)(myrand()%ar.Z)+0.5
2650 // Randomize mineral
2651 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2654 Generate some vein starting from orp
2657 for(u16 j=0; j<2; j++)
2660 (float)(myrand()%ar.X)+0.5,
2661 (float)(myrand()%ar.Y)+0.5,
2662 (float)(myrand()%ar.Z)+0.5
2664 v3f vec = rp - orp;*/
2666 v3s16 maxlen(10, 10, 10);
2668 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2669 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2670 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2675 else if(rp.X >= ar.X)
2679 else if(rp.Y >= ar.Y)
2683 else if(rp.Z >= ar.Z)
2689 s16 max_d = max_vein_diameter;
2690 s16 rs = myrand_range(min_d, max_d);
2692 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2694 v3f fp = orp + vec * f;
2695 v3s16 cp(fp.X, fp.Y, fp.Z);
2697 s16 d1 = d0 + rs - 1;
2698 for(s16 z0=d0; z0<=d1; z0++)
2700 s16 si = rs - abs(z0);
2701 for(s16 x0=-si; x0<=si-1; x0++)
2703 s16 si2 = rs - abs(x0);
2704 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2706 // Don't put mineral to every place
2714 /*if(isInArea(p, ar) == false)
2716 // Check only height
2717 if(y < 0 || y >= ar.Y)
2721 assert(vmanip.m_area.contains(p));
2723 // Just set it to air, it will be changed to
2725 u32 i = vmanip.m_area.index(p);
2726 MapNode *n = &vmanip.m_data[i];
2727 if(n->d == CONTENT_STONE)
2742 //TimeTaker timer1("add mud");
2745 Add mud to the central chunk
2748 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2749 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2751 // Node position in 2d
2752 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2754 // Find ground level
2755 s16 surface_y = find_ground_level(vmanip, p2d);
2758 If topmost node is grass, change it to mud.
2759 It might be if it was flown to there from a neighboring
2760 chunk and then converted.
2763 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2764 MapNode *n = &vmanip.m_data[i];
2765 if(n->d == CONTENT_GRASS)
2774 v3s16 em = vmanip.m_area.getExtent();
2775 s16 y_start = surface_y+1;
2776 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2777 for(s16 y=y_start; y<=y_nodes_max; y++)
2779 MapNode &n = vmanip.m_data[i];
2785 vmanip.m_area.add_y(em, i, 1);
2794 //TimeTaker timer1("flow mud");
2797 Flow mud away from steep edges
2800 // Iterate a few times
2801 for(s16 k=0; k<4; k++)
2804 for(s16 x=0-max_spread_amount+1;
2805 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2807 for(s16 z=0-max_spread_amount+1;
2808 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2811 // Node position in 2d
2812 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2814 v3s16 em = vmanip.m_area.getExtent();
2815 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2817 // Go to ground level
2818 for(y=y_nodes_max; y>=y_nodes_min; y--)
2820 MapNode &n = vmanip.m_data[i];
2821 //if(n.d != CONTENT_AIR)
2822 if(content_walkable(n.d))
2825 vmanip.m_area.add_y(em, i, -1);
2828 // If not mud, do nothing to it
2829 MapNode *n = &vmanip.m_data[i];
2830 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2833 // Make it exactly mud
2837 v3s16(0,0,1), // back
2838 v3s16(1,0,0), // right
2839 v3s16(0,0,-1), // front
2840 v3s16(-1,0,0), // left
2845 for(u32 di=0; di<4; di++)
2847 v3s16 dirp = dirs4[di];
2849 // Check that side is air
2850 vmanip.m_area.add_p(em, i2, dirp);
2851 MapNode *n2 = &vmanip.m_data[i2];
2852 if(content_walkable(n2->d))
2854 // Check that under side is air
2855 vmanip.m_area.add_y(em, i2, -1);
2856 n2 = &vmanip.m_data[i2];
2857 if(content_walkable(n2->d))
2859 // Loop further down until not air
2861 vmanip.m_area.add_y(em, i2, -1);
2862 n2 = &vmanip.m_data[i2];
2863 }while(content_walkable(n2->d) == false);
2864 // Loop one up so that we're in air
2865 vmanip.m_area.add_y(em, i2, 1);
2866 n2 = &vmanip.m_data[i2];
2868 // Move mud to new place
2870 // Set old place to be air
2871 *n = MapNode(CONTENT_AIR);
2874 // Switch mud and other and change mud source to air
2875 //MapNode tempnode = *n2;
2878 // Force old mud position to be air
2892 //TimeTaker timer1("add water");
2895 Add water to the central chunk (and a bit more)
2898 for(s16 x=0-max_spread_amount;
2899 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2901 for(s16 z=0-max_spread_amount;
2902 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2905 // Node position in 2d
2906 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2908 // Find ground level
2909 //s16 surface_y = find_ground_level(vmanip, p2d);
2912 If ground level is over water level, skip.
2913 NOTE: This leaves caves near water without water,
2914 which looks especially crappy when the nearby water
2915 won't start flowing either for some reason
2917 /*if(surface_y > WATER_LEVEL)
2924 v3s16 em = vmanip.m_area.getExtent();
2925 s16 y_start = WATER_LEVEL;
2926 u8 light = LIGHT_MAX;
2927 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2928 MapNode *n = &vmanip.m_data[i];
2930 Add first one to transforming liquid queue
2932 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2934 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2935 m_transforming_liquid.push_back(p);
2937 for(s16 y=y_start; y>=y_nodes_min; y--)
2939 n = &vmanip.m_data[i];
2941 // Stop when there is no water and no air
2942 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2943 && n->d != CONTENT_WATER)
2946 Add bottom one to transforming liquid queue
2948 vmanip.m_area.add_y(em, i, 1);
2949 n = &vmanip.m_data[i];
2950 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2952 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2953 m_transforming_liquid.push_back(p);
2959 n->d = CONTENT_WATERSOURCE;
2960 n->setLight(LIGHTBANK_DAY, light);
2962 /*// Add to transforming liquid queue (in case it'd
2964 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2965 m_transforming_liquid.push_back(p);*/
2968 vmanip.m_area.add_y(em, i, -1);
2982 //TimeTaker timer1("plant trees");
2988 u32 tree_max = relative_area / 60;
2990 u32 count = myrand_range(0, tree_max);
2991 for(u32 i=0; i<count; i++)
2993 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2994 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2995 x += sectorpos_base.X*MAP_BLOCKSIZE;
2996 z += sectorpos_base.Y*MAP_BLOCKSIZE;
2997 s16 y = find_ground_level(vmanip, v2s16(x,z));
2998 // Don't make a tree under water level
3003 make_tree(vmanip, p);
3011 //TimeTaker timer1("grow grass");
3017 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3018 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3019 for(s16 x=0-max_spread_amount;
3020 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3022 for(s16 z=0-max_spread_amount;
3023 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3026 // Node position in 2d
3027 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3030 Find the lowest surface to which enough light ends up
3033 Basically just wait until not air and not leaves.
3037 v3s16 em = vmanip.m_area.getExtent();
3038 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3040 // Go to ground level
3041 for(y=y_nodes_max; y>=y_nodes_min; y--)
3043 MapNode &n = vmanip.m_data[i];
3044 if(n.d != CONTENT_AIR
3045 && n.d != CONTENT_LEAVES)
3047 vmanip.m_area.add_y(em, i, -1);
3049 if(y >= y_nodes_min)
3052 surface_y = y_nodes_min;
3055 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3056 MapNode *n = &vmanip.m_data[i];
3057 if(n->d == CONTENT_MUD)
3058 n->d = CONTENT_GRASS;
3067 core::map<v3s16, bool> light_sources;
3070 // 750ms @cs=8, can't optimize more
3071 //TimeTaker timer1("initial lighting");
3073 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3074 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3075 for(s16 x=0-max_spread_amount+1;
3076 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3078 for(s16 z=0-max_spread_amount+1;
3079 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3082 // Node position in 2d
3083 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3086 Apply initial sunlight
3089 u8 light = LIGHT_SUN;
3090 bool add_to_sources = false;
3091 v3s16 em = vmanip.m_area.getExtent();
3092 s16 y_start = y_nodes_max;
3093 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3094 for(s16 y=y_start; y>=y_nodes_min; y--)
3096 MapNode *n = &vmanip.m_data[i];
3098 if(light_propagates_content(n->d) == false)
3102 else if(light != LIGHT_SUN
3103 || sunlight_propagates_content(n->d) == false)
3109 // This doesn't take much time
3110 if(add_to_sources == false)
3113 Check sides. If side is not air or water, start
3114 adding to light_sources.
3117 v3s16(0,0,1), // back
3118 v3s16(1,0,0), // right
3119 v3s16(0,0,-1), // front
3120 v3s16(-1,0,0), // left
3122 for(u32 di=0; di<4; di++)
3124 v3s16 dirp = dirs4[di];
3126 vmanip.m_area.add_p(em, i2, dirp);
3127 MapNode *n2 = &vmanip.m_data[i2];
3129 n2->d != CONTENT_AIR
3130 && n2->d != CONTENT_WATERSOURCE
3131 && n2->d != CONTENT_WATER
3133 add_to_sources = true;
3139 n->setLight(LIGHTBANK_DAY, light);
3140 n->setLight(LIGHTBANK_NIGHT, 0);
3142 // This doesn't take much time
3143 if(light != 0 && add_to_sources)
3145 // Insert light source
3146 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3149 // Increment index by y
3150 vmanip.m_area.add_y(em, i, -1);
3157 // Spread light around
3159 TimeTaker timer("generateChunkRaw() spreadLight");
3160 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3167 timer_generate.stop();
3170 Blit generated stuff to map
3174 //TimeTaker timer("generateChunkRaw() blitBackAll");
3175 vmanip.blitBackAll(&changed_blocks);
3178 Update day/night difference cache of the MapBlocks
3181 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3182 i.atEnd() == false; i++)
3184 MapBlock *block = i.getNode()->getValue();
3185 block->updateDayNightDiff();
3191 Create chunk metadata
3194 for(s16 x=-1; x<=1; x++)
3195 for(s16 y=-1; y<=1; y++)
3197 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3198 // Add chunk meta information
3199 MapChunk *chunk = getChunk(chunkpos0);
3202 chunk = new MapChunk();
3203 m_chunks.insert(chunkpos0, chunk);
3205 //chunk->setIsVolatile(true);
3206 if(chunk->getGenLevel() > GENERATED_PARTLY)
3207 chunk->setGenLevel(GENERATED_PARTLY);
3211 Set central chunk non-volatile and return it
3213 MapChunk *chunk = getChunk(chunkpos);
3216 //chunk->setIsVolatile(false);
3217 chunk->setGenLevel(GENERATED_FULLY);
3222 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3223 core::map<v3s16, MapBlock*> &changed_blocks)
3225 dstream<<"generateChunk(): Generating chunk "
3226 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3229 /*for(s16 x=-1; x<=1; x++)
3230 for(s16 y=-1; y<=1; y++)*/
3231 for(s16 x=-0; x<=0; x++)
3232 for(s16 y=-0; y<=0; y++)
3234 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3235 MapChunk *chunk = getChunk(chunkpos0);
3236 // Skip if already generated
3237 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3239 generateChunkRaw(chunkpos0, changed_blocks);
3242 assert(chunkNonVolatile(chunkpos1));
3244 MapChunk *chunk = getChunk(chunkpos1);
3248 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3250 DSTACK("%s: p2d=(%d,%d)",
3255 Check if it exists already in memory
3257 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3262 Try to load it from disk (with blocks)
3264 if(loadSectorFull(p2d) == true)
3266 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3269 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3270 throw InvalidPositionException("");
3276 If there is no master heightmap, throw.
3278 if(m_heightmap == NULL)
3280 throw InvalidPositionException("createSector(): no heightmap");
3284 Do not create over-limit
3286 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3287 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3288 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3289 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3290 throw InvalidPositionException("createSector(): pos. over limit");
3293 Generate blank sector
3296 // Number of heightmaps in sector in each direction
3297 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3299 // Heightmap side width
3300 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3302 sector = new ServerMapSector(this, p2d, hm_split);
3304 // Sector position on map in nodes
3305 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3307 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3308 " heightmaps and objects"<<std::endl;*/
3311 Generate sector heightmap
3314 v2s16 mhm_p = p2d * hm_split;
3315 /*f32 corners[4] = {
3316 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3317 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3318 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3319 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3322 // Loop through sub-heightmaps
3323 for(s16 y=0; y<hm_split; y++)
3324 for(s16 x=0; x<hm_split; x++)
3326 v2s16 p_in_sector = v2s16(x,y);
3327 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3329 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3330 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3331 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3332 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3335 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3336 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3339 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3341 sector->setHeightmap(p_in_sector, hm);
3343 //hm->generateContinued(1.0, 0.5, corners);
3344 hm->generateContinued(0.5, 0.5, corners);
3349 // Add dummy objects
3350 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3351 sector->setObjects(objects);
3356 m_sectors.insert(p2d, sector);
3361 MapSector * ServerMap::emergeSector(v2s16 p2d,
3362 core::map<v3s16, MapBlock*> &changed_blocks)
3364 DSTACK("%s: p2d=(%d,%d)",
3371 v2s16 chunkpos = sector_to_chunk(p2d);
3372 /*bool chunk_nonvolatile = false;
3373 MapChunk *chunk = getChunk(chunkpos);
3374 if(chunk && chunk->getIsVolatile() == false)
3375 chunk_nonvolatile = true;*/
3376 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3379 If chunk is not fully generated, generate chunk
3381 if(chunk_nonvolatile == false)
3383 // Generate chunk and neighbors
3384 generateChunk(chunkpos, changed_blocks);
3388 Return sector if it exists now
3390 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3395 Try to load it from disk
3397 if(loadSectorFull(p2d) == true)
3399 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3402 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3403 throw InvalidPositionException("");
3409 generateChunk should have generated the sector
3416 //return generateSector();
3420 NOTE: This is not used for main map generation, only for blocks
3421 that are very high or low
3423 MapBlock * ServerMap::generateBlock(
3425 MapBlock *original_dummy,
3426 ServerMapSector *sector,
3427 core::map<v3s16, MapBlock*> &changed_blocks,
3428 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3431 DSTACK("%s: p=(%d,%d,%d)",
3435 /*dstream<<"generateBlock(): "
3436 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3439 MapBlock *block = original_dummy;
3441 v2s16 p2d(p.X, p.Z);
3445 Do not generate over-limit
3447 if(blockpos_over_limit(p))
3449 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3450 throw InvalidPositionException("generateBlock(): pos. over limit");
3454 If block doesn't exist, create one.
3455 If it exists, it is a dummy. In that case unDummify() it.
3457 NOTE: This already sets the map as the parent of the block
3461 block = sector->createBlankBlockNoInsert(block_y);
3465 // Remove the block so that nobody can get a half-generated one.
3466 sector->removeBlock(block);
3467 // Allocate the block to contain the generated data
3471 /*u8 water_material = CONTENT_WATER;
3472 if(g_settings.getBool("endless_water"))
3473 water_material = CONTENT_WATERSOURCE;*/
3474 u8 water_material = CONTENT_WATERSOURCE;
3476 s32 lowest_ground_y = 32767;
3477 s32 highest_ground_y = -32768;
3480 //sector->printHeightmaps();
3482 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3483 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3485 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3487 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3488 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3489 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3491 dstream<<"WARNING: Surface height not found in sector "
3492 "for block that is being emerged"<<std::endl;
3496 s16 surface_y = surface_y_f;
3497 //avg_ground_y += surface_y;
3498 if(surface_y < lowest_ground_y)
3499 lowest_ground_y = surface_y;
3500 if(surface_y > highest_ground_y)
3501 highest_ground_y = surface_y;
3503 s32 surface_depth = 0;
3505 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3507 //float min_slope = 0.45;
3508 //float max_slope = 0.85;
3509 float min_slope = 0.60;
3510 float max_slope = 1.20;
3511 float min_slope_depth = 5.0;
3512 float max_slope_depth = 0;
3514 if(slope < min_slope)
3515 surface_depth = min_slope_depth;
3516 else if(slope > max_slope)
3517 surface_depth = max_slope_depth;
3519 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3521 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3523 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3528 NOTE: If there are some man-made structures above the
3529 newly created block, they won't be taken into account.
3531 if(real_y > surface_y)
3532 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3538 // If node is over heightmap y, it's air or water
3539 if(real_y > surface_y)
3541 // If under water level, it's water
3542 if(real_y < WATER_LEVEL)
3544 n.d = water_material;
3545 n.setLight(LIGHTBANK_DAY,
3546 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3548 Add to transforming liquid queue (in case it'd
3551 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3552 m_transforming_liquid.push_back(real_pos);
3558 // Else it's ground or dungeons (air)
3561 // If it's surface_depth under ground, it's stone
3562 if(real_y <= surface_y - surface_depth)
3564 n.d = CONTENT_STONE;
3568 // It is mud if it is under the first ground
3569 // level or under water
3570 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3576 n.d = CONTENT_GRASS;
3579 //n.d = CONTENT_MUD;
3581 /*// If under water level, it's mud
3582 if(real_y < WATER_LEVEL)
3584 // Only the topmost node is grass
3585 else if(real_y <= surface_y - 1)
3588 n.d = CONTENT_GRASS;*/
3592 block->setNode(v3s16(x0,y0,z0), n);
3597 Calculate some helper variables
3600 // Completely underground if the highest part of block is under lowest
3602 // This has to be very sure; it's probably one too strict now but
3603 // that's just better.
3604 bool completely_underground =
3605 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3607 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3609 bool mostly_underwater_surface = false;
3610 if(highest_ground_y < WATER_LEVEL
3611 && some_part_underground && !completely_underground)
3612 mostly_underwater_surface = true;
3615 Get local attributes
3618 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3620 float caves_amount = 0.5;
3625 NOTE: BEWARE: Too big amount of attribute points slows verything
3627 1 interpolation from 5000 points takes 2-3ms.
3629 //TimeTaker timer("generateBlock() local attribute retrieval");
3630 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3631 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3632 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3636 //dstream<<"generateBlock(): Done"<<std::endl;
3642 // Initialize temporary table
3643 const s32 ued = MAP_BLOCKSIZE;
3644 bool underground_emptiness[ued*ued*ued];
3645 for(s32 i=0; i<ued*ued*ued; i++)
3647 underground_emptiness[i] = 0;
3654 Initialize orp and ors. Try to find if some neighboring
3655 MapBlock has a tunnel ended in its side
3659 (float)(myrand()%ued)+0.5,
3660 (float)(myrand()%ued)+0.5,
3661 (float)(myrand()%ued)+0.5
3664 bool found_existing = false;
3670 for(s16 y=0; y<ued; y++)
3671 for(s16 x=0; x<ued; x++)
3673 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3674 if(getNode(ap).d == CONTENT_AIR)
3676 orp = v3f(x+1,y+1,0);
3677 found_existing = true;
3678 goto continue_generating;
3682 catch(InvalidPositionException &e){}
3688 for(s16 y=0; y<ued; y++)
3689 for(s16 x=0; x<ued; x++)
3691 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3692 if(getNode(ap).d == CONTENT_AIR)
3694 orp = v3f(x+1,y+1,ued-1);
3695 found_existing = true;
3696 goto continue_generating;
3700 catch(InvalidPositionException &e){}
3706 for(s16 y=0; y<ued; y++)
3707 for(s16 z=0; z<ued; z++)
3709 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3710 if(getNode(ap).d == CONTENT_AIR)
3712 orp = v3f(0,y+1,z+1);
3713 found_existing = true;
3714 goto continue_generating;
3718 catch(InvalidPositionException &e){}
3724 for(s16 y=0; y<ued; y++)
3725 for(s16 z=0; z<ued; z++)
3727 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3728 if(getNode(ap).d == CONTENT_AIR)
3730 orp = v3f(ued-1,y+1,z+1);
3731 found_existing = true;
3732 goto continue_generating;
3736 catch(InvalidPositionException &e){}
3742 for(s16 x=0; x<ued; x++)
3743 for(s16 z=0; z<ued; z++)
3745 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3746 if(getNode(ap).d == CONTENT_AIR)
3748 orp = v3f(x+1,0,z+1);
3749 found_existing = true;
3750 goto continue_generating;
3754 catch(InvalidPositionException &e){}
3760 for(s16 x=0; x<ued; x++)
3761 for(s16 z=0; z<ued; z++)
3763 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3764 if(getNode(ap).d == CONTENT_AIR)
3766 orp = v3f(x+1,ued-1,z+1);
3767 found_existing = true;
3768 goto continue_generating;
3772 catch(InvalidPositionException &e){}
3774 continue_generating:
3777 Choose whether to actually generate dungeon
3779 bool do_generate_dungeons = true;
3780 // Don't generate if no part is underground
3781 if(!some_part_underground)
3783 do_generate_dungeons = false;
3785 // Don't generate if mostly underwater surface
3786 /*else if(mostly_underwater_surface)
3788 do_generate_dungeons = false;
3790 // Partly underground = cave
3791 else if(!completely_underground)
3793 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3795 // Found existing dungeon underground
3796 else if(found_existing && completely_underground)
3798 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3800 // Underground and no dungeons found
3803 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3806 if(do_generate_dungeons)
3809 Generate some tunnel starting from orp and ors
3811 for(u16 i=0; i<3; i++)
3814 (float)(myrand()%ued)+0.5,
3815 (float)(myrand()%ued)+0.5,
3816 (float)(myrand()%ued)+0.5
3820 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3824 for(float f=0; f<1.0; f+=0.04)
3826 v3f fp = orp + vec * f;
3827 v3s16 cp(fp.X, fp.Y, fp.Z);
3829 s16 d1 = d0 + rs - 1;
3830 for(s16 z0=d0; z0<=d1; z0++)
3832 s16 si = rs - abs(z0);
3833 for(s16 x0=-si; x0<=si-1; x0++)
3835 s16 si2 = rs - abs(x0);
3836 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3842 if(isInArea(p, ued) == false)
3844 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3856 // Set to true if has caves.
3857 // Set when some non-air is changed to air when making caves.
3858 bool has_dungeons = false;
3861 Apply temporary cave data to block
3864 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3865 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3867 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3869 MapNode n = block->getNode(v3s16(x0,y0,z0));
3872 if(underground_emptiness[
3873 ued*ued*(z0*ued/MAP_BLOCKSIZE)
3874 +ued*(y0*ued/MAP_BLOCKSIZE)
3875 +(x0*ued/MAP_BLOCKSIZE)])
3877 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
3880 has_dungeons = true;
3886 block->setNode(v3s16(x0,y0,z0), n);
3891 This is used for guessing whether or not the block should
3892 receive sunlight from the top if the block above doesn't exist
3894 block->setIsUnderground(completely_underground);
3897 Force lighting update if some part of block is partly
3898 underground and has caves.
3900 /*if(some_part_underground && !completely_underground && has_dungeons)
3902 //dstream<<"Half-ground caves"<<std::endl;
3903 lighting_invalidated_blocks[block->getPos()] = block;
3906 // DEBUG: Always update lighting
3907 //lighting_invalidated_blocks[block->getPos()] = block;
3913 if(some_part_underground)
3915 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
3920 for(s16 i=0; i<underground_level/4 + 1; i++)
3922 if(myrand()%50 == 0)
3925 (myrand()%(MAP_BLOCKSIZE-2))+1,
3926 (myrand()%(MAP_BLOCKSIZE-2))+1,
3927 (myrand()%(MAP_BLOCKSIZE-2))+1
3933 for(u16 i=0; i<27; i++)
3935 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3937 block->setNode(cp+g_27dirs[i], n);
3945 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
3946 u16 coal_rareness = 60 / coal_amount;
3947 if(coal_rareness == 0)
3949 if(myrand()%coal_rareness == 0)
3951 u16 a = myrand() % 16;
3952 u16 amount = coal_amount * a*a*a / 1000;
3953 for(s16 i=0; i<amount; i++)
3956 (myrand()%(MAP_BLOCKSIZE-2))+1,
3957 (myrand()%(MAP_BLOCKSIZE-2))+1,
3958 (myrand()%(MAP_BLOCKSIZE-2))+1
3962 n.d = CONTENT_STONE;
3963 n.param = MINERAL_COAL;
3965 for(u16 i=0; i<27; i++)
3967 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3969 block->setNode(cp+g_27dirs[i], n);
3977 //TODO: change to iron_amount or whatever
3978 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
3979 u16 iron_rareness = 60 / iron_amount;
3980 if(iron_rareness == 0)
3982 if(myrand()%iron_rareness == 0)
3984 u16 a = myrand() % 16;
3985 u16 amount = iron_amount * a*a*a / 1000;
3986 for(s16 i=0; i<amount; i++)
3989 (myrand()%(MAP_BLOCKSIZE-2))+1,
3990 (myrand()%(MAP_BLOCKSIZE-2))+1,
3991 (myrand()%(MAP_BLOCKSIZE-2))+1
3995 n.d = CONTENT_STONE;
3996 n.param = MINERAL_IRON;
3998 for(u16 i=0; i<27; i++)
4000 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4002 block->setNode(cp+g_27dirs[i], n);
4009 Create a few rats in empty blocks underground
4011 if(completely_underground)
4013 //for(u16 i=0; i<2; i++)
4016 (myrand()%(MAP_BLOCKSIZE-2))+1,
4017 (myrand()%(MAP_BLOCKSIZE-2))+1,
4018 (myrand()%(MAP_BLOCKSIZE-2))+1
4021 // Check that the place is empty
4022 //if(!is_ground_content(block->getNode(cp).d))
4025 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4026 block->addObject(obj);
4032 Add block to sector.
4034 sector->insertBlock(block);
4040 // An y-wise container of changed blocks
4041 core::map<s16, MapBlock*> changed_blocks_sector;
4044 Check if any sector's objects can be placed now.
4047 core::map<v3s16, u8> *objects = sector->getObjects();
4048 core::list<v3s16> objects_to_remove;
4049 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
4050 i.atEnd() == false; i++)
4052 v3s16 p = i.getNode()->getKey();
4054 u8 d = i.getNode()->getValue();
4056 // Ground level point (user for stuff that is on ground)
4058 bool ground_found = true;
4060 // Search real ground level
4064 MapNode n = sector->getNode(gp);
4066 // If not air, go one up and continue to placing the tree
4067 if(n.d != CONTENT_AIR)
4073 // If air, go one down
4074 gp += v3s16(0,-1,0);
4076 }catch(InvalidPositionException &e)
4078 // Ground not found.
4079 ground_found = false;
4080 // This is most close to ground
4087 if(d == SECTOR_OBJECT_TEST)
4089 if(sector->isValidArea(p + v3s16(0,0,0),
4090 p + v3s16(0,0,0), &changed_blocks_sector))
4093 n.d = CONTENT_TORCH;
4094 sector->setNode(p, n);
4095 objects_to_remove.push_back(p);
4098 else if(d == SECTOR_OBJECT_TREE_1)
4100 if(ground_found == false)
4103 v3s16 p_min = gp + v3s16(-1,0,-1);
4104 v3s16 p_max = gp + v3s16(1,5,1);
4105 if(sector->isValidArea(p_min, p_max,
4106 &changed_blocks_sector))
4110 sector->setNode(gp+v3s16(0,0,0), n);
4111 sector->setNode(gp+v3s16(0,1,0), n);
4112 sector->setNode(gp+v3s16(0,2,0), n);
4113 sector->setNode(gp+v3s16(0,3,0), n);
4115 n.d = CONTENT_LEAVES;
4117 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4119 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4120 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4121 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4122 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4123 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4124 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4125 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4126 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4128 sector->setNode(gp+v3s16(0,4,0), n);
4130 sector->setNode(gp+v3s16(-1,4,0), n);
4131 sector->setNode(gp+v3s16(1,4,0), n);
4132 sector->setNode(gp+v3s16(0,4,-1), n);
4133 sector->setNode(gp+v3s16(0,4,1), n);
4134 sector->setNode(gp+v3s16(1,4,1), n);
4135 sector->setNode(gp+v3s16(-1,4,1), n);
4136 sector->setNode(gp+v3s16(-1,4,-1), n);
4137 sector->setNode(gp+v3s16(1,4,-1), n);
4139 sector->setNode(gp+v3s16(-1,3,0), n);
4140 sector->setNode(gp+v3s16(1,3,0), n);
4141 sector->setNode(gp+v3s16(0,3,-1), n);
4142 sector->setNode(gp+v3s16(0,3,1), n);
4143 sector->setNode(gp+v3s16(1,3,1), n);
4144 sector->setNode(gp+v3s16(-1,3,1), n);
4145 sector->setNode(gp+v3s16(-1,3,-1), n);
4146 sector->setNode(gp+v3s16(1,3,-1), n);
4148 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4149 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4150 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4151 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4152 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4153 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4154 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4155 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4157 // Objects are identified by wanted position
4158 objects_to_remove.push_back(p);
4160 // Lighting has to be recalculated for this one.
4161 sector->getBlocksInArea(p_min, p_max,
4162 lighting_invalidated_blocks);
4165 else if(d == SECTOR_OBJECT_BUSH_1)
4167 if(ground_found == false)
4170 if(sector->isValidArea(gp + v3s16(0,0,0),
4171 gp + v3s16(0,0,0), &changed_blocks_sector))
4174 n.d = CONTENT_LEAVES;
4175 sector->setNode(gp+v3s16(0,0,0), n);
4177 // Objects are identified by wanted position
4178 objects_to_remove.push_back(p);
4181 else if(d == SECTOR_OBJECT_RAVINE)
4184 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4185 v3s16 p_max = p + v3s16(6,6,6);
4186 if(sector->isValidArea(p_min, p_max,
4187 &changed_blocks_sector))
4190 n.d = CONTENT_STONE;
4193 s16 depth = maxdepth + (myrand()%10);
4195 s16 minz = -6 - (-2);
4197 for(s16 x=-6; x<=6; x++)
4199 z += -1 + (myrand()%3);
4204 for(s16 y=depth+(myrand()%2); y<=6; y++)
4206 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4209 v3s16 p2 = p + v3s16(x,y,z-2);
4210 //if(is_ground_content(sector->getNode(p2).d))
4211 if(content_features(sector->getNode(p2).d).walkable)
4212 sector->setNode(p2, n);
4215 v3s16 p2 = p + v3s16(x,y,z-1);
4216 if(content_features(sector->getNode(p2).d).walkable)
4217 sector->setNode(p2, n2);
4220 v3s16 p2 = p + v3s16(x,y,z+0);
4221 if(content_features(sector->getNode(p2).d).walkable)
4222 sector->setNode(p2, n2);
4225 v3s16 p2 = p + v3s16(x,y,z+1);
4226 if(content_features(sector->getNode(p2).d).walkable)
4227 sector->setNode(p2, n);
4230 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4231 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4235 objects_to_remove.push_back(p);
4237 // Lighting has to be recalculated for this one.
4238 sector->getBlocksInArea(p_min, p_max,
4239 lighting_invalidated_blocks);
4244 dstream<<"ServerMap::generateBlock(): "
4245 "Invalid heightmap object"
4250 catch(InvalidPositionException &e)
4252 dstream<<"WARNING: "<<__FUNCTION_NAME
4253 <<": while inserting object "<<(int)d
4254 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4255 <<" InvalidPositionException.what()="
4256 <<e.what()<<std::endl;
4257 // This is not too fatal and seems to happen sometimes.
4262 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4263 i != objects_to_remove.end(); i++)
4265 objects->remove(*i);
4269 Translate sector's changed blocks to global changed blocks
4272 for(core::map<s16, MapBlock*>::Iterator
4273 i = changed_blocks_sector.getIterator();
4274 i.atEnd() == false; i++)
4276 MapBlock *block = i.getNode()->getValue();
4278 changed_blocks.insert(block->getPos(), block);
4281 block->setLightingExpired(true);
4288 <<"lighting_invalidated_blocks.size()"
4292 <<" "<<lighting_invalidated_blocks.size()
4293 <<", "<<has_dungeons
4294 <<", "<<completely_underground
4295 <<", "<<some_part_underground
4302 MapBlock * ServerMap::createBlock(v3s16 p)
4304 DSTACK("%s: p=(%d,%d,%d)",
4305 __FUNCTION_NAME, p.X, p.Y, p.Z);
4307 v2s16 p2d(p.X, p.Z);
4310 This will create or load a sector if not found in memory.
4311 If block exists on disk, it will be loaded.
4313 NOTE: On old save formats, this will be slow, as it generates
4314 lighting on blocks for them.
4316 ServerMapSector *sector;
4318 sector = (ServerMapSector*)createSector(p2d);
4319 assert(sector->getId() == MAPSECTOR_SERVER);
4321 /*catch(InvalidPositionException &e)
4323 dstream<<"createBlock: createSector() failed"<<std::endl;
4326 catch(std::exception &e)
4328 dstream<<"createBlock: createSector() failed: "
4329 <<e.what()<<std::endl;
4334 Try to get a block from the sector
4337 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4341 block = sector->createBlankBlock(block_y);
4345 MapBlock * ServerMap::emergeBlock(
4347 bool only_from_disk,
4348 core::map<v3s16, MapBlock*> &changed_blocks,
4349 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4352 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4354 p.X, p.Y, p.Z, only_from_disk);
4356 v2s16 p2d(p.X, p.Z);
4359 This will create or load a sector if not found in memory.
4360 If block exists on disk, it will be loaded.
4362 NOTE: On old save formats, this will be slow, as it generates
4363 lighting on blocks for them.
4365 ServerMapSector *sector;
4367 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4368 assert(sector->getId() == MAPSECTOR_SERVER);
4370 catch(std::exception &e)
4372 dstream<<"emergeBlock: emergeSector() failed: "
4373 <<e.what()<<std::endl;
4378 Try to get a block from the sector
4381 bool does_not_exist = false;
4382 bool lighting_expired = false;
4383 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4387 does_not_exist = true;
4389 else if(block->isDummy() == true)
4391 does_not_exist = true;
4393 else if(block->getLightingExpired())
4395 lighting_expired = true;
4400 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4405 If block was not found on disk and not going to generate a
4406 new one, make sure there is a dummy block in place.
4408 if(only_from_disk && (does_not_exist || lighting_expired))
4410 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4414 // Create dummy block
4415 block = new MapBlock(this, p, true);
4417 // Add block to sector
4418 sector->insertBlock(block);
4424 //dstream<<"Not found on disk, generating."<<std::endl;
4426 //TimeTaker("emergeBlock() generate");
4428 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4431 If the block doesn't exist, generate the block.
4435 block = generateBlock(p, block, sector, changed_blocks,
4436 lighting_invalidated_blocks);
4439 if(lighting_expired)
4441 lighting_invalidated_blocks.insert(p, block);
4445 Initially update sunlight
4449 core::map<v3s16, bool> light_sources;
4450 bool black_air_left = false;
4451 bool bottom_invalid =
4452 block->propagateSunlight(light_sources, true,
4453 &black_air_left, true);
4455 // If sunlight didn't reach everywhere and part of block is
4456 // above ground, lighting has to be properly updated
4457 //if(black_air_left && some_part_underground)
4460 lighting_invalidated_blocks[block->getPos()] = block;
4465 lighting_invalidated_blocks[block->getPos()] = block;
4470 Debug mode operation
4472 bool haxmode = g_settings.getBool("haxmode");
4475 // Don't calculate lighting at all
4476 //lighting_invalidated_blocks.clear();
4482 void ServerMap::createDir(std::string path)
4484 if(fs::CreateDir(path) == false)
4486 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4487 <<"\""<<path<<"\""<<std::endl;
4488 throw BaseException("ServerMap failed to create directory");
4492 std::string ServerMap::getSectorSubDir(v2s16 pos)
4495 snprintf(cc, 9, "%.4x%.4x",
4496 (unsigned int)pos.X&0xffff,
4497 (unsigned int)pos.Y&0xffff);
4499 return std::string(cc);
4502 std::string ServerMap::getSectorDir(v2s16 pos)
4504 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4507 v2s16 ServerMap::getSectorPos(std::string dirname)
4509 if(dirname.size() != 8)
4510 throw InvalidFilenameException("Invalid sector directory name");
4512 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4514 throw InvalidFilenameException("Invalid sector directory name");
4515 v2s16 pos((s16)x, (s16)y);
4519 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4521 v2s16 p2d = getSectorPos(sectordir);
4523 if(blockfile.size() != 4){
4524 throw InvalidFilenameException("Invalid block filename");
4527 int r = sscanf(blockfile.c_str(), "%4x", &y);
4529 throw InvalidFilenameException("Invalid block filename");
4530 return v3s16(p2d.X, y, p2d.Y);
4534 #define ENABLE_SECTOR_SAVING 1
4535 #define ENABLE_SECTOR_LOADING 1
4536 #define ENABLE_BLOCK_SAVING 1
4537 #define ENABLE_BLOCK_LOADING 1
4539 void ServerMap::save(bool only_changed)
4541 DSTACK(__FUNCTION_NAME);
4542 if(m_map_saving_enabled == false)
4544 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4548 if(only_changed == false)
4549 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4552 saveMasterHeightmap();
4554 u32 sector_meta_count = 0;
4555 u32 block_count = 0;
4558 JMutexAutoLock lock(m_sector_mutex);
4560 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4561 for(; i.atEnd() == false; i++)
4563 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4564 assert(sector->getId() == MAPSECTOR_SERVER);
4566 if(ENABLE_SECTOR_SAVING)
4568 if(sector->differs_from_disk || only_changed == false)
4570 saveSectorMeta(sector);
4571 sector_meta_count++;
4574 if(ENABLE_BLOCK_SAVING)
4576 core::list<MapBlock*> blocks;
4577 sector->getBlocks(blocks);
4578 core::list<MapBlock*>::Iterator j;
4579 for(j=blocks.begin(); j!=blocks.end(); j++)
4581 MapBlock *block = *j;
4582 if(block->getChangedFlag() || only_changed == false)
4594 Only print if something happened or saved whole map
4596 if(only_changed == false || sector_meta_count != 0
4597 || block_count != 0)
4599 dstream<<DTIME<<"ServerMap: Written: "
4600 <<sector_meta_count<<" sector metadata files, "
4601 <<block_count<<" block files"
4606 void ServerMap::loadAll()
4608 DSTACK(__FUNCTION_NAME);
4609 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4611 loadMasterHeightmap();
4613 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4615 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4617 JMutexAutoLock lock(m_sector_mutex);
4620 s32 printed_counter = -100000;
4621 s32 count = list.size();
4623 std::vector<fs::DirListNode>::iterator i;
4624 for(i=list.begin(); i!=list.end(); i++)
4626 if(counter > printed_counter + 10)
4628 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4629 printed_counter = counter;
4633 MapSector *sector = NULL;
4635 // We want directories
4639 sector = loadSectorMeta(i->name);
4641 catch(InvalidFilenameException &e)
4643 // This catches unknown crap in directory
4646 if(ENABLE_BLOCK_LOADING)
4648 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4649 (m_savedir+"/sectors/"+i->name);
4650 std::vector<fs::DirListNode>::iterator i2;
4651 for(i2=list2.begin(); i2!=list2.end(); i2++)
4657 loadBlock(i->name, i2->name, sector);
4659 catch(InvalidFilenameException &e)
4661 // This catches unknown crap in directory
4666 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4669 void ServerMap::saveMasterHeightmap()
4671 DSTACK(__FUNCTION_NAME);
4672 createDir(m_savedir);
4674 std::string fullpath = m_savedir + "/master_heightmap";
4675 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4676 if(o.good() == false)
4677 throw FileNotGoodException("Cannot open master heightmap");
4679 // Format used for writing
4680 u8 version = SER_FMT_VER_HIGHEST;
4683 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4685 [0] u8 serialization version
4686 [1] X master heightmap
4688 u32 fullsize = 1 + hmdata.getSize();
4689 SharedBuffer<u8> data(fullsize);
4692 memcpy(&data[1], *hmdata, hmdata.getSize());
4694 o.write((const char*)*data, fullsize);
4697 m_heightmap->serialize(o, version);
4700 void ServerMap::loadMasterHeightmap()
4702 DSTACK(__FUNCTION_NAME);
4703 std::string fullpath = m_savedir + "/master_heightmap";
4704 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4705 if(is.good() == false)
4706 throw FileNotGoodException("Cannot open master heightmap");
4708 if(m_heightmap != NULL)
4711 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4714 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4716 DSTACK(__FUNCTION_NAME);
4717 // Format used for writing
4718 u8 version = SER_FMT_VER_HIGHEST;
4720 v2s16 pos = sector->getPos();
4721 createDir(m_savedir);
4722 createDir(m_savedir+"/sectors");
4723 std::string dir = getSectorDir(pos);
4726 std::string fullpath = dir + "/heightmap";
4727 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4728 if(o.good() == false)
4729 throw FileNotGoodException("Cannot open master heightmap");
4731 sector->serialize(o, version);
4733 sector->differs_from_disk = false;
4736 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4738 DSTACK(__FUNCTION_NAME);
4740 v2s16 p2d = getSectorPos(dirname);
4741 std::string dir = m_savedir + "/sectors/" + dirname;
4743 std::string fullpath = dir + "/heightmap";
4744 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4745 if(is.good() == false)
4746 throw FileNotGoodException("Cannot open sector heightmap");
4748 ServerMapSector *sector = ServerMapSector::deSerialize
4749 (is, this, p2d, &m_hwrapper, m_sectors);
4751 sector->differs_from_disk = false;
4756 bool ServerMap::loadSectorFull(v2s16 p2d)
4758 DSTACK(__FUNCTION_NAME);
4759 std::string sectorsubdir = getSectorSubDir(p2d);
4761 MapSector *sector = NULL;
4763 JMutexAutoLock lock(m_sector_mutex);
4766 sector = loadSectorMeta(sectorsubdir);
4768 catch(InvalidFilenameException &e)
4772 catch(FileNotGoodException &e)
4776 catch(std::exception &e)
4781 if(ENABLE_BLOCK_LOADING)
4783 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4784 (m_savedir+"/sectors/"+sectorsubdir);
4785 std::vector<fs::DirListNode>::iterator i2;
4786 for(i2=list2.begin(); i2!=list2.end(); i2++)
4792 loadBlock(sectorsubdir, i2->name, sector);
4794 catch(InvalidFilenameException &e)
4796 // This catches unknown crap in directory
4804 bool ServerMap::deFlushSector(v2s16 p2d)
4806 DSTACK(__FUNCTION_NAME);
4807 // See if it already exists in memory
4809 MapSector *sector = getSectorNoGenerate(p2d);
4812 catch(InvalidPositionException &e)
4815 Try to load the sector from disk.
4817 if(loadSectorFull(p2d) == true)
4826 void ServerMap::saveBlock(MapBlock *block)
4828 DSTACK(__FUNCTION_NAME);
4830 Dummy blocks are not written
4832 if(block->isDummy())
4834 /*v3s16 p = block->getPos();
4835 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4836 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4840 // Format used for writing
4841 u8 version = SER_FMT_VER_HIGHEST;
4843 v3s16 p3d = block->getPos();
4844 v2s16 p2d(p3d.X, p3d.Z);
4845 createDir(m_savedir);
4846 createDir(m_savedir+"/sectors");
4847 std::string dir = getSectorDir(p2d);
4850 // Block file is map/sectors/xxxxxxxx/xxxx
4852 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4853 std::string fullpath = dir + "/" + cc;
4854 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4855 if(o.good() == false)
4856 throw FileNotGoodException("Cannot open block data");
4859 [0] u8 serialization version
4862 o.write((char*)&version, 1);
4864 block->serialize(o, version);
4867 Versions up from 9 have block objects.
4871 block->serializeObjects(o, version);
4874 // We just wrote it to the disk
4875 block->resetChangedFlag();
4878 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4880 DSTACK(__FUNCTION_NAME);
4884 // Block file is map/sectors/xxxxxxxx/xxxx
4885 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4886 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4887 if(is.good() == false)
4888 throw FileNotGoodException("Cannot open block file");
4890 v3s16 p3d = getBlockPos(sectordir, blockfile);
4891 v2s16 p2d(p3d.X, p3d.Z);
4893 assert(sector->getPos() == p2d);
4895 u8 version = SER_FMT_VER_INVALID;
4896 is.read((char*)&version, 1);
4898 /*u32 block_size = MapBlock::serializedLength(version);
4899 SharedBuffer<u8> data(block_size);
4900 is.read((char*)*data, block_size);*/
4902 // This will always return a sector because we're the server
4903 //MapSector *sector = emergeSector(p2d);
4905 MapBlock *block = NULL;
4906 bool created_new = false;
4908 block = sector->getBlockNoCreate(p3d.Y);
4910 catch(InvalidPositionException &e)
4912 block = sector->createBlankBlockNoInsert(p3d.Y);
4916 // deserialize block data
4917 block->deSerialize(is, version);
4920 Versions up from 9 have block objects.
4924 block->updateObjects(is, version, NULL, 0);
4928 sector->insertBlock(block);
4931 Convert old formats to new and save
4934 // Save old format blocks in new format
4935 if(version < SER_FMT_VER_HIGHEST)
4940 // We just loaded it from the disk, so it's up-to-date.
4941 block->resetChangedFlag();
4944 catch(SerializationError &e)
4946 dstream<<"WARNING: Invalid block data on disk "
4947 "(SerializationError). Ignoring."
4952 // Gets from master heightmap
4953 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4955 assert(m_heightmap != NULL);
4963 corners[0] = m_heightmap->getGroundHeight
4964 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4965 corners[1] = m_heightmap->getGroundHeight
4966 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4967 corners[2] = m_heightmap->getGroundHeight
4968 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4969 corners[3] = m_heightmap->getGroundHeight
4970 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
4973 void ServerMap::PrintInfo(std::ostream &out)
4984 ClientMap::ClientMap(
4986 MapDrawControl &control,
4987 scene::ISceneNode* parent,
4988 scene::ISceneManager* mgr,
4992 scene::ISceneNode(parent, mgr, id),
4999 /*m_box = core::aabbox3d<f32>(0,0,0,
5000 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5001 /*m_box = core::aabbox3d<f32>(0,0,0,
5002 map->getSizeNodes().X * BS,
5003 map->getSizeNodes().Y * BS,
5004 map->getSizeNodes().Z * BS);*/
5005 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5006 BS*1000000,BS*1000000,BS*1000000);
5008 //setPosition(v3f(BS,BS,BS));
5011 ClientMap::~ClientMap()
5013 JMutexAutoLock lock(mesh_mutex);
5022 MapSector * ClientMap::emergeSector(v2s16 p2d)
5024 DSTACK(__FUNCTION_NAME);
5025 // Check that it doesn't exist already
5027 return getSectorNoGenerate(p2d);
5029 catch(InvalidPositionException &e)
5033 // Create a sector with no heightmaps
5034 ClientMapSector *sector = new ClientMapSector(this, p2d);
5037 JMutexAutoLock lock(m_sector_mutex);
5038 m_sectors.insert(p2d, sector);
5044 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5046 DSTACK(__FUNCTION_NAME);
5047 ClientMapSector *sector = NULL;
5049 JMutexAutoLock lock(m_sector_mutex);
5051 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5055 sector = (ClientMapSector*)n->getValue();
5056 assert(sector->getId() == MAPSECTOR_CLIENT);
5060 sector = new ClientMapSector(this, p2d);
5062 JMutexAutoLock lock(m_sector_mutex);
5063 m_sectors.insert(p2d, sector);
5067 sector->deSerialize(is);
5070 void ClientMap::OnRegisterSceneNode()
5074 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5075 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5078 ISceneNode::OnRegisterSceneNode();
5081 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5083 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5084 DSTACK(__FUNCTION_NAME);
5086 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5089 Get time for measuring timeout.
5091 Measuring time is very useful for long delays when the
5092 machine is swapping a lot.
5094 int time1 = time(0);
5096 u32 daynight_ratio = m_client->getDayNightRatio();
5098 m_camera_mutex.Lock();
5099 v3f camera_position = m_camera_position;
5100 v3f camera_direction = m_camera_direction;
5101 m_camera_mutex.Unlock();
5104 Get all blocks and draw all visible ones
5107 v3s16 cam_pos_nodes(
5108 camera_position.X / BS,
5109 camera_position.Y / BS,
5110 camera_position.Z / BS);
5112 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5114 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5115 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5117 // Take a fair amount as we will be dropping more out later
5119 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5120 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5121 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5123 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5124 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5125 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5127 u32 vertex_count = 0;
5129 // For limiting number of mesh updates per frame
5130 u32 mesh_update_count = 0;
5132 u32 blocks_would_have_drawn = 0;
5133 u32 blocks_drawn = 0;
5135 //NOTE: The sectors map should be locked but we're not doing it
5136 // because it'd cause too much delays
5138 int timecheck_counter = 0;
5139 core::map<v2s16, MapSector*>::Iterator si;
5140 si = m_sectors.getIterator();
5141 for(; si.atEnd() == false; si++)
5144 timecheck_counter++;
5145 if(timecheck_counter > 50)
5147 int time2 = time(0);
5148 if(time2 > time1 + 4)
5150 dstream<<"ClientMap::renderMap(): "
5151 "Rendering takes ages, returning."
5158 MapSector *sector = si.getNode()->getValue();
5159 v2s16 sp = sector->getPos();
5161 if(m_control.range_all == false)
5163 if(sp.X < p_blocks_min.X
5164 || sp.X > p_blocks_max.X
5165 || sp.Y < p_blocks_min.Z
5166 || sp.Y > p_blocks_max.Z)
5170 core::list< MapBlock * > sectorblocks;
5171 sector->getBlocks(sectorblocks);
5177 core::list< MapBlock * >::Iterator i;
5178 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5180 MapBlock *block = *i;
5183 Compare block position to camera position, skip
5184 if not seen on display
5187 float range = 100000 * BS;
5188 if(m_control.range_all == false)
5189 range = m_control.wanted_range * BS;
5191 if(isBlockInSight(block->getPos(), camera_position,
5192 camera_direction, range) == false)
5198 v3s16 blockpos_nodes = block->getPosRelative();
5200 // Block center position
5202 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5203 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5204 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5207 // Block position relative to camera
5208 v3f blockpos_relative = blockpos - camera_position;
5210 // Distance in camera direction (+=front, -=back)
5211 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5214 f32 d = blockpos_relative.getLength();
5216 if(m_control.range_all == false)
5218 // If block is far away, don't draw it
5219 if(d > m_control.wanted_range * BS)
5223 // Maximum radius of a block
5224 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5226 // If block is (nearly) touching the camera, don't
5227 // bother validating further (that is, render it anyway)
5228 if(d > block_max_radius * 1.5)
5230 // Cosine of the angle between the camera direction
5231 // and the block direction (camera_direction is an unit vector)
5232 f32 cosangle = dforward / d;
5234 // Compensate for the size of the block
5235 // (as the block has to be shown even if it's a bit off FOV)
5236 // This is an estimate.
5237 cosangle += block_max_radius / dforward;
5239 // If block is not in the field of view, skip it
5240 //if(cosangle < cos(FOV_ANGLE/2))
5241 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5246 v3s16 blockpos_nodes = block->getPosRelative();
5248 // Block center position
5250 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5251 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5252 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5255 // Block position relative to camera
5256 v3f blockpos_relative = blockpos - camera_position;
5259 f32 d = blockpos_relative.getLength();
5266 bool mesh_expired = false;
5269 JMutexAutoLock lock(block->mesh_mutex);
5271 mesh_expired = block->getMeshExpired();
5273 // Mesh has not been expired and there is no mesh:
5274 // block has no content
5275 if(block->mesh == NULL && mesh_expired == false)
5279 f32 faraway = BS*50;
5280 //f32 faraway = m_control.wanted_range * BS;
5283 This has to be done with the mesh_mutex unlocked
5285 // Pretty random but this should work somewhat nicely
5286 if(mesh_expired && (
5287 (mesh_update_count < 3
5288 && (d < faraway || mesh_update_count < 2)
5291 (m_control.range_all && mesh_update_count < 20)
5294 /*if(mesh_expired && mesh_update_count < 6
5295 && (d < faraway || mesh_update_count < 3))*/
5297 mesh_update_count++;
5299 // Mesh has been expired: generate new mesh
5300 //block->updateMeshes(daynight_i);
5301 block->updateMesh(daynight_ratio);
5303 mesh_expired = false;
5307 Don't draw an expired mesh that is far away
5309 /*if(mesh_expired && d >= faraway)
5312 // Instead, delete it
5313 JMutexAutoLock lock(block->mesh_mutex);
5316 block->mesh->drop();
5319 // And continue to next block
5324 Draw the faces of the block
5327 JMutexAutoLock lock(block->mesh_mutex);
5329 scene::SMesh *mesh = block->mesh;
5334 blocks_would_have_drawn++;
5335 if(blocks_drawn >= m_control.wanted_max_blocks
5336 && m_control.range_all == false
5337 && d > m_control.wanted_min_range * BS)
5341 u32 c = mesh->getMeshBufferCount();
5343 for(u32 i=0; i<c; i++)
5345 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5346 const video::SMaterial& material = buf->getMaterial();
5347 video::IMaterialRenderer* rnd =
5348 driver->getMaterialRenderer(material.MaterialType);
5349 bool transparent = (rnd && rnd->isTransparent());
5350 // Render transparent on transparent pass and likewise.
5351 if(transparent == is_transparent_pass)
5353 driver->setMaterial(buf->getMaterial());
5354 driver->drawMeshBuffer(buf);
5355 vertex_count += buf->getVertexCount();
5359 } // foreach sectorblocks
5362 m_control.blocks_drawn = blocks_drawn;
5363 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5365 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5366 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5369 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5370 core::map<v3s16, MapBlock*> *affected_blocks)
5372 bool changed = false;
5374 Add it to all blocks touching it
5377 v3s16(0,0,0), // this
5378 v3s16(0,0,1), // back
5379 v3s16(0,1,0), // top
5380 v3s16(1,0,0), // right
5381 v3s16(0,0,-1), // front
5382 v3s16(0,-1,0), // bottom
5383 v3s16(-1,0,0), // left
5385 for(u16 i=0; i<7; i++)
5387 v3s16 p2 = p + dirs[i];
5388 // Block position of neighbor (or requested) node
5389 v3s16 blockpos = getNodeBlockPos(p2);
5390 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5391 if(blockref == NULL)
5393 // Relative position of requested node
5394 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5395 if(blockref->setTempMod(relpos, mod))
5400 if(changed && affected_blocks!=NULL)
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 affected_blocks->insert(blockpos, blockref);
5416 bool ClientMap::clearTempMod(v3s16 p,
5417 core::map<v3s16, MapBlock*> *affected_blocks)
5419 bool changed = false;
5421 v3s16(0,0,0), // this
5422 v3s16(0,0,1), // back
5423 v3s16(0,1,0), // top
5424 v3s16(1,0,0), // right
5425 v3s16(0,0,-1), // front
5426 v3s16(0,-1,0), // bottom
5427 v3s16(-1,0,0), // left
5429 for(u16 i=0; i<7; i++)
5431 v3s16 p2 = p + dirs[i];
5432 // Block position of neighbor (or requested) node
5433 v3s16 blockpos = getNodeBlockPos(p2);
5434 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5435 if(blockref == NULL)
5437 // Relative position of requested node
5438 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5439 if(blockref->clearTempMod(relpos))
5444 if(changed && affected_blocks!=NULL)
5446 for(u16 i=0; i<7; i++)
5448 v3s16 p2 = p + dirs[i];
5449 // Block position of neighbor (or requested) node
5450 v3s16 blockpos = getNodeBlockPos(p2);
5451 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5452 if(blockref == NULL)
5454 affected_blocks->insert(blockpos, blockref);
5460 void ClientMap::PrintInfo(std::ostream &out)
5471 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5476 MapVoxelManipulator::~MapVoxelManipulator()
5478 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5482 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5484 TimeTaker timer1("emerge", &emerge_time);
5486 // Units of these are MapBlocks
5487 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5488 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5490 VoxelArea block_area_nodes
5491 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5493 addArea(block_area_nodes);
5495 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5496 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5497 for(s32 x=p_min.X; x<=p_max.X; x++)
5500 core::map<v3s16, bool>::Node *n;
5501 n = m_loaded_blocks.find(p);
5505 bool block_data_inexistent = false;
5508 TimeTaker timer1("emerge load", &emerge_load_time);
5510 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5511 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5514 dstream<<std::endl;*/
5516 MapBlock *block = m_map->getBlockNoCreate(p);
5517 if(block->isDummy())
5518 block_data_inexistent = true;
5520 block->copyTo(*this);
5522 catch(InvalidPositionException &e)
5524 block_data_inexistent = true;
5527 if(block_data_inexistent)
5529 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5530 // Fill with VOXELFLAG_INEXISTENT
5531 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5532 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5534 s32 i = m_area.index(a.MinEdge.X,y,z);
5535 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5539 m_loaded_blocks.insert(p, !block_data_inexistent);
5542 //dstream<<"emerge done"<<std::endl;
5546 SUGG: Add an option to only update eg. water and air nodes.
5547 This will make it interfere less with important stuff if
5550 void MapVoxelManipulator::blitBack
5551 (core::map<v3s16, MapBlock*> & modified_blocks)
5553 if(m_area.getExtent() == v3s16(0,0,0))
5556 //TimeTaker timer1("blitBack");
5558 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5559 <<m_loaded_blocks.size()<<std::endl;*/
5562 Initialize block cache
5564 v3s16 blockpos_last;
5565 MapBlock *block = NULL;
5566 bool block_checked_in_modified = false;
5568 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5569 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5570 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5574 u8 f = m_flags[m_area.index(p)];
5575 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5578 MapNode &n = m_data[m_area.index(p)];
5580 v3s16 blockpos = getNodeBlockPos(p);
5585 if(block == NULL || blockpos != blockpos_last){
5586 block = m_map->getBlockNoCreate(blockpos);
5587 blockpos_last = blockpos;
5588 block_checked_in_modified = false;
5591 // Calculate relative position in block
5592 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5594 // Don't continue if nothing has changed here
5595 if(block->getNode(relpos) == n)
5598 //m_map->setNode(m_area.MinEdge + p, n);
5599 block->setNode(relpos, n);
5602 Make sure block is in modified_blocks
5604 if(block_checked_in_modified == false)
5606 modified_blocks[blockpos] = block;
5607 block_checked_in_modified = true;
5610 catch(InvalidPositionException &e)
5616 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5617 MapVoxelManipulator(map)
5621 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5625 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5627 // Just create the area so that it can be pointed to
5628 VoxelManipulator::emerge(a, caller_id);
5631 void ManualMapVoxelManipulator::initialEmerge(
5632 v3s16 blockpos_min, v3s16 blockpos_max)
5634 TimeTaker timer1("initialEmerge", &emerge_time);
5636 // Units of these are MapBlocks
5637 v3s16 p_min = blockpos_min;
5638 v3s16 p_max = blockpos_max;
5640 VoxelArea block_area_nodes
5641 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5643 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5646 dstream<<"initialEmerge: area: ";
5647 block_area_nodes.print(dstream);
5648 dstream<<" ("<<size_MB<<"MB)";
5652 addArea(block_area_nodes);
5654 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5655 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5656 for(s32 x=p_min.X; x<=p_max.X; x++)
5659 core::map<v3s16, bool>::Node *n;
5660 n = m_loaded_blocks.find(p);
5664 bool block_data_inexistent = false;
5667 TimeTaker timer1("emerge load", &emerge_load_time);
5669 MapBlock *block = m_map->getBlockNoCreate(p);
5670 if(block->isDummy())
5671 block_data_inexistent = true;
5673 block->copyTo(*this);
5675 catch(InvalidPositionException &e)
5677 block_data_inexistent = true;
5680 if(block_data_inexistent)
5683 Mark area inexistent
5685 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5686 // Fill with VOXELFLAG_INEXISTENT
5687 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5688 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5690 s32 i = m_area.index(a.MinEdge.X,y,z);
5691 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5695 m_loaded_blocks.insert(p, !block_data_inexistent);
5699 void ManualMapVoxelManipulator::blitBackAll(
5700 core::map<v3s16, MapBlock*> * modified_blocks)
5702 if(m_area.getExtent() == v3s16(0,0,0))
5706 Copy data of all blocks
5708 for(core::map<v3s16, bool>::Iterator
5709 i = m_loaded_blocks.getIterator();
5710 i.atEnd() == false; i++)
5712 bool existed = i.getNode()->getValue();
5713 if(existed == false)
5715 v3s16 p = i.getNode()->getKey();
5716 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5719 dstream<<"WARNING: "<<__FUNCTION_NAME
5720 <<": got NULL block "
5721 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5726 block->copyFrom(*this);
5729 modified_blocks->insert(p, block);