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
2313 // Usually little, sometimes huge
2314 //u32 stone_obstacle_amount = myrand_range(0, myrand_range(0, 100));
2315 u32 stone_obstacle_amount = myrand_range(0, myrand_range(20, 100));
2318 Loop this part, it will make stuff look older and newer nicely
2321 for(u32 i_age=0; i_age<2; i_age++)
2324 // This is set during the next operation.
2325 // Maximum height of the stone surface and obstacles.
2326 // This is used to disable dungeon generation from going too high.
2327 s16 stone_surface_max_y = 0;
2331 //TimeTaker timer1("stone obstacles");
2334 Add some random stone obstacles
2337 for(u32 ri=0; ri<stone_obstacle_amount/3; ri++)
2338 //for(u32 ri=0; ri<7; ri++)
2341 // Randomize max height so usually stuff will be quite low
2342 //s16 maxheight_randomized = myrand_range(0, 25);
2343 s16 maxheight_randomized = myrand_range(0, stone_obstacle_amount/3);
2345 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2347 myrand_range(5, stone_obstacle_max_size),
2348 myrand_range(0, maxheight_randomized),
2349 myrand_range(5, stone_obstacle_max_size)
2352 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2353 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2356 // Minimum space left on top of the obstacle
2357 s16 min_head_space = 10;
2359 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2360 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2362 // Node position in 2d
2363 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2365 // Find stone ground level
2366 // (ignore everything else than mud in already generated chunks)
2367 // and mud amount over the stone level
2371 v3s16 em = vmanip.m_area.getExtent();
2372 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2374 // Go to ground level
2375 for(y=y_nodes_max; y>=y_nodes_min; y--)
2377 MapNode *n = &vmanip.m_data[i];
2378 /*if(content_walkable(n.d)
2379 && n.d != CONTENT_MUD
2380 && n.d != CONTENT_GRASS)
2382 if(n->d == CONTENT_STONE)
2385 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2389 Change to mud because otherwise we might
2390 be throwing mud on grass at the next
2396 vmanip.m_area.add_y(em, i, -1);
2398 if(y >= y_nodes_min)
2401 surface_y = y_nodes_min;
2409 v3s16 em = vmanip.m_area.getExtent();
2410 s16 y_start = surface_y+1;
2411 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2415 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2417 MapNode &n = vmanip.m_data[i];
2418 n.d = CONTENT_STONE;
2420 if(y > stone_surface_max_y)
2421 stone_surface_max_y = y;
2424 if(count >= ob_size.Y)
2427 vmanip.m_area.add_y(em, i, 1);
2431 for(; y<=y_nodes_max; y++)
2433 MapNode &n = vmanip.m_data[i];
2436 if(count >= mud_amount)
2439 vmanip.m_area.add_y(em, i, 1);
2449 //TimeTaker timer1("dungeons");
2454 u32 dungeons_count = relative_volume / 200000;
2455 u32 bruises_count = relative_volume * stone_surface_max_y / 15000000;
2456 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2458 s16 min_tunnel_diameter = 1;
2459 s16 max_tunnel_diameter = 5;
2460 u16 tunnel_routepoints = 10;
2462 bool bruise_surface = (jj < bruises_count);
2466 min_tunnel_diameter = 5;
2467 max_tunnel_diameter = myrand_range(8, 20);
2468 tunnel_routepoints = 7;
2471 // Allowed route area size in nodes
2473 sectorpos_base_size*MAP_BLOCKSIZE,
2474 h_blocks*MAP_BLOCKSIZE,
2475 sectorpos_base_size*MAP_BLOCKSIZE
2478 // Area starting point in nodes
2480 sectorpos_base.X*MAP_BLOCKSIZE,
2481 y_blocks_min*MAP_BLOCKSIZE,
2482 sectorpos_base.Y*MAP_BLOCKSIZE
2486 //(this should be more than the maximum radius of the tunnel)
2487 //s16 insure = 5; // Didn't work with max_d = 20
2489 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2490 ar += v3s16(1,0,1) * more * 2;
2491 of -= v3s16(1,0,1) * more;
2493 s16 route_y_min = 0;
2494 //s16 route_y_max = ar.Y-1;
2495 s16 route_y_max = stone_surface_max_y - of.Y;
2499 /*// Minimum is at y=0
2500 route_y_min = -of.Y - 0;*/
2501 // Minimum is at y=max_tunnel_diameter/4
2502 route_y_min = -of.Y + max_tunnel_diameter/4;
2503 route_y_min = rangelim(route_y_min, 0, route_y_max);
2506 /*dstream<<"route_y_min = "<<route_y_min
2507 <<", route_y_max = "<<route_y_max<<std::endl;*/
2509 // Randomize starting position
2511 (float)(myrand()%ar.X)+0.5,
2512 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2513 (float)(myrand()%ar.Z)+0.5
2516 MapNode airnode(CONTENT_AIR);
2519 Generate some tunnel starting from orp
2522 for(u16 j=0; j<tunnel_routepoints; j++)
2524 v3s16 maxlen(20, 10, 20);
2528 maxlen = v3s16(60,60,60);
2532 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2533 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2534 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2539 else if(rp.X >= ar.X)
2541 if(rp.Y < route_y_min)
2543 else if(rp.Y >= route_y_max)
2544 rp.Y = route_y_max-1;
2547 else if(rp.Z >= ar.Z)
2552 s16 min_d = min_tunnel_diameter;
2553 s16 max_d = max_tunnel_diameter;
2554 s16 rs = myrand_range(min_d, max_d);
2556 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2558 v3f fp = orp + vec * f;
2559 v3s16 cp(fp.X, fp.Y, fp.Z);
2562 s16 d1 = d0 + rs - 1;
2563 for(s16 z0=d0; z0<=d1; z0++)
2565 s16 si = rs - abs(z0);
2566 for(s16 x0=-si; x0<=si-1; x0++)
2568 s16 maxabsxz = abs(x0)>abs(z0)?abs(x0):abs(z0);
2569 s16 si2 = rs - maxabsxz;
2570 //s16 si2 = rs - abs(x0);
2571 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2577 /*if(isInArea(p, ar) == false)
2579 // Check only height
2580 if(y < 0 || y >= ar.Y)
2584 //assert(vmanip.m_area.contains(p));
2585 if(vmanip.m_area.contains(p) == false)
2587 dstream<<"WARNING: "<<__FUNCTION_NAME
2588 <<":"<<__LINE__<<": "
2589 <<"point not in area"
2594 // Just set it to air, it will be changed to
2596 u32 i = vmanip.m_area.index(p);
2597 vmanip.m_data[i] = airnode;
2611 //TimeTaker timer1("ore veins");
2616 for(u32 jj=0; jj<relative_volume/524; jj++)
2618 s16 max_vein_diameter = 3;
2620 // Allowed route area size in nodes
2622 sectorpos_base_size*MAP_BLOCKSIZE,
2623 h_blocks*MAP_BLOCKSIZE,
2624 sectorpos_base_size*MAP_BLOCKSIZE
2627 // Area starting point in nodes
2629 sectorpos_base.X*MAP_BLOCKSIZE,
2630 y_blocks_min*MAP_BLOCKSIZE,
2631 sectorpos_base.Y*MAP_BLOCKSIZE
2635 //(this should be more than the maximum radius of the tunnel)
2637 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2638 ar += v3s16(1,0,1) * more * 2;
2639 of -= v3s16(1,0,1) * more;
2641 // Randomize starting position
2643 (float)(myrand()%ar.X)+0.5,
2644 (float)(myrand()%ar.Y)+0.5,
2645 (float)(myrand()%ar.Z)+0.5
2648 // Randomize mineral
2649 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2652 Generate some vein starting from orp
2655 for(u16 j=0; j<2; j++)
2658 (float)(myrand()%ar.X)+0.5,
2659 (float)(myrand()%ar.Y)+0.5,
2660 (float)(myrand()%ar.Z)+0.5
2662 v3f vec = rp - orp;*/
2664 v3s16 maxlen(10, 10, 10);
2666 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2667 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2668 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2673 else if(rp.X >= ar.X)
2677 else if(rp.Y >= ar.Y)
2681 else if(rp.Z >= ar.Z)
2687 s16 max_d = max_vein_diameter;
2688 s16 rs = myrand_range(min_d, max_d);
2690 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2692 v3f fp = orp + vec * f;
2693 v3s16 cp(fp.X, fp.Y, fp.Z);
2695 s16 d1 = d0 + rs - 1;
2696 for(s16 z0=d0; z0<=d1; z0++)
2698 s16 si = rs - abs(z0);
2699 for(s16 x0=-si; x0<=si-1; x0++)
2701 s16 si2 = rs - abs(x0);
2702 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2704 // Don't put mineral to every place
2712 /*if(isInArea(p, ar) == false)
2714 // Check only height
2715 if(y < 0 || y >= ar.Y)
2719 assert(vmanip.m_area.contains(p));
2721 // Just set it to air, it will be changed to
2723 u32 i = vmanip.m_area.index(p);
2724 MapNode *n = &vmanip.m_data[i];
2725 if(n->d == CONTENT_STONE)
2740 //TimeTaker timer1("add mud");
2743 Add mud to the central chunk
2746 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2747 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2749 // Node position in 2d
2750 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2752 // Find ground level
2753 s16 surface_y = find_ground_level(vmanip, p2d);
2756 If topmost node is grass, change it to mud.
2757 It might be if it was flown to there from a neighboring
2758 chunk and then converted.
2761 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2762 MapNode *n = &vmanip.m_data[i];
2763 if(n->d == CONTENT_GRASS)
2772 v3s16 em = vmanip.m_area.getExtent();
2773 s16 y_start = surface_y+1;
2774 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2775 for(s16 y=y_start; y<=y_nodes_max; y++)
2777 MapNode &n = vmanip.m_data[i];
2783 vmanip.m_area.add_y(em, i, 1);
2792 //TimeTaker timer1("flow mud");
2795 Flow mud away from steep edges
2798 // Iterate a few times
2799 for(s16 k=0; k<4; k++)
2802 for(s16 x=0-max_spread_amount+1;
2803 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2805 for(s16 z=0-max_spread_amount+1;
2806 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2809 // Node position in 2d
2810 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2812 v3s16 em = vmanip.m_area.getExtent();
2813 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2815 // Go to ground level
2816 for(y=y_nodes_max; y>=y_nodes_min; y--)
2818 MapNode &n = vmanip.m_data[i];
2819 //if(n.d != CONTENT_AIR)
2820 if(content_walkable(n.d))
2823 vmanip.m_area.add_y(em, i, -1);
2826 // If not mud, do nothing to it
2827 MapNode *n = &vmanip.m_data[i];
2828 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2831 // Make it exactly mud
2835 v3s16(0,0,1), // back
2836 v3s16(1,0,0), // right
2837 v3s16(0,0,-1), // front
2838 v3s16(-1,0,0), // left
2843 for(u32 di=0; di<4; di++)
2845 v3s16 dirp = dirs4[di];
2847 // Check that side is air
2848 vmanip.m_area.add_p(em, i2, dirp);
2849 MapNode *n2 = &vmanip.m_data[i2];
2850 if(content_walkable(n2->d))
2852 // Check that under side is air
2853 vmanip.m_area.add_y(em, i2, -1);
2854 n2 = &vmanip.m_data[i2];
2855 if(content_walkable(n2->d))
2857 // Loop further down until not air
2859 vmanip.m_area.add_y(em, i2, -1);
2860 n2 = &vmanip.m_data[i2];
2861 }while(content_walkable(n2->d) == false);
2862 // Loop one up so that we're in air
2863 vmanip.m_area.add_y(em, i2, 1);
2864 n2 = &vmanip.m_data[i2];
2866 // Move mud to new place
2868 // Set old place to be air
2869 *n = MapNode(CONTENT_AIR);
2872 // Switch mud and other and change mud source to air
2873 //MapNode tempnode = *n2;
2876 // Force old mud position to be air
2890 //TimeTaker timer1("add water");
2893 Add water to the central chunk (and a bit more)
2896 for(s16 x=0-max_spread_amount;
2897 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2899 for(s16 z=0-max_spread_amount;
2900 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2903 // Node position in 2d
2904 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2906 // Find ground level
2907 //s16 surface_y = find_ground_level(vmanip, p2d);
2910 If ground level is over water level, skip.
2911 NOTE: This leaves caves near water without water,
2912 which looks especially crappy when the nearby water
2913 won't start flowing either for some reason
2915 /*if(surface_y > WATER_LEVEL)
2922 v3s16 em = vmanip.m_area.getExtent();
2923 s16 y_start = WATER_LEVEL;
2924 u8 light = LIGHT_MAX;
2925 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2926 MapNode *n = &vmanip.m_data[i];
2928 Add first one to transforming liquid queue
2930 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2932 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2933 m_transforming_liquid.push_back(p);
2935 for(s16 y=y_start; y>=y_nodes_min; y--)
2937 n = &vmanip.m_data[i];
2939 // Stop when there is no water and no air
2940 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2941 && n->d != CONTENT_WATER)
2944 Add bottom one to transforming liquid queue
2946 vmanip.m_area.add_y(em, i, 1);
2947 n = &vmanip.m_data[i];
2948 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2950 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2951 m_transforming_liquid.push_back(p);
2957 n->d = CONTENT_WATERSOURCE;
2958 n->setLight(LIGHTBANK_DAY, light);
2960 /*// Add to transforming liquid queue (in case it'd
2962 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2963 m_transforming_liquid.push_back(p);*/
2966 vmanip.m_area.add_y(em, i, -1);
2980 //TimeTaker timer1("plant trees");
2986 u32 tree_max = relative_area / 60;
2988 u32 count = myrand_range(0, tree_max);
2989 for(u32 i=0; i<count; i++)
2991 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2992 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2993 x += sectorpos_base.X*MAP_BLOCKSIZE;
2994 z += sectorpos_base.Y*MAP_BLOCKSIZE;
2995 s16 y = find_ground_level(vmanip, v2s16(x,z));
2996 // Don't make a tree under water level
3001 make_tree(vmanip, p);
3009 //TimeTaker timer1("grow grass");
3015 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3016 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3017 for(s16 x=0-max_spread_amount;
3018 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3020 for(s16 z=0-max_spread_amount;
3021 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3024 // Node position in 2d
3025 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3028 Find the lowest surface to which enough light ends up
3031 Basically just wait until not air and not leaves.
3035 v3s16 em = vmanip.m_area.getExtent();
3036 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3038 // Go to ground level
3039 for(y=y_nodes_max; y>=y_nodes_min; y--)
3041 MapNode &n = vmanip.m_data[i];
3042 if(n.d != CONTENT_AIR
3043 && n.d != CONTENT_LEAVES)
3045 vmanip.m_area.add_y(em, i, -1);
3047 if(y >= y_nodes_min)
3050 surface_y = y_nodes_min;
3053 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3054 MapNode *n = &vmanip.m_data[i];
3055 if(n->d == CONTENT_MUD)
3056 n->d = CONTENT_GRASS;
3065 core::map<v3s16, bool> light_sources;
3068 // 750ms @cs=8, can't optimize more
3069 //TimeTaker timer1("initial lighting");
3071 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3072 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3073 for(s16 x=0-max_spread_amount+1;
3074 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3076 for(s16 z=0-max_spread_amount+1;
3077 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3080 // Node position in 2d
3081 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3084 Apply initial sunlight
3087 u8 light = LIGHT_SUN;
3088 bool add_to_sources = false;
3089 v3s16 em = vmanip.m_area.getExtent();
3090 s16 y_start = y_nodes_max;
3091 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3092 for(s16 y=y_start; y>=y_nodes_min; y--)
3094 MapNode *n = &vmanip.m_data[i];
3096 if(light_propagates_content(n->d) == false)
3100 else if(light != LIGHT_SUN
3101 || sunlight_propagates_content(n->d) == false)
3107 // This doesn't take much time
3108 if(add_to_sources == false)
3111 Check sides. If side is not air or water, start
3112 adding to light_sources.
3115 v3s16(0,0,1), // back
3116 v3s16(1,0,0), // right
3117 v3s16(0,0,-1), // front
3118 v3s16(-1,0,0), // left
3120 for(u32 di=0; di<4; di++)
3122 v3s16 dirp = dirs4[di];
3124 vmanip.m_area.add_p(em, i2, dirp);
3125 MapNode *n2 = &vmanip.m_data[i2];
3127 n2->d != CONTENT_AIR
3128 && n2->d != CONTENT_WATERSOURCE
3129 && n2->d != CONTENT_WATER
3131 add_to_sources = true;
3137 n->setLight(LIGHTBANK_DAY, light);
3138 n->setLight(LIGHTBANK_NIGHT, 0);
3140 // This doesn't take much time
3141 if(light != 0 && add_to_sources)
3143 // Insert light source
3144 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3147 // Increment index by y
3148 vmanip.m_area.add_y(em, i, -1);
3155 // Spread light around
3157 TimeTaker timer("generateChunkRaw() spreadLight");
3158 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3165 timer_generate.stop();
3168 Blit generated stuff to map
3172 //TimeTaker timer("generateChunkRaw() blitBackAll");
3173 vmanip.blitBackAll(&changed_blocks);
3176 Update day/night difference cache of the MapBlocks
3179 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3180 i.atEnd() == false; i++)
3182 MapBlock *block = i.getNode()->getValue();
3183 block->updateDayNightDiff();
3189 Create chunk metadata
3192 for(s16 x=-1; x<=1; x++)
3193 for(s16 y=-1; y<=1; y++)
3195 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3196 // Add chunk meta information
3197 MapChunk *chunk = getChunk(chunkpos0);
3200 chunk = new MapChunk();
3201 m_chunks.insert(chunkpos0, chunk);
3203 //chunk->setIsVolatile(true);
3204 if(chunk->getGenLevel() > GENERATED_PARTLY)
3205 chunk->setGenLevel(GENERATED_PARTLY);
3209 Set central chunk non-volatile and return it
3211 MapChunk *chunk = getChunk(chunkpos);
3214 //chunk->setIsVolatile(false);
3215 chunk->setGenLevel(GENERATED_FULLY);
3220 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3221 core::map<v3s16, MapBlock*> &changed_blocks)
3223 dstream<<"generateChunk(): Generating chunk "
3224 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3227 /*for(s16 x=-1; x<=1; x++)
3228 for(s16 y=-1; y<=1; y++)*/
3229 for(s16 x=-0; x<=0; x++)
3230 for(s16 y=-0; y<=0; y++)
3232 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3233 MapChunk *chunk = getChunk(chunkpos0);
3234 // Skip if already generated
3235 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3237 generateChunkRaw(chunkpos0, changed_blocks);
3240 assert(chunkNonVolatile(chunkpos1));
3242 MapChunk *chunk = getChunk(chunkpos1);
3246 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3248 DSTACK("%s: p2d=(%d,%d)",
3253 Check if it exists already in memory
3255 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3260 Try to load it from disk (with blocks)
3262 if(loadSectorFull(p2d) == true)
3264 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3267 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3268 throw InvalidPositionException("");
3274 If there is no master heightmap, throw.
3276 if(m_heightmap == NULL)
3278 throw InvalidPositionException("createSector(): no heightmap");
3282 Do not create over-limit
3284 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3285 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3286 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3287 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3288 throw InvalidPositionException("createSector(): pos. over limit");
3291 Generate blank sector
3294 // Number of heightmaps in sector in each direction
3295 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3297 // Heightmap side width
3298 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3300 sector = new ServerMapSector(this, p2d, hm_split);
3302 // Sector position on map in nodes
3303 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3305 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3306 " heightmaps and objects"<<std::endl;*/
3309 Generate sector heightmap
3312 v2s16 mhm_p = p2d * hm_split;
3313 /*f32 corners[4] = {
3314 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3315 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3316 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3317 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3320 // Loop through sub-heightmaps
3321 for(s16 y=0; y<hm_split; y++)
3322 for(s16 x=0; x<hm_split; x++)
3324 v2s16 p_in_sector = v2s16(x,y);
3325 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3327 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3328 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3329 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3330 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3333 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3334 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3337 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3339 sector->setHeightmap(p_in_sector, hm);
3341 //hm->generateContinued(1.0, 0.5, corners);
3342 hm->generateContinued(0.5, 0.5, corners);
3347 // Add dummy objects
3348 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3349 sector->setObjects(objects);
3354 m_sectors.insert(p2d, sector);
3359 MapSector * ServerMap::emergeSector(v2s16 p2d,
3360 core::map<v3s16, MapBlock*> &changed_blocks)
3362 DSTACK("%s: p2d=(%d,%d)",
3369 v2s16 chunkpos = sector_to_chunk(p2d);
3370 /*bool chunk_nonvolatile = false;
3371 MapChunk *chunk = getChunk(chunkpos);
3372 if(chunk && chunk->getIsVolatile() == false)
3373 chunk_nonvolatile = true;*/
3374 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3377 If chunk is not fully generated, generate chunk
3379 if(chunk_nonvolatile == false)
3381 // Generate chunk and neighbors
3382 generateChunk(chunkpos, changed_blocks);
3386 Return sector if it exists now
3388 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3393 Try to load it from disk
3395 if(loadSectorFull(p2d) == true)
3397 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3400 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3401 throw InvalidPositionException("");
3407 generateChunk should have generated the sector
3414 //return generateSector();
3418 NOTE: This is not used for main map generation, only for blocks
3419 that are very high or low
3421 MapBlock * ServerMap::generateBlock(
3423 MapBlock *original_dummy,
3424 ServerMapSector *sector,
3425 core::map<v3s16, MapBlock*> &changed_blocks,
3426 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3429 DSTACK("%s: p=(%d,%d,%d)",
3433 /*dstream<<"generateBlock(): "
3434 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3437 MapBlock *block = original_dummy;
3439 v2s16 p2d(p.X, p.Z);
3443 Do not generate over-limit
3445 if(blockpos_over_limit(p))
3447 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3448 throw InvalidPositionException("generateBlock(): pos. over limit");
3452 If block doesn't exist, create one.
3453 If it exists, it is a dummy. In that case unDummify() it.
3455 NOTE: This already sets the map as the parent of the block
3459 block = sector->createBlankBlockNoInsert(block_y);
3463 // Remove the block so that nobody can get a half-generated one.
3464 sector->removeBlock(block);
3465 // Allocate the block to contain the generated data
3469 /*u8 water_material = CONTENT_WATER;
3470 if(g_settings.getBool("endless_water"))
3471 water_material = CONTENT_WATERSOURCE;*/
3472 u8 water_material = CONTENT_WATERSOURCE;
3474 s32 lowest_ground_y = 32767;
3475 s32 highest_ground_y = -32768;
3478 //sector->printHeightmaps();
3480 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3481 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3483 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3485 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3486 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3487 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3489 dstream<<"WARNING: Surface height not found in sector "
3490 "for block that is being emerged"<<std::endl;
3494 s16 surface_y = surface_y_f;
3495 //avg_ground_y += surface_y;
3496 if(surface_y < lowest_ground_y)
3497 lowest_ground_y = surface_y;
3498 if(surface_y > highest_ground_y)
3499 highest_ground_y = surface_y;
3501 s32 surface_depth = 0;
3503 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3505 //float min_slope = 0.45;
3506 //float max_slope = 0.85;
3507 float min_slope = 0.60;
3508 float max_slope = 1.20;
3509 float min_slope_depth = 5.0;
3510 float max_slope_depth = 0;
3512 if(slope < min_slope)
3513 surface_depth = min_slope_depth;
3514 else if(slope > max_slope)
3515 surface_depth = max_slope_depth;
3517 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3519 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3521 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3526 NOTE: If there are some man-made structures above the
3527 newly created block, they won't be taken into account.
3529 if(real_y > surface_y)
3530 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3536 // If node is over heightmap y, it's air or water
3537 if(real_y > surface_y)
3539 // If under water level, it's water
3540 if(real_y < WATER_LEVEL)
3542 n.d = water_material;
3543 n.setLight(LIGHTBANK_DAY,
3544 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3546 Add to transforming liquid queue (in case it'd
3549 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3550 m_transforming_liquid.push_back(real_pos);
3556 // Else it's ground or dungeons (air)
3559 // If it's surface_depth under ground, it's stone
3560 if(real_y <= surface_y - surface_depth)
3562 n.d = CONTENT_STONE;
3566 // It is mud if it is under the first ground
3567 // level or under water
3568 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3574 n.d = CONTENT_GRASS;
3577 //n.d = CONTENT_MUD;
3579 /*// If under water level, it's mud
3580 if(real_y < WATER_LEVEL)
3582 // Only the topmost node is grass
3583 else if(real_y <= surface_y - 1)
3586 n.d = CONTENT_GRASS;*/
3590 block->setNode(v3s16(x0,y0,z0), n);
3595 Calculate some helper variables
3598 // Completely underground if the highest part of block is under lowest
3600 // This has to be very sure; it's probably one too strict now but
3601 // that's just better.
3602 bool completely_underground =
3603 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3605 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3607 bool mostly_underwater_surface = false;
3608 if(highest_ground_y < WATER_LEVEL
3609 && some_part_underground && !completely_underground)
3610 mostly_underwater_surface = true;
3613 Get local attributes
3616 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3618 float caves_amount = 0.5;
3623 NOTE: BEWARE: Too big amount of attribute points slows verything
3625 1 interpolation from 5000 points takes 2-3ms.
3627 //TimeTaker timer("generateBlock() local attribute retrieval");
3628 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3629 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3630 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3634 //dstream<<"generateBlock(): Done"<<std::endl;
3640 // Initialize temporary table
3641 const s32 ued = MAP_BLOCKSIZE;
3642 bool underground_emptiness[ued*ued*ued];
3643 for(s32 i=0; i<ued*ued*ued; i++)
3645 underground_emptiness[i] = 0;
3652 Initialize orp and ors. Try to find if some neighboring
3653 MapBlock has a tunnel ended in its side
3657 (float)(myrand()%ued)+0.5,
3658 (float)(myrand()%ued)+0.5,
3659 (float)(myrand()%ued)+0.5
3662 bool found_existing = false;
3668 for(s16 y=0; y<ued; y++)
3669 for(s16 x=0; x<ued; x++)
3671 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3672 if(getNode(ap).d == CONTENT_AIR)
3674 orp = v3f(x+1,y+1,0);
3675 found_existing = true;
3676 goto continue_generating;
3680 catch(InvalidPositionException &e){}
3686 for(s16 y=0; y<ued; y++)
3687 for(s16 x=0; x<ued; x++)
3689 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3690 if(getNode(ap).d == CONTENT_AIR)
3692 orp = v3f(x+1,y+1,ued-1);
3693 found_existing = true;
3694 goto continue_generating;
3698 catch(InvalidPositionException &e){}
3704 for(s16 y=0; y<ued; y++)
3705 for(s16 z=0; z<ued; z++)
3707 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3708 if(getNode(ap).d == CONTENT_AIR)
3710 orp = v3f(0,y+1,z+1);
3711 found_existing = true;
3712 goto continue_generating;
3716 catch(InvalidPositionException &e){}
3722 for(s16 y=0; y<ued; y++)
3723 for(s16 z=0; z<ued; z++)
3725 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3726 if(getNode(ap).d == CONTENT_AIR)
3728 orp = v3f(ued-1,y+1,z+1);
3729 found_existing = true;
3730 goto continue_generating;
3734 catch(InvalidPositionException &e){}
3740 for(s16 x=0; x<ued; x++)
3741 for(s16 z=0; z<ued; z++)
3743 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3744 if(getNode(ap).d == CONTENT_AIR)
3746 orp = v3f(x+1,0,z+1);
3747 found_existing = true;
3748 goto continue_generating;
3752 catch(InvalidPositionException &e){}
3758 for(s16 x=0; x<ued; x++)
3759 for(s16 z=0; z<ued; z++)
3761 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3762 if(getNode(ap).d == CONTENT_AIR)
3764 orp = v3f(x+1,ued-1,z+1);
3765 found_existing = true;
3766 goto continue_generating;
3770 catch(InvalidPositionException &e){}
3772 continue_generating:
3775 Choose whether to actually generate dungeon
3777 bool do_generate_dungeons = true;
3778 // Don't generate if no part is underground
3779 if(!some_part_underground)
3781 do_generate_dungeons = false;
3783 // Don't generate if mostly underwater surface
3784 /*else if(mostly_underwater_surface)
3786 do_generate_dungeons = false;
3788 // Partly underground = cave
3789 else if(!completely_underground)
3791 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3793 // Found existing dungeon underground
3794 else if(found_existing && completely_underground)
3796 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3798 // Underground and no dungeons found
3801 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3804 if(do_generate_dungeons)
3807 Generate some tunnel starting from orp and ors
3809 for(u16 i=0; i<3; i++)
3812 (float)(myrand()%ued)+0.5,
3813 (float)(myrand()%ued)+0.5,
3814 (float)(myrand()%ued)+0.5
3818 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3822 for(float f=0; f<1.0; f+=0.04)
3824 v3f fp = orp + vec * f;
3825 v3s16 cp(fp.X, fp.Y, fp.Z);
3827 s16 d1 = d0 + rs - 1;
3828 for(s16 z0=d0; z0<=d1; z0++)
3830 s16 si = rs - abs(z0);
3831 for(s16 x0=-si; x0<=si-1; x0++)
3833 s16 si2 = rs - abs(x0);
3834 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3840 if(isInArea(p, ued) == false)
3842 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3854 // Set to true if has caves.
3855 // Set when some non-air is changed to air when making caves.
3856 bool has_dungeons = false;
3859 Apply temporary cave data to block
3862 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3863 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3865 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3867 MapNode n = block->getNode(v3s16(x0,y0,z0));
3870 if(underground_emptiness[
3871 ued*ued*(z0*ued/MAP_BLOCKSIZE)
3872 +ued*(y0*ued/MAP_BLOCKSIZE)
3873 +(x0*ued/MAP_BLOCKSIZE)])
3875 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
3878 has_dungeons = true;
3884 block->setNode(v3s16(x0,y0,z0), n);
3889 This is used for guessing whether or not the block should
3890 receive sunlight from the top if the block above doesn't exist
3892 block->setIsUnderground(completely_underground);
3895 Force lighting update if some part of block is partly
3896 underground and has caves.
3898 /*if(some_part_underground && !completely_underground && has_dungeons)
3900 //dstream<<"Half-ground caves"<<std::endl;
3901 lighting_invalidated_blocks[block->getPos()] = block;
3904 // DEBUG: Always update lighting
3905 //lighting_invalidated_blocks[block->getPos()] = block;
3911 if(some_part_underground)
3913 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
3918 for(s16 i=0; i<underground_level/4 + 1; i++)
3920 if(myrand()%50 == 0)
3923 (myrand()%(MAP_BLOCKSIZE-2))+1,
3924 (myrand()%(MAP_BLOCKSIZE-2))+1,
3925 (myrand()%(MAP_BLOCKSIZE-2))+1
3931 for(u16 i=0; i<27; i++)
3933 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3935 block->setNode(cp+g_27dirs[i], n);
3943 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
3944 u16 coal_rareness = 60 / coal_amount;
3945 if(coal_rareness == 0)
3947 if(myrand()%coal_rareness == 0)
3949 u16 a = myrand() % 16;
3950 u16 amount = coal_amount * a*a*a / 1000;
3951 for(s16 i=0; i<amount; i++)
3954 (myrand()%(MAP_BLOCKSIZE-2))+1,
3955 (myrand()%(MAP_BLOCKSIZE-2))+1,
3956 (myrand()%(MAP_BLOCKSIZE-2))+1
3960 n.d = CONTENT_STONE;
3961 n.param = MINERAL_COAL;
3963 for(u16 i=0; i<27; i++)
3965 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3967 block->setNode(cp+g_27dirs[i], n);
3975 //TODO: change to iron_amount or whatever
3976 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
3977 u16 iron_rareness = 60 / iron_amount;
3978 if(iron_rareness == 0)
3980 if(myrand()%iron_rareness == 0)
3982 u16 a = myrand() % 16;
3983 u16 amount = iron_amount * a*a*a / 1000;
3984 for(s16 i=0; i<amount; i++)
3987 (myrand()%(MAP_BLOCKSIZE-2))+1,
3988 (myrand()%(MAP_BLOCKSIZE-2))+1,
3989 (myrand()%(MAP_BLOCKSIZE-2))+1
3993 n.d = CONTENT_STONE;
3994 n.param = MINERAL_IRON;
3996 for(u16 i=0; i<27; i++)
3998 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4000 block->setNode(cp+g_27dirs[i], n);
4007 Create a few rats in empty blocks underground
4009 if(completely_underground)
4011 //for(u16 i=0; i<2; i++)
4014 (myrand()%(MAP_BLOCKSIZE-2))+1,
4015 (myrand()%(MAP_BLOCKSIZE-2))+1,
4016 (myrand()%(MAP_BLOCKSIZE-2))+1
4019 // Check that the place is empty
4020 //if(!is_ground_content(block->getNode(cp).d))
4023 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4024 block->addObject(obj);
4030 Add block to sector.
4032 sector->insertBlock(block);
4038 // An y-wise container of changed blocks
4039 core::map<s16, MapBlock*> changed_blocks_sector;
4042 Check if any sector's objects can be placed now.
4045 core::map<v3s16, u8> *objects = sector->getObjects();
4046 core::list<v3s16> objects_to_remove;
4047 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
4048 i.atEnd() == false; i++)
4050 v3s16 p = i.getNode()->getKey();
4052 u8 d = i.getNode()->getValue();
4054 // Ground level point (user for stuff that is on ground)
4056 bool ground_found = true;
4058 // Search real ground level
4062 MapNode n = sector->getNode(gp);
4064 // If not air, go one up and continue to placing the tree
4065 if(n.d != CONTENT_AIR)
4071 // If air, go one down
4072 gp += v3s16(0,-1,0);
4074 }catch(InvalidPositionException &e)
4076 // Ground not found.
4077 ground_found = false;
4078 // This is most close to ground
4085 if(d == SECTOR_OBJECT_TEST)
4087 if(sector->isValidArea(p + v3s16(0,0,0),
4088 p + v3s16(0,0,0), &changed_blocks_sector))
4091 n.d = CONTENT_TORCH;
4092 sector->setNode(p, n);
4093 objects_to_remove.push_back(p);
4096 else if(d == SECTOR_OBJECT_TREE_1)
4098 if(ground_found == false)
4101 v3s16 p_min = gp + v3s16(-1,0,-1);
4102 v3s16 p_max = gp + v3s16(1,5,1);
4103 if(sector->isValidArea(p_min, p_max,
4104 &changed_blocks_sector))
4108 sector->setNode(gp+v3s16(0,0,0), n);
4109 sector->setNode(gp+v3s16(0,1,0), n);
4110 sector->setNode(gp+v3s16(0,2,0), n);
4111 sector->setNode(gp+v3s16(0,3,0), n);
4113 n.d = CONTENT_LEAVES;
4115 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4117 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4118 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4119 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4120 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4121 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4122 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,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);*/
4126 sector->setNode(gp+v3s16(0,4,0), n);
4128 sector->setNode(gp+v3s16(-1,4,0), n);
4129 sector->setNode(gp+v3s16(1,4,0), n);
4130 sector->setNode(gp+v3s16(0,4,-1), n);
4131 sector->setNode(gp+v3s16(0,4,1), n);
4132 sector->setNode(gp+v3s16(1,4,1), n);
4133 sector->setNode(gp+v3s16(-1,4,1), n);
4134 sector->setNode(gp+v3s16(-1,4,-1), n);
4135 sector->setNode(gp+v3s16(1,4,-1), n);
4137 sector->setNode(gp+v3s16(-1,3,0), n);
4138 sector->setNode(gp+v3s16(1,3,0), n);
4139 sector->setNode(gp+v3s16(0,3,-1), n);
4140 sector->setNode(gp+v3s16(0,3,1), n);
4141 sector->setNode(gp+v3s16(1,3,1), n);
4142 sector->setNode(gp+v3s16(-1,3,1), n);
4143 sector->setNode(gp+v3s16(-1,3,-1), n);
4144 sector->setNode(gp+v3s16(1,3,-1), n);
4146 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4147 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4148 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4149 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4150 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4151 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,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);*/
4155 // Objects are identified by wanted position
4156 objects_to_remove.push_back(p);
4158 // Lighting has to be recalculated for this one.
4159 sector->getBlocksInArea(p_min, p_max,
4160 lighting_invalidated_blocks);
4163 else if(d == SECTOR_OBJECT_BUSH_1)
4165 if(ground_found == false)
4168 if(sector->isValidArea(gp + v3s16(0,0,0),
4169 gp + v3s16(0,0,0), &changed_blocks_sector))
4172 n.d = CONTENT_LEAVES;
4173 sector->setNode(gp+v3s16(0,0,0), n);
4175 // Objects are identified by wanted position
4176 objects_to_remove.push_back(p);
4179 else if(d == SECTOR_OBJECT_RAVINE)
4182 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4183 v3s16 p_max = p + v3s16(6,6,6);
4184 if(sector->isValidArea(p_min, p_max,
4185 &changed_blocks_sector))
4188 n.d = CONTENT_STONE;
4191 s16 depth = maxdepth + (myrand()%10);
4193 s16 minz = -6 - (-2);
4195 for(s16 x=-6; x<=6; x++)
4197 z += -1 + (myrand()%3);
4202 for(s16 y=depth+(myrand()%2); y<=6; y++)
4204 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4207 v3s16 p2 = p + v3s16(x,y,z-2);
4208 //if(is_ground_content(sector->getNode(p2).d))
4209 if(content_features(sector->getNode(p2).d).walkable)
4210 sector->setNode(p2, n);
4213 v3s16 p2 = p + v3s16(x,y,z-1);
4214 if(content_features(sector->getNode(p2).d).walkable)
4215 sector->setNode(p2, n2);
4218 v3s16 p2 = p + v3s16(x,y,z+0);
4219 if(content_features(sector->getNode(p2).d).walkable)
4220 sector->setNode(p2, n2);
4223 v3s16 p2 = p + v3s16(x,y,z+1);
4224 if(content_features(sector->getNode(p2).d).walkable)
4225 sector->setNode(p2, n);
4228 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4229 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4233 objects_to_remove.push_back(p);
4235 // Lighting has to be recalculated for this one.
4236 sector->getBlocksInArea(p_min, p_max,
4237 lighting_invalidated_blocks);
4242 dstream<<"ServerMap::generateBlock(): "
4243 "Invalid heightmap object"
4248 catch(InvalidPositionException &e)
4250 dstream<<"WARNING: "<<__FUNCTION_NAME
4251 <<": while inserting object "<<(int)d
4252 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4253 <<" InvalidPositionException.what()="
4254 <<e.what()<<std::endl;
4255 // This is not too fatal and seems to happen sometimes.
4260 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4261 i != objects_to_remove.end(); i++)
4263 objects->remove(*i);
4267 Translate sector's changed blocks to global changed blocks
4270 for(core::map<s16, MapBlock*>::Iterator
4271 i = changed_blocks_sector.getIterator();
4272 i.atEnd() == false; i++)
4274 MapBlock *block = i.getNode()->getValue();
4276 changed_blocks.insert(block->getPos(), block);
4279 block->setLightingExpired(true);
4286 <<"lighting_invalidated_blocks.size()"
4290 <<" "<<lighting_invalidated_blocks.size()
4291 <<", "<<has_dungeons
4292 <<", "<<completely_underground
4293 <<", "<<some_part_underground
4300 MapBlock * ServerMap::createBlock(v3s16 p)
4302 DSTACK("%s: p=(%d,%d,%d)",
4303 __FUNCTION_NAME, p.X, p.Y, p.Z);
4305 v2s16 p2d(p.X, p.Z);
4308 This will create or load a sector if not found in memory.
4309 If block exists on disk, it will be loaded.
4311 NOTE: On old save formats, this will be slow, as it generates
4312 lighting on blocks for them.
4314 ServerMapSector *sector;
4316 sector = (ServerMapSector*)createSector(p2d);
4317 assert(sector->getId() == MAPSECTOR_SERVER);
4319 /*catch(InvalidPositionException &e)
4321 dstream<<"createBlock: createSector() failed"<<std::endl;
4324 catch(std::exception &e)
4326 dstream<<"createBlock: createSector() failed: "
4327 <<e.what()<<std::endl;
4332 Try to get a block from the sector
4335 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4339 block = sector->createBlankBlock(block_y);
4343 MapBlock * ServerMap::emergeBlock(
4345 bool only_from_disk,
4346 core::map<v3s16, MapBlock*> &changed_blocks,
4347 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4350 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4352 p.X, p.Y, p.Z, only_from_disk);
4354 v2s16 p2d(p.X, p.Z);
4357 This will create or load a sector if not found in memory.
4358 If block exists on disk, it will be loaded.
4360 NOTE: On old save formats, this will be slow, as it generates
4361 lighting on blocks for them.
4363 ServerMapSector *sector;
4365 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4366 assert(sector->getId() == MAPSECTOR_SERVER);
4368 catch(std::exception &e)
4370 dstream<<"emergeBlock: emergeSector() failed: "
4371 <<e.what()<<std::endl;
4376 Try to get a block from the sector
4379 bool does_not_exist = false;
4380 bool lighting_expired = false;
4381 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4385 does_not_exist = true;
4387 else if(block->isDummy() == true)
4389 does_not_exist = true;
4391 else if(block->getLightingExpired())
4393 lighting_expired = true;
4398 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4403 If block was not found on disk and not going to generate a
4404 new one, make sure there is a dummy block in place.
4406 if(only_from_disk && (does_not_exist || lighting_expired))
4408 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4412 // Create dummy block
4413 block = new MapBlock(this, p, true);
4415 // Add block to sector
4416 sector->insertBlock(block);
4422 //dstream<<"Not found on disk, generating."<<std::endl;
4424 //TimeTaker("emergeBlock() generate");
4426 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4429 If the block doesn't exist, generate the block.
4433 block = generateBlock(p, block, sector, changed_blocks,
4434 lighting_invalidated_blocks);
4437 if(lighting_expired)
4439 lighting_invalidated_blocks.insert(p, block);
4443 Initially update sunlight
4447 core::map<v3s16, bool> light_sources;
4448 bool black_air_left = false;
4449 bool bottom_invalid =
4450 block->propagateSunlight(light_sources, true,
4451 &black_air_left, true);
4453 // If sunlight didn't reach everywhere and part of block is
4454 // above ground, lighting has to be properly updated
4455 //if(black_air_left && some_part_underground)
4458 lighting_invalidated_blocks[block->getPos()] = block;
4463 lighting_invalidated_blocks[block->getPos()] = block;
4468 Debug mode operation
4470 bool haxmode = g_settings.getBool("haxmode");
4473 // Don't calculate lighting at all
4474 //lighting_invalidated_blocks.clear();
4480 void ServerMap::createDir(std::string path)
4482 if(fs::CreateDir(path) == false)
4484 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4485 <<"\""<<path<<"\""<<std::endl;
4486 throw BaseException("ServerMap failed to create directory");
4490 std::string ServerMap::getSectorSubDir(v2s16 pos)
4493 snprintf(cc, 9, "%.4x%.4x",
4494 (unsigned int)pos.X&0xffff,
4495 (unsigned int)pos.Y&0xffff);
4497 return std::string(cc);
4500 std::string ServerMap::getSectorDir(v2s16 pos)
4502 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4505 v2s16 ServerMap::getSectorPos(std::string dirname)
4507 if(dirname.size() != 8)
4508 throw InvalidFilenameException("Invalid sector directory name");
4510 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4512 throw InvalidFilenameException("Invalid sector directory name");
4513 v2s16 pos((s16)x, (s16)y);
4517 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4519 v2s16 p2d = getSectorPos(sectordir);
4521 if(blockfile.size() != 4){
4522 throw InvalidFilenameException("Invalid block filename");
4525 int r = sscanf(blockfile.c_str(), "%4x", &y);
4527 throw InvalidFilenameException("Invalid block filename");
4528 return v3s16(p2d.X, y, p2d.Y);
4532 #define ENABLE_SECTOR_SAVING 1
4533 #define ENABLE_SECTOR_LOADING 1
4534 #define ENABLE_BLOCK_SAVING 1
4535 #define ENABLE_BLOCK_LOADING 1
4537 void ServerMap::save(bool only_changed)
4539 DSTACK(__FUNCTION_NAME);
4540 if(m_map_saving_enabled == false)
4542 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4546 if(only_changed == false)
4547 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4550 saveMasterHeightmap();
4552 u32 sector_meta_count = 0;
4553 u32 block_count = 0;
4556 JMutexAutoLock lock(m_sector_mutex);
4558 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4559 for(; i.atEnd() == false; i++)
4561 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4562 assert(sector->getId() == MAPSECTOR_SERVER);
4564 if(ENABLE_SECTOR_SAVING)
4566 if(sector->differs_from_disk || only_changed == false)
4568 saveSectorMeta(sector);
4569 sector_meta_count++;
4572 if(ENABLE_BLOCK_SAVING)
4574 core::list<MapBlock*> blocks;
4575 sector->getBlocks(blocks);
4576 core::list<MapBlock*>::Iterator j;
4577 for(j=blocks.begin(); j!=blocks.end(); j++)
4579 MapBlock *block = *j;
4580 if(block->getChangedFlag() || only_changed == false)
4592 Only print if something happened or saved whole map
4594 if(only_changed == false || sector_meta_count != 0
4595 || block_count != 0)
4597 dstream<<DTIME<<"ServerMap: Written: "
4598 <<sector_meta_count<<" sector metadata files, "
4599 <<block_count<<" block files"
4604 void ServerMap::loadAll()
4606 DSTACK(__FUNCTION_NAME);
4607 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4609 loadMasterHeightmap();
4611 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4613 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4615 JMutexAutoLock lock(m_sector_mutex);
4618 s32 printed_counter = -100000;
4619 s32 count = list.size();
4621 std::vector<fs::DirListNode>::iterator i;
4622 for(i=list.begin(); i!=list.end(); i++)
4624 if(counter > printed_counter + 10)
4626 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4627 printed_counter = counter;
4631 MapSector *sector = NULL;
4633 // We want directories
4637 sector = loadSectorMeta(i->name);
4639 catch(InvalidFilenameException &e)
4641 // This catches unknown crap in directory
4644 if(ENABLE_BLOCK_LOADING)
4646 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4647 (m_savedir+"/sectors/"+i->name);
4648 std::vector<fs::DirListNode>::iterator i2;
4649 for(i2=list2.begin(); i2!=list2.end(); i2++)
4655 loadBlock(i->name, i2->name, sector);
4657 catch(InvalidFilenameException &e)
4659 // This catches unknown crap in directory
4664 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4667 void ServerMap::saveMasterHeightmap()
4669 DSTACK(__FUNCTION_NAME);
4670 createDir(m_savedir);
4672 std::string fullpath = m_savedir + "/master_heightmap";
4673 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4674 if(o.good() == false)
4675 throw FileNotGoodException("Cannot open master heightmap");
4677 // Format used for writing
4678 u8 version = SER_FMT_VER_HIGHEST;
4681 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4683 [0] u8 serialization version
4684 [1] X master heightmap
4686 u32 fullsize = 1 + hmdata.getSize();
4687 SharedBuffer<u8> data(fullsize);
4690 memcpy(&data[1], *hmdata, hmdata.getSize());
4692 o.write((const char*)*data, fullsize);
4695 m_heightmap->serialize(o, version);
4698 void ServerMap::loadMasterHeightmap()
4700 DSTACK(__FUNCTION_NAME);
4701 std::string fullpath = m_savedir + "/master_heightmap";
4702 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4703 if(is.good() == false)
4704 throw FileNotGoodException("Cannot open master heightmap");
4706 if(m_heightmap != NULL)
4709 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4712 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4714 DSTACK(__FUNCTION_NAME);
4715 // Format used for writing
4716 u8 version = SER_FMT_VER_HIGHEST;
4718 v2s16 pos = sector->getPos();
4719 createDir(m_savedir);
4720 createDir(m_savedir+"/sectors");
4721 std::string dir = getSectorDir(pos);
4724 std::string fullpath = dir + "/heightmap";
4725 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4726 if(o.good() == false)
4727 throw FileNotGoodException("Cannot open master heightmap");
4729 sector->serialize(o, version);
4731 sector->differs_from_disk = false;
4734 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4736 DSTACK(__FUNCTION_NAME);
4738 v2s16 p2d = getSectorPos(dirname);
4739 std::string dir = m_savedir + "/sectors/" + dirname;
4741 std::string fullpath = dir + "/heightmap";
4742 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4743 if(is.good() == false)
4744 throw FileNotGoodException("Cannot open sector heightmap");
4746 ServerMapSector *sector = ServerMapSector::deSerialize
4747 (is, this, p2d, &m_hwrapper, m_sectors);
4749 sector->differs_from_disk = false;
4754 bool ServerMap::loadSectorFull(v2s16 p2d)
4756 DSTACK(__FUNCTION_NAME);
4757 std::string sectorsubdir = getSectorSubDir(p2d);
4759 MapSector *sector = NULL;
4761 JMutexAutoLock lock(m_sector_mutex);
4764 sector = loadSectorMeta(sectorsubdir);
4766 catch(InvalidFilenameException &e)
4770 catch(FileNotGoodException &e)
4774 catch(std::exception &e)
4779 if(ENABLE_BLOCK_LOADING)
4781 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4782 (m_savedir+"/sectors/"+sectorsubdir);
4783 std::vector<fs::DirListNode>::iterator i2;
4784 for(i2=list2.begin(); i2!=list2.end(); i2++)
4790 loadBlock(sectorsubdir, i2->name, sector);
4792 catch(InvalidFilenameException &e)
4794 // This catches unknown crap in directory
4802 bool ServerMap::deFlushSector(v2s16 p2d)
4804 DSTACK(__FUNCTION_NAME);
4805 // See if it already exists in memory
4807 MapSector *sector = getSectorNoGenerate(p2d);
4810 catch(InvalidPositionException &e)
4813 Try to load the sector from disk.
4815 if(loadSectorFull(p2d) == true)
4824 void ServerMap::saveBlock(MapBlock *block)
4826 DSTACK(__FUNCTION_NAME);
4828 Dummy blocks are not written
4830 if(block->isDummy())
4832 /*v3s16 p = block->getPos();
4833 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4834 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4838 // Format used for writing
4839 u8 version = SER_FMT_VER_HIGHEST;
4841 v3s16 p3d = block->getPos();
4842 v2s16 p2d(p3d.X, p3d.Z);
4843 createDir(m_savedir);
4844 createDir(m_savedir+"/sectors");
4845 std::string dir = getSectorDir(p2d);
4848 // Block file is map/sectors/xxxxxxxx/xxxx
4850 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4851 std::string fullpath = dir + "/" + cc;
4852 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4853 if(o.good() == false)
4854 throw FileNotGoodException("Cannot open block data");
4857 [0] u8 serialization version
4860 o.write((char*)&version, 1);
4862 block->serialize(o, version);
4865 Versions up from 9 have block objects.
4869 block->serializeObjects(o, version);
4872 // We just wrote it to the disk
4873 block->resetChangedFlag();
4876 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4878 DSTACK(__FUNCTION_NAME);
4882 // Block file is map/sectors/xxxxxxxx/xxxx
4883 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4884 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4885 if(is.good() == false)
4886 throw FileNotGoodException("Cannot open block file");
4888 v3s16 p3d = getBlockPos(sectordir, blockfile);
4889 v2s16 p2d(p3d.X, p3d.Z);
4891 assert(sector->getPos() == p2d);
4893 u8 version = SER_FMT_VER_INVALID;
4894 is.read((char*)&version, 1);
4896 /*u32 block_size = MapBlock::serializedLength(version);
4897 SharedBuffer<u8> data(block_size);
4898 is.read((char*)*data, block_size);*/
4900 // This will always return a sector because we're the server
4901 //MapSector *sector = emergeSector(p2d);
4903 MapBlock *block = NULL;
4904 bool created_new = false;
4906 block = sector->getBlockNoCreate(p3d.Y);
4908 catch(InvalidPositionException &e)
4910 block = sector->createBlankBlockNoInsert(p3d.Y);
4914 // deserialize block data
4915 block->deSerialize(is, version);
4918 Versions up from 9 have block objects.
4922 block->updateObjects(is, version, NULL, 0);
4926 sector->insertBlock(block);
4929 Convert old formats to new and save
4932 // Save old format blocks in new format
4933 if(version < SER_FMT_VER_HIGHEST)
4938 // We just loaded it from the disk, so it's up-to-date.
4939 block->resetChangedFlag();
4942 catch(SerializationError &e)
4944 dstream<<"WARNING: Invalid block data on disk "
4945 "(SerializationError). Ignoring."
4950 // Gets from master heightmap
4951 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4953 assert(m_heightmap != NULL);
4961 corners[0] = m_heightmap->getGroundHeight
4962 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4963 corners[1] = m_heightmap->getGroundHeight
4964 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4965 corners[2] = m_heightmap->getGroundHeight
4966 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4967 corners[3] = m_heightmap->getGroundHeight
4968 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
4971 void ServerMap::PrintInfo(std::ostream &out)
4982 ClientMap::ClientMap(
4984 MapDrawControl &control,
4985 scene::ISceneNode* parent,
4986 scene::ISceneManager* mgr,
4990 scene::ISceneNode(parent, mgr, id),
4997 /*m_box = core::aabbox3d<f32>(0,0,0,
4998 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4999 /*m_box = core::aabbox3d<f32>(0,0,0,
5000 map->getSizeNodes().X * BS,
5001 map->getSizeNodes().Y * BS,
5002 map->getSizeNodes().Z * BS);*/
5003 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5004 BS*1000000,BS*1000000,BS*1000000);
5006 //setPosition(v3f(BS,BS,BS));
5009 ClientMap::~ClientMap()
5011 JMutexAutoLock lock(mesh_mutex);
5020 MapSector * ClientMap::emergeSector(v2s16 p2d)
5022 DSTACK(__FUNCTION_NAME);
5023 // Check that it doesn't exist already
5025 return getSectorNoGenerate(p2d);
5027 catch(InvalidPositionException &e)
5031 // Create a sector with no heightmaps
5032 ClientMapSector *sector = new ClientMapSector(this, p2d);
5035 JMutexAutoLock lock(m_sector_mutex);
5036 m_sectors.insert(p2d, sector);
5042 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5044 DSTACK(__FUNCTION_NAME);
5045 ClientMapSector *sector = NULL;
5047 JMutexAutoLock lock(m_sector_mutex);
5049 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5053 sector = (ClientMapSector*)n->getValue();
5054 assert(sector->getId() == MAPSECTOR_CLIENT);
5058 sector = new ClientMapSector(this, p2d);
5060 JMutexAutoLock lock(m_sector_mutex);
5061 m_sectors.insert(p2d, sector);
5065 sector->deSerialize(is);
5068 void ClientMap::OnRegisterSceneNode()
5072 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5073 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5076 ISceneNode::OnRegisterSceneNode();
5079 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5081 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5082 DSTACK(__FUNCTION_NAME);
5084 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5087 Get time for measuring timeout.
5089 Measuring time is very useful for long delays when the
5090 machine is swapping a lot.
5092 int time1 = time(0);
5094 u32 daynight_ratio = m_client->getDayNightRatio();
5096 m_camera_mutex.Lock();
5097 v3f camera_position = m_camera_position;
5098 v3f camera_direction = m_camera_direction;
5099 m_camera_mutex.Unlock();
5102 Get all blocks and draw all visible ones
5105 v3s16 cam_pos_nodes(
5106 camera_position.X / BS,
5107 camera_position.Y / BS,
5108 camera_position.Z / BS);
5110 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5112 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5113 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5115 // Take a fair amount as we will be dropping more out later
5117 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5118 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5119 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5121 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5122 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5123 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5125 u32 vertex_count = 0;
5127 // For limiting number of mesh updates per frame
5128 u32 mesh_update_count = 0;
5130 u32 blocks_would_have_drawn = 0;
5131 u32 blocks_drawn = 0;
5133 //NOTE: The sectors map should be locked but we're not doing it
5134 // because it'd cause too much delays
5136 int timecheck_counter = 0;
5137 core::map<v2s16, MapSector*>::Iterator si;
5138 si = m_sectors.getIterator();
5139 for(; si.atEnd() == false; si++)
5142 timecheck_counter++;
5143 if(timecheck_counter > 50)
5145 int time2 = time(0);
5146 if(time2 > time1 + 4)
5148 dstream<<"ClientMap::renderMap(): "
5149 "Rendering takes ages, returning."
5156 MapSector *sector = si.getNode()->getValue();
5157 v2s16 sp = sector->getPos();
5159 if(m_control.range_all == false)
5161 if(sp.X < p_blocks_min.X
5162 || sp.X > p_blocks_max.X
5163 || sp.Y < p_blocks_min.Z
5164 || sp.Y > p_blocks_max.Z)
5168 core::list< MapBlock * > sectorblocks;
5169 sector->getBlocks(sectorblocks);
5175 core::list< MapBlock * >::Iterator i;
5176 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5178 MapBlock *block = *i;
5181 Compare block position to camera position, skip
5182 if not seen on display
5185 float range = 100000 * BS;
5186 if(m_control.range_all == false)
5187 range = m_control.wanted_range * BS;
5189 if(isBlockInSight(block->getPos(), camera_position,
5190 camera_direction, range) == false)
5196 v3s16 blockpos_nodes = block->getPosRelative();
5198 // Block center position
5200 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5201 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5202 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5205 // Block position relative to camera
5206 v3f blockpos_relative = blockpos - camera_position;
5208 // Distance in camera direction (+=front, -=back)
5209 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5212 f32 d = blockpos_relative.getLength();
5214 if(m_control.range_all == false)
5216 // If block is far away, don't draw it
5217 if(d > m_control.wanted_range * BS)
5221 // Maximum radius of a block
5222 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5224 // If block is (nearly) touching the camera, don't
5225 // bother validating further (that is, render it anyway)
5226 if(d > block_max_radius * 1.5)
5228 // Cosine of the angle between the camera direction
5229 // and the block direction (camera_direction is an unit vector)
5230 f32 cosangle = dforward / d;
5232 // Compensate for the size of the block
5233 // (as the block has to be shown even if it's a bit off FOV)
5234 // This is an estimate.
5235 cosangle += block_max_radius / dforward;
5237 // If block is not in the field of view, skip it
5238 //if(cosangle < cos(FOV_ANGLE/2))
5239 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5244 v3s16 blockpos_nodes = block->getPosRelative();
5246 // Block center position
5248 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5249 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5250 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5253 // Block position relative to camera
5254 v3f blockpos_relative = blockpos - camera_position;
5257 f32 d = blockpos_relative.getLength();
5264 bool mesh_expired = false;
5267 JMutexAutoLock lock(block->mesh_mutex);
5269 mesh_expired = block->getMeshExpired();
5271 // Mesh has not been expired and there is no mesh:
5272 // block has no content
5273 if(block->mesh == NULL && mesh_expired == false)
5277 f32 faraway = BS*50;
5278 //f32 faraway = m_control.wanted_range * BS;
5281 This has to be done with the mesh_mutex unlocked
5283 // Pretty random but this should work somewhat nicely
5284 if(mesh_expired && (
5285 (mesh_update_count < 3
5286 && (d < faraway || mesh_update_count < 2)
5289 (m_control.range_all && mesh_update_count < 20)
5292 /*if(mesh_expired && mesh_update_count < 6
5293 && (d < faraway || mesh_update_count < 3))*/
5295 mesh_update_count++;
5297 // Mesh has been expired: generate new mesh
5298 //block->updateMeshes(daynight_i);
5299 block->updateMesh(daynight_ratio);
5301 mesh_expired = false;
5305 Don't draw an expired mesh that is far away
5307 /*if(mesh_expired && d >= faraway)
5310 // Instead, delete it
5311 JMutexAutoLock lock(block->mesh_mutex);
5314 block->mesh->drop();
5317 // And continue to next block
5322 Draw the faces of the block
5325 JMutexAutoLock lock(block->mesh_mutex);
5327 scene::SMesh *mesh = block->mesh;
5332 blocks_would_have_drawn++;
5333 if(blocks_drawn >= m_control.wanted_max_blocks
5334 && m_control.range_all == false
5335 && d > m_control.wanted_min_range * BS)
5339 u32 c = mesh->getMeshBufferCount();
5341 for(u32 i=0; i<c; i++)
5343 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5344 const video::SMaterial& material = buf->getMaterial();
5345 video::IMaterialRenderer* rnd =
5346 driver->getMaterialRenderer(material.MaterialType);
5347 bool transparent = (rnd && rnd->isTransparent());
5348 // Render transparent on transparent pass and likewise.
5349 if(transparent == is_transparent_pass)
5351 driver->setMaterial(buf->getMaterial());
5352 driver->drawMeshBuffer(buf);
5353 vertex_count += buf->getVertexCount();
5357 } // foreach sectorblocks
5360 m_control.blocks_drawn = blocks_drawn;
5361 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5363 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5364 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5367 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5368 core::map<v3s16, MapBlock*> *affected_blocks)
5370 bool changed = false;
5372 Add it to all blocks touching it
5375 v3s16(0,0,0), // this
5376 v3s16(0,0,1), // back
5377 v3s16(0,1,0), // top
5378 v3s16(1,0,0), // right
5379 v3s16(0,0,-1), // front
5380 v3s16(0,-1,0), // bottom
5381 v3s16(-1,0,0), // left
5383 for(u16 i=0; i<7; i++)
5385 v3s16 p2 = p + dirs[i];
5386 // Block position of neighbor (or requested) node
5387 v3s16 blockpos = getNodeBlockPos(p2);
5388 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5389 if(blockref == NULL)
5391 // Relative position of requested node
5392 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5393 if(blockref->setTempMod(relpos, mod))
5398 if(changed && affected_blocks!=NULL)
5400 for(u16 i=0; i<7; i++)
5402 v3s16 p2 = p + dirs[i];
5403 // Block position of neighbor (or requested) node
5404 v3s16 blockpos = getNodeBlockPos(p2);
5405 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5406 if(blockref == NULL)
5408 affected_blocks->insert(blockpos, blockref);
5414 bool ClientMap::clearTempMod(v3s16 p,
5415 core::map<v3s16, MapBlock*> *affected_blocks)
5417 bool changed = false;
5419 v3s16(0,0,0), // this
5420 v3s16(0,0,1), // back
5421 v3s16(0,1,0), // top
5422 v3s16(1,0,0), // right
5423 v3s16(0,0,-1), // front
5424 v3s16(0,-1,0), // bottom
5425 v3s16(-1,0,0), // left
5427 for(u16 i=0; i<7; i++)
5429 v3s16 p2 = p + dirs[i];
5430 // Block position of neighbor (or requested) node
5431 v3s16 blockpos = getNodeBlockPos(p2);
5432 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5433 if(blockref == NULL)
5435 // Relative position of requested node
5436 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5437 if(blockref->clearTempMod(relpos))
5442 if(changed && affected_blocks!=NULL)
5444 for(u16 i=0; i<7; i++)
5446 v3s16 p2 = p + dirs[i];
5447 // Block position of neighbor (or requested) node
5448 v3s16 blockpos = getNodeBlockPos(p2);
5449 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5450 if(blockref == NULL)
5452 affected_blocks->insert(blockpos, blockref);
5458 void ClientMap::PrintInfo(std::ostream &out)
5469 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5474 MapVoxelManipulator::~MapVoxelManipulator()
5476 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5480 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5482 TimeTaker timer1("emerge", &emerge_time);
5484 // Units of these are MapBlocks
5485 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5486 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5488 VoxelArea block_area_nodes
5489 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5491 addArea(block_area_nodes);
5493 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5494 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5495 for(s32 x=p_min.X; x<=p_max.X; x++)
5498 core::map<v3s16, bool>::Node *n;
5499 n = m_loaded_blocks.find(p);
5503 bool block_data_inexistent = false;
5506 TimeTaker timer1("emerge load", &emerge_load_time);
5508 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5509 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5512 dstream<<std::endl;*/
5514 MapBlock *block = m_map->getBlockNoCreate(p);
5515 if(block->isDummy())
5516 block_data_inexistent = true;
5518 block->copyTo(*this);
5520 catch(InvalidPositionException &e)
5522 block_data_inexistent = true;
5525 if(block_data_inexistent)
5527 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5528 // Fill with VOXELFLAG_INEXISTENT
5529 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5530 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5532 s32 i = m_area.index(a.MinEdge.X,y,z);
5533 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5537 m_loaded_blocks.insert(p, !block_data_inexistent);
5540 //dstream<<"emerge done"<<std::endl;
5544 SUGG: Add an option to only update eg. water and air nodes.
5545 This will make it interfere less with important stuff if
5548 void MapVoxelManipulator::blitBack
5549 (core::map<v3s16, MapBlock*> & modified_blocks)
5551 if(m_area.getExtent() == v3s16(0,0,0))
5554 //TimeTaker timer1("blitBack");
5556 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5557 <<m_loaded_blocks.size()<<std::endl;*/
5560 Initialize block cache
5562 v3s16 blockpos_last;
5563 MapBlock *block = NULL;
5564 bool block_checked_in_modified = false;
5566 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5567 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5568 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5572 u8 f = m_flags[m_area.index(p)];
5573 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5576 MapNode &n = m_data[m_area.index(p)];
5578 v3s16 blockpos = getNodeBlockPos(p);
5583 if(block == NULL || blockpos != blockpos_last){
5584 block = m_map->getBlockNoCreate(blockpos);
5585 blockpos_last = blockpos;
5586 block_checked_in_modified = false;
5589 // Calculate relative position in block
5590 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5592 // Don't continue if nothing has changed here
5593 if(block->getNode(relpos) == n)
5596 //m_map->setNode(m_area.MinEdge + p, n);
5597 block->setNode(relpos, n);
5600 Make sure block is in modified_blocks
5602 if(block_checked_in_modified == false)
5604 modified_blocks[blockpos] = block;
5605 block_checked_in_modified = true;
5608 catch(InvalidPositionException &e)
5614 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5615 MapVoxelManipulator(map)
5619 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5623 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5625 // Just create the area so that it can be pointed to
5626 VoxelManipulator::emerge(a, caller_id);
5629 void ManualMapVoxelManipulator::initialEmerge(
5630 v3s16 blockpos_min, v3s16 blockpos_max)
5632 TimeTaker timer1("initialEmerge", &emerge_time);
5634 // Units of these are MapBlocks
5635 v3s16 p_min = blockpos_min;
5636 v3s16 p_max = blockpos_max;
5638 VoxelArea block_area_nodes
5639 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5641 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5644 dstream<<"initialEmerge: area: ";
5645 block_area_nodes.print(dstream);
5646 dstream<<" ("<<size_MB<<"MB)";
5650 addArea(block_area_nodes);
5652 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5653 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5654 for(s32 x=p_min.X; x<=p_max.X; x++)
5657 core::map<v3s16, bool>::Node *n;
5658 n = m_loaded_blocks.find(p);
5662 bool block_data_inexistent = false;
5665 TimeTaker timer1("emerge load", &emerge_load_time);
5667 MapBlock *block = m_map->getBlockNoCreate(p);
5668 if(block->isDummy())
5669 block_data_inexistent = true;
5671 block->copyTo(*this);
5673 catch(InvalidPositionException &e)
5675 block_data_inexistent = true;
5678 if(block_data_inexistent)
5681 Mark area inexistent
5683 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5684 // Fill with VOXELFLAG_INEXISTENT
5685 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5686 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5688 s32 i = m_area.index(a.MinEdge.X,y,z);
5689 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5693 m_loaded_blocks.insert(p, !block_data_inexistent);
5697 void ManualMapVoxelManipulator::blitBackAll(
5698 core::map<v3s16, MapBlock*> * modified_blocks)
5700 if(m_area.getExtent() == v3s16(0,0,0))
5704 Copy data of all blocks
5706 for(core::map<v3s16, bool>::Iterator
5707 i = m_loaded_blocks.getIterator();
5708 i.atEnd() == false; i++)
5710 bool existed = i.getNode()->getValue();
5711 if(existed == false)
5713 v3s16 p = i.getNode()->getKey();
5714 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5717 dstream<<"WARNING: "<<__FUNCTION_NAME
5718 <<": got NULL block "
5719 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5724 block->copyFrom(*this);
5727 modified_blocks->insert(p, block);