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 if(initial_size != 0)
1459 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1461 while(m_transforming_liquid.size() != 0)
1464 Get a queued transforming liquid node
1466 v3s16 p0 = m_transforming_liquid.pop_front();
1468 MapNode n0 = getNode(p0);
1470 // Don't deal with non-liquids
1471 if(content_liquid(n0.d) == false)
1474 bool is_source = !content_flowing_liquid(n0.d);
1476 u8 liquid_level = 8;
1477 if(is_source == false)
1478 liquid_level = n0.param2 & 0x0f;
1480 // Turn possible source into non-source
1481 u8 nonsource_c = make_liquid_flowing(n0.d);
1484 If not source, check that some node flows into this one
1485 and what is the level of liquid in this one
1487 if(is_source == false)
1489 s8 new_liquid_level_max = -1;
1491 v3s16 dirs_from[5] = {
1492 v3s16(0,1,0), // top
1493 v3s16(0,0,1), // back
1494 v3s16(1,0,0), // right
1495 v3s16(0,0,-1), // front
1496 v3s16(-1,0,0), // left
1498 for(u16 i=0; i<5; i++)
1503 bool from_top = (i==0);
1505 v3s16 p2 = p0 + dirs_from[i];
1506 MapNode n2 = getNode(p2);
1508 if(content_liquid(n2.d))
1510 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1511 // Check that the liquids are the same type
1512 if(n2_nonsource_c != nonsource_c)
1514 dstream<<"WARNING: Not handling: different liquids"
1515 " collide"<<std::endl;
1518 bool n2_is_source = !content_flowing_liquid(n2.d);
1519 s8 n2_liquid_level = 8;
1520 if(n2_is_source == false)
1521 n2_liquid_level = n2.param2 & 0x07;
1523 s8 new_liquid_level = -1;
1526 //new_liquid_level = 7;
1527 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1528 new_liquid_level = 7;
1530 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1532 else if(n2_liquid_level > 0)
1534 new_liquid_level = n2_liquid_level - 1;
1537 if(new_liquid_level > new_liquid_level_max)
1538 new_liquid_level_max = new_liquid_level;
1541 }catch(InvalidPositionException &e)
1547 If liquid level should be something else, update it and
1548 add all the neighboring water nodes to the transform queue.
1550 if(new_liquid_level_max != liquid_level)
1552 if(new_liquid_level_max == -1)
1554 // Remove water alltoghether
1561 n0.param2 = new_liquid_level_max;
1565 // Block has been modified
1567 v3s16 blockpos = getNodeBlockPos(p0);
1568 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1570 modified_blocks.insert(blockpos, block);
1574 Add neighboring non-source liquid nodes to transform queue.
1577 v3s16(0,0,1), // back
1578 v3s16(0,1,0), // top
1579 v3s16(1,0,0), // right
1580 v3s16(0,0,-1), // front
1581 v3s16(0,-1,0), // bottom
1582 v3s16(-1,0,0), // left
1584 for(u16 i=0; i<6; i++)
1589 v3s16 p2 = p0 + dirs[i];
1591 MapNode n2 = getNode(p2);
1592 if(content_flowing_liquid(n2.d))
1594 m_transforming_liquid.push_back(p2);
1597 }catch(InvalidPositionException &e)
1604 // Get a new one from queue if the node has turned into non-water
1605 if(content_liquid(n0.d) == false)
1609 Flow water from this node
1611 v3s16 dirs_to[5] = {
1612 v3s16(0,-1,0), // bottom
1613 v3s16(0,0,1), // back
1614 v3s16(1,0,0), // right
1615 v3s16(0,0,-1), // front
1616 v3s16(-1,0,0), // left
1618 for(u16 i=0; i<5; i++)
1623 bool to_bottom = (i == 0);
1625 // If liquid is at lowest possible height, it's not going
1626 // anywhere except down
1627 if(liquid_level == 0 && to_bottom == false)
1630 u8 liquid_next_level = 0;
1631 // If going to bottom
1634 //liquid_next_level = 7;
1635 if(liquid_level >= 7 - WATER_DROP_BOOST)
1636 liquid_next_level = 7;
1638 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1641 liquid_next_level = liquid_level - 1;
1643 bool n2_changed = false;
1644 bool flowed = false;
1646 v3s16 p2 = p0 + dirs_to[i];
1648 MapNode n2 = getNode(p2);
1649 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1651 if(content_liquid(n2.d))
1653 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1654 // Check that the liquids are the same type
1655 if(n2_nonsource_c != nonsource_c)
1657 dstream<<"WARNING: Not handling: different liquids"
1658 " collide"<<std::endl;
1661 bool n2_is_source = !content_flowing_liquid(n2.d);
1662 u8 n2_liquid_level = 8;
1663 if(n2_is_source == false)
1664 n2_liquid_level = n2.param2 & 0x07;
1673 // Just flow into the source, nothing changes.
1674 // n2_changed is not set because destination didn't change
1679 if(liquid_next_level > liquid_level)
1681 n2.param2 = liquid_next_level;
1689 else if(n2.d == CONTENT_AIR)
1692 n2.param2 = liquid_next_level;
1699 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1703 m_transforming_liquid.push_back(p2);
1705 v3s16 blockpos = getNodeBlockPos(p2);
1706 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1708 modified_blocks.insert(blockpos, block);
1711 // If n2_changed to bottom, don't flow anywhere else
1712 if(to_bottom && flowed && !is_source)
1715 }catch(InvalidPositionException &e)
1721 //if(loopcount >= 100000)
1722 if(loopcount >= initial_size * 1)
1725 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1732 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1744 Experimental and debug stuff
1748 dstream<<"Generating map point attribute lists"<<std::endl;
1750 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1751 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1752 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1753 //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1754 //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1758 NOTE: BEWARE: Too big amount of these will make map generation
1759 slow. Especially those that are read by every block emerge.
1767 for(u32 i=0; i<500; i++)
1769 /*u32 lim = MAP_GENERATION_LIMIT;
1773 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1776 -lim + myrand()%(lim*2),
1778 -lim + myrand()%(lim*2)
1780 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1781 plants_amount = pow(plants_amount, 5);
1782 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1784 float plants_amount = 0;
1787 plants_amount = 1.5;
1789 else if(myrand()%4 == 0)
1791 plants_amount = 0.5;
1793 else if(myrand()%2 == 0)
1795 plants_amount = 0.03;
1799 plants_amount = 0.0;
1803 list_plants_amount->addPoint(p, Attribute(plants_amount));
1806 for(u32 i=0; i<500; i++)
1808 /*u32 lim = MAP_GENERATION_LIMIT;
1812 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1815 -lim + myrand()%(lim*2),
1817 -lim + myrand()%(lim*2)
1820 float caves_amount = 0;
1825 else if(myrand()%3 == 0)
1831 caves_amount = 0.05;
1834 list_caves_amount->addPoint(p, Attribute(caves_amount));
1837 for(u32 i=0; i<500; i++)
1839 /*u32 lim = MAP_GENERATION_LIMIT;
1843 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1846 -lim + (myrand()%(lim*2)),
1848 -lim + (myrand()%(lim*2))
1851 /*s32 bh_i = (myrand()%200) - 50;
1852 float baseheight = (float)bh_i;
1856 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1857 randmax = pow(randmax, e);
1859 //float randmax = (float)(myrand()%60);
1860 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1862 float baseheight = 0;
1864 float randfactor = 0;
1866 /*if(myrand()%5 == 0)
1872 else if(myrand()%6 == 0)
1878 else if(myrand()%4 == 0)
1884 else if(myrand()%3 == 0)
1910 list_baseheight->addPoint(p, Attribute(baseheight));
1911 list_randmax->addPoint(p, Attribute(randmax));
1912 list_randfactor->addPoint(p, Attribute(randfactor));
1916 // Add only one entry
1917 list_baseheight->addPoint(v3s16(0,0,0), Attribute(-4));
1918 list_randmax->addPoint(v3s16(0,0,0), Attribute(22));
1919 //list_randmax->addPoint(v3s16(0,0,0), Attribute(0));
1920 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1923 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1924 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1925 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1929 Try to load map; if not found, create a new one.
1932 m_savedir = savedir;
1933 m_map_saving_enabled = false;
1937 // If directory exists, check contents and load if possible
1938 if(fs::PathExists(m_savedir))
1940 // If directory is empty, it is safe to save into it.
1941 if(fs::GetDirListing(m_savedir).size() == 0)
1943 dstream<<DTIME<<"Server: Empty save directory is valid."
1945 m_map_saving_enabled = true;
1949 // Load master heightmap
1950 loadMasterHeightmap();
1952 // Load sector (0,0) and throw and exception on fail
1953 if(loadSectorFull(v2s16(0,0)) == false)
1954 throw LoadError("Failed to load sector (0,0)");
1956 dstream<<DTIME<<"Server: Successfully loaded master "
1957 "heightmap and sector (0,0) from "<<savedir<<
1958 ", assuming valid save directory."
1961 m_map_saving_enabled = true;
1962 // Map loaded, not creating new one
1966 // If directory doesn't exist, it is safe to save to it
1968 m_map_saving_enabled = true;
1971 catch(std::exception &e)
1973 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1974 <<", exception: "<<e.what()<<std::endl;
1975 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1976 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1979 dstream<<DTIME<<"Initializing new map."<<std::endl;
1981 // Create master heightmap
1982 m_heightmap = new UnlimitedHeightmap
1985 // Set map parameters
1988 // Create zero sector
1989 emergeSector(v2s16(0,0));
1991 // Initially write whole map
1995 ServerMap::~ServerMap()
1999 if(m_map_saving_enabled)
2002 // Save only changed parts
2004 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2008 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2011 catch(std::exception &e)
2013 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2014 <<", exception: "<<e.what()<<std::endl;
2017 if(m_heightmap != NULL)
2023 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2024 for(; i.atEnd() == false; i++)
2026 MapChunk *chunk = i.getNode()->getValue();
2032 Some helper functions
2035 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2037 v3s16 em = vmanip.m_area.getExtent();
2038 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2039 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2040 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2042 for(y=y_nodes_max; y>=y_nodes_min; y--)
2044 MapNode &n = vmanip.m_data[i];
2045 if(content_walkable(n.d))
2048 vmanip.m_area.add_y(em, i, -1);
2050 if(y >= y_nodes_min)
2056 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2058 MapNode treenode(CONTENT_TREE);
2059 MapNode leavesnode(CONTENT_LEAVES);
2061 s16 trunk_h = myrand_range(3, 6);
2063 for(s16 ii=0; ii<trunk_h; ii++)
2065 if(vmanip.m_area.contains(p1))
2066 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2070 // p1 is now the last piece of the trunk
2073 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2074 SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2075 for(s32 i=0; i<leaves_a.getVolume(); i++)
2078 // Force leaves at near the end of the trunk
2081 for(s16 z=-d; z<=d; z++)
2082 for(s16 y=-d; y<=d; y++)
2083 for(s16 x=-d; x<=d; x++)
2085 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2089 // Add leaves randomly
2090 for(u32 iii=0; iii<7; iii++)
2095 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2096 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2097 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2100 for(s16 z=0; z<=d; z++)
2101 for(s16 y=0; y<=d; y++)
2102 for(s16 x=0; x<=d; x++)
2104 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2108 // Blit leaves to vmanip
2109 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2110 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2111 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2115 if(vmanip.m_area.contains(p) == false)
2117 u32 vi = vmanip.m_area.index(p);
2118 if(vmanip.m_data[vi].d != CONTENT_AIR)
2120 u32 i = leaves_a.index(x,y,z);
2121 if(leaves_d[i] == 1)
2122 vmanip.m_data[vi] = leavesnode;
2126 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2127 core::map<v3s16, MapBlock*> &changed_blocks)
2130 Don't generate if already fully generated
2133 MapChunk *chunk = getChunk(chunkpos);
2134 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2136 dstream<<"generateChunkRaw(): Chunk "
2137 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2138 <<" already generated"<<std::endl;
2143 dstream<<"generateChunkRaw(): Generating chunk "
2144 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2147 TimeTaker timer("generateChunkRaw()");
2149 // The distance how far into the neighbors the generator is allowed to go.
2150 s16 max_spread_amount_sectors = 2;
2151 assert(max_spread_amount_sectors <= m_chunksize);
2152 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2153 // Minimum amount of space left on sides for mud to fall in
2154 s16 min_mud_fall_space = 2;
2155 // Maximum diameter of stone obstacles in X and Z
2156 s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2157 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2159 s16 y_blocks_min = -4;
2160 s16 y_blocks_max = 3;
2161 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2162 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2163 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2165 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2166 s16 sectorpos_base_size = m_chunksize;
2168 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2169 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2170 v2s16 sectorpos_bigbase =
2171 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2172 s16 sectorpos_bigbase_size =
2173 sectorpos_base_size + 2 * max_spread_amount_sectors;
2175 v3s16 bigarea_blocks_min(
2176 sectorpos_bigbase.X,
2181 v3s16 bigarea_blocks_max(
2182 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2184 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2187 // Relative values to control amount of stuff in one chunk
2188 u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2189 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;
2190 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2191 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2192 *(u32)h_blocks*MAP_BLOCKSIZE;
2195 The limiting edges of the lighting update, inclusive.
2197 s16 lighting_min_d = 0-max_spread_amount;
2198 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2201 Create the whole area of this and the neighboring chunks
2204 TimeTaker timer("generateChunkRaw() create area");
2206 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2207 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2209 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2210 ServerMapSector *sector = createSector(sectorpos);
2213 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2215 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2216 MapBlock *block = createBlock(blockpos);
2218 // Lighting won't be calculated
2219 //block->setLightingExpired(true);
2220 // Lighting will be calculated
2221 block->setLightingExpired(false);
2224 Block gets sunlight if this is true.
2226 This should be set to true when the top side of a block
2227 is completely exposed to the sky.
2229 Actually this doesn't matter now because the
2230 initial lighting is done here.
2232 block->setIsUnderground(y != y_blocks_max);
2238 Clear all light emitted
2241 core::map<v3s16, u8> unlight_from;
2244 Now we have a big empty area.
2246 Make a ManualMapVoxelManipulator that contains this and the
2250 ManualMapVoxelManipulator vmanip(this);
2251 // Add the area we just generated
2253 TimeTaker timer("generateChunkRaw() initialEmerge");
2254 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2257 TimeTaker timer_generate("generateChunkRaw() generate");
2260 Generate general ground level to full area
2265 //TimeTaker timer1("ground level");
2267 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2268 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2271 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2274 Skip of already generated
2277 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2278 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2282 // Ground height at this point
2283 float surface_y_f = 0.0;
2285 A hack to get the ground height from the sector.
2289 v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
2290 v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
2291 MapSector *sector = getSectorNoGenerate(sectorpos);
2293 float h = sector->getGroundHeight(sector_relpos);
2294 if(h > GROUNDHEIGHT_VALID_MINVALUE)
2297 dstream<<"WARNING: "<<__FUNCTION_NAME
2298 <<": sector->getGroundHeight returned bad height"<<std::endl;
2300 // Convert to integer
2301 s16 surface_y = (s16)surface_y_f;
2304 Fill ground with stone
2307 // Use fast index incrementing
2308 v3s16 em = vmanip.m_area.getExtent();
2309 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2310 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2312 vmanip.m_data[i].d = CONTENT_STONE;
2314 vmanip.m_area.add_y(em, i, 1);
2322 Randomize some parameters
2325 u32 stone_obstacle_amount = 0;
2326 if(myrand() % 2 == 0)
2327 stone_obstacle_amount = myrand_range(0, myrand_range(20, 150));
2329 stone_obstacle_amount = myrand_range(0, myrand_range(20, 50));
2330 //u32 stone_obstacle_amount =
2331 // myrand_range(0, myrand_range(20, myrand_range(80,150)));
2334 Loop this part, it will make stuff look older and newer nicely
2337 for(u32 i_age=0; i_age<2; i_age++)
2340 // This is set during the next operation.
2341 // Maximum height of the stone surface and obstacles.
2342 // This is used to disable dungeon generation from going too high.
2343 s16 stone_surface_max_y = 0;
2347 //TimeTaker timer1("stone obstacles");
2350 Add some random stone obstacles
2353 for(u32 ri=0; ri<stone_obstacle_amount/3; ri++)
2354 //for(u32 ri=0; ri<7; ri++)
2357 // Randomize max height so usually stuff will be quite low
2358 //s16 maxheight_randomized = myrand_range(0, 25);
2359 s16 maxheight_randomized = myrand_range(0, stone_obstacle_amount/3);
2361 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2363 myrand_range(5, stone_obstacle_max_size),
2364 myrand_range(0, maxheight_randomized),
2365 myrand_range(5, stone_obstacle_max_size)
2368 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2369 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2372 Limit by 1 to not obstruct sunlight at borders, because
2373 it would fuck up lighting in some places because we're
2374 leaving out removing light from the borders for optimization
2378 myrand_range(1, sectorpos_base_size*MAP_BLOCKSIZE-1-1),
2379 myrand_range(1, sectorpos_base_size*MAP_BLOCKSIZE-1-1)
2382 // Minimum space left on top of the obstacle
2383 s16 min_head_space = 12;
2385 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2386 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2388 // Node position in 2d
2389 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2391 // Find stone ground level
2392 // (ignore everything else than mud in already generated chunks)
2393 // and mud amount over the stone level
2397 v3s16 em = vmanip.m_area.getExtent();
2398 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2400 // Go to ground level
2401 for(y=y_nodes_max; y>=y_nodes_min; y--)
2403 MapNode *n = &vmanip.m_data[i];
2404 /*if(content_walkable(n.d)
2405 && n.d != CONTENT_MUD
2406 && n.d != CONTENT_GRASS)
2408 if(n->d == CONTENT_STONE)
2411 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2415 Change to mud because otherwise we might
2416 be throwing mud on grass at the next
2422 vmanip.m_area.add_y(em, i, -1);
2424 if(y >= y_nodes_min)
2427 surface_y = y_nodes_min;
2435 v3s16 em = vmanip.m_area.getExtent();
2436 s16 y_start = surface_y+1;
2437 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2441 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2443 MapNode &n = vmanip.m_data[i];
2444 n.d = CONTENT_STONE;
2446 if(y > stone_surface_max_y)
2447 stone_surface_max_y = y;
2450 if(count >= ob_size.Y)
2453 vmanip.m_area.add_y(em, i, 1);
2457 for(; y<=y_nodes_max - min_head_space; y++)
2459 MapNode &n = vmanip.m_data[i];
2462 if(count >= mud_amount)
2465 vmanip.m_area.add_y(em, i, 1);
2475 //TimeTaker timer1("dungeons");
2480 u32 dungeons_count = relative_volume / 200000;
2481 u32 bruises_count = relative_volume * stone_surface_max_y / 200000 / 50;
2482 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2484 s16 min_tunnel_diameter = 3;
2485 s16 max_tunnel_diameter = 6;
2486 u16 tunnel_routepoints = 15;
2488 bool bruise_surface = (jj < bruises_count);
2492 min_tunnel_diameter = 5;
2493 max_tunnel_diameter = myrand_range(10, 20);
2494 tunnel_routepoints = 3;
2497 // Allowed route area size in nodes
2499 sectorpos_base_size*MAP_BLOCKSIZE,
2500 h_blocks*MAP_BLOCKSIZE,
2501 sectorpos_base_size*MAP_BLOCKSIZE
2504 // Area starting point in nodes
2506 sectorpos_base.X*MAP_BLOCKSIZE,
2507 y_blocks_min*MAP_BLOCKSIZE,
2508 sectorpos_base.Y*MAP_BLOCKSIZE
2512 //(this should be more than the maximum radius of the tunnel)
2513 //s16 insure = 5; // Didn't work with max_d = 20
2515 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2516 ar += v3s16(1,0,1) * more * 2;
2517 of -= v3s16(1,0,1) * more;
2519 s16 route_y_min = 0;
2520 //s16 route_y_max = ar.Y-1;
2521 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
2522 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2526 /*// Minimum is at y=0
2527 route_y_min = -of.Y - 0;*/
2528 // Minimum is at y=max_tunnel_diameter/4
2529 //route_y_min = -of.Y + max_tunnel_diameter/4;
2530 //s16 min = -of.Y + max_tunnel_diameter/4;
2531 s16 min = -of.Y + 0;
2532 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2533 route_y_min = rangelim(route_y_min, 0, route_y_max);
2536 /*dstream<<"route_y_min = "<<route_y_min
2537 <<", route_y_max = "<<route_y_max<<std::endl;*/
2539 // Randomize starting position
2541 (float)(myrand()%ar.X)+0.5,
2542 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2543 (float)(myrand()%ar.Z)+0.5
2546 MapNode airnode(CONTENT_AIR);
2549 Generate some tunnel starting from orp
2552 for(u16 j=0; j<tunnel_routepoints; j++)
2554 v3s16 maxlen(20, 10, 20);
2558 maxlen = v3s16(60,60,60);
2562 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2563 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2564 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2569 else if(rp.X >= ar.X)
2571 if(rp.Y < route_y_min)
2573 else if(rp.Y >= route_y_max)
2574 rp.Y = route_y_max-1;
2577 else if(rp.Z >= ar.Z)
2582 s16 min_d = min_tunnel_diameter;
2583 s16 max_d = max_tunnel_diameter;
2584 s16 rs = myrand_range(min_d, max_d);
2586 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2588 v3f fp = orp + vec * f;
2589 v3s16 cp(fp.X, fp.Y, fp.Z);
2592 s16 d1 = d0 + rs - 1;
2593 for(s16 z0=d0; z0<=d1; z0++)
2595 s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2596 for(s16 x0=-si; x0<=si-1; x0++)
2598 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2599 s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2600 //s16 si2 = rs - abs(x0);
2601 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2607 /*if(isInArea(p, ar) == false)
2609 // Check only height
2610 if(y < 0 || y >= ar.Y)
2614 //assert(vmanip.m_area.contains(p));
2615 if(vmanip.m_area.contains(p) == false)
2617 dstream<<"WARNING: "<<__FUNCTION_NAME
2618 <<":"<<__LINE__<<": "
2619 <<"point not in area"
2624 // Just set it to air, it will be changed to
2626 u32 i = vmanip.m_area.index(p);
2627 vmanip.m_data[i] = airnode;
2641 //TimeTaker timer1("ore veins");
2646 for(u32 jj=0; jj<relative_volume/2000; jj++)
2648 s16 max_vein_diameter = 3;
2650 // Allowed route area size in nodes
2652 sectorpos_base_size*MAP_BLOCKSIZE,
2653 h_blocks*MAP_BLOCKSIZE,
2654 sectorpos_base_size*MAP_BLOCKSIZE
2657 // Area starting point in nodes
2659 sectorpos_base.X*MAP_BLOCKSIZE,
2660 y_blocks_min*MAP_BLOCKSIZE,
2661 sectorpos_base.Y*MAP_BLOCKSIZE
2665 //(this should be more than the maximum radius of the tunnel)
2667 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2668 ar += v3s16(1,0,1) * more * 2;
2669 of -= v3s16(1,0,1) * more;
2671 // Randomize starting position
2673 (float)(myrand()%ar.X)+0.5,
2674 (float)(myrand()%ar.Y)+0.5,
2675 (float)(myrand()%ar.Z)+0.5
2678 // Randomize mineral
2679 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2682 Generate some vein starting from orp
2685 for(u16 j=0; j<2; j++)
2688 (float)(myrand()%ar.X)+0.5,
2689 (float)(myrand()%ar.Y)+0.5,
2690 (float)(myrand()%ar.Z)+0.5
2692 v3f vec = rp - orp;*/
2694 v3s16 maxlen(10, 10, 10);
2696 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2697 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2698 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2703 else if(rp.X >= ar.X)
2707 else if(rp.Y >= ar.Y)
2711 else if(rp.Z >= ar.Z)
2717 s16 max_d = max_vein_diameter;
2718 s16 rs = myrand_range(min_d, max_d);
2720 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2722 v3f fp = orp + vec * f;
2723 v3s16 cp(fp.X, fp.Y, fp.Z);
2725 s16 d1 = d0 + rs - 1;
2726 for(s16 z0=d0; z0<=d1; z0++)
2728 s16 si = rs - abs(z0);
2729 for(s16 x0=-si; x0<=si-1; x0++)
2731 s16 si2 = rs - abs(x0);
2732 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2734 // Don't put mineral to every place
2742 /*if(isInArea(p, ar) == false)
2744 // Check only height
2745 if(y < 0 || y >= ar.Y)
2749 assert(vmanip.m_area.contains(p));
2751 // Just set it to air, it will be changed to
2753 u32 i = vmanip.m_area.index(p);
2754 MapNode *n = &vmanip.m_data[i];
2755 if(n->d == CONTENT_STONE)
2770 //TimeTaker timer1("add mud");
2773 Add mud to the central chunk
2776 s16 mud_add_amount = myrand_range(1, 5);
2778 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2779 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2781 // Node position in 2d
2782 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2784 // Find ground level
2785 s16 surface_y = find_ground_level(vmanip, p2d);
2788 If topmost node is grass, change it to mud.
2789 It might be if it was flown to there from a neighboring
2790 chunk and then converted.
2793 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2794 MapNode *n = &vmanip.m_data[i];
2795 if(n->d == CONTENT_GRASS)
2804 v3s16 em = vmanip.m_area.getExtent();
2805 s16 y_start = surface_y+1;
2806 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2807 for(s16 y=y_start; y<=y_nodes_max; y++)
2809 MapNode &n = vmanip.m_data[i];
2812 if(mudcount >= mud_add_amount)
2815 vmanip.m_area.add_y(em, i, 1);
2824 //TimeTaker timer1("flow mud");
2827 Flow mud away from steep edges
2830 // Iterate a few times
2831 for(s16 k=0; k<4; k++)
2834 /*for(s16 x=0-max_spread_amount+1;
2835 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2837 for(s16 z=0-max_spread_amount+1;
2838 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2842 Firstly, limit area by 1 because mud is flown into neighbors.
2843 Secondly, limit by 1 more to not obstruct sunlight at borders,
2844 because it would fuck up lighting in some places because we're
2845 leaving out removing light from the borders for optimization
2848 /*for(s16 x=0-max_spread_amount+2;
2849 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2851 for(s16 z=0-max_spread_amount+2;
2852 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2854 for(s16 x=0-max_spread_amount+1;
2855 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2857 for(s16 z=0-max_spread_amount+1;
2858 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2861 // Node position in 2d
2862 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2864 v3s16 em = vmanip.m_area.getExtent();
2865 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2872 for(; y>=y_nodes_min; y--)
2874 n = &vmanip.m_data[i];
2875 //if(content_walkable(n->d))
2877 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2880 vmanip.m_area.add_y(em, i, -1);
2883 // Stop if out of area
2884 //if(vmanip.m_area.contains(i) == false)
2888 /*// If not mud, do nothing to it
2889 MapNode *n = &vmanip.m_data[i];
2890 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2893 // Make it exactly mud
2896 /*s16 recurse_count = 0;
2900 v3s16(0,0,1), // back
2901 v3s16(1,0,0), // right
2902 v3s16(0,0,-1), // front
2903 v3s16(-1,0,0), // left
2906 // Theck that upper is air or doesn't exist.
2907 // Only drop mud if upper doesn't contain anything that
2908 // would keep the mud in place.
2910 vmanip.m_area.add_y(em, i3, 1);
2911 if(vmanip.m_area.contains(i3) == false
2912 || content_walkable(vmanip.m_data[i3].d) == false)
2917 for(u32 di=0; di<4; di++)
2919 v3s16 dirp = dirs4[di];
2922 vmanip.m_area.add_p(em, i2, dirp);
2923 // Fail if out of area
2924 if(vmanip.m_area.contains(i2) == false)
2926 // Check that side is air
2927 MapNode *n2 = &vmanip.m_data[i2];
2928 if(content_walkable(n2->d))
2930 // Check that under side is air
2931 vmanip.m_area.add_y(em, i2, -1);
2932 // Fail if out of area
2933 if(vmanip.m_area.contains(i2) == false)
2935 n2 = &vmanip.m_data[i2];
2936 if(content_walkable(n2->d))
2938 // Loop further down until not air
2940 vmanip.m_area.add_y(em, i2, -1);
2941 // Fail if out of area
2942 if(vmanip.m_area.contains(i2) == false)
2944 n2 = &vmanip.m_data[i2];
2945 }while(content_walkable(n2->d) == false);
2946 // Loop one up so that we're in air
2947 vmanip.m_area.add_y(em, i2, 1);
2948 n2 = &vmanip.m_data[i2];
2950 // Move mud to new place
2952 // Set old place to be air
2953 *n = MapNode(CONTENT_AIR);
2960 // Continue from next y
2970 //TimeTaker timer1("add water");
2973 Add water to the central chunk (and a bit more)
2976 for(s16 x=0-max_spread_amount;
2977 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2979 for(s16 z=0-max_spread_amount;
2980 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2983 // Node position in 2d
2984 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2986 // Find ground level
2987 //s16 surface_y = find_ground_level(vmanip, p2d);
2990 If ground level is over water level, skip.
2991 NOTE: This leaves caves near water without water,
2992 which looks especially crappy when the nearby water
2993 won't start flowing either for some reason
2995 /*if(surface_y > WATER_LEVEL)
3002 v3s16 em = vmanip.m_area.getExtent();
3003 u8 light = LIGHT_MAX;
3004 // Start at global water surface level
3005 s16 y_start = WATER_LEVEL;
3006 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3007 MapNode *n = &vmanip.m_data[i];
3009 /*// Add first one to transforming liquid queue, if water
3010 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3012 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3013 m_transforming_liquid.push_back(p);
3016 for(s16 y=y_start; y>=y_nodes_min; y--)
3018 n = &vmanip.m_data[i];
3020 // Stop when there is no water and no air
3021 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3022 && n->d != CONTENT_WATER)
3024 /*// Add bottom one to transforming liquid queue
3025 vmanip.m_area.add_y(em, i, 1);
3026 n = &vmanip.m_data[i];
3027 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3029 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3030 m_transforming_liquid.push_back(p);
3036 n->d = CONTENT_WATERSOURCE;
3037 n->setLight(LIGHTBANK_DAY, light);
3039 // Add to transforming liquid queue (in case it'd
3041 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3042 m_transforming_liquid.push_back(p);
3045 vmanip.m_area.add_y(em, i, -1);
3059 //TimeTaker timer1("plant trees");
3065 u32 tree_max = relative_area / 60;
3067 u32 count = myrand_range(0, tree_max);
3068 for(u32 i=0; i<count; i++)
3070 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3071 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3072 x += sectorpos_base.X*MAP_BLOCKSIZE;
3073 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3074 s16 y = find_ground_level(vmanip, v2s16(x,z));
3075 // Don't make a tree under water level
3080 make_tree(vmanip, p);
3088 //TimeTaker timer1("grow grass");
3094 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3095 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3096 for(s16 x=0-max_spread_amount;
3097 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3099 for(s16 z=0-max_spread_amount;
3100 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3103 // Node position in 2d
3104 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3107 Find the lowest surface to which enough light ends up
3110 Basically just wait until not air and not leaves.
3114 v3s16 em = vmanip.m_area.getExtent();
3115 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3117 // Go to ground level
3118 for(y=y_nodes_max; y>=y_nodes_min; y--)
3120 MapNode &n = vmanip.m_data[i];
3121 if(n.d != CONTENT_AIR
3122 && n.d != CONTENT_LEAVES)
3124 vmanip.m_area.add_y(em, i, -1);
3126 if(y >= y_nodes_min)
3129 surface_y = y_nodes_min;
3132 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3133 MapNode *n = &vmanip.m_data[i];
3134 if(n->d == CONTENT_MUD)
3135 n->d = CONTENT_GRASS;
3144 core::map<v3s16, bool> light_sources;
3147 // 750ms @cs=8, can't optimize more
3148 //TimeTaker timer1("initial lighting");
3152 Go through the edges and add all nodes that have light to light_sources
3156 for(s16 i=0; i<4; i++)
3158 for(s16 j=lighting_min_d;
3165 if(i == 0 || i == 1)
3167 x = (i==0) ? lighting_min_d : lighting_max_d;
3176 z = (i==0) ? lighting_min_d : lighting_max_d;
3183 // Node position in 2d
3184 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3187 v3s16 em = vmanip.m_area.getExtent();
3188 s16 y_start = y_nodes_max;
3189 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3190 for(s16 y=y_start; y>=y_nodes_min; y--)
3192 MapNode *n = &vmanip.m_data[i];
3193 if(n->getLight(LIGHTBANK_DAY) != 0)
3195 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3202 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3203 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3204 /*for(s16 x=0-max_spread_amount+1;
3205 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3207 for(s16 z=0-max_spread_amount+1;
3208 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3212 This has to be 1 smaller than the actual area, because
3213 neighboring nodes are checked.
3215 for(s16 x=lighting_min_d+1;
3216 x<=lighting_max_d-1;
3218 for(s16 z=lighting_min_d+1;
3219 z<=lighting_max_d-1;
3222 // Node position in 2d
3223 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3226 Apply initial sunlight
3229 u8 light = LIGHT_SUN;
3230 bool add_to_sources = false;
3231 v3s16 em = vmanip.m_area.getExtent();
3232 s16 y_start = y_nodes_max;
3233 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3234 for(s16 y=y_start; y>=y_nodes_min; y--)
3236 MapNode *n = &vmanip.m_data[i];
3238 if(light_propagates_content(n->d) == false)
3242 else if(light != LIGHT_SUN
3243 || sunlight_propagates_content(n->d) == false)
3249 // This doesn't take much time
3250 if(add_to_sources == false)
3253 Check sides. If side is not air or water, start
3254 adding to light_sources.
3257 v3s16(0,0,1), // back
3258 v3s16(1,0,0), // right
3259 v3s16(0,0,-1), // front
3260 v3s16(-1,0,0), // left
3262 for(u32 di=0; di<4; di++)
3264 v3s16 dirp = dirs4[di];
3266 vmanip.m_area.add_p(em, i2, dirp);
3267 MapNode *n2 = &vmanip.m_data[i2];
3269 n2->d != CONTENT_AIR
3270 && n2->d != CONTENT_WATERSOURCE
3271 && n2->d != CONTENT_WATER
3273 add_to_sources = true;
3279 n->setLight(LIGHTBANK_DAY, light);
3280 n->setLight(LIGHTBANK_NIGHT, 0);
3282 // This doesn't take much time
3283 if(light != 0 && add_to_sources)
3285 // Insert light source
3286 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3289 // Increment index by y
3290 vmanip.m_area.add_y(em, i, -1);
3297 // Spread light around
3299 TimeTaker timer("generateChunkRaw() spreadLight");
3300 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3307 timer_generate.stop();
3310 Blit generated stuff to map
3314 //TimeTaker timer("generateChunkRaw() blitBackAll");
3315 vmanip.blitBackAll(&changed_blocks);
3318 Update day/night difference cache of the MapBlocks
3321 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3322 i.atEnd() == false; i++)
3324 MapBlock *block = i.getNode()->getValue();
3325 block->updateDayNightDiff();
3331 Create chunk metadata
3334 for(s16 x=-1; x<=1; x++)
3335 for(s16 y=-1; y<=1; y++)
3337 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3338 // Add chunk meta information
3339 MapChunk *chunk = getChunk(chunkpos0);
3342 chunk = new MapChunk();
3343 m_chunks.insert(chunkpos0, chunk);
3345 //chunk->setIsVolatile(true);
3346 if(chunk->getGenLevel() > GENERATED_PARTLY)
3347 chunk->setGenLevel(GENERATED_PARTLY);
3351 Set central chunk non-volatile and return it
3353 MapChunk *chunk = getChunk(chunkpos);
3356 //chunk->setIsVolatile(false);
3357 chunk->setGenLevel(GENERATED_FULLY);
3362 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3363 core::map<v3s16, MapBlock*> &changed_blocks)
3365 dstream<<"generateChunk(): Generating chunk "
3366 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3369 /*for(s16 x=-1; x<=1; x++)
3370 for(s16 y=-1; y<=1; y++)*/
3371 for(s16 x=-0; x<=0; x++)
3372 for(s16 y=-0; y<=0; y++)
3374 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3375 MapChunk *chunk = getChunk(chunkpos0);
3376 // Skip if already generated
3377 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3379 generateChunkRaw(chunkpos0, changed_blocks);
3382 assert(chunkNonVolatile(chunkpos1));
3384 MapChunk *chunk = getChunk(chunkpos1);
3388 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3390 DSTACK("%s: p2d=(%d,%d)",
3395 Check if it exists already in memory
3397 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3402 Try to load it from disk (with blocks)
3404 if(loadSectorFull(p2d) == true)
3406 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3409 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3410 throw InvalidPositionException("");
3416 If there is no master heightmap, throw.
3418 if(m_heightmap == NULL)
3420 throw InvalidPositionException("createSector(): no heightmap");
3424 Do not create over-limit
3426 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3427 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3428 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3429 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3430 throw InvalidPositionException("createSector(): pos. over limit");
3433 Generate blank sector
3436 // Number of heightmaps in sector in each direction
3437 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3439 // Heightmap side width
3440 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3442 sector = new ServerMapSector(this, p2d, hm_split);
3444 // Sector position on map in nodes
3445 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3447 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3448 " heightmaps and objects"<<std::endl;*/
3451 Generate sector heightmap
3454 v2s16 mhm_p = p2d * hm_split;
3455 /*f32 corners[4] = {
3456 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3457 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3458 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3459 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3462 // Loop through sub-heightmaps
3463 for(s16 y=0; y<hm_split; y++)
3464 for(s16 x=0; x<hm_split; x++)
3466 v2s16 p_in_sector = v2s16(x,y);
3467 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3469 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3470 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3471 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3472 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3475 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3476 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3479 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3481 sector->setHeightmap(p_in_sector, hm);
3483 //hm->generateContinued(1.0, 0.5, corners);
3484 hm->generateContinued(0.5, 0.5, corners);
3489 // Add dummy objects
3490 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3491 sector->setObjects(objects);
3496 m_sectors.insert(p2d, sector);
3501 MapSector * ServerMap::emergeSector(v2s16 p2d,
3502 core::map<v3s16, MapBlock*> &changed_blocks)
3504 DSTACK("%s: p2d=(%d,%d)",
3511 v2s16 chunkpos = sector_to_chunk(p2d);
3512 /*bool chunk_nonvolatile = false;
3513 MapChunk *chunk = getChunk(chunkpos);
3514 if(chunk && chunk->getIsVolatile() == false)
3515 chunk_nonvolatile = true;*/
3516 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3519 If chunk is not fully generated, generate chunk
3521 if(chunk_nonvolatile == false)
3523 // Generate chunk and neighbors
3524 generateChunk(chunkpos, changed_blocks);
3528 Return sector if it exists now
3530 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3535 Try to load it from disk
3537 if(loadSectorFull(p2d) == true)
3539 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3542 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3543 throw InvalidPositionException("");
3549 generateChunk should have generated the sector
3556 //return generateSector();
3560 NOTE: This is not used for main map generation, only for blocks
3561 that are very high or low
3563 MapBlock * ServerMap::generateBlock(
3565 MapBlock *original_dummy,
3566 ServerMapSector *sector,
3567 core::map<v3s16, MapBlock*> &changed_blocks,
3568 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3571 DSTACK("%s: p=(%d,%d,%d)",
3575 /*dstream<<"generateBlock(): "
3576 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3579 MapBlock *block = original_dummy;
3581 v2s16 p2d(p.X, p.Z);
3585 Do not generate over-limit
3587 if(blockpos_over_limit(p))
3589 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3590 throw InvalidPositionException("generateBlock(): pos. over limit");
3594 If block doesn't exist, create one.
3595 If it exists, it is a dummy. In that case unDummify() it.
3597 NOTE: This already sets the map as the parent of the block
3601 block = sector->createBlankBlockNoInsert(block_y);
3605 // Remove the block so that nobody can get a half-generated one.
3606 sector->removeBlock(block);
3607 // Allocate the block to contain the generated data
3611 /*u8 water_material = CONTENT_WATER;
3612 if(g_settings.getBool("endless_water"))
3613 water_material = CONTENT_WATERSOURCE;*/
3614 u8 water_material = CONTENT_WATERSOURCE;
3616 s32 lowest_ground_y = 32767;
3617 s32 highest_ground_y = -32768;
3620 //sector->printHeightmaps();
3622 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3623 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3625 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3627 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3628 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3629 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3631 dstream<<"WARNING: Surface height not found in sector "
3632 "for block that is being emerged"<<std::endl;
3636 s16 surface_y = surface_y_f;
3637 //avg_ground_y += surface_y;
3638 if(surface_y < lowest_ground_y)
3639 lowest_ground_y = surface_y;
3640 if(surface_y > highest_ground_y)
3641 highest_ground_y = surface_y;
3643 s32 surface_depth = 0;
3645 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3647 //float min_slope = 0.45;
3648 //float max_slope = 0.85;
3649 float min_slope = 0.60;
3650 float max_slope = 1.20;
3651 float min_slope_depth = 5.0;
3652 float max_slope_depth = 0;
3654 if(slope < min_slope)
3655 surface_depth = min_slope_depth;
3656 else if(slope > max_slope)
3657 surface_depth = max_slope_depth;
3659 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3661 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3663 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3668 NOTE: If there are some man-made structures above the
3669 newly created block, they won't be taken into account.
3671 if(real_y > surface_y)
3672 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3678 // If node is over heightmap y, it's air or water
3679 if(real_y > surface_y)
3681 // If under water level, it's water
3682 if(real_y < WATER_LEVEL)
3684 n.d = water_material;
3685 n.setLight(LIGHTBANK_DAY,
3686 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3688 Add to transforming liquid queue (in case it'd
3691 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3692 m_transforming_liquid.push_back(real_pos);
3698 // Else it's ground or dungeons (air)
3701 // If it's surface_depth under ground, it's stone
3702 if(real_y <= surface_y - surface_depth)
3704 n.d = CONTENT_STONE;
3708 // It is mud if it is under the first ground
3709 // level or under water
3710 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3716 n.d = CONTENT_GRASS;
3719 //n.d = CONTENT_MUD;
3721 /*// If under water level, it's mud
3722 if(real_y < WATER_LEVEL)
3724 // Only the topmost node is grass
3725 else if(real_y <= surface_y - 1)
3728 n.d = CONTENT_GRASS;*/
3732 block->setNode(v3s16(x0,y0,z0), n);
3737 Calculate some helper variables
3740 // Completely underground if the highest part of block is under lowest
3742 // This has to be very sure; it's probably one too strict now but
3743 // that's just better.
3744 bool completely_underground =
3745 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3747 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3749 bool mostly_underwater_surface = false;
3750 if(highest_ground_y < WATER_LEVEL
3751 && some_part_underground && !completely_underground)
3752 mostly_underwater_surface = true;
3755 Get local attributes
3758 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3760 float caves_amount = 0.5;
3765 NOTE: BEWARE: Too big amount of attribute points slows verything
3767 1 interpolation from 5000 points takes 2-3ms.
3769 //TimeTaker timer("generateBlock() local attribute retrieval");
3770 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3771 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3772 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3776 //dstream<<"generateBlock(): Done"<<std::endl;
3782 // Initialize temporary table
3783 const s32 ued = MAP_BLOCKSIZE;
3784 bool underground_emptiness[ued*ued*ued];
3785 for(s32 i=0; i<ued*ued*ued; i++)
3787 underground_emptiness[i] = 0;
3794 Initialize orp and ors. Try to find if some neighboring
3795 MapBlock has a tunnel ended in its side
3799 (float)(myrand()%ued)+0.5,
3800 (float)(myrand()%ued)+0.5,
3801 (float)(myrand()%ued)+0.5
3804 bool found_existing = false;
3810 for(s16 y=0; y<ued; y++)
3811 for(s16 x=0; x<ued; x++)
3813 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3814 if(getNode(ap).d == CONTENT_AIR)
3816 orp = v3f(x+1,y+1,0);
3817 found_existing = true;
3818 goto continue_generating;
3822 catch(InvalidPositionException &e){}
3828 for(s16 y=0; y<ued; y++)
3829 for(s16 x=0; x<ued; x++)
3831 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3832 if(getNode(ap).d == CONTENT_AIR)
3834 orp = v3f(x+1,y+1,ued-1);
3835 found_existing = true;
3836 goto continue_generating;
3840 catch(InvalidPositionException &e){}
3846 for(s16 y=0; y<ued; y++)
3847 for(s16 z=0; z<ued; z++)
3849 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3850 if(getNode(ap).d == CONTENT_AIR)
3852 orp = v3f(0,y+1,z+1);
3853 found_existing = true;
3854 goto continue_generating;
3858 catch(InvalidPositionException &e){}
3864 for(s16 y=0; y<ued; y++)
3865 for(s16 z=0; z<ued; z++)
3867 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3868 if(getNode(ap).d == CONTENT_AIR)
3870 orp = v3f(ued-1,y+1,z+1);
3871 found_existing = true;
3872 goto continue_generating;
3876 catch(InvalidPositionException &e){}
3882 for(s16 x=0; x<ued; x++)
3883 for(s16 z=0; z<ued; z++)
3885 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3886 if(getNode(ap).d == CONTENT_AIR)
3888 orp = v3f(x+1,0,z+1);
3889 found_existing = true;
3890 goto continue_generating;
3894 catch(InvalidPositionException &e){}
3900 for(s16 x=0; x<ued; x++)
3901 for(s16 z=0; z<ued; z++)
3903 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3904 if(getNode(ap).d == CONTENT_AIR)
3906 orp = v3f(x+1,ued-1,z+1);
3907 found_existing = true;
3908 goto continue_generating;
3912 catch(InvalidPositionException &e){}
3914 continue_generating:
3917 Choose whether to actually generate dungeon
3919 bool do_generate_dungeons = true;
3920 // Don't generate if no part is underground
3921 if(!some_part_underground)
3923 do_generate_dungeons = false;
3925 // Don't generate if mostly underwater surface
3926 /*else if(mostly_underwater_surface)
3928 do_generate_dungeons = false;
3930 // Partly underground = cave
3931 else if(!completely_underground)
3933 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3935 // Found existing dungeon underground
3936 else if(found_existing && completely_underground)
3938 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3940 // Underground and no dungeons found
3943 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3946 if(do_generate_dungeons)
3949 Generate some tunnel starting from orp and ors
3951 for(u16 i=0; i<3; i++)
3954 (float)(myrand()%ued)+0.5,
3955 (float)(myrand()%ued)+0.5,
3956 (float)(myrand()%ued)+0.5
3960 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3964 for(float f=0; f<1.0; f+=0.04)
3966 v3f fp = orp + vec * f;
3967 v3s16 cp(fp.X, fp.Y, fp.Z);
3969 s16 d1 = d0 + rs - 1;
3970 for(s16 z0=d0; z0<=d1; z0++)
3972 s16 si = rs - abs(z0);
3973 for(s16 x0=-si; x0<=si-1; x0++)
3975 s16 si2 = rs - abs(x0);
3976 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3982 if(isInArea(p, ued) == false)
3984 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3996 // Set to true if has caves.
3997 // Set when some non-air is changed to air when making caves.
3998 bool has_dungeons = false;
4001 Apply temporary cave data to block
4004 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4005 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4007 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4009 MapNode n = block->getNode(v3s16(x0,y0,z0));
4012 if(underground_emptiness[
4013 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4014 +ued*(y0*ued/MAP_BLOCKSIZE)
4015 +(x0*ued/MAP_BLOCKSIZE)])
4017 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4020 has_dungeons = true;
4026 block->setNode(v3s16(x0,y0,z0), n);
4031 This is used for guessing whether or not the block should
4032 receive sunlight from the top if the block above doesn't exist
4034 block->setIsUnderground(completely_underground);
4037 Force lighting update if some part of block is partly
4038 underground and has caves.
4040 /*if(some_part_underground && !completely_underground && has_dungeons)
4042 //dstream<<"Half-ground caves"<<std::endl;
4043 lighting_invalidated_blocks[block->getPos()] = block;
4046 // DEBUG: Always update lighting
4047 //lighting_invalidated_blocks[block->getPos()] = block;
4053 if(some_part_underground)
4055 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4060 for(s16 i=0; i<underground_level/4 + 1; i++)
4062 if(myrand()%50 == 0)
4065 (myrand()%(MAP_BLOCKSIZE-2))+1,
4066 (myrand()%(MAP_BLOCKSIZE-2))+1,
4067 (myrand()%(MAP_BLOCKSIZE-2))+1
4073 for(u16 i=0; i<27; i++)
4075 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4077 block->setNode(cp+g_27dirs[i], n);
4085 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
4086 u16 coal_rareness = 60 / coal_amount;
4087 if(coal_rareness == 0)
4089 if(myrand()%coal_rareness == 0)
4091 u16 a = myrand() % 16;
4092 u16 amount = coal_amount * a*a*a / 1000;
4093 for(s16 i=0; i<amount; i++)
4096 (myrand()%(MAP_BLOCKSIZE-2))+1,
4097 (myrand()%(MAP_BLOCKSIZE-2))+1,
4098 (myrand()%(MAP_BLOCKSIZE-2))+1
4102 n.d = CONTENT_STONE;
4103 n.param = MINERAL_COAL;
4105 for(u16 i=0; i<27; i++)
4107 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4109 block->setNode(cp+g_27dirs[i], n);
4117 //TODO: change to iron_amount or whatever
4118 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
4119 u16 iron_rareness = 60 / iron_amount;
4120 if(iron_rareness == 0)
4122 if(myrand()%iron_rareness == 0)
4124 u16 a = myrand() % 16;
4125 u16 amount = iron_amount * a*a*a / 1000;
4126 for(s16 i=0; i<amount; i++)
4129 (myrand()%(MAP_BLOCKSIZE-2))+1,
4130 (myrand()%(MAP_BLOCKSIZE-2))+1,
4131 (myrand()%(MAP_BLOCKSIZE-2))+1
4135 n.d = CONTENT_STONE;
4136 n.param = MINERAL_IRON;
4138 for(u16 i=0; i<27; i++)
4140 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4142 block->setNode(cp+g_27dirs[i], n);
4149 Create a few rats in empty blocks underground
4151 if(completely_underground)
4153 //for(u16 i=0; i<2; i++)
4156 (myrand()%(MAP_BLOCKSIZE-2))+1,
4157 (myrand()%(MAP_BLOCKSIZE-2))+1,
4158 (myrand()%(MAP_BLOCKSIZE-2))+1
4161 // Check that the place is empty
4162 //if(!is_ground_content(block->getNode(cp).d))
4165 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4166 block->addObject(obj);
4172 Add block to sector.
4174 sector->insertBlock(block);
4180 // An y-wise container of changed blocks
4181 core::map<s16, MapBlock*> changed_blocks_sector;
4184 Check if any sector's objects can be placed now.
4187 core::map<v3s16, u8> *objects = sector->getObjects();
4188 core::list<v3s16> objects_to_remove;
4189 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
4190 i.atEnd() == false; i++)
4192 v3s16 p = i.getNode()->getKey();
4194 u8 d = i.getNode()->getValue();
4196 // Ground level point (user for stuff that is on ground)
4198 bool ground_found = true;
4200 // Search real ground level
4204 MapNode n = sector->getNode(gp);
4206 // If not air, go one up and continue to placing the tree
4207 if(n.d != CONTENT_AIR)
4213 // If air, go one down
4214 gp += v3s16(0,-1,0);
4216 }catch(InvalidPositionException &e)
4218 // Ground not found.
4219 ground_found = false;
4220 // This is most close to ground
4227 if(d == SECTOR_OBJECT_TEST)
4229 if(sector->isValidArea(p + v3s16(0,0,0),
4230 p + v3s16(0,0,0), &changed_blocks_sector))
4233 n.d = CONTENT_TORCH;
4234 sector->setNode(p, n);
4235 objects_to_remove.push_back(p);
4238 else if(d == SECTOR_OBJECT_TREE_1)
4240 if(ground_found == false)
4243 v3s16 p_min = gp + v3s16(-1,0,-1);
4244 v3s16 p_max = gp + v3s16(1,5,1);
4245 if(sector->isValidArea(p_min, p_max,
4246 &changed_blocks_sector))
4250 sector->setNode(gp+v3s16(0,0,0), n);
4251 sector->setNode(gp+v3s16(0,1,0), n);
4252 sector->setNode(gp+v3s16(0,2,0), n);
4253 sector->setNode(gp+v3s16(0,3,0), n);
4255 n.d = CONTENT_LEAVES;
4257 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4259 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4260 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4261 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4262 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4263 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4264 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4265 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4266 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4268 sector->setNode(gp+v3s16(0,4,0), n);
4270 sector->setNode(gp+v3s16(-1,4,0), n);
4271 sector->setNode(gp+v3s16(1,4,0), n);
4272 sector->setNode(gp+v3s16(0,4,-1), n);
4273 sector->setNode(gp+v3s16(0,4,1), n);
4274 sector->setNode(gp+v3s16(1,4,1), n);
4275 sector->setNode(gp+v3s16(-1,4,1), n);
4276 sector->setNode(gp+v3s16(-1,4,-1), n);
4277 sector->setNode(gp+v3s16(1,4,-1), n);
4279 sector->setNode(gp+v3s16(-1,3,0), n);
4280 sector->setNode(gp+v3s16(1,3,0), n);
4281 sector->setNode(gp+v3s16(0,3,-1), n);
4282 sector->setNode(gp+v3s16(0,3,1), n);
4283 sector->setNode(gp+v3s16(1,3,1), n);
4284 sector->setNode(gp+v3s16(-1,3,1), n);
4285 sector->setNode(gp+v3s16(-1,3,-1), n);
4286 sector->setNode(gp+v3s16(1,3,-1), n);
4288 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4289 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4290 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4291 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4292 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4293 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4294 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4295 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4297 // Objects are identified by wanted position
4298 objects_to_remove.push_back(p);
4300 // Lighting has to be recalculated for this one.
4301 sector->getBlocksInArea(p_min, p_max,
4302 lighting_invalidated_blocks);
4305 else if(d == SECTOR_OBJECT_BUSH_1)
4307 if(ground_found == false)
4310 if(sector->isValidArea(gp + v3s16(0,0,0),
4311 gp + v3s16(0,0,0), &changed_blocks_sector))
4314 n.d = CONTENT_LEAVES;
4315 sector->setNode(gp+v3s16(0,0,0), n);
4317 // Objects are identified by wanted position
4318 objects_to_remove.push_back(p);
4321 else if(d == SECTOR_OBJECT_RAVINE)
4324 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4325 v3s16 p_max = p + v3s16(6,6,6);
4326 if(sector->isValidArea(p_min, p_max,
4327 &changed_blocks_sector))
4330 n.d = CONTENT_STONE;
4333 s16 depth = maxdepth + (myrand()%10);
4335 s16 minz = -6 - (-2);
4337 for(s16 x=-6; x<=6; x++)
4339 z += -1 + (myrand()%3);
4344 for(s16 y=depth+(myrand()%2); y<=6; y++)
4346 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4349 v3s16 p2 = p + v3s16(x,y,z-2);
4350 //if(is_ground_content(sector->getNode(p2).d))
4351 if(content_features(sector->getNode(p2).d).walkable)
4352 sector->setNode(p2, n);
4355 v3s16 p2 = p + v3s16(x,y,z-1);
4356 if(content_features(sector->getNode(p2).d).walkable)
4357 sector->setNode(p2, n2);
4360 v3s16 p2 = p + v3s16(x,y,z+0);
4361 if(content_features(sector->getNode(p2).d).walkable)
4362 sector->setNode(p2, n2);
4365 v3s16 p2 = p + v3s16(x,y,z+1);
4366 if(content_features(sector->getNode(p2).d).walkable)
4367 sector->setNode(p2, n);
4370 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4371 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4375 objects_to_remove.push_back(p);
4377 // Lighting has to be recalculated for this one.
4378 sector->getBlocksInArea(p_min, p_max,
4379 lighting_invalidated_blocks);
4384 dstream<<"ServerMap::generateBlock(): "
4385 "Invalid heightmap object"
4390 catch(InvalidPositionException &e)
4392 dstream<<"WARNING: "<<__FUNCTION_NAME
4393 <<": while inserting object "<<(int)d
4394 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4395 <<" InvalidPositionException.what()="
4396 <<e.what()<<std::endl;
4397 // This is not too fatal and seems to happen sometimes.
4402 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4403 i != objects_to_remove.end(); i++)
4405 objects->remove(*i);
4409 Translate sector's changed blocks to global changed blocks
4412 for(core::map<s16, MapBlock*>::Iterator
4413 i = changed_blocks_sector.getIterator();
4414 i.atEnd() == false; i++)
4416 MapBlock *block = i.getNode()->getValue();
4418 changed_blocks.insert(block->getPos(), block);
4421 block->setLightingExpired(true);
4428 <<"lighting_invalidated_blocks.size()"
4432 <<" "<<lighting_invalidated_blocks.size()
4433 <<", "<<has_dungeons
4434 <<", "<<completely_underground
4435 <<", "<<some_part_underground
4442 MapBlock * ServerMap::createBlock(v3s16 p)
4444 DSTACK("%s: p=(%d,%d,%d)",
4445 __FUNCTION_NAME, p.X, p.Y, p.Z);
4447 v2s16 p2d(p.X, p.Z);
4450 This will create or load a sector if not found in memory.
4451 If block exists on disk, it will be loaded.
4453 NOTE: On old save formats, this will be slow, as it generates
4454 lighting on blocks for them.
4456 ServerMapSector *sector;
4458 sector = (ServerMapSector*)createSector(p2d);
4459 assert(sector->getId() == MAPSECTOR_SERVER);
4461 /*catch(InvalidPositionException &e)
4463 dstream<<"createBlock: createSector() failed"<<std::endl;
4466 catch(std::exception &e)
4468 dstream<<"createBlock: createSector() failed: "
4469 <<e.what()<<std::endl;
4474 Try to get a block from the sector
4477 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4481 block = sector->createBlankBlock(block_y);
4485 MapBlock * ServerMap::emergeBlock(
4487 bool only_from_disk,
4488 core::map<v3s16, MapBlock*> &changed_blocks,
4489 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4492 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4494 p.X, p.Y, p.Z, only_from_disk);
4496 v2s16 p2d(p.X, p.Z);
4499 This will create or load a sector if not found in memory.
4500 If block exists on disk, it will be loaded.
4502 NOTE: On old save formats, this will be slow, as it generates
4503 lighting on blocks for them.
4505 ServerMapSector *sector;
4507 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4508 assert(sector->getId() == MAPSECTOR_SERVER);
4510 catch(std::exception &e)
4512 dstream<<"emergeBlock: emergeSector() failed: "
4513 <<e.what()<<std::endl;
4518 Try to get a block from the sector
4521 bool does_not_exist = false;
4522 bool lighting_expired = false;
4523 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4527 does_not_exist = true;
4529 else if(block->isDummy() == true)
4531 does_not_exist = true;
4533 else if(block->getLightingExpired())
4535 lighting_expired = true;
4540 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4545 If block was not found on disk and not going to generate a
4546 new one, make sure there is a dummy block in place.
4548 if(only_from_disk && (does_not_exist || lighting_expired))
4550 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4554 // Create dummy block
4555 block = new MapBlock(this, p, true);
4557 // Add block to sector
4558 sector->insertBlock(block);
4564 //dstream<<"Not found on disk, generating."<<std::endl;
4566 //TimeTaker("emergeBlock() generate");
4568 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4571 If the block doesn't exist, generate the block.
4575 block = generateBlock(p, block, sector, changed_blocks,
4576 lighting_invalidated_blocks);
4579 if(lighting_expired)
4581 lighting_invalidated_blocks.insert(p, block);
4585 Initially update sunlight
4589 core::map<v3s16, bool> light_sources;
4590 bool black_air_left = false;
4591 bool bottom_invalid =
4592 block->propagateSunlight(light_sources, true,
4593 &black_air_left, true);
4595 // If sunlight didn't reach everywhere and part of block is
4596 // above ground, lighting has to be properly updated
4597 //if(black_air_left && some_part_underground)
4600 lighting_invalidated_blocks[block->getPos()] = block;
4605 lighting_invalidated_blocks[block->getPos()] = block;
4610 Debug mode operation
4612 bool haxmode = g_settings.getBool("haxmode");
4615 // Don't calculate lighting at all
4616 //lighting_invalidated_blocks.clear();
4622 void ServerMap::createDir(std::string path)
4624 if(fs::CreateDir(path) == false)
4626 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4627 <<"\""<<path<<"\""<<std::endl;
4628 throw BaseException("ServerMap failed to create directory");
4632 std::string ServerMap::getSectorSubDir(v2s16 pos)
4635 snprintf(cc, 9, "%.4x%.4x",
4636 (unsigned int)pos.X&0xffff,
4637 (unsigned int)pos.Y&0xffff);
4639 return std::string(cc);
4642 std::string ServerMap::getSectorDir(v2s16 pos)
4644 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4647 v2s16 ServerMap::getSectorPos(std::string dirname)
4649 if(dirname.size() != 8)
4650 throw InvalidFilenameException("Invalid sector directory name");
4652 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4654 throw InvalidFilenameException("Invalid sector directory name");
4655 v2s16 pos((s16)x, (s16)y);
4659 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4661 v2s16 p2d = getSectorPos(sectordir);
4663 if(blockfile.size() != 4){
4664 throw InvalidFilenameException("Invalid block filename");
4667 int r = sscanf(blockfile.c_str(), "%4x", &y);
4669 throw InvalidFilenameException("Invalid block filename");
4670 return v3s16(p2d.X, y, p2d.Y);
4674 #define ENABLE_SECTOR_SAVING 1
4675 #define ENABLE_SECTOR_LOADING 1
4676 #define ENABLE_BLOCK_SAVING 1
4677 #define ENABLE_BLOCK_LOADING 1
4679 void ServerMap::save(bool only_changed)
4681 DSTACK(__FUNCTION_NAME);
4682 if(m_map_saving_enabled == false)
4684 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4688 if(only_changed == false)
4689 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4692 saveMasterHeightmap();
4694 u32 sector_meta_count = 0;
4695 u32 block_count = 0;
4698 JMutexAutoLock lock(m_sector_mutex);
4700 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4701 for(; i.atEnd() == false; i++)
4703 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4704 assert(sector->getId() == MAPSECTOR_SERVER);
4706 if(ENABLE_SECTOR_SAVING)
4708 if(sector->differs_from_disk || only_changed == false)
4710 saveSectorMeta(sector);
4711 sector_meta_count++;
4714 if(ENABLE_BLOCK_SAVING)
4716 core::list<MapBlock*> blocks;
4717 sector->getBlocks(blocks);
4718 core::list<MapBlock*>::Iterator j;
4719 for(j=blocks.begin(); j!=blocks.end(); j++)
4721 MapBlock *block = *j;
4722 if(block->getChangedFlag() || only_changed == false)
4727 /*dstream<<"ServerMap: Written block ("
4728 <<block->getPos().X<<","
4729 <<block->getPos().Y<<","
4730 <<block->getPos().Z<<")"
4740 Only print if something happened or saved whole map
4742 if(only_changed == false || sector_meta_count != 0
4743 || block_count != 0)
4745 dstream<<DTIME<<"ServerMap: Written: "
4746 <<sector_meta_count<<" sector metadata files, "
4747 <<block_count<<" block files"
4752 void ServerMap::loadAll()
4754 DSTACK(__FUNCTION_NAME);
4755 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4757 loadMasterHeightmap();
4759 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4761 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4763 JMutexAutoLock lock(m_sector_mutex);
4766 s32 printed_counter = -100000;
4767 s32 count = list.size();
4769 std::vector<fs::DirListNode>::iterator i;
4770 for(i=list.begin(); i!=list.end(); i++)
4772 if(counter > printed_counter + 10)
4774 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4775 printed_counter = counter;
4779 MapSector *sector = NULL;
4781 // We want directories
4785 sector = loadSectorMeta(i->name);
4787 catch(InvalidFilenameException &e)
4789 // This catches unknown crap in directory
4792 if(ENABLE_BLOCK_LOADING)
4794 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4795 (m_savedir+"/sectors/"+i->name);
4796 std::vector<fs::DirListNode>::iterator i2;
4797 for(i2=list2.begin(); i2!=list2.end(); i2++)
4803 loadBlock(i->name, i2->name, sector);
4805 catch(InvalidFilenameException &e)
4807 // This catches unknown crap in directory
4812 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4815 void ServerMap::saveMasterHeightmap()
4817 DSTACK(__FUNCTION_NAME);
4818 createDir(m_savedir);
4820 std::string fullpath = m_savedir + "/master_heightmap";
4821 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4822 if(o.good() == false)
4823 throw FileNotGoodException("Cannot open master heightmap");
4825 // Format used for writing
4826 u8 version = SER_FMT_VER_HIGHEST;
4829 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4831 [0] u8 serialization version
4832 [1] X master heightmap
4834 u32 fullsize = 1 + hmdata.getSize();
4835 SharedBuffer<u8> data(fullsize);
4838 memcpy(&data[1], *hmdata, hmdata.getSize());
4840 o.write((const char*)*data, fullsize);
4843 m_heightmap->serialize(o, version);
4846 void ServerMap::loadMasterHeightmap()
4848 DSTACK(__FUNCTION_NAME);
4849 std::string fullpath = m_savedir + "/master_heightmap";
4850 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4851 if(is.good() == false)
4852 throw FileNotGoodException("Cannot open master heightmap");
4854 if(m_heightmap != NULL)
4857 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4860 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4862 DSTACK(__FUNCTION_NAME);
4863 // Format used for writing
4864 u8 version = SER_FMT_VER_HIGHEST;
4866 v2s16 pos = sector->getPos();
4867 createDir(m_savedir);
4868 createDir(m_savedir+"/sectors");
4869 std::string dir = getSectorDir(pos);
4872 std::string fullpath = dir + "/heightmap";
4873 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4874 if(o.good() == false)
4875 throw FileNotGoodException("Cannot open master heightmap");
4877 sector->serialize(o, version);
4879 sector->differs_from_disk = false;
4882 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4884 DSTACK(__FUNCTION_NAME);
4886 v2s16 p2d = getSectorPos(dirname);
4887 std::string dir = m_savedir + "/sectors/" + dirname;
4889 std::string fullpath = dir + "/heightmap";
4890 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4891 if(is.good() == false)
4892 throw FileNotGoodException("Cannot open sector heightmap");
4894 ServerMapSector *sector = ServerMapSector::deSerialize
4895 (is, this, p2d, &m_hwrapper, m_sectors);
4897 sector->differs_from_disk = false;
4902 bool ServerMap::loadSectorFull(v2s16 p2d)
4904 DSTACK(__FUNCTION_NAME);
4905 std::string sectorsubdir = getSectorSubDir(p2d);
4907 MapSector *sector = NULL;
4909 JMutexAutoLock lock(m_sector_mutex);
4912 sector = loadSectorMeta(sectorsubdir);
4914 catch(InvalidFilenameException &e)
4918 catch(FileNotGoodException &e)
4922 catch(std::exception &e)
4927 if(ENABLE_BLOCK_LOADING)
4929 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4930 (m_savedir+"/sectors/"+sectorsubdir);
4931 std::vector<fs::DirListNode>::iterator i2;
4932 for(i2=list2.begin(); i2!=list2.end(); i2++)
4938 loadBlock(sectorsubdir, i2->name, sector);
4940 catch(InvalidFilenameException &e)
4942 // This catches unknown crap in directory
4950 bool ServerMap::deFlushSector(v2s16 p2d)
4952 DSTACK(__FUNCTION_NAME);
4953 // See if it already exists in memory
4955 MapSector *sector = getSectorNoGenerate(p2d);
4958 catch(InvalidPositionException &e)
4961 Try to load the sector from disk.
4963 if(loadSectorFull(p2d) == true)
4972 void ServerMap::saveBlock(MapBlock *block)
4974 DSTACK(__FUNCTION_NAME);
4976 Dummy blocks are not written
4978 if(block->isDummy())
4980 /*v3s16 p = block->getPos();
4981 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4982 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4986 // Format used for writing
4987 u8 version = SER_FMT_VER_HIGHEST;
4989 v3s16 p3d = block->getPos();
4990 v2s16 p2d(p3d.X, p3d.Z);
4991 createDir(m_savedir);
4992 createDir(m_savedir+"/sectors");
4993 std::string dir = getSectorDir(p2d);
4996 // Block file is map/sectors/xxxxxxxx/xxxx
4998 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4999 std::string fullpath = dir + "/" + cc;
5000 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5001 if(o.good() == false)
5002 throw FileNotGoodException("Cannot open block data");
5005 [0] u8 serialization version
5008 o.write((char*)&version, 1);
5010 block->serialize(o, version);
5013 Versions up from 9 have block objects.
5017 block->serializeObjects(o, version);
5020 // We just wrote it to the disk
5021 block->resetChangedFlag();
5024 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5026 DSTACK(__FUNCTION_NAME);
5030 // Block file is map/sectors/xxxxxxxx/xxxx
5031 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5032 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5033 if(is.good() == false)
5034 throw FileNotGoodException("Cannot open block file");
5036 v3s16 p3d = getBlockPos(sectordir, blockfile);
5037 v2s16 p2d(p3d.X, p3d.Z);
5039 assert(sector->getPos() == p2d);
5041 u8 version = SER_FMT_VER_INVALID;
5042 is.read((char*)&version, 1);
5044 /*u32 block_size = MapBlock::serializedLength(version);
5045 SharedBuffer<u8> data(block_size);
5046 is.read((char*)*data, block_size);*/
5048 // This will always return a sector because we're the server
5049 //MapSector *sector = emergeSector(p2d);
5051 MapBlock *block = NULL;
5052 bool created_new = false;
5054 block = sector->getBlockNoCreate(p3d.Y);
5056 catch(InvalidPositionException &e)
5058 block = sector->createBlankBlockNoInsert(p3d.Y);
5062 // deserialize block data
5063 block->deSerialize(is, version);
5066 Versions up from 9 have block objects.
5070 block->updateObjects(is, version, NULL, 0);
5074 sector->insertBlock(block);
5077 Convert old formats to new and save
5080 // Save old format blocks in new format
5081 if(version < SER_FMT_VER_HIGHEST)
5086 // We just loaded it from the disk, so it's up-to-date.
5087 block->resetChangedFlag();
5090 catch(SerializationError &e)
5092 dstream<<"WARNING: Invalid block data on disk "
5093 "(SerializationError). Ignoring."
5098 // Gets from master heightmap
5099 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5101 assert(m_heightmap != NULL);
5109 corners[0] = m_heightmap->getGroundHeight
5110 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5111 corners[1] = m_heightmap->getGroundHeight
5112 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5113 corners[2] = m_heightmap->getGroundHeight
5114 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5115 corners[3] = m_heightmap->getGroundHeight
5116 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
5119 void ServerMap::PrintInfo(std::ostream &out)
5130 ClientMap::ClientMap(
5132 MapDrawControl &control,
5133 scene::ISceneNode* parent,
5134 scene::ISceneManager* mgr,
5138 scene::ISceneNode(parent, mgr, id),
5145 /*m_box = core::aabbox3d<f32>(0,0,0,
5146 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5147 /*m_box = core::aabbox3d<f32>(0,0,0,
5148 map->getSizeNodes().X * BS,
5149 map->getSizeNodes().Y * BS,
5150 map->getSizeNodes().Z * BS);*/
5151 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5152 BS*1000000,BS*1000000,BS*1000000);
5154 //setPosition(v3f(BS,BS,BS));
5157 ClientMap::~ClientMap()
5159 JMutexAutoLock lock(mesh_mutex);
5168 MapSector * ClientMap::emergeSector(v2s16 p2d)
5170 DSTACK(__FUNCTION_NAME);
5171 // Check that it doesn't exist already
5173 return getSectorNoGenerate(p2d);
5175 catch(InvalidPositionException &e)
5179 // Create a sector with no heightmaps
5180 ClientMapSector *sector = new ClientMapSector(this, p2d);
5183 JMutexAutoLock lock(m_sector_mutex);
5184 m_sectors.insert(p2d, sector);
5190 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5192 DSTACK(__FUNCTION_NAME);
5193 ClientMapSector *sector = NULL;
5195 JMutexAutoLock lock(m_sector_mutex);
5197 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5201 sector = (ClientMapSector*)n->getValue();
5202 assert(sector->getId() == MAPSECTOR_CLIENT);
5206 sector = new ClientMapSector(this, p2d);
5208 JMutexAutoLock lock(m_sector_mutex);
5209 m_sectors.insert(p2d, sector);
5213 sector->deSerialize(is);
5216 void ClientMap::OnRegisterSceneNode()
5220 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5221 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5224 ISceneNode::OnRegisterSceneNode();
5227 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5229 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5230 DSTACK(__FUNCTION_NAME);
5232 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5235 Get time for measuring timeout.
5237 Measuring time is very useful for long delays when the
5238 machine is swapping a lot.
5240 int time1 = time(0);
5242 u32 daynight_ratio = m_client->getDayNightRatio();
5244 m_camera_mutex.Lock();
5245 v3f camera_position = m_camera_position;
5246 v3f camera_direction = m_camera_direction;
5247 m_camera_mutex.Unlock();
5250 Get all blocks and draw all visible ones
5253 v3s16 cam_pos_nodes(
5254 camera_position.X / BS,
5255 camera_position.Y / BS,
5256 camera_position.Z / BS);
5258 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5260 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5261 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5263 // Take a fair amount as we will be dropping more out later
5265 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5266 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5267 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5269 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5270 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5271 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5273 u32 vertex_count = 0;
5275 // For limiting number of mesh updates per frame
5276 u32 mesh_update_count = 0;
5278 u32 blocks_would_have_drawn = 0;
5279 u32 blocks_drawn = 0;
5281 //NOTE: The sectors map should be locked but we're not doing it
5282 // because it'd cause too much delays
5284 int timecheck_counter = 0;
5285 core::map<v2s16, MapSector*>::Iterator si;
5286 si = m_sectors.getIterator();
5287 for(; si.atEnd() == false; si++)
5290 timecheck_counter++;
5291 if(timecheck_counter > 50)
5293 int time2 = time(0);
5294 if(time2 > time1 + 4)
5296 dstream<<"ClientMap::renderMap(): "
5297 "Rendering takes ages, returning."
5304 MapSector *sector = si.getNode()->getValue();
5305 v2s16 sp = sector->getPos();
5307 if(m_control.range_all == false)
5309 if(sp.X < p_blocks_min.X
5310 || sp.X > p_blocks_max.X
5311 || sp.Y < p_blocks_min.Z
5312 || sp.Y > p_blocks_max.Z)
5316 core::list< MapBlock * > sectorblocks;
5317 sector->getBlocks(sectorblocks);
5323 core::list< MapBlock * >::Iterator i;
5324 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5326 MapBlock *block = *i;
5329 Compare block position to camera position, skip
5330 if not seen on display
5333 float range = 100000 * BS;
5334 if(m_control.range_all == false)
5335 range = m_control.wanted_range * BS;
5338 if(isBlockInSight(block->getPos(), camera_position,
5339 camera_direction, range, &d) == false)
5345 v3s16 blockpos_nodes = block->getPosRelative();
5347 // Block center position
5349 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5350 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5351 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5354 // Block position relative to camera
5355 v3f blockpos_relative = blockpos - camera_position;
5357 // Distance in camera direction (+=front, -=back)
5358 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5361 f32 d = blockpos_relative.getLength();
5363 if(m_control.range_all == false)
5365 // If block is far away, don't draw it
5366 if(d > m_control.wanted_range * BS)
5370 // Maximum radius of a block
5371 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5373 // If block is (nearly) touching the camera, don't
5374 // bother validating further (that is, render it anyway)
5375 if(d > block_max_radius * 1.5)
5377 // Cosine of the angle between the camera direction
5378 // and the block direction (camera_direction is an unit vector)
5379 f32 cosangle = dforward / d;
5381 // Compensate for the size of the block
5382 // (as the block has to be shown even if it's a bit off FOV)
5383 // This is an estimate.
5384 cosangle += block_max_radius / dforward;
5386 // If block is not in the field of view, skip it
5387 //if(cosangle < cos(FOV_ANGLE/2))
5388 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5393 v3s16 blockpos_nodes = block->getPosRelative();
5395 // Block center position
5397 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5398 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5399 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5402 // Block position relative to camera
5403 v3f blockpos_relative = blockpos - camera_position;
5406 f32 d = blockpos_relative.getLength();
5414 bool mesh_expired = false;
5417 JMutexAutoLock lock(block->mesh_mutex);
5419 mesh_expired = block->getMeshExpired();
5421 // Mesh has not been expired and there is no mesh:
5422 // block has no content
5423 if(block->mesh == NULL && mesh_expired == false)
5427 f32 faraway = BS*50;
5428 //f32 faraway = m_control.wanted_range * BS;
5431 This has to be done with the mesh_mutex unlocked
5433 // Pretty random but this should work somewhat nicely
5434 if(mesh_expired && (
5435 (mesh_update_count < 3
5436 && (d < faraway || mesh_update_count < 2)
5439 (m_control.range_all && mesh_update_count < 20)
5442 /*if(mesh_expired && mesh_update_count < 6
5443 && (d < faraway || mesh_update_count < 3))*/
5445 mesh_update_count++;
5447 // Mesh has been expired: generate new mesh
5448 //block->updateMeshes(daynight_i);
5449 block->updateMesh(daynight_ratio);
5451 mesh_expired = false;
5455 Don't draw an expired mesh that is far away
5457 /*if(mesh_expired && d >= faraway)
5460 // Instead, delete it
5461 JMutexAutoLock lock(block->mesh_mutex);
5464 block->mesh->drop();
5467 // And continue to next block
5472 Draw the faces of the block
5475 JMutexAutoLock lock(block->mesh_mutex);
5477 scene::SMesh *mesh = block->mesh;
5482 blocks_would_have_drawn++;
5483 if(blocks_drawn >= m_control.wanted_max_blocks
5484 && m_control.range_all == false
5485 && d > m_control.wanted_min_range * BS)
5489 u32 c = mesh->getMeshBufferCount();
5491 for(u32 i=0; i<c; i++)
5493 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5494 const video::SMaterial& material = buf->getMaterial();
5495 video::IMaterialRenderer* rnd =
5496 driver->getMaterialRenderer(material.MaterialType);
5497 bool transparent = (rnd && rnd->isTransparent());
5498 // Render transparent on transparent pass and likewise.
5499 if(transparent == is_transparent_pass)
5501 driver->setMaterial(buf->getMaterial());
5502 driver->drawMeshBuffer(buf);
5503 vertex_count += buf->getVertexCount();
5507 } // foreach sectorblocks
5510 m_control.blocks_drawn = blocks_drawn;
5511 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5513 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5514 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5517 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5518 core::map<v3s16, MapBlock*> *affected_blocks)
5520 bool changed = false;
5522 Add it to all blocks touching it
5525 v3s16(0,0,0), // this
5526 v3s16(0,0,1), // back
5527 v3s16(0,1,0), // top
5528 v3s16(1,0,0), // right
5529 v3s16(0,0,-1), // front
5530 v3s16(0,-1,0), // bottom
5531 v3s16(-1,0,0), // left
5533 for(u16 i=0; i<7; i++)
5535 v3s16 p2 = p + dirs[i];
5536 // Block position of neighbor (or requested) node
5537 v3s16 blockpos = getNodeBlockPos(p2);
5538 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5539 if(blockref == NULL)
5541 // Relative position of requested node
5542 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5543 if(blockref->setTempMod(relpos, mod))
5548 if(changed && affected_blocks!=NULL)
5550 for(u16 i=0; i<7; i++)
5552 v3s16 p2 = p + dirs[i];
5553 // Block position of neighbor (or requested) node
5554 v3s16 blockpos = getNodeBlockPos(p2);
5555 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5556 if(blockref == NULL)
5558 affected_blocks->insert(blockpos, blockref);
5564 bool ClientMap::clearTempMod(v3s16 p,
5565 core::map<v3s16, MapBlock*> *affected_blocks)
5567 bool changed = false;
5569 v3s16(0,0,0), // this
5570 v3s16(0,0,1), // back
5571 v3s16(0,1,0), // top
5572 v3s16(1,0,0), // right
5573 v3s16(0,0,-1), // front
5574 v3s16(0,-1,0), // bottom
5575 v3s16(-1,0,0), // left
5577 for(u16 i=0; i<7; i++)
5579 v3s16 p2 = p + dirs[i];
5580 // Block position of neighbor (or requested) node
5581 v3s16 blockpos = getNodeBlockPos(p2);
5582 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5583 if(blockref == NULL)
5585 // Relative position of requested node
5586 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5587 if(blockref->clearTempMod(relpos))
5592 if(changed && affected_blocks!=NULL)
5594 for(u16 i=0; i<7; i++)
5596 v3s16 p2 = p + dirs[i];
5597 // Block position of neighbor (or requested) node
5598 v3s16 blockpos = getNodeBlockPos(p2);
5599 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5600 if(blockref == NULL)
5602 affected_blocks->insert(blockpos, blockref);
5608 void ClientMap::PrintInfo(std::ostream &out)
5619 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5624 MapVoxelManipulator::~MapVoxelManipulator()
5626 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5630 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5632 TimeTaker timer1("emerge", &emerge_time);
5634 // Units of these are MapBlocks
5635 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5636 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5638 VoxelArea block_area_nodes
5639 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5641 addArea(block_area_nodes);
5643 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5644 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5645 for(s32 x=p_min.X; x<=p_max.X; x++)
5648 core::map<v3s16, bool>::Node *n;
5649 n = m_loaded_blocks.find(p);
5653 bool block_data_inexistent = false;
5656 TimeTaker timer1("emerge load", &emerge_load_time);
5658 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5659 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5662 dstream<<std::endl;*/
5664 MapBlock *block = m_map->getBlockNoCreate(p);
5665 if(block->isDummy())
5666 block_data_inexistent = true;
5668 block->copyTo(*this);
5670 catch(InvalidPositionException &e)
5672 block_data_inexistent = true;
5675 if(block_data_inexistent)
5677 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5678 // Fill with VOXELFLAG_INEXISTENT
5679 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5680 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5682 s32 i = m_area.index(a.MinEdge.X,y,z);
5683 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5687 m_loaded_blocks.insert(p, !block_data_inexistent);
5690 //dstream<<"emerge done"<<std::endl;
5694 SUGG: Add an option to only update eg. water and air nodes.
5695 This will make it interfere less with important stuff if
5698 void MapVoxelManipulator::blitBack
5699 (core::map<v3s16, MapBlock*> & modified_blocks)
5701 if(m_area.getExtent() == v3s16(0,0,0))
5704 //TimeTaker timer1("blitBack");
5706 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5707 <<m_loaded_blocks.size()<<std::endl;*/
5710 Initialize block cache
5712 v3s16 blockpos_last;
5713 MapBlock *block = NULL;
5714 bool block_checked_in_modified = false;
5716 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5717 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5718 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5722 u8 f = m_flags[m_area.index(p)];
5723 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5726 MapNode &n = m_data[m_area.index(p)];
5728 v3s16 blockpos = getNodeBlockPos(p);
5733 if(block == NULL || blockpos != blockpos_last){
5734 block = m_map->getBlockNoCreate(blockpos);
5735 blockpos_last = blockpos;
5736 block_checked_in_modified = false;
5739 // Calculate relative position in block
5740 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5742 // Don't continue if nothing has changed here
5743 if(block->getNode(relpos) == n)
5746 //m_map->setNode(m_area.MinEdge + p, n);
5747 block->setNode(relpos, n);
5750 Make sure block is in modified_blocks
5752 if(block_checked_in_modified == false)
5754 modified_blocks[blockpos] = block;
5755 block_checked_in_modified = true;
5758 catch(InvalidPositionException &e)
5764 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5765 MapVoxelManipulator(map)
5769 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5773 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5775 // Just create the area so that it can be pointed to
5776 VoxelManipulator::emerge(a, caller_id);
5779 void ManualMapVoxelManipulator::initialEmerge(
5780 v3s16 blockpos_min, v3s16 blockpos_max)
5782 TimeTaker timer1("initialEmerge", &emerge_time);
5784 // Units of these are MapBlocks
5785 v3s16 p_min = blockpos_min;
5786 v3s16 p_max = blockpos_max;
5788 VoxelArea block_area_nodes
5789 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5791 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5794 dstream<<"initialEmerge: area: ";
5795 block_area_nodes.print(dstream);
5796 dstream<<" ("<<size_MB<<"MB)";
5800 addArea(block_area_nodes);
5802 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5803 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5804 for(s32 x=p_min.X; x<=p_max.X; x++)
5807 core::map<v3s16, bool>::Node *n;
5808 n = m_loaded_blocks.find(p);
5812 bool block_data_inexistent = false;
5815 TimeTaker timer1("emerge load", &emerge_load_time);
5817 MapBlock *block = m_map->getBlockNoCreate(p);
5818 if(block->isDummy())
5819 block_data_inexistent = true;
5821 block->copyTo(*this);
5823 catch(InvalidPositionException &e)
5825 block_data_inexistent = true;
5828 if(block_data_inexistent)
5831 Mark area inexistent
5833 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5834 // Fill with VOXELFLAG_INEXISTENT
5835 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5836 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5838 s32 i = m_area.index(a.MinEdge.X,y,z);
5839 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5843 m_loaded_blocks.insert(p, !block_data_inexistent);
5847 void ManualMapVoxelManipulator::blitBackAll(
5848 core::map<v3s16, MapBlock*> * modified_blocks)
5850 if(m_area.getExtent() == v3s16(0,0,0))
5854 Copy data of all blocks
5856 for(core::map<v3s16, bool>::Iterator
5857 i = m_loaded_blocks.getIterator();
5858 i.atEnd() == false; i++)
5860 bool existed = i.getNode()->getValue();
5861 if(existed == false)
5863 v3s16 p = i.getNode()->getKey();
5864 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5867 dstream<<"WARNING: "<<__FUNCTION_NAME
5868 <<": got NULL block "
5869 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5874 block->copyFrom(*this);
5877 modified_blocks->insert(p, block);