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(0));
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, 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)
2388 vmanip.m_area.add_y(em, i, -1);
2390 if(y >= y_nodes_min)
2393 surface_y = y_nodes_min;
2401 v3s16 em = vmanip.m_area.getExtent();
2402 s16 y_start = surface_y+1;
2403 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2407 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2409 MapNode &n = vmanip.m_data[i];
2410 n.d = CONTENT_STONE;
2412 if(y > stone_surface_max_y)
2413 stone_surface_max_y = y;
2416 if(count >= ob_size.Y)
2419 vmanip.m_area.add_y(em, i, 1);
2423 for(; y<=y_nodes_max; y++)
2425 MapNode &n = vmanip.m_data[i];
2428 if(count >= mud_amount)
2431 vmanip.m_area.add_y(em, i, 1);
2441 //TimeTaker timer1("dungeons");
2446 u32 dungeons_count = relative_volume/200000;
2447 for(u32 jj=0; jj<dungeons_count; jj++)
2449 s16 min_tunnel_diameter = 1;
2450 s16 max_tunnel_diameter = 5;
2451 u16 tunnel_routepoints = 15;
2453 u32 bruise_surface_maxindex =
2454 dungeons_count / 10 * stone_surface_max_y / 10;
2455 bruise_surface_maxindex =
2456 rangelim(bruise_surface_maxindex, 0, dungeons_count/2);
2457 bool bruise_surface = (jj < bruise_surface_maxindex);
2461 min_tunnel_diameter = 5;
2462 max_tunnel_diameter = 10;
2463 tunnel_routepoints = 10;
2466 // Allowed route area size in nodes
2468 sectorpos_base_size*MAP_BLOCKSIZE,
2469 h_blocks*MAP_BLOCKSIZE,
2470 sectorpos_base_size*MAP_BLOCKSIZE
2473 // Area starting point in nodes
2475 sectorpos_base.X*MAP_BLOCKSIZE,
2476 y_blocks_min*MAP_BLOCKSIZE,
2477 sectorpos_base.Y*MAP_BLOCKSIZE
2481 //(this should be more than the maximum radius of the tunnel)
2483 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2484 ar += v3s16(1,0,1) * more * 2;
2485 of -= v3s16(1,0,1) * more;
2487 s16 route_y_min = 0;
2488 //s16 route_y_max = ar.Y-1;
2489 s16 route_y_max = stone_surface_max_y - of.Y;
2493 // Minimum is at y=0
2494 route_y_min = -of.Y - 0;
2495 route_y_min = rangelim(route_y_min, 0, route_y_max);
2498 /*dstream<<"route_y_min = "<<route_y_min
2499 <<", route_y_max = "<<route_y_max<<std::endl;*/
2501 // Randomize starting position
2503 (float)(myrand()%ar.X)+0.5,
2504 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2505 (float)(myrand()%ar.Z)+0.5
2508 MapNode airnode(CONTENT_AIR);
2511 Generate some tunnel starting from orp
2514 for(u16 j=0; j<tunnel_routepoints; j++)
2516 v3s16 maxlen(20, 10, 20);
2520 maxlen = v3s16(60,60,60);
2524 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2525 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2526 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2531 else if(rp.X >= ar.X)
2533 if(rp.Y < route_y_min)
2535 else if(rp.Y >= route_y_max)
2536 rp.Y = route_y_max-1;
2539 else if(rp.Z >= ar.Z)
2544 s16 min_d = min_tunnel_diameter;
2545 s16 max_d = max_tunnel_diameter;
2546 s16 rs = myrand_range(min_d, max_d);
2548 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2550 v3f fp = orp + vec * f;
2551 v3s16 cp(fp.X, fp.Y, fp.Z);
2554 s16 d1 = d0 + rs - 1;
2555 for(s16 z0=d0; z0<=d1; z0++)
2557 s16 si = rs - abs(z0);
2558 for(s16 x0=-si; x0<=si-1; x0++)
2560 s16 si2 = rs - abs(x0);
2561 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2567 /*if(isInArea(p, ar) == false)
2569 // Check only height
2570 if(y < 0 || y >= ar.Y)
2574 //assert(vmanip.m_area.contains(p));
2575 if(vmanip.m_area.contains(p) == false)
2577 dstream<<"WARNING: "<<__FUNCTION_NAME
2578 <<":"<<__LINE__<<": "
2579 <<"point not in area"
2584 // Just set it to air, it will be changed to
2586 u32 i = vmanip.m_area.index(p);
2587 vmanip.m_data[i] = airnode;
2601 //TimeTaker timer1("ore veins");
2606 for(u32 jj=0; jj<relative_volume/524; jj++)
2608 s16 max_vein_diameter = 3;
2610 // Allowed route area size in nodes
2612 sectorpos_base_size*MAP_BLOCKSIZE,
2613 h_blocks*MAP_BLOCKSIZE,
2614 sectorpos_base_size*MAP_BLOCKSIZE
2617 // Area starting point in nodes
2619 sectorpos_base.X*MAP_BLOCKSIZE,
2620 y_blocks_min*MAP_BLOCKSIZE,
2621 sectorpos_base.Y*MAP_BLOCKSIZE
2625 //(this should be more than the maximum radius of the tunnel)
2627 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2628 ar += v3s16(1,0,1) * more * 2;
2629 of -= v3s16(1,0,1) * more;
2631 // Randomize starting position
2633 (float)(myrand()%ar.X)+0.5,
2634 (float)(myrand()%ar.Y)+0.5,
2635 (float)(myrand()%ar.Z)+0.5
2638 // Randomize mineral
2639 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2642 Generate some vein starting from orp
2645 for(u16 j=0; j<2; j++)
2648 (float)(myrand()%ar.X)+0.5,
2649 (float)(myrand()%ar.Y)+0.5,
2650 (float)(myrand()%ar.Z)+0.5
2652 v3f vec = rp - orp;*/
2654 v3s16 maxlen(10, 10, 10);
2656 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2657 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2658 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2663 else if(rp.X >= ar.X)
2667 else if(rp.Y >= ar.Y)
2671 else if(rp.Z >= ar.Z)
2677 s16 max_d = max_vein_diameter;
2678 s16 rs = myrand_range(min_d, max_d);
2680 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2682 v3f fp = orp + vec * f;
2683 v3s16 cp(fp.X, fp.Y, fp.Z);
2685 s16 d1 = d0 + rs - 1;
2686 for(s16 z0=d0; z0<=d1; z0++)
2688 s16 si = rs - abs(z0);
2689 for(s16 x0=-si; x0<=si-1; x0++)
2691 s16 si2 = rs - abs(x0);
2692 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2694 // Don't put mineral to every place
2702 /*if(isInArea(p, ar) == false)
2704 // Check only height
2705 if(y < 0 || y >= ar.Y)
2709 assert(vmanip.m_area.contains(p));
2711 // Just set it to air, it will be changed to
2713 u32 i = vmanip.m_area.index(p);
2714 MapNode *n = &vmanip.m_data[i];
2715 if(n->d == CONTENT_STONE)
2730 //TimeTaker timer1("add mud");
2733 Add mud to the central chunk
2736 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2737 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2739 // Node position in 2d
2740 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2742 // Find ground level
2743 s16 surface_y = find_ground_level(vmanip, p2d);
2746 If topmost node is grass, change it to mud.
2747 It might be if it was flown to there from a neighboring
2748 chunk and then converted.
2751 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2752 MapNode *n = &vmanip.m_data[i];
2753 if(n->d == CONTENT_GRASS)
2762 v3s16 em = vmanip.m_area.getExtent();
2763 s16 y_start = surface_y+1;
2764 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2765 for(s16 y=y_start; y<=y_nodes_max; y++)
2767 MapNode &n = vmanip.m_data[i];
2773 vmanip.m_area.add_y(em, i, 1);
2782 //TimeTaker timer1("flow mud");
2785 Flow mud away from steep edges
2788 // Iterate a few times
2789 for(s16 k=0; k<4; k++)
2792 for(s16 x=0-max_spread_amount+1;
2793 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2795 for(s16 z=0-max_spread_amount+1;
2796 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2799 // Node position in 2d
2800 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2802 v3s16 em = vmanip.m_area.getExtent();
2803 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2805 // Go to ground level
2806 for(y=y_nodes_max; y>=y_nodes_min; y--)
2808 MapNode &n = vmanip.m_data[i];
2809 //if(n.d != CONTENT_AIR)
2810 if(content_walkable(n.d))
2813 vmanip.m_area.add_y(em, i, -1);
2816 // If not mud, do nothing to it
2817 MapNode *n = &vmanip.m_data[i];
2818 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2821 // Make it exactly mud
2825 v3s16(0,0,1), // back
2826 v3s16(1,0,0), // right
2827 v3s16(0,0,-1), // front
2828 v3s16(-1,0,0), // left
2833 for(u32 di=0; di<4; di++)
2835 v3s16 dirp = dirs4[di];
2837 // Check that side is air
2838 vmanip.m_area.add_p(em, i2, dirp);
2839 MapNode *n2 = &vmanip.m_data[i2];
2840 if(content_walkable(n2->d))
2842 // Check that under side is air
2843 vmanip.m_area.add_y(em, i2, -1);
2844 n2 = &vmanip.m_data[i2];
2845 if(content_walkable(n2->d))
2847 // Loop further down until not air
2849 vmanip.m_area.add_y(em, i2, -1);
2850 n2 = &vmanip.m_data[i2];
2851 }while(content_walkable(n2->d) == false);
2852 // Loop one up so that we're in air
2853 vmanip.m_area.add_y(em, i2, 1);
2854 n2 = &vmanip.m_data[i2];
2856 // Move mud to new place
2858 // Set old place to be air
2859 *n = MapNode(CONTENT_AIR);
2862 // Switch mud and other and change mud source to air
2863 //MapNode tempnode = *n2;
2866 // Force old mud position to be air
2880 //TimeTaker timer1("add water");
2883 Add water to the central chunk (and a bit more)
2886 for(s16 x=0-max_spread_amount;
2887 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2889 for(s16 z=0-max_spread_amount;
2890 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2893 // Node position in 2d
2894 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2896 // Find ground level
2897 //s16 surface_y = find_ground_level(vmanip, p2d);
2900 If ground level is over water level, skip.
2901 NOTE: This leaves caves near water without water,
2902 which looks especially crappy when the nearby water
2903 won't start flowing either for some reason
2905 /*if(surface_y > WATER_LEVEL)
2912 v3s16 em = vmanip.m_area.getExtent();
2913 s16 y_start = WATER_LEVEL;
2914 u8 light = LIGHT_MAX;
2915 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2916 MapNode *n = &vmanip.m_data[i];
2918 Add first one to transforming liquid queue
2920 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2922 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2923 m_transforming_liquid.push_back(p);
2925 for(s16 y=y_start; y>=y_nodes_min; y--)
2927 n = &vmanip.m_data[i];
2929 // Stop when there is no water and no air
2930 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2931 && n->d != CONTENT_WATER)
2934 Add bottom one to transforming liquid queue
2936 vmanip.m_area.add_y(em, i, 1);
2937 n = &vmanip.m_data[i];
2938 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2940 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2941 m_transforming_liquid.push_back(p);
2947 n->d = CONTENT_WATERSOURCE;
2948 n->setLight(LIGHTBANK_DAY, light);
2950 /*// Add to transforming liquid queue (in case it'd
2952 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2953 m_transforming_liquid.push_back(p);*/
2956 vmanip.m_area.add_y(em, i, -1);
2970 //TimeTaker timer1("plant trees");
2976 u32 tree_max = relative_area / 100;
2978 u32 count = myrand_range(0, tree_max);
2979 for(u32 i=0; i<count; i++)
2981 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2982 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2983 x += sectorpos_base.X*MAP_BLOCKSIZE;
2984 z += sectorpos_base.Y*MAP_BLOCKSIZE;
2985 s16 y = find_ground_level(vmanip, v2s16(x,z));
2986 // Don't make a tree under water level
2991 make_tree(vmanip, p);
2999 //TimeTaker timer1("grow grass");
3005 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3006 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3007 for(s16 x=0-max_spread_amount;
3008 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3010 for(s16 z=0-max_spread_amount;
3011 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3014 // Node position in 2d
3015 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3018 Find the lowest surface to which enough light ends up
3021 Basically just wait until not air and not leaves.
3025 v3s16 em = vmanip.m_area.getExtent();
3026 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3028 // Go to ground level
3029 for(y=y_nodes_max; y>=y_nodes_min; y--)
3031 MapNode &n = vmanip.m_data[i];
3032 if(n.d != CONTENT_AIR
3033 && n.d != CONTENT_LEAVES)
3035 vmanip.m_area.add_y(em, i, -1);
3037 if(y >= y_nodes_min)
3040 surface_y = y_nodes_min;
3043 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3044 MapNode *n = &vmanip.m_data[i];
3045 if(n->d == CONTENT_MUD)
3046 n->d = CONTENT_GRASS;
3055 core::map<v3s16, bool> light_sources;
3058 // 750ms @cs=8, can't optimize more
3059 //TimeTaker timer1("initial lighting");
3061 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3062 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3063 for(s16 x=0-max_spread_amount+1;
3064 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3066 for(s16 z=0-max_spread_amount+1;
3067 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3070 // Node position in 2d
3071 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3074 Apply initial sunlight
3077 u8 light = LIGHT_SUN;
3078 bool add_to_sources = false;
3079 v3s16 em = vmanip.m_area.getExtent();
3080 s16 y_start = y_nodes_max;
3081 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3082 for(s16 y=y_start; y>=y_nodes_min; y--)
3084 MapNode *n = &vmanip.m_data[i];
3086 if(light_propagates_content(n->d) == false)
3090 else if(light != LIGHT_SUN
3091 || sunlight_propagates_content(n->d) == false)
3097 // This doesn't take much time
3098 if(add_to_sources == false)
3101 Check sides. If side is not air or water, start
3102 adding to light_sources.
3105 v3s16(0,0,1), // back
3106 v3s16(1,0,0), // right
3107 v3s16(0,0,-1), // front
3108 v3s16(-1,0,0), // left
3110 for(u32 di=0; di<4; di++)
3112 v3s16 dirp = dirs4[di];
3114 vmanip.m_area.add_p(em, i2, dirp);
3115 MapNode *n2 = &vmanip.m_data[i2];
3117 n2->d != CONTENT_AIR
3118 && n2->d != CONTENT_WATERSOURCE
3119 && n2->d != CONTENT_WATER
3121 add_to_sources = true;
3127 n->setLight(LIGHTBANK_DAY, light);
3128 n->setLight(LIGHTBANK_NIGHT, 0);
3130 // This doesn't take much time
3131 if(light != 0 && add_to_sources)
3133 // Insert light source
3134 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3137 // Increment index by y
3138 vmanip.m_area.add_y(em, i, -1);
3145 // Spread light around
3147 TimeTaker timer("generateChunkRaw() spreadLight");
3148 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3155 timer_generate.stop();
3158 Blit generated stuff to map
3162 //TimeTaker timer("generateChunkRaw() blitBackAll");
3163 vmanip.blitBackAll(&changed_blocks);
3166 Update day/night difference cache of the MapBlocks
3169 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3170 i.atEnd() == false; i++)
3172 MapBlock *block = i.getNode()->getValue();
3173 block->updateDayNightDiff();
3179 Create chunk metadata
3182 for(s16 x=-1; x<=1; x++)
3183 for(s16 y=-1; y<=1; y++)
3185 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3186 // Add chunk meta information
3187 MapChunk *chunk = getChunk(chunkpos0);
3190 chunk = new MapChunk();
3191 m_chunks.insert(chunkpos0, chunk);
3193 //chunk->setIsVolatile(true);
3194 if(chunk->getGenLevel() > GENERATED_PARTLY)
3195 chunk->setGenLevel(GENERATED_PARTLY);
3199 Set central chunk non-volatile and return it
3201 MapChunk *chunk = getChunk(chunkpos);
3204 //chunk->setIsVolatile(false);
3205 chunk->setGenLevel(GENERATED_FULLY);
3210 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3211 core::map<v3s16, MapBlock*> &changed_blocks)
3213 dstream<<"generateChunk(): Generating chunk "
3214 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3217 /*for(s16 x=-1; x<=1; x++)
3218 for(s16 y=-1; y<=1; y++)*/
3219 for(s16 x=-0; x<=0; x++)
3220 for(s16 y=-0; y<=0; y++)
3222 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3223 MapChunk *chunk = getChunk(chunkpos0);
3224 // Skip if already generated
3225 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3227 generateChunkRaw(chunkpos0, changed_blocks);
3230 assert(chunkNonVolatile(chunkpos1));
3232 MapChunk *chunk = getChunk(chunkpos1);
3236 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3238 DSTACK("%s: p2d=(%d,%d)",
3243 Check if it exists already in memory
3245 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3250 Try to load it from disk (with blocks)
3252 if(loadSectorFull(p2d) == true)
3254 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3257 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3258 throw InvalidPositionException("");
3264 If there is no master heightmap, throw.
3266 if(m_heightmap == NULL)
3268 throw InvalidPositionException("createSector(): no heightmap");
3272 Do not create over-limit
3274 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3275 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3276 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3277 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3278 throw InvalidPositionException("createSector(): pos. over limit");
3281 Generate blank sector
3284 // Number of heightmaps in sector in each direction
3285 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3287 // Heightmap side width
3288 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3290 sector = new ServerMapSector(this, p2d, hm_split);
3292 // Sector position on map in nodes
3293 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3295 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3296 " heightmaps and objects"<<std::endl;*/
3299 Generate sector heightmap
3302 v2s16 mhm_p = p2d * hm_split;
3303 /*f32 corners[4] = {
3304 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3305 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3306 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3307 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3310 // Loop through sub-heightmaps
3311 for(s16 y=0; y<hm_split; y++)
3312 for(s16 x=0; x<hm_split; x++)
3314 v2s16 p_in_sector = v2s16(x,y);
3315 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3317 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3318 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3319 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3320 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3323 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3324 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3327 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3329 sector->setHeightmap(p_in_sector, hm);
3331 //hm->generateContinued(1.0, 0.5, corners);
3332 hm->generateContinued(0.5, 0.5, corners);
3337 // Add dummy objects
3338 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3339 sector->setObjects(objects);
3344 m_sectors.insert(p2d, sector);
3349 MapSector * ServerMap::emergeSector(v2s16 p2d,
3350 core::map<v3s16, MapBlock*> &changed_blocks)
3352 DSTACK("%s: p2d=(%d,%d)",
3359 v2s16 chunkpos = sector_to_chunk(p2d);
3360 /*bool chunk_nonvolatile = false;
3361 MapChunk *chunk = getChunk(chunkpos);
3362 if(chunk && chunk->getIsVolatile() == false)
3363 chunk_nonvolatile = true;*/
3364 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3367 If chunk is not fully generated, generate chunk
3369 if(chunk_nonvolatile == false)
3371 // Generate chunk and neighbors
3372 generateChunk(chunkpos, changed_blocks);
3376 Return sector if it exists now
3378 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3383 Try to load it from disk
3385 if(loadSectorFull(p2d) == true)
3387 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3390 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3391 throw InvalidPositionException("");
3397 generateChunk should have generated the sector
3404 //return generateSector();
3408 NOTE: This is not used for main map generation, only for blocks
3409 that are very high or low
3411 MapBlock * ServerMap::generateBlock(
3413 MapBlock *original_dummy,
3414 ServerMapSector *sector,
3415 core::map<v3s16, MapBlock*> &changed_blocks,
3416 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3419 DSTACK("%s: p=(%d,%d,%d)",
3423 /*dstream<<"generateBlock(): "
3424 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3427 MapBlock *block = original_dummy;
3429 v2s16 p2d(p.X, p.Z);
3433 Do not generate over-limit
3435 if(blockpos_over_limit(p))
3437 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3438 throw InvalidPositionException("generateBlock(): pos. over limit");
3442 If block doesn't exist, create one.
3443 If it exists, it is a dummy. In that case unDummify() it.
3445 NOTE: This already sets the map as the parent of the block
3449 block = sector->createBlankBlockNoInsert(block_y);
3453 // Remove the block so that nobody can get a half-generated one.
3454 sector->removeBlock(block);
3455 // Allocate the block to contain the generated data
3459 /*u8 water_material = CONTENT_WATER;
3460 if(g_settings.getBool("endless_water"))
3461 water_material = CONTENT_WATERSOURCE;*/
3462 u8 water_material = CONTENT_WATERSOURCE;
3464 s32 lowest_ground_y = 32767;
3465 s32 highest_ground_y = -32768;
3468 //sector->printHeightmaps();
3470 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3471 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3473 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3475 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3476 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3477 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3479 dstream<<"WARNING: Surface height not found in sector "
3480 "for block that is being emerged"<<std::endl;
3484 s16 surface_y = surface_y_f;
3485 //avg_ground_y += surface_y;
3486 if(surface_y < lowest_ground_y)
3487 lowest_ground_y = surface_y;
3488 if(surface_y > highest_ground_y)
3489 highest_ground_y = surface_y;
3491 s32 surface_depth = 0;
3493 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3495 //float min_slope = 0.45;
3496 //float max_slope = 0.85;
3497 float min_slope = 0.60;
3498 float max_slope = 1.20;
3499 float min_slope_depth = 5.0;
3500 float max_slope_depth = 0;
3502 if(slope < min_slope)
3503 surface_depth = min_slope_depth;
3504 else if(slope > max_slope)
3505 surface_depth = max_slope_depth;
3507 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3509 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3511 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3516 NOTE: If there are some man-made structures above the
3517 newly created block, they won't be taken into account.
3519 if(real_y > surface_y)
3520 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3526 // If node is over heightmap y, it's air or water
3527 if(real_y > surface_y)
3529 // If under water level, it's water
3530 if(real_y < WATER_LEVEL)
3532 n.d = water_material;
3533 n.setLight(LIGHTBANK_DAY,
3534 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3536 Add to transforming liquid queue (in case it'd
3539 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3540 m_transforming_liquid.push_back(real_pos);
3546 // Else it's ground or dungeons (air)
3549 // If it's surface_depth under ground, it's stone
3550 if(real_y <= surface_y - surface_depth)
3552 n.d = CONTENT_STONE;
3556 // It is mud if it is under the first ground
3557 // level or under water
3558 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3564 n.d = CONTENT_GRASS;
3567 //n.d = CONTENT_MUD;
3569 /*// If under water level, it's mud
3570 if(real_y < WATER_LEVEL)
3572 // Only the topmost node is grass
3573 else if(real_y <= surface_y - 1)
3576 n.d = CONTENT_GRASS;*/
3580 block->setNode(v3s16(x0,y0,z0), n);
3585 Calculate some helper variables
3588 // Completely underground if the highest part of block is under lowest
3590 // This has to be very sure; it's probably one too strict now but
3591 // that's just better.
3592 bool completely_underground =
3593 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3595 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3597 bool mostly_underwater_surface = false;
3598 if(highest_ground_y < WATER_LEVEL
3599 && some_part_underground && !completely_underground)
3600 mostly_underwater_surface = true;
3603 Get local attributes
3606 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3608 float caves_amount = 0.5;
3613 NOTE: BEWARE: Too big amount of attribute points slows verything
3615 1 interpolation from 5000 points takes 2-3ms.
3617 //TimeTaker timer("generateBlock() local attribute retrieval");
3618 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3619 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3620 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3624 //dstream<<"generateBlock(): Done"<<std::endl;
3630 // Initialize temporary table
3631 const s32 ued = MAP_BLOCKSIZE;
3632 bool underground_emptiness[ued*ued*ued];
3633 for(s32 i=0; i<ued*ued*ued; i++)
3635 underground_emptiness[i] = 0;
3642 Initialize orp and ors. Try to find if some neighboring
3643 MapBlock has a tunnel ended in its side
3647 (float)(myrand()%ued)+0.5,
3648 (float)(myrand()%ued)+0.5,
3649 (float)(myrand()%ued)+0.5
3652 bool found_existing = false;
3658 for(s16 y=0; y<ued; y++)
3659 for(s16 x=0; x<ued; x++)
3661 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3662 if(getNode(ap).d == CONTENT_AIR)
3664 orp = v3f(x+1,y+1,0);
3665 found_existing = true;
3666 goto continue_generating;
3670 catch(InvalidPositionException &e){}
3676 for(s16 y=0; y<ued; y++)
3677 for(s16 x=0; x<ued; x++)
3679 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3680 if(getNode(ap).d == CONTENT_AIR)
3682 orp = v3f(x+1,y+1,ued-1);
3683 found_existing = true;
3684 goto continue_generating;
3688 catch(InvalidPositionException &e){}
3694 for(s16 y=0; y<ued; y++)
3695 for(s16 z=0; z<ued; z++)
3697 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3698 if(getNode(ap).d == CONTENT_AIR)
3700 orp = v3f(0,y+1,z+1);
3701 found_existing = true;
3702 goto continue_generating;
3706 catch(InvalidPositionException &e){}
3712 for(s16 y=0; y<ued; y++)
3713 for(s16 z=0; z<ued; z++)
3715 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3716 if(getNode(ap).d == CONTENT_AIR)
3718 orp = v3f(ued-1,y+1,z+1);
3719 found_existing = true;
3720 goto continue_generating;
3724 catch(InvalidPositionException &e){}
3730 for(s16 x=0; x<ued; x++)
3731 for(s16 z=0; z<ued; z++)
3733 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3734 if(getNode(ap).d == CONTENT_AIR)
3736 orp = v3f(x+1,0,z+1);
3737 found_existing = true;
3738 goto continue_generating;
3742 catch(InvalidPositionException &e){}
3748 for(s16 x=0; x<ued; x++)
3749 for(s16 z=0; z<ued; z++)
3751 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3752 if(getNode(ap).d == CONTENT_AIR)
3754 orp = v3f(x+1,ued-1,z+1);
3755 found_existing = true;
3756 goto continue_generating;
3760 catch(InvalidPositionException &e){}
3762 continue_generating:
3765 Choose whether to actually generate dungeon
3767 bool do_generate_dungeons = true;
3768 // Don't generate if no part is underground
3769 if(!some_part_underground)
3771 do_generate_dungeons = false;
3773 // Don't generate if mostly underwater surface
3774 /*else if(mostly_underwater_surface)
3776 do_generate_dungeons = false;
3778 // Partly underground = cave
3779 else if(!completely_underground)
3781 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3783 // Found existing dungeon underground
3784 else if(found_existing && completely_underground)
3786 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3788 // Underground and no dungeons found
3791 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3794 if(do_generate_dungeons)
3797 Generate some tunnel starting from orp and ors
3799 for(u16 i=0; i<3; i++)
3802 (float)(myrand()%ued)+0.5,
3803 (float)(myrand()%ued)+0.5,
3804 (float)(myrand()%ued)+0.5
3808 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3812 for(float f=0; f<1.0; f+=0.04)
3814 v3f fp = orp + vec * f;
3815 v3s16 cp(fp.X, fp.Y, fp.Z);
3817 s16 d1 = d0 + rs - 1;
3818 for(s16 z0=d0; z0<=d1; z0++)
3820 s16 si = rs - abs(z0);
3821 for(s16 x0=-si; x0<=si-1; x0++)
3823 s16 si2 = rs - abs(x0);
3824 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3830 if(isInArea(p, ued) == false)
3832 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3844 // Set to true if has caves.
3845 // Set when some non-air is changed to air when making caves.
3846 bool has_dungeons = false;
3849 Apply temporary cave data to block
3852 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3853 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3855 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3857 MapNode n = block->getNode(v3s16(x0,y0,z0));
3860 if(underground_emptiness[
3861 ued*ued*(z0*ued/MAP_BLOCKSIZE)
3862 +ued*(y0*ued/MAP_BLOCKSIZE)
3863 +(x0*ued/MAP_BLOCKSIZE)])
3865 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
3868 has_dungeons = true;
3874 block->setNode(v3s16(x0,y0,z0), n);
3879 This is used for guessing whether or not the block should
3880 receive sunlight from the top if the block above doesn't exist
3882 block->setIsUnderground(completely_underground);
3885 Force lighting update if some part of block is partly
3886 underground and has caves.
3888 /*if(some_part_underground && !completely_underground && has_dungeons)
3890 //dstream<<"Half-ground caves"<<std::endl;
3891 lighting_invalidated_blocks[block->getPos()] = block;
3894 // DEBUG: Always update lighting
3895 //lighting_invalidated_blocks[block->getPos()] = block;
3901 if(some_part_underground)
3903 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
3908 for(s16 i=0; i<underground_level/4 + 1; i++)
3910 if(myrand()%50 == 0)
3913 (myrand()%(MAP_BLOCKSIZE-2))+1,
3914 (myrand()%(MAP_BLOCKSIZE-2))+1,
3915 (myrand()%(MAP_BLOCKSIZE-2))+1
3921 for(u16 i=0; i<27; i++)
3923 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3925 block->setNode(cp+g_27dirs[i], n);
3933 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
3934 u16 coal_rareness = 60 / coal_amount;
3935 if(coal_rareness == 0)
3937 if(myrand()%coal_rareness == 0)
3939 u16 a = myrand() % 16;
3940 u16 amount = coal_amount * a*a*a / 1000;
3941 for(s16 i=0; i<amount; i++)
3944 (myrand()%(MAP_BLOCKSIZE-2))+1,
3945 (myrand()%(MAP_BLOCKSIZE-2))+1,
3946 (myrand()%(MAP_BLOCKSIZE-2))+1
3950 n.d = CONTENT_STONE;
3951 n.param = MINERAL_COAL;
3953 for(u16 i=0; i<27; i++)
3955 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3957 block->setNode(cp+g_27dirs[i], n);
3965 //TODO: change to iron_amount or whatever
3966 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
3967 u16 iron_rareness = 60 / iron_amount;
3968 if(iron_rareness == 0)
3970 if(myrand()%iron_rareness == 0)
3972 u16 a = myrand() % 16;
3973 u16 amount = iron_amount * a*a*a / 1000;
3974 for(s16 i=0; i<amount; i++)
3977 (myrand()%(MAP_BLOCKSIZE-2))+1,
3978 (myrand()%(MAP_BLOCKSIZE-2))+1,
3979 (myrand()%(MAP_BLOCKSIZE-2))+1
3983 n.d = CONTENT_STONE;
3984 n.param = MINERAL_IRON;
3986 for(u16 i=0; i<27; i++)
3988 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3990 block->setNode(cp+g_27dirs[i], n);
3997 Create a few rats in empty blocks underground
3999 if(completely_underground)
4001 //for(u16 i=0; i<2; i++)
4004 (myrand()%(MAP_BLOCKSIZE-2))+1,
4005 (myrand()%(MAP_BLOCKSIZE-2))+1,
4006 (myrand()%(MAP_BLOCKSIZE-2))+1
4009 // Check that the place is empty
4010 //if(!is_ground_content(block->getNode(cp).d))
4013 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4014 block->addObject(obj);
4020 Add block to sector.
4022 sector->insertBlock(block);
4028 // An y-wise container of changed blocks
4029 core::map<s16, MapBlock*> changed_blocks_sector;
4032 Check if any sector's objects can be placed now.
4035 core::map<v3s16, u8> *objects = sector->getObjects();
4036 core::list<v3s16> objects_to_remove;
4037 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
4038 i.atEnd() == false; i++)
4040 v3s16 p = i.getNode()->getKey();
4042 u8 d = i.getNode()->getValue();
4044 // Ground level point (user for stuff that is on ground)
4046 bool ground_found = true;
4048 // Search real ground level
4052 MapNode n = sector->getNode(gp);
4054 // If not air, go one up and continue to placing the tree
4055 if(n.d != CONTENT_AIR)
4061 // If air, go one down
4062 gp += v3s16(0,-1,0);
4064 }catch(InvalidPositionException &e)
4066 // Ground not found.
4067 ground_found = false;
4068 // This is most close to ground
4075 if(d == SECTOR_OBJECT_TEST)
4077 if(sector->isValidArea(p + v3s16(0,0,0),
4078 p + v3s16(0,0,0), &changed_blocks_sector))
4081 n.d = CONTENT_TORCH;
4082 sector->setNode(p, n);
4083 objects_to_remove.push_back(p);
4086 else if(d == SECTOR_OBJECT_TREE_1)
4088 if(ground_found == false)
4091 v3s16 p_min = gp + v3s16(-1,0,-1);
4092 v3s16 p_max = gp + v3s16(1,5,1);
4093 if(sector->isValidArea(p_min, p_max,
4094 &changed_blocks_sector))
4098 sector->setNode(gp+v3s16(0,0,0), n);
4099 sector->setNode(gp+v3s16(0,1,0), n);
4100 sector->setNode(gp+v3s16(0,2,0), n);
4101 sector->setNode(gp+v3s16(0,3,0), n);
4103 n.d = CONTENT_LEAVES;
4105 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4107 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4108 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4109 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4110 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4111 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4112 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4113 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4114 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4116 sector->setNode(gp+v3s16(0,4,0), n);
4118 sector->setNode(gp+v3s16(-1,4,0), n);
4119 sector->setNode(gp+v3s16(1,4,0), n);
4120 sector->setNode(gp+v3s16(0,4,-1), n);
4121 sector->setNode(gp+v3s16(0,4,1), n);
4122 sector->setNode(gp+v3s16(1,4,1), n);
4123 sector->setNode(gp+v3s16(-1,4,1), n);
4124 sector->setNode(gp+v3s16(-1,4,-1), n);
4125 sector->setNode(gp+v3s16(1,4,-1), n);
4127 sector->setNode(gp+v3s16(-1,3,0), n);
4128 sector->setNode(gp+v3s16(1,3,0), n);
4129 sector->setNode(gp+v3s16(0,3,-1), n);
4130 sector->setNode(gp+v3s16(0,3,1), n);
4131 sector->setNode(gp+v3s16(1,3,1), n);
4132 sector->setNode(gp+v3s16(-1,3,1), n);
4133 sector->setNode(gp+v3s16(-1,3,-1), n);
4134 sector->setNode(gp+v3s16(1,3,-1), n);
4136 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4137 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4138 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4139 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4140 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4141 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4142 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4143 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4145 // Objects are identified by wanted position
4146 objects_to_remove.push_back(p);
4148 // Lighting has to be recalculated for this one.
4149 sector->getBlocksInArea(p_min, p_max,
4150 lighting_invalidated_blocks);
4153 else if(d == SECTOR_OBJECT_BUSH_1)
4155 if(ground_found == false)
4158 if(sector->isValidArea(gp + v3s16(0,0,0),
4159 gp + v3s16(0,0,0), &changed_blocks_sector))
4162 n.d = CONTENT_LEAVES;
4163 sector->setNode(gp+v3s16(0,0,0), n);
4165 // Objects are identified by wanted position
4166 objects_to_remove.push_back(p);
4169 else if(d == SECTOR_OBJECT_RAVINE)
4172 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4173 v3s16 p_max = p + v3s16(6,6,6);
4174 if(sector->isValidArea(p_min, p_max,
4175 &changed_blocks_sector))
4178 n.d = CONTENT_STONE;
4181 s16 depth = maxdepth + (myrand()%10);
4183 s16 minz = -6 - (-2);
4185 for(s16 x=-6; x<=6; x++)
4187 z += -1 + (myrand()%3);
4192 for(s16 y=depth+(myrand()%2); y<=6; y++)
4194 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4197 v3s16 p2 = p + v3s16(x,y,z-2);
4198 //if(is_ground_content(sector->getNode(p2).d))
4199 if(content_features(sector->getNode(p2).d).walkable)
4200 sector->setNode(p2, n);
4203 v3s16 p2 = p + v3s16(x,y,z-1);
4204 if(content_features(sector->getNode(p2).d).walkable)
4205 sector->setNode(p2, n2);
4208 v3s16 p2 = p + v3s16(x,y,z+0);
4209 if(content_features(sector->getNode(p2).d).walkable)
4210 sector->setNode(p2, n2);
4213 v3s16 p2 = p + v3s16(x,y,z+1);
4214 if(content_features(sector->getNode(p2).d).walkable)
4215 sector->setNode(p2, n);
4218 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4219 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4223 objects_to_remove.push_back(p);
4225 // Lighting has to be recalculated for this one.
4226 sector->getBlocksInArea(p_min, p_max,
4227 lighting_invalidated_blocks);
4232 dstream<<"ServerMap::generateBlock(): "
4233 "Invalid heightmap object"
4238 catch(InvalidPositionException &e)
4240 dstream<<"WARNING: "<<__FUNCTION_NAME
4241 <<": while inserting object "<<(int)d
4242 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4243 <<" InvalidPositionException.what()="
4244 <<e.what()<<std::endl;
4245 // This is not too fatal and seems to happen sometimes.
4250 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4251 i != objects_to_remove.end(); i++)
4253 objects->remove(*i);
4257 Translate sector's changed blocks to global changed blocks
4260 for(core::map<s16, MapBlock*>::Iterator
4261 i = changed_blocks_sector.getIterator();
4262 i.atEnd() == false; i++)
4264 MapBlock *block = i.getNode()->getValue();
4266 changed_blocks.insert(block->getPos(), block);
4269 block->setLightingExpired(true);
4276 <<"lighting_invalidated_blocks.size()"
4280 <<" "<<lighting_invalidated_blocks.size()
4281 <<", "<<has_dungeons
4282 <<", "<<completely_underground
4283 <<", "<<some_part_underground
4290 MapBlock * ServerMap::createBlock(v3s16 p)
4292 DSTACK("%s: p=(%d,%d,%d)",
4293 __FUNCTION_NAME, p.X, p.Y, p.Z);
4295 v2s16 p2d(p.X, p.Z);
4298 This will create or load a sector if not found in memory.
4299 If block exists on disk, it will be loaded.
4301 NOTE: On old save formats, this will be slow, as it generates
4302 lighting on blocks for them.
4304 ServerMapSector *sector;
4306 sector = (ServerMapSector*)createSector(p2d);
4307 assert(sector->getId() == MAPSECTOR_SERVER);
4309 /*catch(InvalidPositionException &e)
4311 dstream<<"createBlock: createSector() failed"<<std::endl;
4314 catch(std::exception &e)
4316 dstream<<"createBlock: createSector() failed: "
4317 <<e.what()<<std::endl;
4322 Try to get a block from the sector
4325 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4329 block = sector->createBlankBlock(block_y);
4333 MapBlock * ServerMap::emergeBlock(
4335 bool only_from_disk,
4336 core::map<v3s16, MapBlock*> &changed_blocks,
4337 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4340 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4342 p.X, p.Y, p.Z, only_from_disk);
4344 v2s16 p2d(p.X, p.Z);
4347 This will create or load a sector if not found in memory.
4348 If block exists on disk, it will be loaded.
4350 NOTE: On old save formats, this will be slow, as it generates
4351 lighting on blocks for them.
4353 ServerMapSector *sector;
4355 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4356 assert(sector->getId() == MAPSECTOR_SERVER);
4358 catch(std::exception &e)
4360 dstream<<"emergeBlock: emergeSector() failed: "
4361 <<e.what()<<std::endl;
4366 Try to get a block from the sector
4369 bool does_not_exist = false;
4370 bool lighting_expired = false;
4371 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4375 does_not_exist = true;
4377 else if(block->isDummy() == true)
4379 does_not_exist = true;
4381 else if(block->getLightingExpired())
4383 lighting_expired = true;
4388 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4393 If block was not found on disk and not going to generate a
4394 new one, make sure there is a dummy block in place.
4396 if(only_from_disk && (does_not_exist || lighting_expired))
4398 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4402 // Create dummy block
4403 block = new MapBlock(this, p, true);
4405 // Add block to sector
4406 sector->insertBlock(block);
4412 //dstream<<"Not found on disk, generating."<<std::endl;
4414 //TimeTaker("emergeBlock() generate");
4416 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4419 If the block doesn't exist, generate the block.
4423 block = generateBlock(p, block, sector, changed_blocks,
4424 lighting_invalidated_blocks);
4427 if(lighting_expired)
4429 lighting_invalidated_blocks.insert(p, block);
4433 Initially update sunlight
4437 core::map<v3s16, bool> light_sources;
4438 bool black_air_left = false;
4439 bool bottom_invalid =
4440 block->propagateSunlight(light_sources, true,
4441 &black_air_left, true);
4443 // If sunlight didn't reach everywhere and part of block is
4444 // above ground, lighting has to be properly updated
4445 //if(black_air_left && some_part_underground)
4448 lighting_invalidated_blocks[block->getPos()] = block;
4453 lighting_invalidated_blocks[block->getPos()] = block;
4458 Debug mode operation
4460 bool haxmode = g_settings.getBool("haxmode");
4463 // Don't calculate lighting at all
4464 //lighting_invalidated_blocks.clear();
4470 void ServerMap::createDir(std::string path)
4472 if(fs::CreateDir(path) == false)
4474 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4475 <<"\""<<path<<"\""<<std::endl;
4476 throw BaseException("ServerMap failed to create directory");
4480 std::string ServerMap::getSectorSubDir(v2s16 pos)
4483 snprintf(cc, 9, "%.4x%.4x",
4484 (unsigned int)pos.X&0xffff,
4485 (unsigned int)pos.Y&0xffff);
4487 return std::string(cc);
4490 std::string ServerMap::getSectorDir(v2s16 pos)
4492 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4495 v2s16 ServerMap::getSectorPos(std::string dirname)
4497 if(dirname.size() != 8)
4498 throw InvalidFilenameException("Invalid sector directory name");
4500 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4502 throw InvalidFilenameException("Invalid sector directory name");
4503 v2s16 pos((s16)x, (s16)y);
4507 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4509 v2s16 p2d = getSectorPos(sectordir);
4511 if(blockfile.size() != 4){
4512 throw InvalidFilenameException("Invalid block filename");
4515 int r = sscanf(blockfile.c_str(), "%4x", &y);
4517 throw InvalidFilenameException("Invalid block filename");
4518 return v3s16(p2d.X, y, p2d.Y);
4522 #define ENABLE_SECTOR_SAVING 1
4523 #define ENABLE_SECTOR_LOADING 1
4524 #define ENABLE_BLOCK_SAVING 1
4525 #define ENABLE_BLOCK_LOADING 1
4527 void ServerMap::save(bool only_changed)
4529 DSTACK(__FUNCTION_NAME);
4530 if(m_map_saving_enabled == false)
4532 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4536 if(only_changed == false)
4537 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4540 saveMasterHeightmap();
4542 u32 sector_meta_count = 0;
4543 u32 block_count = 0;
4546 JMutexAutoLock lock(m_sector_mutex);
4548 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4549 for(; i.atEnd() == false; i++)
4551 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4552 assert(sector->getId() == MAPSECTOR_SERVER);
4554 if(ENABLE_SECTOR_SAVING)
4556 if(sector->differs_from_disk || only_changed == false)
4558 saveSectorMeta(sector);
4559 sector_meta_count++;
4562 if(ENABLE_BLOCK_SAVING)
4564 core::list<MapBlock*> blocks;
4565 sector->getBlocks(blocks);
4566 core::list<MapBlock*>::Iterator j;
4567 for(j=blocks.begin(); j!=blocks.end(); j++)
4569 MapBlock *block = *j;
4570 if(block->getChangedFlag() || only_changed == false)
4582 Only print if something happened or saved whole map
4584 if(only_changed == false || sector_meta_count != 0
4585 || block_count != 0)
4587 dstream<<DTIME<<"ServerMap: Written: "
4588 <<sector_meta_count<<" sector metadata files, "
4589 <<block_count<<" block files"
4594 void ServerMap::loadAll()
4596 DSTACK(__FUNCTION_NAME);
4597 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4599 loadMasterHeightmap();
4601 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4603 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4605 JMutexAutoLock lock(m_sector_mutex);
4608 s32 printed_counter = -100000;
4609 s32 count = list.size();
4611 std::vector<fs::DirListNode>::iterator i;
4612 for(i=list.begin(); i!=list.end(); i++)
4614 if(counter > printed_counter + 10)
4616 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4617 printed_counter = counter;
4621 MapSector *sector = NULL;
4623 // We want directories
4627 sector = loadSectorMeta(i->name);
4629 catch(InvalidFilenameException &e)
4631 // This catches unknown crap in directory
4634 if(ENABLE_BLOCK_LOADING)
4636 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4637 (m_savedir+"/sectors/"+i->name);
4638 std::vector<fs::DirListNode>::iterator i2;
4639 for(i2=list2.begin(); i2!=list2.end(); i2++)
4645 loadBlock(i->name, i2->name, sector);
4647 catch(InvalidFilenameException &e)
4649 // This catches unknown crap in directory
4654 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4657 void ServerMap::saveMasterHeightmap()
4659 DSTACK(__FUNCTION_NAME);
4660 createDir(m_savedir);
4662 std::string fullpath = m_savedir + "/master_heightmap";
4663 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4664 if(o.good() == false)
4665 throw FileNotGoodException("Cannot open master heightmap");
4667 // Format used for writing
4668 u8 version = SER_FMT_VER_HIGHEST;
4671 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4673 [0] u8 serialization version
4674 [1] X master heightmap
4676 u32 fullsize = 1 + hmdata.getSize();
4677 SharedBuffer<u8> data(fullsize);
4680 memcpy(&data[1], *hmdata, hmdata.getSize());
4682 o.write((const char*)*data, fullsize);
4685 m_heightmap->serialize(o, version);
4688 void ServerMap::loadMasterHeightmap()
4690 DSTACK(__FUNCTION_NAME);
4691 std::string fullpath = m_savedir + "/master_heightmap";
4692 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4693 if(is.good() == false)
4694 throw FileNotGoodException("Cannot open master heightmap");
4696 if(m_heightmap != NULL)
4699 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4702 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4704 DSTACK(__FUNCTION_NAME);
4705 // Format used for writing
4706 u8 version = SER_FMT_VER_HIGHEST;
4708 v2s16 pos = sector->getPos();
4709 createDir(m_savedir);
4710 createDir(m_savedir+"/sectors");
4711 std::string dir = getSectorDir(pos);
4714 std::string fullpath = dir + "/heightmap";
4715 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4716 if(o.good() == false)
4717 throw FileNotGoodException("Cannot open master heightmap");
4719 sector->serialize(o, version);
4721 sector->differs_from_disk = false;
4724 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4726 DSTACK(__FUNCTION_NAME);
4728 v2s16 p2d = getSectorPos(dirname);
4729 std::string dir = m_savedir + "/sectors/" + dirname;
4731 std::string fullpath = dir + "/heightmap";
4732 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4733 if(is.good() == false)
4734 throw FileNotGoodException("Cannot open sector heightmap");
4736 ServerMapSector *sector = ServerMapSector::deSerialize
4737 (is, this, p2d, &m_hwrapper, m_sectors);
4739 sector->differs_from_disk = false;
4744 bool ServerMap::loadSectorFull(v2s16 p2d)
4746 DSTACK(__FUNCTION_NAME);
4747 std::string sectorsubdir = getSectorSubDir(p2d);
4749 MapSector *sector = NULL;
4751 JMutexAutoLock lock(m_sector_mutex);
4754 sector = loadSectorMeta(sectorsubdir);
4756 catch(InvalidFilenameException &e)
4760 catch(FileNotGoodException &e)
4764 catch(std::exception &e)
4769 if(ENABLE_BLOCK_LOADING)
4771 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4772 (m_savedir+"/sectors/"+sectorsubdir);
4773 std::vector<fs::DirListNode>::iterator i2;
4774 for(i2=list2.begin(); i2!=list2.end(); i2++)
4780 loadBlock(sectorsubdir, i2->name, sector);
4782 catch(InvalidFilenameException &e)
4784 // This catches unknown crap in directory
4792 bool ServerMap::deFlushSector(v2s16 p2d)
4794 DSTACK(__FUNCTION_NAME);
4795 // See if it already exists in memory
4797 MapSector *sector = getSectorNoGenerate(p2d);
4800 catch(InvalidPositionException &e)
4803 Try to load the sector from disk.
4805 if(loadSectorFull(p2d) == true)
4814 void ServerMap::saveBlock(MapBlock *block)
4816 DSTACK(__FUNCTION_NAME);
4818 Dummy blocks are not written
4820 if(block->isDummy())
4822 /*v3s16 p = block->getPos();
4823 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4824 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4828 // Format used for writing
4829 u8 version = SER_FMT_VER_HIGHEST;
4831 v3s16 p3d = block->getPos();
4832 v2s16 p2d(p3d.X, p3d.Z);
4833 createDir(m_savedir);
4834 createDir(m_savedir+"/sectors");
4835 std::string dir = getSectorDir(p2d);
4838 // Block file is map/sectors/xxxxxxxx/xxxx
4840 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4841 std::string fullpath = dir + "/" + cc;
4842 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4843 if(o.good() == false)
4844 throw FileNotGoodException("Cannot open block data");
4847 [0] u8 serialization version
4850 o.write((char*)&version, 1);
4852 block->serialize(o, version);
4855 Versions up from 9 have block objects.
4859 block->serializeObjects(o, version);
4862 // We just wrote it to the disk
4863 block->resetChangedFlag();
4866 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4868 DSTACK(__FUNCTION_NAME);
4872 // Block file is map/sectors/xxxxxxxx/xxxx
4873 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4874 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4875 if(is.good() == false)
4876 throw FileNotGoodException("Cannot open block file");
4878 v3s16 p3d = getBlockPos(sectordir, blockfile);
4879 v2s16 p2d(p3d.X, p3d.Z);
4881 assert(sector->getPos() == p2d);
4883 u8 version = SER_FMT_VER_INVALID;
4884 is.read((char*)&version, 1);
4886 /*u32 block_size = MapBlock::serializedLength(version);
4887 SharedBuffer<u8> data(block_size);
4888 is.read((char*)*data, block_size);*/
4890 // This will always return a sector because we're the server
4891 //MapSector *sector = emergeSector(p2d);
4893 MapBlock *block = NULL;
4894 bool created_new = false;
4896 block = sector->getBlockNoCreate(p3d.Y);
4898 catch(InvalidPositionException &e)
4900 block = sector->createBlankBlockNoInsert(p3d.Y);
4904 // deserialize block data
4905 block->deSerialize(is, version);
4908 Versions up from 9 have block objects.
4912 block->updateObjects(is, version, NULL, 0);
4916 sector->insertBlock(block);
4919 Convert old formats to new and save
4922 // Save old format blocks in new format
4923 if(version < SER_FMT_VER_HIGHEST)
4928 // We just loaded it from the disk, so it's up-to-date.
4929 block->resetChangedFlag();
4932 catch(SerializationError &e)
4934 dstream<<"WARNING: Invalid block data on disk "
4935 "(SerializationError). Ignoring."
4940 // Gets from master heightmap
4941 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4943 assert(m_heightmap != NULL);
4951 corners[0] = m_heightmap->getGroundHeight
4952 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4953 corners[1] = m_heightmap->getGroundHeight
4954 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4955 corners[2] = m_heightmap->getGroundHeight
4956 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4957 corners[3] = m_heightmap->getGroundHeight
4958 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
4961 void ServerMap::PrintInfo(std::ostream &out)
4972 ClientMap::ClientMap(
4974 MapDrawControl &control,
4975 scene::ISceneNode* parent,
4976 scene::ISceneManager* mgr,
4980 scene::ISceneNode(parent, mgr, id),
4987 /*m_box = core::aabbox3d<f32>(0,0,0,
4988 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4989 /*m_box = core::aabbox3d<f32>(0,0,0,
4990 map->getSizeNodes().X * BS,
4991 map->getSizeNodes().Y * BS,
4992 map->getSizeNodes().Z * BS);*/
4993 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4994 BS*1000000,BS*1000000,BS*1000000);
4996 //setPosition(v3f(BS,BS,BS));
4999 ClientMap::~ClientMap()
5001 JMutexAutoLock lock(mesh_mutex);
5010 MapSector * ClientMap::emergeSector(v2s16 p2d)
5012 DSTACK(__FUNCTION_NAME);
5013 // Check that it doesn't exist already
5015 return getSectorNoGenerate(p2d);
5017 catch(InvalidPositionException &e)
5021 // Create a sector with no heightmaps
5022 ClientMapSector *sector = new ClientMapSector(this, p2d);
5025 JMutexAutoLock lock(m_sector_mutex);
5026 m_sectors.insert(p2d, sector);
5032 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5034 DSTACK(__FUNCTION_NAME);
5035 ClientMapSector *sector = NULL;
5037 JMutexAutoLock lock(m_sector_mutex);
5039 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5043 sector = (ClientMapSector*)n->getValue();
5044 assert(sector->getId() == MAPSECTOR_CLIENT);
5048 sector = new ClientMapSector(this, p2d);
5050 JMutexAutoLock lock(m_sector_mutex);
5051 m_sectors.insert(p2d, sector);
5055 sector->deSerialize(is);
5058 void ClientMap::OnRegisterSceneNode()
5062 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5063 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5066 ISceneNode::OnRegisterSceneNode();
5069 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5071 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5072 DSTACK(__FUNCTION_NAME);
5074 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5077 Get time for measuring timeout.
5079 Measuring time is very useful for long delays when the
5080 machine is swapping a lot.
5082 int time1 = time(0);
5084 u32 daynight_ratio = m_client->getDayNightRatio();
5086 m_camera_mutex.Lock();
5087 v3f camera_position = m_camera_position;
5088 v3f camera_direction = m_camera_direction;
5089 m_camera_mutex.Unlock();
5092 Get all blocks and draw all visible ones
5095 v3s16 cam_pos_nodes(
5096 camera_position.X / BS,
5097 camera_position.Y / BS,
5098 camera_position.Z / BS);
5100 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5102 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5103 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5105 // Take a fair amount as we will be dropping more out later
5107 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5108 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5109 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5111 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5112 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5113 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5115 u32 vertex_count = 0;
5117 // For limiting number of mesh updates per frame
5118 u32 mesh_update_count = 0;
5120 u32 blocks_would_have_drawn = 0;
5121 u32 blocks_drawn = 0;
5123 //NOTE: The sectors map should be locked but we're not doing it
5124 // because it'd cause too much delays
5126 int timecheck_counter = 0;
5127 core::map<v2s16, MapSector*>::Iterator si;
5128 si = m_sectors.getIterator();
5129 for(; si.atEnd() == false; si++)
5132 timecheck_counter++;
5133 if(timecheck_counter > 50)
5135 int time2 = time(0);
5136 if(time2 > time1 + 4)
5138 dstream<<"ClientMap::renderMap(): "
5139 "Rendering takes ages, returning."
5146 MapSector *sector = si.getNode()->getValue();
5147 v2s16 sp = sector->getPos();
5149 if(m_control.range_all == false)
5151 if(sp.X < p_blocks_min.X
5152 || sp.X > p_blocks_max.X
5153 || sp.Y < p_blocks_min.Z
5154 || sp.Y > p_blocks_max.Z)
5158 core::list< MapBlock * > sectorblocks;
5159 sector->getBlocks(sectorblocks);
5165 core::list< MapBlock * >::Iterator i;
5166 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5168 MapBlock *block = *i;
5171 Compare block position to camera position, skip
5172 if not seen on display
5175 float range = 100000 * BS;
5176 if(m_control.range_all == false)
5177 range = m_control.wanted_range * BS;
5179 if(isBlockInSight(block->getPos(), camera_position,
5180 camera_direction, range) == false)
5186 v3s16 blockpos_nodes = block->getPosRelative();
5188 // Block center position
5190 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5191 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5192 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5195 // Block position relative to camera
5196 v3f blockpos_relative = blockpos - camera_position;
5198 // Distance in camera direction (+=front, -=back)
5199 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5202 f32 d = blockpos_relative.getLength();
5204 if(m_control.range_all == false)
5206 // If block is far away, don't draw it
5207 if(d > m_control.wanted_range * BS)
5211 // Maximum radius of a block
5212 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5214 // If block is (nearly) touching the camera, don't
5215 // bother validating further (that is, render it anyway)
5216 if(d > block_max_radius * 1.5)
5218 // Cosine of the angle between the camera direction
5219 // and the block direction (camera_direction is an unit vector)
5220 f32 cosangle = dforward / d;
5222 // Compensate for the size of the block
5223 // (as the block has to be shown even if it's a bit off FOV)
5224 // This is an estimate.
5225 cosangle += block_max_radius / dforward;
5227 // If block is not in the field of view, skip it
5228 //if(cosangle < cos(FOV_ANGLE/2))
5229 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5234 v3s16 blockpos_nodes = block->getPosRelative();
5236 // Block center position
5238 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5239 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5240 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5243 // Block position relative to camera
5244 v3f blockpos_relative = blockpos - camera_position;
5247 f32 d = blockpos_relative.getLength();
5254 bool mesh_expired = false;
5257 JMutexAutoLock lock(block->mesh_mutex);
5259 mesh_expired = block->getMeshExpired();
5261 // Mesh has not been expired and there is no mesh:
5262 // block has no content
5263 if(block->mesh == NULL && mesh_expired == false)
5267 f32 faraway = BS*50;
5268 //f32 faraway = m_control.wanted_range * BS;
5271 This has to be done with the mesh_mutex unlocked
5273 // Pretty random but this should work somewhat nicely
5274 if(mesh_expired && (
5275 (mesh_update_count < 3
5276 && (d < faraway || mesh_update_count < 2)
5279 (m_control.range_all && mesh_update_count < 20)
5282 /*if(mesh_expired && mesh_update_count < 6
5283 && (d < faraway || mesh_update_count < 3))*/
5285 mesh_update_count++;
5287 // Mesh has been expired: generate new mesh
5288 //block->updateMeshes(daynight_i);
5289 block->updateMesh(daynight_ratio);
5291 mesh_expired = false;
5295 Don't draw an expired mesh that is far away
5297 /*if(mesh_expired && d >= faraway)
5300 // Instead, delete it
5301 JMutexAutoLock lock(block->mesh_mutex);
5304 block->mesh->drop();
5307 // And continue to next block
5312 Draw the faces of the block
5315 JMutexAutoLock lock(block->mesh_mutex);
5317 scene::SMesh *mesh = block->mesh;
5322 blocks_would_have_drawn++;
5323 if(blocks_drawn >= m_control.wanted_max_blocks
5324 && m_control.range_all == false
5325 && d > m_control.wanted_min_range * BS)
5329 u32 c = mesh->getMeshBufferCount();
5331 for(u32 i=0; i<c; i++)
5333 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5334 const video::SMaterial& material = buf->getMaterial();
5335 video::IMaterialRenderer* rnd =
5336 driver->getMaterialRenderer(material.MaterialType);
5337 bool transparent = (rnd && rnd->isTransparent());
5338 // Render transparent on transparent pass and likewise.
5339 if(transparent == is_transparent_pass)
5341 driver->setMaterial(buf->getMaterial());
5342 driver->drawMeshBuffer(buf);
5343 vertex_count += buf->getVertexCount();
5347 } // foreach sectorblocks
5350 m_control.blocks_drawn = blocks_drawn;
5351 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5353 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5354 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5357 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5358 core::map<v3s16, MapBlock*> *affected_blocks)
5360 bool changed = false;
5362 Add it to all blocks touching it
5365 v3s16(0,0,0), // this
5366 v3s16(0,0,1), // back
5367 v3s16(0,1,0), // top
5368 v3s16(1,0,0), // right
5369 v3s16(0,0,-1), // front
5370 v3s16(0,-1,0), // bottom
5371 v3s16(-1,0,0), // left
5373 for(u16 i=0; i<7; i++)
5375 v3s16 p2 = p + dirs[i];
5376 // Block position of neighbor (or requested) node
5377 v3s16 blockpos = getNodeBlockPos(p2);
5378 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5379 if(blockref == NULL)
5381 // Relative position of requested node
5382 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5383 if(blockref->setTempMod(relpos, mod))
5388 if(changed && affected_blocks!=NULL)
5390 for(u16 i=0; i<7; i++)
5392 v3s16 p2 = p + dirs[i];
5393 // Block position of neighbor (or requested) node
5394 v3s16 blockpos = getNodeBlockPos(p2);
5395 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5396 if(blockref == NULL)
5398 affected_blocks->insert(blockpos, blockref);
5404 bool ClientMap::clearTempMod(v3s16 p,
5405 core::map<v3s16, MapBlock*> *affected_blocks)
5407 bool changed = false;
5409 v3s16(0,0,0), // this
5410 v3s16(0,0,1), // back
5411 v3s16(0,1,0), // top
5412 v3s16(1,0,0), // right
5413 v3s16(0,0,-1), // front
5414 v3s16(0,-1,0), // bottom
5415 v3s16(-1,0,0), // left
5417 for(u16 i=0; i<7; i++)
5419 v3s16 p2 = p + dirs[i];
5420 // Block position of neighbor (or requested) node
5421 v3s16 blockpos = getNodeBlockPos(p2);
5422 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5423 if(blockref == NULL)
5425 // Relative position of requested node
5426 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5427 if(blockref->clearTempMod(relpos))
5432 if(changed && affected_blocks!=NULL)
5434 for(u16 i=0; i<7; i++)
5436 v3s16 p2 = p + dirs[i];
5437 // Block position of neighbor (or requested) node
5438 v3s16 blockpos = getNodeBlockPos(p2);
5439 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5440 if(blockref == NULL)
5442 affected_blocks->insert(blockpos, blockref);
5448 void ClientMap::PrintInfo(std::ostream &out)
5459 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5464 MapVoxelManipulator::~MapVoxelManipulator()
5466 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5470 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5472 TimeTaker timer1("emerge", &emerge_time);
5474 // Units of these are MapBlocks
5475 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5476 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5478 VoxelArea block_area_nodes
5479 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5481 addArea(block_area_nodes);
5483 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5484 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5485 for(s32 x=p_min.X; x<=p_max.X; x++)
5488 core::map<v3s16, bool>::Node *n;
5489 n = m_loaded_blocks.find(p);
5493 bool block_data_inexistent = false;
5496 TimeTaker timer1("emerge load", &emerge_load_time);
5498 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5499 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5502 dstream<<std::endl;*/
5504 MapBlock *block = m_map->getBlockNoCreate(p);
5505 if(block->isDummy())
5506 block_data_inexistent = true;
5508 block->copyTo(*this);
5510 catch(InvalidPositionException &e)
5512 block_data_inexistent = true;
5515 if(block_data_inexistent)
5517 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5518 // Fill with VOXELFLAG_INEXISTENT
5519 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5520 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5522 s32 i = m_area.index(a.MinEdge.X,y,z);
5523 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5527 m_loaded_blocks.insert(p, !block_data_inexistent);
5530 //dstream<<"emerge done"<<std::endl;
5534 SUGG: Add an option to only update eg. water and air nodes.
5535 This will make it interfere less with important stuff if
5538 void MapVoxelManipulator::blitBack
5539 (core::map<v3s16, MapBlock*> & modified_blocks)
5541 if(m_area.getExtent() == v3s16(0,0,0))
5544 //TimeTaker timer1("blitBack");
5546 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5547 <<m_loaded_blocks.size()<<std::endl;*/
5550 Initialize block cache
5552 v3s16 blockpos_last;
5553 MapBlock *block = NULL;
5554 bool block_checked_in_modified = false;
5556 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5557 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5558 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5562 u8 f = m_flags[m_area.index(p)];
5563 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5566 MapNode &n = m_data[m_area.index(p)];
5568 v3s16 blockpos = getNodeBlockPos(p);
5573 if(block == NULL || blockpos != blockpos_last){
5574 block = m_map->getBlockNoCreate(blockpos);
5575 blockpos_last = blockpos;
5576 block_checked_in_modified = false;
5579 // Calculate relative position in block
5580 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5582 // Don't continue if nothing has changed here
5583 if(block->getNode(relpos) == n)
5586 //m_map->setNode(m_area.MinEdge + p, n);
5587 block->setNode(relpos, n);
5590 Make sure block is in modified_blocks
5592 if(block_checked_in_modified == false)
5594 modified_blocks[blockpos] = block;
5595 block_checked_in_modified = true;
5598 catch(InvalidPositionException &e)
5604 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5605 MapVoxelManipulator(map)
5609 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5613 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5615 // Just create the area so that it can be pointed to
5616 VoxelManipulator::emerge(a, caller_id);
5619 void ManualMapVoxelManipulator::initialEmerge(
5620 v3s16 blockpos_min, v3s16 blockpos_max)
5622 TimeTaker timer1("initialEmerge", &emerge_time);
5624 // Units of these are MapBlocks
5625 v3s16 p_min = blockpos_min;
5626 v3s16 p_max = blockpos_max;
5628 VoxelArea block_area_nodes
5629 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5631 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5634 dstream<<"initialEmerge: area: ";
5635 block_area_nodes.print(dstream);
5636 dstream<<" ("<<size_MB<<"MB)";
5640 addArea(block_area_nodes);
5642 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5643 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5644 for(s32 x=p_min.X; x<=p_max.X; x++)
5647 core::map<v3s16, bool>::Node *n;
5648 n = m_loaded_blocks.find(p);
5652 bool block_data_inexistent = false;
5655 TimeTaker timer1("emerge load", &emerge_load_time);
5657 MapBlock *block = m_map->getBlockNoCreate(p);
5658 if(block->isDummy())
5659 block_data_inexistent = true;
5661 block->copyTo(*this);
5663 catch(InvalidPositionException &e)
5665 block_data_inexistent = true;
5668 if(block_data_inexistent)
5671 Mark area inexistent
5673 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5674 // Fill with VOXELFLAG_INEXISTENT
5675 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5676 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5678 s32 i = m_area.index(a.MinEdge.X,y,z);
5679 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5683 m_loaded_blocks.insert(p, !block_data_inexistent);
5687 void ManualMapVoxelManipulator::blitBackAll(
5688 core::map<v3s16, MapBlock*> * modified_blocks)
5690 if(m_area.getExtent() == v3s16(0,0,0))
5694 Copy data of all blocks
5696 for(core::map<v3s16, bool>::Iterator
5697 i = m_loaded_blocks.getIterator();
5698 i.atEnd() == false; i++)
5700 bool existed = i.getNode()->getValue();
5701 if(existed == false)
5703 v3s16 p = i.getNode()->getKey();
5704 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5707 dstream<<"WARNING: "<<__FUNCTION_NAME
5708 <<": got NULL block "
5709 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5714 block->copyFrom(*this);
5717 modified_blocks->insert(p, block);