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(30));
1918 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1921 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1922 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1923 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1927 Try to load map; if not found, create a new one.
1930 m_savedir = savedir;
1931 m_map_saving_enabled = false;
1935 // If directory exists, check contents and load if possible
1936 if(fs::PathExists(m_savedir))
1938 // If directory is empty, it is safe to save into it.
1939 if(fs::GetDirListing(m_savedir).size() == 0)
1941 dstream<<DTIME<<"Server: Empty save directory is valid."
1943 m_map_saving_enabled = true;
1947 // Load master heightmap
1948 loadMasterHeightmap();
1950 // Load sector (0,0) and throw and exception on fail
1951 if(loadSectorFull(v2s16(0,0)) == false)
1952 throw LoadError("Failed to load sector (0,0)");
1954 dstream<<DTIME<<"Server: Successfully loaded master "
1955 "heightmap and sector (0,0) from "<<savedir<<
1956 ", assuming valid save directory."
1959 m_map_saving_enabled = true;
1960 // Map loaded, not creating new one
1964 // If directory doesn't exist, it is safe to save to it
1966 m_map_saving_enabled = true;
1969 catch(std::exception &e)
1971 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1972 <<", exception: "<<e.what()<<std::endl;
1973 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1974 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1977 dstream<<DTIME<<"Initializing new map."<<std::endl;
1979 // Create master heightmap
1980 m_heightmap = new UnlimitedHeightmap
1983 // Set map parameters
1986 // Create zero sector
1987 emergeSector(v2s16(0,0));
1989 // Initially write whole map
1993 ServerMap::~ServerMap()
1997 if(m_map_saving_enabled)
2000 // Save only changed parts
2002 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2006 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2009 catch(std::exception &e)
2011 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2012 <<", exception: "<<e.what()<<std::endl;
2015 if(m_heightmap != NULL)
2021 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2022 for(; i.atEnd() == false; i++)
2024 MapChunk *chunk = i.getNode()->getValue();
2030 Some helper functions
2033 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2035 v3s16 em = vmanip.m_area.getExtent();
2036 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2037 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2038 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2040 for(y=y_nodes_max; y>=y_nodes_min; y--)
2042 MapNode &n = vmanip.m_data[i];
2043 if(content_walkable(n.d))
2046 vmanip.m_area.add_y(em, i, -1);
2048 if(y >= y_nodes_min)
2054 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2056 MapNode treenode(CONTENT_TREE);
2057 MapNode leavesnode(CONTENT_LEAVES);
2059 s16 trunk_h = myrand_range(2, 6);
2061 for(s16 ii=0; ii<trunk_h; ii++)
2063 if(vmanip.m_area.contains(p1))
2064 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2068 // p1 is now the last piece of the trunk
2071 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2072 SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2073 for(s32 i=0; i<leaves_a.getVolume(); i++)
2076 // Force leaves at near the end of the trunk
2079 for(s16 z=-d; z<=d; z++)
2080 for(s16 y=-d; y<=d; y++)
2081 for(s16 x=-d; x<=d; x++)
2083 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2087 // Add leaves randomly
2088 for(u32 iii=0; iii<7; iii++)
2093 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2094 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2095 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2098 for(s16 z=0; z<=d; z++)
2099 for(s16 y=0; y<=d; y++)
2100 for(s16 x=0; x<=d; x++)
2102 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2106 // Blit leaves to vmanip
2107 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2108 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2109 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2113 if(vmanip.m_area.contains(p) == false)
2115 u32 vi = vmanip.m_area.index(p);
2116 if(vmanip.m_data[vi].d != CONTENT_AIR)
2118 u32 i = leaves_a.index(x,y,z);
2119 if(leaves_d[i] == 1)
2120 vmanip.m_data[vi] = leavesnode;
2124 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2125 core::map<v3s16, MapBlock*> &changed_blocks)
2128 Don't generate if already fully generated
2131 MapChunk *chunk = getChunk(chunkpos);
2132 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2134 dstream<<"generateChunkRaw(): Chunk "
2135 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2136 <<" already generated"<<std::endl;
2141 dstream<<"generateChunkRaw(): Generating chunk "
2142 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2145 TimeTaker timer("generateChunkRaw()");
2147 // The distance how far into the neighbors the generator is allowed to go
2148 s16 max_spread_amount_sectors = 2;
2149 assert(max_spread_amount_sectors <= m_chunksize);
2150 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2151 // Minimum amount of space left on sides for mud to fall in
2152 s16 min_mud_fall_space = 2;
2153 // Maximum diameter of stone obstacles in X and Z
2154 s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2155 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2157 s16 y_blocks_min = -4;
2158 s16 y_blocks_max = 3;
2159 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2160 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2161 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2163 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2164 s16 sectorpos_base_size = m_chunksize;
2166 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2167 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2168 v2s16 sectorpos_bigbase =
2169 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2170 s16 sectorpos_bigbase_size =
2171 sectorpos_base_size + 2 * max_spread_amount_sectors;
2173 v3s16 bigarea_blocks_min(
2174 sectorpos_bigbase.X,
2179 v3s16 bigarea_blocks_max(
2180 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2182 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2185 // Relative values to control amount of stuff in one chunk
2186 u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2187 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;
2188 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2189 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2190 *(u32)h_blocks*MAP_BLOCKSIZE;
2193 Create the whole area of this and the neighboring chunks
2196 TimeTaker timer("generateChunkRaw() create area");
2198 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2199 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2201 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2202 ServerMapSector *sector = createSector(sectorpos);
2205 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2207 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2208 MapBlock *block = createBlock(blockpos);
2210 // Lighting won't be calculated
2211 //block->setLightingExpired(true);
2212 // Lighting will be calculated
2213 block->setLightingExpired(false);
2216 Block gets sunlight if this is true.
2218 This should be set to true when the top side of a block
2219 is completely exposed to the sky.
2221 Actually this doesn't matter now because the
2222 initial lighting is done here.
2224 block->setIsUnderground(y != y_blocks_max);
2230 Now we have a big empty area.
2232 Make a ManualMapVoxelManipulator that contains this and the
2236 ManualMapVoxelManipulator vmanip(this);
2237 // Add the area we just generated
2239 TimeTaker timer("generateChunkRaw() initialEmerge");
2240 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2243 TimeTaker timer_generate("generateChunkRaw() generate");
2246 Generate general ground level to full area
2251 //TimeTaker timer1("ground level");
2253 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2254 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2257 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2260 Skip of already generated
2263 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2264 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2268 // Ground height at this point
2269 float surface_y_f = 0.0;
2271 A hack to get the ground height from the sector.
2275 v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
2276 v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
2277 MapSector *sector = getSectorNoGenerate(sectorpos);
2279 float h = sector->getGroundHeight(sector_relpos);
2280 if(h > GROUNDHEIGHT_VALID_MINVALUE)
2283 dstream<<"WARNING: "<<__FUNCTION_NAME
2284 <<": sector->getGroundHeight returned bad height"<<std::endl;
2286 // Convert to integer
2287 s16 surface_y = (s16)surface_y_f;
2290 Fill ground with stone
2293 // Use fast index incrementing
2294 v3s16 em = vmanip.m_area.getExtent();
2295 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2296 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2298 vmanip.m_data[i].d = CONTENT_STONE;
2300 vmanip.m_area.add_y(em, i, 1);
2307 // This is set during the next operation.
2308 // Maximum height of the stone surface and obstacles.
2309 // This is used to disable dungeon generation from going too high.
2310 s16 stone_surface_max_y = 0;
2314 //TimeTaker timer1("stone obstacles");
2317 Add some random stone obstacles
2320 for(u32 ri=0; ri<15; ri++)
2322 // Randomize max height so usually stuff will be quite low
2323 s16 maxheight_randomized = myrand_range(0, 30);
2325 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2327 myrand_range(5, stone_obstacle_max_size),
2328 myrand_range(0, maxheight_randomized),
2329 myrand_range(5, stone_obstacle_max_size)
2332 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2333 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2336 // Minimum space left on top of the obstacle
2337 s16 min_head_space = 10;
2339 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2340 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2342 // Node position in 2d
2343 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2345 // Find stone ground level
2346 // (ignore everything else than mud in already generated chunks)
2347 // and mud amount over the stone level
2351 v3s16 em = vmanip.m_area.getExtent();
2352 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2354 // Go to ground level
2355 for(y=y_nodes_max; y>=y_nodes_min; y--)
2357 MapNode &n = vmanip.m_data[i];
2358 /*if(content_walkable(n.d)
2359 && n.d != CONTENT_MUD
2360 && n.d != CONTENT_GRASS)
2362 if(n.d == CONTENT_STONE)
2365 if(n.d == CONTENT_MUD || n.d == CONTENT_GRASS)
2368 vmanip.m_area.add_y(em, i, -1);
2370 if(y >= y_nodes_min)
2373 surface_y = y_nodes_min;
2381 v3s16 em = vmanip.m_area.getExtent();
2382 s16 y_start = surface_y+1;
2383 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2387 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2389 MapNode &n = vmanip.m_data[i];
2390 n.d = CONTENT_STONE;
2392 if(y > stone_surface_max_y)
2393 stone_surface_max_y = y;
2396 if(count >= ob_size.Y)
2399 vmanip.m_area.add_y(em, i, 1);
2403 for(; y<=y_nodes_max; y++)
2405 MapNode &n = vmanip.m_data[i];
2408 if(count >= mud_amount)
2411 vmanip.m_area.add_y(em, i, 1);
2421 //TimeTaker timer1("dungeons");
2426 u32 dungeons_count = relative_volume/200000;
2427 for(u32 jj=0; jj<dungeons_count; jj++)
2429 s16 min_tunnel_diameter = 1;
2430 s16 max_tunnel_diameter = 5;
2431 u16 tunnel_routepoints = 15;
2433 bool bruise_surface = (jj < dungeons_count / 3);
2437 min_tunnel_diameter = 5;
2438 max_tunnel_diameter = 10;
2439 tunnel_routepoints = 10;
2442 // Allowed route area size in nodes
2444 sectorpos_base_size*MAP_BLOCKSIZE,
2445 h_blocks*MAP_BLOCKSIZE,
2446 sectorpos_base_size*MAP_BLOCKSIZE
2449 // Area starting point in nodes
2451 sectorpos_base.X*MAP_BLOCKSIZE,
2452 y_blocks_min*MAP_BLOCKSIZE,
2453 sectorpos_base.Y*MAP_BLOCKSIZE
2457 //(this should be more than the maximum radius of the tunnel)
2459 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2460 ar += v3s16(1,0,1) * more * 2;
2461 of -= v3s16(1,0,1) * more;
2463 s16 route_y_min = 0;
2464 //s16 route_y_max = ar.Y-1;
2465 s16 route_y_max = stone_surface_max_y - of.Y;
2469 // Minimum is at y=0
2470 route_y_min = -of.Y - 0;
2471 route_y_min = rangelim(route_y_min, 0, route_y_max);
2474 /*dstream<<"route_y_min = "<<route_y_min
2475 <<", route_y_max = "<<route_y_max<<std::endl;*/
2477 // Randomize starting position
2479 (float)(myrand()%ar.X)+0.5,
2480 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2481 (float)(myrand()%ar.Z)+0.5
2484 MapNode airnode(CONTENT_AIR);
2487 Generate some tunnel starting from orp
2490 for(u16 j=0; j<tunnel_routepoints; j++)
2492 v3s16 maxlen(20, 10, 20);
2496 maxlen = v3s16(60,60,60);
2500 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2501 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2502 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2507 else if(rp.X >= ar.X)
2509 if(rp.Y < route_y_min)
2511 else if(rp.Y >= route_y_max)
2512 rp.Y = route_y_max-1;
2515 else if(rp.Z >= ar.Z)
2520 s16 min_d = min_tunnel_diameter;
2521 s16 max_d = max_tunnel_diameter;
2522 s16 rs = myrand_range(min_d, max_d);
2524 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2526 v3f fp = orp + vec * f;
2527 v3s16 cp(fp.X, fp.Y, fp.Z);
2530 s16 d1 = d0 + rs - 1;
2531 for(s16 z0=d0; z0<=d1; z0++)
2533 s16 si = rs - abs(z0);
2534 for(s16 x0=-si; x0<=si-1; x0++)
2536 s16 si2 = rs - abs(x0);
2537 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2543 /*if(isInArea(p, ar) == false)
2545 // Check only height
2546 if(y < 0 || y >= ar.Y)
2550 //assert(vmanip.m_area.contains(p));
2551 if(vmanip.m_area.contains(p) == false)
2553 dstream<<"WARNING: "<<__FUNCTION_NAME
2554 <<":"<<__LINE__<<": "
2555 <<"point not in area"
2560 // Just set it to air, it will be changed to
2562 u32 i = vmanip.m_area.index(p);
2563 vmanip.m_data[i] = airnode;
2577 //TimeTaker timer1("ore veins");
2582 for(u32 jj=0; jj<relative_volume/524; jj++)
2584 s16 max_vein_diameter = 3;
2586 // Allowed route area size in nodes
2588 sectorpos_base_size*MAP_BLOCKSIZE,
2589 h_blocks*MAP_BLOCKSIZE,
2590 sectorpos_base_size*MAP_BLOCKSIZE
2593 // Area starting point in nodes
2595 sectorpos_base.X*MAP_BLOCKSIZE,
2596 y_blocks_min*MAP_BLOCKSIZE,
2597 sectorpos_base.Y*MAP_BLOCKSIZE
2601 //(this should be more than the maximum radius of the tunnel)
2603 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2604 ar += v3s16(1,0,1) * more * 2;
2605 of -= v3s16(1,0,1) * more;
2607 // Randomize starting position
2609 (float)(myrand()%ar.X)+0.5,
2610 (float)(myrand()%ar.Y)+0.5,
2611 (float)(myrand()%ar.Z)+0.5
2614 // Randomize mineral
2615 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2618 Generate some vein starting from orp
2621 for(u16 j=0; j<2; j++)
2624 (float)(myrand()%ar.X)+0.5,
2625 (float)(myrand()%ar.Y)+0.5,
2626 (float)(myrand()%ar.Z)+0.5
2628 v3f vec = rp - orp;*/
2630 v3s16 maxlen(10, 10, 10);
2632 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2633 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2634 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2639 else if(rp.X >= ar.X)
2643 else if(rp.Y >= ar.Y)
2647 else if(rp.Z >= ar.Z)
2653 s16 max_d = max_vein_diameter;
2654 s16 rs = myrand_range(min_d, max_d);
2656 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2658 v3f fp = orp + vec * f;
2659 v3s16 cp(fp.X, fp.Y, fp.Z);
2661 s16 d1 = d0 + rs - 1;
2662 for(s16 z0=d0; z0<=d1; z0++)
2664 s16 si = rs - abs(z0);
2665 for(s16 x0=-si; x0<=si-1; x0++)
2667 s16 si2 = rs - abs(x0);
2668 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2670 // Don't put mineral to every place
2678 /*if(isInArea(p, ar) == false)
2680 // Check only height
2681 if(y < 0 || y >= ar.Y)
2685 assert(vmanip.m_area.contains(p));
2687 // Just set it to air, it will be changed to
2689 u32 i = vmanip.m_area.index(p);
2690 MapNode *n = &vmanip.m_data[i];
2691 if(n->d == CONTENT_STONE)
2706 //TimeTaker timer1("add mud");
2709 Add mud to the central chunk
2712 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2713 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2715 // Node position in 2d
2716 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2718 // Find ground level
2719 s16 surface_y = find_ground_level(vmanip, p2d);
2726 v3s16 em = vmanip.m_area.getExtent();
2727 s16 y_start = surface_y+1;
2728 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2729 for(s16 y=y_start; y<=y_nodes_max; y++)
2731 MapNode &n = vmanip.m_data[i];
2737 vmanip.m_area.add_y(em, i, 1);
2746 //TimeTaker timer1("flow mud");
2749 Flow mud away from steep edges
2752 // Iterate a few times
2753 for(s16 k=0; k<4; k++)
2756 for(s16 x=0-max_spread_amount+1;
2757 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2759 for(s16 z=0-max_spread_amount+1;
2760 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2763 // Node position in 2d
2764 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2766 v3s16 em = vmanip.m_area.getExtent();
2767 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2769 // Go to ground level
2770 for(y=y_nodes_max; y>=y_nodes_min; y--)
2772 MapNode &n = vmanip.m_data[i];
2773 //if(n.d != CONTENT_AIR)
2774 if(content_walkable(n.d))
2777 vmanip.m_area.add_y(em, i, -1);
2780 // If not mud, do nothing to it
2781 MapNode *n = &vmanip.m_data[i];
2782 if(n->d != CONTENT_MUD)
2786 v3s16(0,0,1), // back
2787 v3s16(1,0,0), // right
2788 v3s16(0,0,-1), // front
2789 v3s16(-1,0,0), // left
2794 for(u32 di=0; di<4; di++)
2796 v3s16 dirp = dirs4[di];
2798 // Check that side is air
2799 vmanip.m_area.add_p(em, i2, dirp);
2800 MapNode *n2 = &vmanip.m_data[i2];
2801 if(content_walkable(n2->d))
2803 // Check that under side is air
2804 vmanip.m_area.add_y(em, i2, -1);
2805 n2 = &vmanip.m_data[i2];
2806 if(content_walkable(n2->d))
2808 // Loop further down until not air
2810 vmanip.m_area.add_y(em, i2, -1);
2811 n2 = &vmanip.m_data[i2];
2812 }while(content_walkable(n2->d) == false);
2813 // Loop one up so that we're in air
2814 vmanip.m_area.add_y(em, i2, 1);
2815 n2 = &vmanip.m_data[i2];
2817 // Move mud to new place
2819 // Set old place to be air
2820 *n = MapNode(CONTENT_AIR);
2823 // Switch mud and other and change mud source to air
2824 //MapNode tempnode = *n2;
2827 // Force old mud position to be air
2841 //TimeTaker timer1("add water");
2844 Add water to the central chunk (and a bit more)
2847 for(s16 x=0-max_spread_amount;
2848 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2850 for(s16 z=0-max_spread_amount;
2851 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2854 // Node position in 2d
2855 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2857 // Find ground level
2858 //s16 surface_y = find_ground_level(vmanip, p2d);
2861 If ground level is over water level, skip.
2862 NOTE: This leaves caves near water without water,
2863 which looks especially crappy when the nearby water
2864 won't start flowing either for some reason
2866 /*if(surface_y > WATER_LEVEL)
2873 v3s16 em = vmanip.m_area.getExtent();
2874 s16 y_start = WATER_LEVEL;
2875 u8 light = LIGHT_MAX;
2876 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2877 MapNode *n = &vmanip.m_data[i];
2879 Add first one to transforming liquid queue
2881 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2883 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2884 m_transforming_liquid.push_back(p);
2886 for(s16 y=y_start; y>=y_nodes_min; y--)
2888 n = &vmanip.m_data[i];
2890 // Stop when there is no water and no air
2891 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2892 && n->d != CONTENT_WATER)
2895 Add bottom one to transforming liquid queue
2897 vmanip.m_area.add_y(em, i, 1);
2898 n = &vmanip.m_data[i];
2899 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2901 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2902 m_transforming_liquid.push_back(p);
2908 n->d = CONTENT_WATERSOURCE;
2909 n->setLight(LIGHTBANK_DAY, light);
2911 /*// Add to transforming liquid queue (in case it'd
2913 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2914 m_transforming_liquid.push_back(p);*/
2917 vmanip.m_area.add_y(em, i, -1);
2928 //TimeTaker timer1("plant trees");
2934 u32 tree_max = relative_area / 100;
2936 u32 count = myrand_range(0, tree_max);
2937 for(u32 i=0; i<count; i++)
2939 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2940 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2941 x += sectorpos_base.X*MAP_BLOCKSIZE;
2942 z += sectorpos_base.Y*MAP_BLOCKSIZE;
2943 s16 y = find_ground_level(vmanip, v2s16(x,z));
2944 // Don't make a tree under water level
2949 make_tree(vmanip, p);
2956 //TimeTaker timer1("grow grass");
2962 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
2963 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
2964 for(s16 x=0-max_spread_amount;
2965 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2967 for(s16 z=0-max_spread_amount;
2968 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2971 // Node position in 2d
2972 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2975 Find the lowest surface to which enough light ends up
2978 Basically just wait until not air and not leaves.
2982 v3s16 em = vmanip.m_area.getExtent();
2983 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2985 // Go to ground level
2986 for(y=y_nodes_max; y>=y_nodes_min; y--)
2988 MapNode &n = vmanip.m_data[i];
2989 if(n.d != CONTENT_AIR
2990 && n.d != CONTENT_LEAVES)
2992 vmanip.m_area.add_y(em, i, -1);
2994 if(y >= y_nodes_min)
2997 surface_y = y_nodes_min;
3000 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3001 MapNode *n = &vmanip.m_data[i];
3002 if(n->d == CONTENT_MUD)
3003 n->d = CONTENT_GRASS;
3012 core::map<v3s16, bool> light_sources;
3015 // 750ms @cs=8, can't optimize more
3016 //TimeTaker timer1("initial lighting");
3018 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3019 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3020 for(s16 x=0-max_spread_amount+1;
3021 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3023 for(s16 z=0-max_spread_amount+1;
3024 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3027 // Node position in 2d
3028 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3031 Apply initial sunlight
3034 u8 light = LIGHT_SUN;
3035 bool add_to_sources = false;
3036 v3s16 em = vmanip.m_area.getExtent();
3037 s16 y_start = y_nodes_max;
3038 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3039 for(s16 y=y_start; y>=y_nodes_min; y--)
3041 MapNode *n = &vmanip.m_data[i];
3043 if(light_propagates_content(n->d) == false)
3047 else if(light != LIGHT_SUN
3048 || sunlight_propagates_content(n->d) == false)
3054 // This doesn't take much time
3055 if(add_to_sources == false)
3058 Check sides. If side is not air or water, start
3059 adding to light_sources.
3062 v3s16(0,0,1), // back
3063 v3s16(1,0,0), // right
3064 v3s16(0,0,-1), // front
3065 v3s16(-1,0,0), // left
3067 for(u32 di=0; di<4; di++)
3069 v3s16 dirp = dirs4[di];
3071 vmanip.m_area.add_p(em, i2, dirp);
3072 MapNode *n2 = &vmanip.m_data[i2];
3074 n2->d != CONTENT_AIR
3075 && n2->d != CONTENT_WATERSOURCE
3076 && n2->d != CONTENT_WATER
3078 add_to_sources = true;
3084 n->setLight(LIGHTBANK_DAY, light);
3085 n->setLight(LIGHTBANK_NIGHT, 0);
3087 // This doesn't take much time
3088 if(light != 0 && add_to_sources)
3090 // Insert light source
3091 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3094 // Increment index by y
3095 vmanip.m_area.add_y(em, i, -1);
3102 // Spread light around
3104 TimeTaker timer("generateChunkRaw() spreadLight");
3105 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3112 timer_generate.stop();
3115 Blit generated stuff to map
3119 //TimeTaker timer("generateChunkRaw() blitBackAll");
3120 vmanip.blitBackAll(&changed_blocks);
3123 Update day/night difference cache of the MapBlocks
3126 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3127 i.atEnd() == false; i++)
3129 MapBlock *block = i.getNode()->getValue();
3130 block->updateDayNightDiff();
3136 Create chunk metadata
3139 for(s16 x=-1; x<=1; x++)
3140 for(s16 y=-1; y<=1; y++)
3142 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3143 // Add chunk meta information
3144 MapChunk *chunk = getChunk(chunkpos0);
3147 chunk = new MapChunk();
3148 m_chunks.insert(chunkpos0, chunk);
3150 //chunk->setIsVolatile(true);
3151 if(chunk->getGenLevel() > GENERATED_PARTLY)
3152 chunk->setGenLevel(GENERATED_PARTLY);
3156 Set central chunk non-volatile and return it
3158 MapChunk *chunk = getChunk(chunkpos);
3161 //chunk->setIsVolatile(false);
3162 chunk->setGenLevel(GENERATED_FULLY);
3167 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3168 core::map<v3s16, MapBlock*> &changed_blocks)
3170 dstream<<"generateChunk(): Generating chunk "
3171 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3174 /*for(s16 x=-1; x<=1; x++)
3175 for(s16 y=-1; y<=1; y++)*/
3176 for(s16 x=-0; x<=0; x++)
3177 for(s16 y=-0; y<=0; y++)
3179 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3180 MapChunk *chunk = getChunk(chunkpos0);
3181 // Skip if already generated
3182 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3184 generateChunkRaw(chunkpos0, changed_blocks);
3187 assert(chunkNonVolatile(chunkpos1));
3189 MapChunk *chunk = getChunk(chunkpos1);
3193 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3195 DSTACK("%s: p2d=(%d,%d)",
3200 Check if it exists already in memory
3202 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3207 Try to load it from disk (with blocks)
3209 if(loadSectorFull(p2d) == true)
3211 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3214 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3215 throw InvalidPositionException("");
3221 If there is no master heightmap, throw.
3223 if(m_heightmap == NULL)
3225 throw InvalidPositionException("createSector(): no heightmap");
3229 Do not create over-limit
3231 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3232 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3233 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3234 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3235 throw InvalidPositionException("createSector(): pos. over limit");
3238 Generate blank sector
3241 // Number of heightmaps in sector in each direction
3242 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3244 // Heightmap side width
3245 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3247 sector = new ServerMapSector(this, p2d, hm_split);
3249 // Sector position on map in nodes
3250 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3252 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3253 " heightmaps and objects"<<std::endl;*/
3256 Generate sector heightmap
3259 v2s16 mhm_p = p2d * hm_split;
3260 /*f32 corners[4] = {
3261 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3262 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3263 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3264 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3267 // Loop through sub-heightmaps
3268 for(s16 y=0; y<hm_split; y++)
3269 for(s16 x=0; x<hm_split; x++)
3271 v2s16 p_in_sector = v2s16(x,y);
3272 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3274 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3275 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3276 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3277 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3280 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3281 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3284 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3286 sector->setHeightmap(p_in_sector, hm);
3288 //hm->generateContinued(1.0, 0.5, corners);
3289 hm->generateContinued(0.5, 0.5, corners);
3294 // Add dummy objects
3295 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3296 sector->setObjects(objects);
3301 m_sectors.insert(p2d, sector);
3306 MapSector * ServerMap::emergeSector(v2s16 p2d,
3307 core::map<v3s16, MapBlock*> &changed_blocks)
3309 DSTACK("%s: p2d=(%d,%d)",
3316 v2s16 chunkpos = sector_to_chunk(p2d);
3317 /*bool chunk_nonvolatile = false;
3318 MapChunk *chunk = getChunk(chunkpos);
3319 if(chunk && chunk->getIsVolatile() == false)
3320 chunk_nonvolatile = true;*/
3321 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3324 If chunk is not fully generated, generate chunk
3326 if(chunk_nonvolatile == false)
3328 // Generate chunk and neighbors
3329 generateChunk(chunkpos, changed_blocks);
3333 Return sector if it exists now
3335 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3340 Try to load it from disk
3342 if(loadSectorFull(p2d) == true)
3344 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3347 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3348 throw InvalidPositionException("");
3354 generateChunk should have generated the sector
3361 //return generateSector();
3365 NOTE: This is not used for main map generation, only for blocks
3366 that are very high or low
3368 MapBlock * ServerMap::generateBlock(
3370 MapBlock *original_dummy,
3371 ServerMapSector *sector,
3372 core::map<v3s16, MapBlock*> &changed_blocks,
3373 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3376 DSTACK("%s: p=(%d,%d,%d)",
3380 /*dstream<<"generateBlock(): "
3381 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3384 MapBlock *block = original_dummy;
3386 v2s16 p2d(p.X, p.Z);
3390 Do not generate over-limit
3392 if(blockpos_over_limit(p))
3394 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3395 throw InvalidPositionException("generateBlock(): pos. over limit");
3399 If block doesn't exist, create one.
3400 If it exists, it is a dummy. In that case unDummify() it.
3402 NOTE: This already sets the map as the parent of the block
3406 block = sector->createBlankBlockNoInsert(block_y);
3410 // Remove the block so that nobody can get a half-generated one.
3411 sector->removeBlock(block);
3412 // Allocate the block to contain the generated data
3416 /*u8 water_material = CONTENT_WATER;
3417 if(g_settings.getBool("endless_water"))
3418 water_material = CONTENT_WATERSOURCE;*/
3419 u8 water_material = CONTENT_WATERSOURCE;
3421 s32 lowest_ground_y = 32767;
3422 s32 highest_ground_y = -32768;
3425 //sector->printHeightmaps();
3427 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3428 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3430 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3432 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3433 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3434 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3436 dstream<<"WARNING: Surface height not found in sector "
3437 "for block that is being emerged"<<std::endl;
3441 s16 surface_y = surface_y_f;
3442 //avg_ground_y += surface_y;
3443 if(surface_y < lowest_ground_y)
3444 lowest_ground_y = surface_y;
3445 if(surface_y > highest_ground_y)
3446 highest_ground_y = surface_y;
3448 s32 surface_depth = 0;
3450 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3452 //float min_slope = 0.45;
3453 //float max_slope = 0.85;
3454 float min_slope = 0.60;
3455 float max_slope = 1.20;
3456 float min_slope_depth = 5.0;
3457 float max_slope_depth = 0;
3459 if(slope < min_slope)
3460 surface_depth = min_slope_depth;
3461 else if(slope > max_slope)
3462 surface_depth = max_slope_depth;
3464 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3466 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3468 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3473 NOTE: If there are some man-made structures above the
3474 newly created block, they won't be taken into account.
3476 if(real_y > surface_y)
3477 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3483 // If node is over heightmap y, it's air or water
3484 if(real_y > surface_y)
3486 // If under water level, it's water
3487 if(real_y < WATER_LEVEL)
3489 n.d = water_material;
3490 n.setLight(LIGHTBANK_DAY,
3491 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3493 Add to transforming liquid queue (in case it'd
3496 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3497 m_transforming_liquid.push_back(real_pos);
3503 // Else it's ground or dungeons (air)
3506 // If it's surface_depth under ground, it's stone
3507 if(real_y <= surface_y - surface_depth)
3509 n.d = CONTENT_STONE;
3513 // It is mud if it is under the first ground
3514 // level or under water
3515 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3521 n.d = CONTENT_GRASS;
3524 //n.d = CONTENT_MUD;
3526 /*// If under water level, it's mud
3527 if(real_y < WATER_LEVEL)
3529 // Only the topmost node is grass
3530 else if(real_y <= surface_y - 1)
3533 n.d = CONTENT_GRASS;*/
3537 block->setNode(v3s16(x0,y0,z0), n);
3542 Calculate some helper variables
3545 // Completely underground if the highest part of block is under lowest
3547 // This has to be very sure; it's probably one too strict now but
3548 // that's just better.
3549 bool completely_underground =
3550 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3552 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3554 bool mostly_underwater_surface = false;
3555 if(highest_ground_y < WATER_LEVEL
3556 && some_part_underground && !completely_underground)
3557 mostly_underwater_surface = true;
3560 Get local attributes
3563 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3565 float caves_amount = 0.5;
3570 NOTE: BEWARE: Too big amount of attribute points slows verything
3572 1 interpolation from 5000 points takes 2-3ms.
3574 //TimeTaker timer("generateBlock() local attribute retrieval");
3575 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3576 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3577 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3581 //dstream<<"generateBlock(): Done"<<std::endl;
3587 // Initialize temporary table
3588 const s32 ued = MAP_BLOCKSIZE;
3589 bool underground_emptiness[ued*ued*ued];
3590 for(s32 i=0; i<ued*ued*ued; i++)
3592 underground_emptiness[i] = 0;
3599 Initialize orp and ors. Try to find if some neighboring
3600 MapBlock has a tunnel ended in its side
3604 (float)(myrand()%ued)+0.5,
3605 (float)(myrand()%ued)+0.5,
3606 (float)(myrand()%ued)+0.5
3609 bool found_existing = false;
3615 for(s16 y=0; y<ued; y++)
3616 for(s16 x=0; x<ued; x++)
3618 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3619 if(getNode(ap).d == CONTENT_AIR)
3621 orp = v3f(x+1,y+1,0);
3622 found_existing = true;
3623 goto continue_generating;
3627 catch(InvalidPositionException &e){}
3633 for(s16 y=0; y<ued; y++)
3634 for(s16 x=0; x<ued; x++)
3636 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3637 if(getNode(ap).d == CONTENT_AIR)
3639 orp = v3f(x+1,y+1,ued-1);
3640 found_existing = true;
3641 goto continue_generating;
3645 catch(InvalidPositionException &e){}
3651 for(s16 y=0; y<ued; y++)
3652 for(s16 z=0; z<ued; z++)
3654 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3655 if(getNode(ap).d == CONTENT_AIR)
3657 orp = v3f(0,y+1,z+1);
3658 found_existing = true;
3659 goto continue_generating;
3663 catch(InvalidPositionException &e){}
3669 for(s16 y=0; y<ued; y++)
3670 for(s16 z=0; z<ued; z++)
3672 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3673 if(getNode(ap).d == CONTENT_AIR)
3675 orp = v3f(ued-1,y+1,z+1);
3676 found_existing = true;
3677 goto continue_generating;
3681 catch(InvalidPositionException &e){}
3687 for(s16 x=0; x<ued; x++)
3688 for(s16 z=0; z<ued; z++)
3690 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3691 if(getNode(ap).d == CONTENT_AIR)
3693 orp = v3f(x+1,0,z+1);
3694 found_existing = true;
3695 goto continue_generating;
3699 catch(InvalidPositionException &e){}
3705 for(s16 x=0; x<ued; x++)
3706 for(s16 z=0; z<ued; z++)
3708 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3709 if(getNode(ap).d == CONTENT_AIR)
3711 orp = v3f(x+1,ued-1,z+1);
3712 found_existing = true;
3713 goto continue_generating;
3717 catch(InvalidPositionException &e){}
3719 continue_generating:
3722 Choose whether to actually generate dungeon
3724 bool do_generate_dungeons = true;
3725 // Don't generate if no part is underground
3726 if(!some_part_underground)
3728 do_generate_dungeons = false;
3730 // Don't generate if mostly underwater surface
3731 /*else if(mostly_underwater_surface)
3733 do_generate_dungeons = false;
3735 // Partly underground = cave
3736 else if(!completely_underground)
3738 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3740 // Found existing dungeon underground
3741 else if(found_existing && completely_underground)
3743 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3745 // Underground and no dungeons found
3748 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3751 if(do_generate_dungeons)
3754 Generate some tunnel starting from orp and ors
3756 for(u16 i=0; i<3; i++)
3759 (float)(myrand()%ued)+0.5,
3760 (float)(myrand()%ued)+0.5,
3761 (float)(myrand()%ued)+0.5
3765 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3769 for(float f=0; f<1.0; f+=0.04)
3771 v3f fp = orp + vec * f;
3772 v3s16 cp(fp.X, fp.Y, fp.Z);
3774 s16 d1 = d0 + rs - 1;
3775 for(s16 z0=d0; z0<=d1; z0++)
3777 s16 si = rs - abs(z0);
3778 for(s16 x0=-si; x0<=si-1; x0++)
3780 s16 si2 = rs - abs(x0);
3781 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3787 if(isInArea(p, ued) == false)
3789 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3801 // Set to true if has caves.
3802 // Set when some non-air is changed to air when making caves.
3803 bool has_dungeons = false;
3806 Apply temporary cave data to block
3809 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3810 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3812 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3814 MapNode n = block->getNode(v3s16(x0,y0,z0));
3817 if(underground_emptiness[
3818 ued*ued*(z0*ued/MAP_BLOCKSIZE)
3819 +ued*(y0*ued/MAP_BLOCKSIZE)
3820 +(x0*ued/MAP_BLOCKSIZE)])
3822 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
3825 has_dungeons = true;
3831 block->setNode(v3s16(x0,y0,z0), n);
3836 This is used for guessing whether or not the block should
3837 receive sunlight from the top if the block above doesn't exist
3839 block->setIsUnderground(completely_underground);
3842 Force lighting update if some part of block is partly
3843 underground and has caves.
3845 /*if(some_part_underground && !completely_underground && has_dungeons)
3847 //dstream<<"Half-ground caves"<<std::endl;
3848 lighting_invalidated_blocks[block->getPos()] = block;
3851 // DEBUG: Always update lighting
3852 //lighting_invalidated_blocks[block->getPos()] = block;
3858 if(some_part_underground)
3860 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
3865 for(s16 i=0; i<underground_level/4 + 1; i++)
3867 if(myrand()%50 == 0)
3870 (myrand()%(MAP_BLOCKSIZE-2))+1,
3871 (myrand()%(MAP_BLOCKSIZE-2))+1,
3872 (myrand()%(MAP_BLOCKSIZE-2))+1
3878 for(u16 i=0; i<27; i++)
3880 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3882 block->setNode(cp+g_27dirs[i], n);
3890 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
3891 u16 coal_rareness = 60 / coal_amount;
3892 if(coal_rareness == 0)
3894 if(myrand()%coal_rareness == 0)
3896 u16 a = myrand() % 16;
3897 u16 amount = coal_amount * a*a*a / 1000;
3898 for(s16 i=0; i<amount; i++)
3901 (myrand()%(MAP_BLOCKSIZE-2))+1,
3902 (myrand()%(MAP_BLOCKSIZE-2))+1,
3903 (myrand()%(MAP_BLOCKSIZE-2))+1
3907 n.d = CONTENT_STONE;
3908 n.param = MINERAL_COAL;
3910 for(u16 i=0; i<27; i++)
3912 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3914 block->setNode(cp+g_27dirs[i], n);
3922 //TODO: change to iron_amount or whatever
3923 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
3924 u16 iron_rareness = 60 / iron_amount;
3925 if(iron_rareness == 0)
3927 if(myrand()%iron_rareness == 0)
3929 u16 a = myrand() % 16;
3930 u16 amount = iron_amount * a*a*a / 1000;
3931 for(s16 i=0; i<amount; i++)
3934 (myrand()%(MAP_BLOCKSIZE-2))+1,
3935 (myrand()%(MAP_BLOCKSIZE-2))+1,
3936 (myrand()%(MAP_BLOCKSIZE-2))+1
3940 n.d = CONTENT_STONE;
3941 n.param = MINERAL_IRON;
3943 for(u16 i=0; i<27; i++)
3945 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3947 block->setNode(cp+g_27dirs[i], n);
3954 Create a few rats in empty blocks underground
3956 if(completely_underground)
3958 //for(u16 i=0; i<2; i++)
3961 (myrand()%(MAP_BLOCKSIZE-2))+1,
3962 (myrand()%(MAP_BLOCKSIZE-2))+1,
3963 (myrand()%(MAP_BLOCKSIZE-2))+1
3966 // Check that the place is empty
3967 //if(!is_ground_content(block->getNode(cp).d))
3970 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
3971 block->addObject(obj);
3977 Add block to sector.
3979 sector->insertBlock(block);
3985 // An y-wise container of changed blocks
3986 core::map<s16, MapBlock*> changed_blocks_sector;
3989 Check if any sector's objects can be placed now.
3992 core::map<v3s16, u8> *objects = sector->getObjects();
3993 core::list<v3s16> objects_to_remove;
3994 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
3995 i.atEnd() == false; i++)
3997 v3s16 p = i.getNode()->getKey();
3999 u8 d = i.getNode()->getValue();
4001 // Ground level point (user for stuff that is on ground)
4003 bool ground_found = true;
4005 // Search real ground level
4009 MapNode n = sector->getNode(gp);
4011 // If not air, go one up and continue to placing the tree
4012 if(n.d != CONTENT_AIR)
4018 // If air, go one down
4019 gp += v3s16(0,-1,0);
4021 }catch(InvalidPositionException &e)
4023 // Ground not found.
4024 ground_found = false;
4025 // This is most close to ground
4032 if(d == SECTOR_OBJECT_TEST)
4034 if(sector->isValidArea(p + v3s16(0,0,0),
4035 p + v3s16(0,0,0), &changed_blocks_sector))
4038 n.d = CONTENT_TORCH;
4039 sector->setNode(p, n);
4040 objects_to_remove.push_back(p);
4043 else if(d == SECTOR_OBJECT_TREE_1)
4045 if(ground_found == false)
4048 v3s16 p_min = gp + v3s16(-1,0,-1);
4049 v3s16 p_max = gp + v3s16(1,5,1);
4050 if(sector->isValidArea(p_min, p_max,
4051 &changed_blocks_sector))
4055 sector->setNode(gp+v3s16(0,0,0), n);
4056 sector->setNode(gp+v3s16(0,1,0), n);
4057 sector->setNode(gp+v3s16(0,2,0), n);
4058 sector->setNode(gp+v3s16(0,3,0), n);
4060 n.d = CONTENT_LEAVES;
4062 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4064 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4065 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4066 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4067 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4068 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4069 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4070 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4071 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4073 sector->setNode(gp+v3s16(0,4,0), n);
4075 sector->setNode(gp+v3s16(-1,4,0), n);
4076 sector->setNode(gp+v3s16(1,4,0), n);
4077 sector->setNode(gp+v3s16(0,4,-1), n);
4078 sector->setNode(gp+v3s16(0,4,1), n);
4079 sector->setNode(gp+v3s16(1,4,1), n);
4080 sector->setNode(gp+v3s16(-1,4,1), n);
4081 sector->setNode(gp+v3s16(-1,4,-1), n);
4082 sector->setNode(gp+v3s16(1,4,-1), n);
4084 sector->setNode(gp+v3s16(-1,3,0), n);
4085 sector->setNode(gp+v3s16(1,3,0), n);
4086 sector->setNode(gp+v3s16(0,3,-1), n);
4087 sector->setNode(gp+v3s16(0,3,1), n);
4088 sector->setNode(gp+v3s16(1,3,1), n);
4089 sector->setNode(gp+v3s16(-1,3,1), n);
4090 sector->setNode(gp+v3s16(-1,3,-1), n);
4091 sector->setNode(gp+v3s16(1,3,-1), n);
4093 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4094 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4095 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4096 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4097 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4098 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4099 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4100 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4102 // Objects are identified by wanted position
4103 objects_to_remove.push_back(p);
4105 // Lighting has to be recalculated for this one.
4106 sector->getBlocksInArea(p_min, p_max,
4107 lighting_invalidated_blocks);
4110 else if(d == SECTOR_OBJECT_BUSH_1)
4112 if(ground_found == false)
4115 if(sector->isValidArea(gp + v3s16(0,0,0),
4116 gp + v3s16(0,0,0), &changed_blocks_sector))
4119 n.d = CONTENT_LEAVES;
4120 sector->setNode(gp+v3s16(0,0,0), n);
4122 // Objects are identified by wanted position
4123 objects_to_remove.push_back(p);
4126 else if(d == SECTOR_OBJECT_RAVINE)
4129 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4130 v3s16 p_max = p + v3s16(6,6,6);
4131 if(sector->isValidArea(p_min, p_max,
4132 &changed_blocks_sector))
4135 n.d = CONTENT_STONE;
4138 s16 depth = maxdepth + (myrand()%10);
4140 s16 minz = -6 - (-2);
4142 for(s16 x=-6; x<=6; x++)
4144 z += -1 + (myrand()%3);
4149 for(s16 y=depth+(myrand()%2); y<=6; y++)
4151 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4154 v3s16 p2 = p + v3s16(x,y,z-2);
4155 //if(is_ground_content(sector->getNode(p2).d))
4156 if(content_features(sector->getNode(p2).d).walkable)
4157 sector->setNode(p2, n);
4160 v3s16 p2 = p + v3s16(x,y,z-1);
4161 if(content_features(sector->getNode(p2).d).walkable)
4162 sector->setNode(p2, n2);
4165 v3s16 p2 = p + v3s16(x,y,z+0);
4166 if(content_features(sector->getNode(p2).d).walkable)
4167 sector->setNode(p2, n2);
4170 v3s16 p2 = p + v3s16(x,y,z+1);
4171 if(content_features(sector->getNode(p2).d).walkable)
4172 sector->setNode(p2, n);
4175 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4176 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4180 objects_to_remove.push_back(p);
4182 // Lighting has to be recalculated for this one.
4183 sector->getBlocksInArea(p_min, p_max,
4184 lighting_invalidated_blocks);
4189 dstream<<"ServerMap::generateBlock(): "
4190 "Invalid heightmap object"
4195 catch(InvalidPositionException &e)
4197 dstream<<"WARNING: "<<__FUNCTION_NAME
4198 <<": while inserting object "<<(int)d
4199 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4200 <<" InvalidPositionException.what()="
4201 <<e.what()<<std::endl;
4202 // This is not too fatal and seems to happen sometimes.
4207 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4208 i != objects_to_remove.end(); i++)
4210 objects->remove(*i);
4214 Translate sector's changed blocks to global changed blocks
4217 for(core::map<s16, MapBlock*>::Iterator
4218 i = changed_blocks_sector.getIterator();
4219 i.atEnd() == false; i++)
4221 MapBlock *block = i.getNode()->getValue();
4223 changed_blocks.insert(block->getPos(), block);
4226 block->setLightingExpired(true);
4233 <<"lighting_invalidated_blocks.size()"
4237 <<" "<<lighting_invalidated_blocks.size()
4238 <<", "<<has_dungeons
4239 <<", "<<completely_underground
4240 <<", "<<some_part_underground
4247 MapBlock * ServerMap::createBlock(v3s16 p)
4249 DSTACK("%s: p=(%d,%d,%d)",
4250 __FUNCTION_NAME, p.X, p.Y, p.Z);
4252 v2s16 p2d(p.X, p.Z);
4255 This will create or load a sector if not found in memory.
4256 If block exists on disk, it will be loaded.
4258 NOTE: On old save formats, this will be slow, as it generates
4259 lighting on blocks for them.
4261 ServerMapSector *sector;
4263 sector = (ServerMapSector*)createSector(p2d);
4264 assert(sector->getId() == MAPSECTOR_SERVER);
4266 /*catch(InvalidPositionException &e)
4268 dstream<<"createBlock: createSector() failed"<<std::endl;
4271 catch(std::exception &e)
4273 dstream<<"createBlock: createSector() failed: "
4274 <<e.what()<<std::endl;
4279 Try to get a block from the sector
4282 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4286 block = sector->createBlankBlock(block_y);
4290 MapBlock * ServerMap::emergeBlock(
4292 bool only_from_disk,
4293 core::map<v3s16, MapBlock*> &changed_blocks,
4294 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4297 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4299 p.X, p.Y, p.Z, only_from_disk);
4301 v2s16 p2d(p.X, p.Z);
4304 This will create or load a sector if not found in memory.
4305 If block exists on disk, it will be loaded.
4307 NOTE: On old save formats, this will be slow, as it generates
4308 lighting on blocks for them.
4310 ServerMapSector *sector;
4312 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4313 assert(sector->getId() == MAPSECTOR_SERVER);
4315 catch(std::exception &e)
4317 dstream<<"emergeBlock: emergeSector() failed: "
4318 <<e.what()<<std::endl;
4323 Try to get a block from the sector
4326 bool does_not_exist = false;
4327 bool lighting_expired = false;
4328 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4332 does_not_exist = true;
4334 else if(block->isDummy() == true)
4336 does_not_exist = true;
4338 else if(block->getLightingExpired())
4340 lighting_expired = true;
4345 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4350 If block was not found on disk and not going to generate a
4351 new one, make sure there is a dummy block in place.
4353 if(only_from_disk && (does_not_exist || lighting_expired))
4355 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4359 // Create dummy block
4360 block = new MapBlock(this, p, true);
4362 // Add block to sector
4363 sector->insertBlock(block);
4369 //dstream<<"Not found on disk, generating."<<std::endl;
4371 //TimeTaker("emergeBlock() generate");
4373 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4376 If the block doesn't exist, generate the block.
4380 block = generateBlock(p, block, sector, changed_blocks,
4381 lighting_invalidated_blocks);
4384 if(lighting_expired)
4386 lighting_invalidated_blocks.insert(p, block);
4390 Initially update sunlight
4394 core::map<v3s16, bool> light_sources;
4395 bool black_air_left = false;
4396 bool bottom_invalid =
4397 block->propagateSunlight(light_sources, true,
4398 &black_air_left, true);
4400 // If sunlight didn't reach everywhere and part of block is
4401 // above ground, lighting has to be properly updated
4402 //if(black_air_left && some_part_underground)
4405 lighting_invalidated_blocks[block->getPos()] = block;
4410 lighting_invalidated_blocks[block->getPos()] = block;
4415 Debug mode operation
4417 bool haxmode = g_settings.getBool("haxmode");
4420 // Don't calculate lighting at all
4421 //lighting_invalidated_blocks.clear();
4427 void ServerMap::createDir(std::string path)
4429 if(fs::CreateDir(path) == false)
4431 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4432 <<"\""<<path<<"\""<<std::endl;
4433 throw BaseException("ServerMap failed to create directory");
4437 std::string ServerMap::getSectorSubDir(v2s16 pos)
4440 snprintf(cc, 9, "%.4x%.4x",
4441 (unsigned int)pos.X&0xffff,
4442 (unsigned int)pos.Y&0xffff);
4444 return std::string(cc);
4447 std::string ServerMap::getSectorDir(v2s16 pos)
4449 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4452 v2s16 ServerMap::getSectorPos(std::string dirname)
4454 if(dirname.size() != 8)
4455 throw InvalidFilenameException("Invalid sector directory name");
4457 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4459 throw InvalidFilenameException("Invalid sector directory name");
4460 v2s16 pos((s16)x, (s16)y);
4464 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4466 v2s16 p2d = getSectorPos(sectordir);
4468 if(blockfile.size() != 4){
4469 throw InvalidFilenameException("Invalid block filename");
4472 int r = sscanf(blockfile.c_str(), "%4x", &y);
4474 throw InvalidFilenameException("Invalid block filename");
4475 return v3s16(p2d.X, y, p2d.Y);
4479 #define ENABLE_SECTOR_SAVING 1
4480 #define ENABLE_SECTOR_LOADING 1
4481 #define ENABLE_BLOCK_SAVING 1
4482 #define ENABLE_BLOCK_LOADING 1
4484 void ServerMap::save(bool only_changed)
4486 DSTACK(__FUNCTION_NAME);
4487 if(m_map_saving_enabled == false)
4489 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4493 if(only_changed == false)
4494 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4497 saveMasterHeightmap();
4499 u32 sector_meta_count = 0;
4500 u32 block_count = 0;
4503 JMutexAutoLock lock(m_sector_mutex);
4505 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4506 for(; i.atEnd() == false; i++)
4508 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4509 assert(sector->getId() == MAPSECTOR_SERVER);
4511 if(ENABLE_SECTOR_SAVING)
4513 if(sector->differs_from_disk || only_changed == false)
4515 saveSectorMeta(sector);
4516 sector_meta_count++;
4519 if(ENABLE_BLOCK_SAVING)
4521 core::list<MapBlock*> blocks;
4522 sector->getBlocks(blocks);
4523 core::list<MapBlock*>::Iterator j;
4524 for(j=blocks.begin(); j!=blocks.end(); j++)
4526 MapBlock *block = *j;
4527 if(block->getChangedFlag() || only_changed == false)
4539 Only print if something happened or saved whole map
4541 if(only_changed == false || sector_meta_count != 0
4542 || block_count != 0)
4544 dstream<<DTIME<<"ServerMap: Written: "
4545 <<sector_meta_count<<" sector metadata files, "
4546 <<block_count<<" block files"
4551 void ServerMap::loadAll()
4553 DSTACK(__FUNCTION_NAME);
4554 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4556 loadMasterHeightmap();
4558 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4560 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4562 JMutexAutoLock lock(m_sector_mutex);
4565 s32 printed_counter = -100000;
4566 s32 count = list.size();
4568 std::vector<fs::DirListNode>::iterator i;
4569 for(i=list.begin(); i!=list.end(); i++)
4571 if(counter > printed_counter + 10)
4573 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4574 printed_counter = counter;
4578 MapSector *sector = NULL;
4580 // We want directories
4584 sector = loadSectorMeta(i->name);
4586 catch(InvalidFilenameException &e)
4588 // This catches unknown crap in directory
4591 if(ENABLE_BLOCK_LOADING)
4593 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4594 (m_savedir+"/sectors/"+i->name);
4595 std::vector<fs::DirListNode>::iterator i2;
4596 for(i2=list2.begin(); i2!=list2.end(); i2++)
4602 loadBlock(i->name, i2->name, sector);
4604 catch(InvalidFilenameException &e)
4606 // This catches unknown crap in directory
4611 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4614 void ServerMap::saveMasterHeightmap()
4616 DSTACK(__FUNCTION_NAME);
4617 createDir(m_savedir);
4619 std::string fullpath = m_savedir + "/master_heightmap";
4620 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4621 if(o.good() == false)
4622 throw FileNotGoodException("Cannot open master heightmap");
4624 // Format used for writing
4625 u8 version = SER_FMT_VER_HIGHEST;
4628 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4630 [0] u8 serialization version
4631 [1] X master heightmap
4633 u32 fullsize = 1 + hmdata.getSize();
4634 SharedBuffer<u8> data(fullsize);
4637 memcpy(&data[1], *hmdata, hmdata.getSize());
4639 o.write((const char*)*data, fullsize);
4642 m_heightmap->serialize(o, version);
4645 void ServerMap::loadMasterHeightmap()
4647 DSTACK(__FUNCTION_NAME);
4648 std::string fullpath = m_savedir + "/master_heightmap";
4649 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4650 if(is.good() == false)
4651 throw FileNotGoodException("Cannot open master heightmap");
4653 if(m_heightmap != NULL)
4656 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4659 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4661 DSTACK(__FUNCTION_NAME);
4662 // Format used for writing
4663 u8 version = SER_FMT_VER_HIGHEST;
4665 v2s16 pos = sector->getPos();
4666 createDir(m_savedir);
4667 createDir(m_savedir+"/sectors");
4668 std::string dir = getSectorDir(pos);
4671 std::string fullpath = dir + "/heightmap";
4672 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4673 if(o.good() == false)
4674 throw FileNotGoodException("Cannot open master heightmap");
4676 sector->serialize(o, version);
4678 sector->differs_from_disk = false;
4681 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4683 DSTACK(__FUNCTION_NAME);
4685 v2s16 p2d = getSectorPos(dirname);
4686 std::string dir = m_savedir + "/sectors/" + dirname;
4688 std::string fullpath = dir + "/heightmap";
4689 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4690 if(is.good() == false)
4691 throw FileNotGoodException("Cannot open sector heightmap");
4693 ServerMapSector *sector = ServerMapSector::deSerialize
4694 (is, this, p2d, &m_hwrapper, m_sectors);
4696 sector->differs_from_disk = false;
4701 bool ServerMap::loadSectorFull(v2s16 p2d)
4703 DSTACK(__FUNCTION_NAME);
4704 std::string sectorsubdir = getSectorSubDir(p2d);
4706 MapSector *sector = NULL;
4708 JMutexAutoLock lock(m_sector_mutex);
4711 sector = loadSectorMeta(sectorsubdir);
4713 catch(InvalidFilenameException &e)
4717 catch(FileNotGoodException &e)
4721 catch(std::exception &e)
4726 if(ENABLE_BLOCK_LOADING)
4728 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4729 (m_savedir+"/sectors/"+sectorsubdir);
4730 std::vector<fs::DirListNode>::iterator i2;
4731 for(i2=list2.begin(); i2!=list2.end(); i2++)
4737 loadBlock(sectorsubdir, i2->name, sector);
4739 catch(InvalidFilenameException &e)
4741 // This catches unknown crap in directory
4749 bool ServerMap::deFlushSector(v2s16 p2d)
4751 DSTACK(__FUNCTION_NAME);
4752 // See if it already exists in memory
4754 MapSector *sector = getSectorNoGenerate(p2d);
4757 catch(InvalidPositionException &e)
4760 Try to load the sector from disk.
4762 if(loadSectorFull(p2d) == true)
4771 void ServerMap::saveBlock(MapBlock *block)
4773 DSTACK(__FUNCTION_NAME);
4775 Dummy blocks are not written
4777 if(block->isDummy())
4779 /*v3s16 p = block->getPos();
4780 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4781 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4785 // Format used for writing
4786 u8 version = SER_FMT_VER_HIGHEST;
4788 v3s16 p3d = block->getPos();
4789 v2s16 p2d(p3d.X, p3d.Z);
4790 createDir(m_savedir);
4791 createDir(m_savedir+"/sectors");
4792 std::string dir = getSectorDir(p2d);
4795 // Block file is map/sectors/xxxxxxxx/xxxx
4797 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4798 std::string fullpath = dir + "/" + cc;
4799 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4800 if(o.good() == false)
4801 throw FileNotGoodException("Cannot open block data");
4804 [0] u8 serialization version
4807 o.write((char*)&version, 1);
4809 block->serialize(o, version);
4812 Versions up from 9 have block objects.
4816 block->serializeObjects(o, version);
4819 // We just wrote it to the disk
4820 block->resetChangedFlag();
4823 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4825 DSTACK(__FUNCTION_NAME);
4829 // Block file is map/sectors/xxxxxxxx/xxxx
4830 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4831 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4832 if(is.good() == false)
4833 throw FileNotGoodException("Cannot open block file");
4835 v3s16 p3d = getBlockPos(sectordir, blockfile);
4836 v2s16 p2d(p3d.X, p3d.Z);
4838 assert(sector->getPos() == p2d);
4840 u8 version = SER_FMT_VER_INVALID;
4841 is.read((char*)&version, 1);
4843 /*u32 block_size = MapBlock::serializedLength(version);
4844 SharedBuffer<u8> data(block_size);
4845 is.read((char*)*data, block_size);*/
4847 // This will always return a sector because we're the server
4848 //MapSector *sector = emergeSector(p2d);
4850 MapBlock *block = NULL;
4851 bool created_new = false;
4853 block = sector->getBlockNoCreate(p3d.Y);
4855 catch(InvalidPositionException &e)
4857 block = sector->createBlankBlockNoInsert(p3d.Y);
4861 // deserialize block data
4862 block->deSerialize(is, version);
4865 Versions up from 9 have block objects.
4869 block->updateObjects(is, version, NULL, 0);
4873 sector->insertBlock(block);
4876 Convert old formats to new and save
4879 // Save old format blocks in new format
4880 if(version < SER_FMT_VER_HIGHEST)
4885 // We just loaded it from the disk, so it's up-to-date.
4886 block->resetChangedFlag();
4889 catch(SerializationError &e)
4891 dstream<<"WARNING: Invalid block data on disk "
4892 "(SerializationError). Ignoring."
4897 // Gets from master heightmap
4898 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4900 assert(m_heightmap != NULL);
4908 corners[0] = m_heightmap->getGroundHeight
4909 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4910 corners[1] = m_heightmap->getGroundHeight
4911 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4912 corners[2] = m_heightmap->getGroundHeight
4913 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4914 corners[3] = m_heightmap->getGroundHeight
4915 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
4918 void ServerMap::PrintInfo(std::ostream &out)
4929 ClientMap::ClientMap(
4931 MapDrawControl &control,
4932 scene::ISceneNode* parent,
4933 scene::ISceneManager* mgr,
4937 scene::ISceneNode(parent, mgr, id),
4944 /*m_box = core::aabbox3d<f32>(0,0,0,
4945 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4946 /*m_box = core::aabbox3d<f32>(0,0,0,
4947 map->getSizeNodes().X * BS,
4948 map->getSizeNodes().Y * BS,
4949 map->getSizeNodes().Z * BS);*/
4950 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4951 BS*1000000,BS*1000000,BS*1000000);
4953 //setPosition(v3f(BS,BS,BS));
4956 ClientMap::~ClientMap()
4958 JMutexAutoLock lock(mesh_mutex);
4967 MapSector * ClientMap::emergeSector(v2s16 p2d)
4969 DSTACK(__FUNCTION_NAME);
4970 // Check that it doesn't exist already
4972 return getSectorNoGenerate(p2d);
4974 catch(InvalidPositionException &e)
4978 // Create a sector with no heightmaps
4979 ClientMapSector *sector = new ClientMapSector(this, p2d);
4982 JMutexAutoLock lock(m_sector_mutex);
4983 m_sectors.insert(p2d, sector);
4989 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
4991 DSTACK(__FUNCTION_NAME);
4992 ClientMapSector *sector = NULL;
4994 JMutexAutoLock lock(m_sector_mutex);
4996 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5000 sector = (ClientMapSector*)n->getValue();
5001 assert(sector->getId() == MAPSECTOR_CLIENT);
5005 sector = new ClientMapSector(this, p2d);
5007 JMutexAutoLock lock(m_sector_mutex);
5008 m_sectors.insert(p2d, sector);
5012 sector->deSerialize(is);
5015 void ClientMap::OnRegisterSceneNode()
5019 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5020 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5023 ISceneNode::OnRegisterSceneNode();
5026 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5028 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5029 DSTACK(__FUNCTION_NAME);
5031 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5034 Get time for measuring timeout.
5036 Measuring time is very useful for long delays when the
5037 machine is swapping a lot.
5039 int time1 = time(0);
5041 u32 daynight_ratio = m_client->getDayNightRatio();
5043 m_camera_mutex.Lock();
5044 v3f camera_position = m_camera_position;
5045 v3f camera_direction = m_camera_direction;
5046 m_camera_mutex.Unlock();
5049 Get all blocks and draw all visible ones
5052 v3s16 cam_pos_nodes(
5053 camera_position.X / BS,
5054 camera_position.Y / BS,
5055 camera_position.Z / BS);
5057 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5059 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5060 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5062 // Take a fair amount as we will be dropping more out later
5064 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5065 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5066 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5068 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5069 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5070 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5072 u32 vertex_count = 0;
5074 // For limiting number of mesh updates per frame
5075 u32 mesh_update_count = 0;
5077 u32 blocks_would_have_drawn = 0;
5078 u32 blocks_drawn = 0;
5080 //NOTE: The sectors map should be locked but we're not doing it
5081 // because it'd cause too much delays
5083 int timecheck_counter = 0;
5084 core::map<v2s16, MapSector*>::Iterator si;
5085 si = m_sectors.getIterator();
5086 for(; si.atEnd() == false; si++)
5089 timecheck_counter++;
5090 if(timecheck_counter > 50)
5092 int time2 = time(0);
5093 if(time2 > time1 + 4)
5095 dstream<<"ClientMap::renderMap(): "
5096 "Rendering takes ages, returning."
5103 MapSector *sector = si.getNode()->getValue();
5104 v2s16 sp = sector->getPos();
5106 if(m_control.range_all == false)
5108 if(sp.X < p_blocks_min.X
5109 || sp.X > p_blocks_max.X
5110 || sp.Y < p_blocks_min.Z
5111 || sp.Y > p_blocks_max.Z)
5115 core::list< MapBlock * > sectorblocks;
5116 sector->getBlocks(sectorblocks);
5122 core::list< MapBlock * >::Iterator i;
5123 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5125 MapBlock *block = *i;
5128 Compare block position to camera position, skip
5129 if not seen on display
5132 float range = 100000 * BS;
5133 if(m_control.range_all == false)
5134 range = m_control.wanted_range * BS;
5136 if(isBlockInSight(block->getPos(), camera_position,
5137 camera_direction, range) == false)
5143 v3s16 blockpos_nodes = block->getPosRelative();
5145 // Block center position
5147 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5148 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5149 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5152 // Block position relative to camera
5153 v3f blockpos_relative = blockpos - camera_position;
5155 // Distance in camera direction (+=front, -=back)
5156 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5159 f32 d = blockpos_relative.getLength();
5161 if(m_control.range_all == false)
5163 // If block is far away, don't draw it
5164 if(d > m_control.wanted_range * BS)
5168 // Maximum radius of a block
5169 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5171 // If block is (nearly) touching the camera, don't
5172 // bother validating further (that is, render it anyway)
5173 if(d > block_max_radius * 1.5)
5175 // Cosine of the angle between the camera direction
5176 // and the block direction (camera_direction is an unit vector)
5177 f32 cosangle = dforward / d;
5179 // Compensate for the size of the block
5180 // (as the block has to be shown even if it's a bit off FOV)
5181 // This is an estimate.
5182 cosangle += block_max_radius / dforward;
5184 // If block is not in the field of view, skip it
5185 //if(cosangle < cos(FOV_ANGLE/2))
5186 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5191 v3s16 blockpos_nodes = block->getPosRelative();
5193 // Block center position
5195 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5196 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5197 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5200 // Block position relative to camera
5201 v3f blockpos_relative = blockpos - camera_position;
5204 f32 d = blockpos_relative.getLength();
5211 bool mesh_expired = false;
5214 JMutexAutoLock lock(block->mesh_mutex);
5216 mesh_expired = block->getMeshExpired();
5218 // Mesh has not been expired and there is no mesh:
5219 // block has no content
5220 if(block->mesh == NULL && mesh_expired == false)
5224 f32 faraway = BS*50;
5225 //f32 faraway = m_control.wanted_range * BS;
5228 This has to be done with the mesh_mutex unlocked
5230 // Pretty random but this should work somewhat nicely
5231 if(mesh_expired && (
5232 (mesh_update_count < 3
5233 && (d < faraway || mesh_update_count < 2)
5236 (m_control.range_all && mesh_update_count < 20)
5239 /*if(mesh_expired && mesh_update_count < 6
5240 && (d < faraway || mesh_update_count < 3))*/
5242 mesh_update_count++;
5244 // Mesh has been expired: generate new mesh
5245 //block->updateMeshes(daynight_i);
5246 block->updateMesh(daynight_ratio);
5248 mesh_expired = false;
5252 Don't draw an expired mesh that is far away
5254 /*if(mesh_expired && d >= faraway)
5257 // Instead, delete it
5258 JMutexAutoLock lock(block->mesh_mutex);
5261 block->mesh->drop();
5264 // And continue to next block
5269 Draw the faces of the block
5272 JMutexAutoLock lock(block->mesh_mutex);
5274 scene::SMesh *mesh = block->mesh;
5279 blocks_would_have_drawn++;
5280 if(blocks_drawn >= m_control.wanted_max_blocks
5281 && m_control.range_all == false
5282 && d > m_control.wanted_min_range * BS)
5286 u32 c = mesh->getMeshBufferCount();
5288 for(u32 i=0; i<c; i++)
5290 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5291 const video::SMaterial& material = buf->getMaterial();
5292 video::IMaterialRenderer* rnd =
5293 driver->getMaterialRenderer(material.MaterialType);
5294 bool transparent = (rnd && rnd->isTransparent());
5295 // Render transparent on transparent pass and likewise.
5296 if(transparent == is_transparent_pass)
5298 driver->setMaterial(buf->getMaterial());
5299 driver->drawMeshBuffer(buf);
5300 vertex_count += buf->getVertexCount();
5304 } // foreach sectorblocks
5307 m_control.blocks_drawn = blocks_drawn;
5308 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5310 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5311 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5314 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5315 core::map<v3s16, MapBlock*> *affected_blocks)
5317 bool changed = false;
5319 Add it to all blocks touching it
5322 v3s16(0,0,0), // this
5323 v3s16(0,0,1), // back
5324 v3s16(0,1,0), // top
5325 v3s16(1,0,0), // right
5326 v3s16(0,0,-1), // front
5327 v3s16(0,-1,0), // bottom
5328 v3s16(-1,0,0), // left
5330 for(u16 i=0; i<7; i++)
5332 v3s16 p2 = p + dirs[i];
5333 // Block position of neighbor (or requested) node
5334 v3s16 blockpos = getNodeBlockPos(p2);
5335 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5336 if(blockref == NULL)
5338 // Relative position of requested node
5339 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5340 if(blockref->setTempMod(relpos, mod))
5345 if(changed && affected_blocks!=NULL)
5347 for(u16 i=0; i<7; i++)
5349 v3s16 p2 = p + dirs[i];
5350 // Block position of neighbor (or requested) node
5351 v3s16 blockpos = getNodeBlockPos(p2);
5352 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5353 if(blockref == NULL)
5355 affected_blocks->insert(blockpos, blockref);
5361 bool ClientMap::clearTempMod(v3s16 p,
5362 core::map<v3s16, MapBlock*> *affected_blocks)
5364 bool changed = false;
5366 v3s16(0,0,0), // this
5367 v3s16(0,0,1), // back
5368 v3s16(0,1,0), // top
5369 v3s16(1,0,0), // right
5370 v3s16(0,0,-1), // front
5371 v3s16(0,-1,0), // bottom
5372 v3s16(-1,0,0), // left
5374 for(u16 i=0; i<7; i++)
5376 v3s16 p2 = p + dirs[i];
5377 // Block position of neighbor (or requested) node
5378 v3s16 blockpos = getNodeBlockPos(p2);
5379 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5380 if(blockref == NULL)
5382 // Relative position of requested node
5383 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5384 if(blockref->clearTempMod(relpos))
5389 if(changed && affected_blocks!=NULL)
5391 for(u16 i=0; i<7; i++)
5393 v3s16 p2 = p + dirs[i];
5394 // Block position of neighbor (or requested) node
5395 v3s16 blockpos = getNodeBlockPos(p2);
5396 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5397 if(blockref == NULL)
5399 affected_blocks->insert(blockpos, blockref);
5405 void ClientMap::PrintInfo(std::ostream &out)
5416 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5421 MapVoxelManipulator::~MapVoxelManipulator()
5423 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5427 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5429 TimeTaker timer1("emerge", &emerge_time);
5431 // Units of these are MapBlocks
5432 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5433 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5435 VoxelArea block_area_nodes
5436 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5438 addArea(block_area_nodes);
5440 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5441 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5442 for(s32 x=p_min.X; x<=p_max.X; x++)
5445 core::map<v3s16, bool>::Node *n;
5446 n = m_loaded_blocks.find(p);
5450 bool block_data_inexistent = false;
5453 TimeTaker timer1("emerge load", &emerge_load_time);
5455 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5456 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5459 dstream<<std::endl;*/
5461 MapBlock *block = m_map->getBlockNoCreate(p);
5462 if(block->isDummy())
5463 block_data_inexistent = true;
5465 block->copyTo(*this);
5467 catch(InvalidPositionException &e)
5469 block_data_inexistent = true;
5472 if(block_data_inexistent)
5474 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5475 // Fill with VOXELFLAG_INEXISTENT
5476 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5477 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5479 s32 i = m_area.index(a.MinEdge.X,y,z);
5480 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5484 m_loaded_blocks.insert(p, !block_data_inexistent);
5487 //dstream<<"emerge done"<<std::endl;
5491 SUGG: Add an option to only update eg. water and air nodes.
5492 This will make it interfere less with important stuff if
5495 void MapVoxelManipulator::blitBack
5496 (core::map<v3s16, MapBlock*> & modified_blocks)
5498 if(m_area.getExtent() == v3s16(0,0,0))
5501 //TimeTaker timer1("blitBack");
5503 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5504 <<m_loaded_blocks.size()<<std::endl;*/
5507 Initialize block cache
5509 v3s16 blockpos_last;
5510 MapBlock *block = NULL;
5511 bool block_checked_in_modified = false;
5513 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5514 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5515 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5519 u8 f = m_flags[m_area.index(p)];
5520 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5523 MapNode &n = m_data[m_area.index(p)];
5525 v3s16 blockpos = getNodeBlockPos(p);
5530 if(block == NULL || blockpos != blockpos_last){
5531 block = m_map->getBlockNoCreate(blockpos);
5532 blockpos_last = blockpos;
5533 block_checked_in_modified = false;
5536 // Calculate relative position in block
5537 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5539 // Don't continue if nothing has changed here
5540 if(block->getNode(relpos) == n)
5543 //m_map->setNode(m_area.MinEdge + p, n);
5544 block->setNode(relpos, n);
5547 Make sure block is in modified_blocks
5549 if(block_checked_in_modified == false)
5551 modified_blocks[blockpos] = block;
5552 block_checked_in_modified = true;
5555 catch(InvalidPositionException &e)
5561 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5562 MapVoxelManipulator(map)
5566 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5570 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5572 // Just create the area so that it can be pointed to
5573 VoxelManipulator::emerge(a, caller_id);
5576 void ManualMapVoxelManipulator::initialEmerge(
5577 v3s16 blockpos_min, v3s16 blockpos_max)
5579 TimeTaker timer1("initialEmerge", &emerge_time);
5581 // Units of these are MapBlocks
5582 v3s16 p_min = blockpos_min;
5583 v3s16 p_max = blockpos_max;
5585 VoxelArea block_area_nodes
5586 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5588 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5591 dstream<<"initialEmerge: area: ";
5592 block_area_nodes.print(dstream);
5593 dstream<<" ("<<size_MB<<"MB)";
5597 addArea(block_area_nodes);
5599 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5600 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5601 for(s32 x=p_min.X; x<=p_max.X; x++)
5604 core::map<v3s16, bool>::Node *n;
5605 n = m_loaded_blocks.find(p);
5609 bool block_data_inexistent = false;
5612 TimeTaker timer1("emerge load", &emerge_load_time);
5614 MapBlock *block = m_map->getBlockNoCreate(p);
5615 if(block->isDummy())
5616 block_data_inexistent = true;
5618 block->copyTo(*this);
5620 catch(InvalidPositionException &e)
5622 block_data_inexistent = true;
5625 if(block_data_inexistent)
5628 Mark area inexistent
5630 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5631 // Fill with VOXELFLAG_INEXISTENT
5632 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5633 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5635 s32 i = m_area.index(a.MinEdge.X,y,z);
5636 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5640 m_loaded_blocks.insert(p, !block_data_inexistent);
5644 void ManualMapVoxelManipulator::blitBackAll(
5645 core::map<v3s16, MapBlock*> * modified_blocks)
5647 if(m_area.getExtent() == v3s16(0,0,0))
5651 Copy data of all blocks
5653 for(core::map<v3s16, bool>::Iterator
5654 i = m_loaded_blocks.getIterator();
5655 i.atEnd() == false; i++)
5657 bool existed = i.getNode()->getValue();
5658 if(existed == false)
5660 v3s16 p = i.getNode()->getKey();
5661 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5664 dstream<<"WARNING: "<<__FUNCTION_NAME
5665 <<": got NULL block "
5666 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5671 block->copyFrom(*this);
5674 modified_blocks->insert(p, block);