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"
35 Map::Map(std::ostream &dout):
37 m_camera_position(0,0,0),
38 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):
1738 //m_chunksize = 16; // Too slow
1739 m_chunksize = 8; // Takes a few seconds
1743 // TODO: Save to and load from a file
1744 m_seed = (((u64)(myrand()%0xffff)<<0)
1745 + ((u64)(myrand()%0xffff)<<16)
1746 + ((u64)(myrand()%0xffff)<<32)
1747 + ((u64)(myrand()%0xffff)<<48));
1750 Experimental and debug stuff
1757 Try to load map; if not found, create a new one.
1760 m_savedir = savedir;
1761 m_map_saving_enabled = false;
1765 // If directory exists, check contents and load if possible
1766 if(fs::PathExists(m_savedir))
1768 // If directory is empty, it is safe to save into it.
1769 if(fs::GetDirListing(m_savedir).size() == 0)
1771 dstream<<DTIME<<"Server: Empty save directory is valid."
1773 m_map_saving_enabled = true;
1777 // Load map metadata (seed, chunksize)
1780 // Load chunk metadata
1783 /*// Load sector (0,0) and throw and exception on fail
1784 if(loadSectorFull(v2s16(0,0)) == false)
1785 throw LoadError("Failed to load sector (0,0)");*/
1787 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1788 "metadata and sector (0,0) from "<<savedir<<
1789 ", assuming valid save directory."
1792 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1793 <<"and chunk metadata from "<<savedir
1794 <<", assuming valid save directory."
1797 m_map_saving_enabled = true;
1798 // Map loaded, not creating new one
1802 // If directory doesn't exist, it is safe to save to it
1804 m_map_saving_enabled = true;
1807 catch(std::exception &e)
1809 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1810 <<", exception: "<<e.what()<<std::endl;
1811 dstream<<"Please remove the map or fix it."<<std::endl;
1812 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1815 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1817 // Create zero sector
1818 emergeSector(v2s16(0,0));
1820 // Initially write whole map
1824 ServerMap::~ServerMap()
1828 if(m_map_saving_enabled)
1831 // Save only changed parts
1833 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1837 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1840 catch(std::exception &e)
1842 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1843 <<", exception: "<<e.what()<<std::endl;
1849 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1850 for(; i.atEnd() == false; i++)
1852 MapChunk *chunk = i.getNode()->getValue();
1858 Some helper functions for the map generator
1861 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1863 v3s16 em = vmanip.m_area.getExtent();
1864 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1865 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1866 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1868 for(y=y_nodes_max; y>=y_nodes_min; y--)
1870 MapNode &n = vmanip.m_data[i];
1871 if(content_walkable(n.d))
1874 vmanip.m_area.add_y(em, i, -1);
1876 if(y >= y_nodes_min)
1882 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1884 v3s16 em = vmanip.m_area.getExtent();
1885 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1886 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1887 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1889 for(y=y_nodes_max; y>=y_nodes_min; y--)
1891 MapNode &n = vmanip.m_data[i];
1892 if(content_walkable(n.d)
1893 && n.d != CONTENT_TREE
1894 && n.d != CONTENT_LEAVES)
1897 vmanip.m_area.add_y(em, i, -1);
1899 if(y >= y_nodes_min)
1905 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1907 MapNode treenode(CONTENT_TREE);
1908 MapNode leavesnode(CONTENT_LEAVES);
1910 s16 trunk_h = myrand_range(3, 6);
1912 for(s16 ii=0; ii<trunk_h; ii++)
1914 if(vmanip.m_area.contains(p1))
1915 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1919 // p1 is now the last piece of the trunk
1922 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1923 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1924 Buffer<u8> leaves_d(leaves_a.getVolume());
1925 for(s32 i=0; i<leaves_a.getVolume(); i++)
1928 // Force leaves at near the end of the trunk
1931 for(s16 z=-d; z<=d; z++)
1932 for(s16 y=-d; y<=d; y++)
1933 for(s16 x=-d; x<=d; x++)
1935 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1939 // Add leaves randomly
1940 for(u32 iii=0; iii<7; iii++)
1945 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1946 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1947 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1950 for(s16 z=0; z<=d; z++)
1951 for(s16 y=0; y<=d; y++)
1952 for(s16 x=0; x<=d; x++)
1954 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1958 // Blit leaves to vmanip
1959 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1960 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1961 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1965 if(vmanip.m_area.contains(p) == false)
1967 u32 vi = vmanip.m_area.index(p);
1968 if(vmanip.m_data[vi].d != CONTENT_AIR)
1970 u32 i = leaves_a.index(x,y,z);
1971 if(leaves_d[i] == 1)
1972 vmanip.m_data[vi] = leavesnode;
1977 Noise functions. Make sure seed is mangled differently in each one.
1980 // Amount of trees per area in nodes
1981 double tree_amount_2d(u64 seed, v2s16 p)
1983 double noise = noise2d_perlin(
1984 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1986 double zeroval = -0.3;
1990 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1993 /*double base_rock_level_2d(u64 seed, v2s16 p)
1995 return WATER_LEVEL - 6.0 + 25. * noise2d_perlin(
1996 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2000 /*double highlands_level_2d(u64 seed, v2s16 p)
2002 double a = noise2d_perlin(
2003 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2008 return WATER_LEVEL + 25;
2009 return WATER_LEVEL + 55. * noise2d_perlin(
2010 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2011 seed+85039, 6, 0.69);
2017 double base_rock_level_2d(u64 seed, v2s16 p)
2019 // The base ground level
2020 double base = (double)WATER_LEVEL - 4.0 + 25. * noise2d_perlin(
2021 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2022 (seed>>32)+654879876, 6, 0.6);
2024 /*// A bit hillier one
2025 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2026 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2027 (seed>>27)+90340, 6, 0.69);
2031 // Higher ground level
2032 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2033 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2034 seed+85039, 5, 0.69);
2035 //higher = 30; // For debugging
2037 // Limit higher to at least base
2041 // Steepness factor of cliffs
2042 double b = 1.0 + 1.0 * noise2d_perlin(
2043 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2045 b = rangelim(b, 0.0, 1000.0);
2048 b = rangelim(b, 3.0, 1000.0);
2049 //dstream<<"b="<<b<<std::endl;
2052 // Offset to more low
2053 double a_off = -0.15;
2054 // High/low selector
2055 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2056 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2057 seed-359, 6, 0.7));*/
2058 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2059 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2060 seed-359, 5, 0.60));
2062 a = rangelim(a, 0.0, 1.0);
2064 //dstream<<"a="<<a<<std::endl;
2066 double h = base*(1.0-a) + higher*a;
2073 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2076 This is the main map generation method
2079 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2080 core::map<v3s16, MapBlock*> &changed_blocks,
2083 DSTACK(__FUNCTION_NAME);
2086 Don't generate if already fully generated
2090 MapChunk *chunk = getChunk(chunkpos);
2091 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2093 dstream<<"generateChunkRaw(): Chunk "
2094 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2095 <<" already generated"<<std::endl;
2100 dstream<<"generateChunkRaw(): Generating chunk "
2101 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2104 TimeTaker timer("generateChunkRaw()");
2106 // The distance how far into the neighbors the generator is allowed to go.
2107 s16 max_spread_amount_sectors = 2;
2108 assert(max_spread_amount_sectors <= m_chunksize);
2109 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2111 // Minimum amount of space left on sides for mud to fall in
2112 //s16 min_mud_fall_space = 2;
2114 // Maximum diameter of stone obstacles in X and Z
2115 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2116 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2118 s16 y_blocks_min = -4;
2119 s16 y_blocks_max = 3;
2120 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2121 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2122 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2124 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2125 s16 sectorpos_base_size = m_chunksize;
2127 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2128 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2129 v2s16 sectorpos_bigbase =
2130 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2131 s16 sectorpos_bigbase_size =
2132 sectorpos_base_size + 2 * max_spread_amount_sectors;
2134 v3s16 bigarea_blocks_min(
2135 sectorpos_bigbase.X,
2140 v3s16 bigarea_blocks_max(
2141 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2143 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2146 // Relative values to control amount of stuff in one chunk
2147 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2148 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2149 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2150 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2151 *(u32)h_blocks*MAP_BLOCKSIZE;
2154 The limiting edges of the lighting update, inclusive.
2156 s16 lighting_min_d = 0-max_spread_amount;
2157 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2160 Create the whole area of this and the neighboring chunks
2163 TimeTaker timer("generateChunkRaw() create area");
2165 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2166 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2168 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2169 ServerMapSector *sector = createSector(sectorpos);
2172 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2174 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2175 MapBlock *block = createBlock(blockpos);
2177 // Lighting won't be calculated
2178 //block->setLightingExpired(true);
2179 // Lighting will be calculated
2180 block->setLightingExpired(false);
2183 Block gets sunlight if this is true.
2185 This should be set to true when the top side of a block
2186 is completely exposed to the sky.
2188 Actually this doesn't matter now because the
2189 initial lighting is done here.
2191 block->setIsUnderground(y != y_blocks_max);
2197 Now we have a big empty area.
2199 Make a ManualMapVoxelManipulator that contains this and the
2203 ManualMapVoxelManipulator vmanip(this);
2204 // Add the area we just generated
2206 TimeTaker timer("generateChunkRaw() initialEmerge");
2207 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2211 vmanip.clearFlag(0xff);
2213 TimeTaker timer_generate("generateChunkRaw() generate");
2215 // Maximum height of the stone surface and obstacles.
2216 // This is used to disable dungeon generation from going too high.
2217 s16 stone_surface_max_y = 0;
2220 Generate general ground level to full area
2225 //TimeTaker timer1("ground level");
2227 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2228 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2231 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2234 Skip of already generated
2237 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2238 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2242 // Ground height at this point
2243 float surface_y_f = 0.0;
2245 // Use perlin noise for ground height
2246 surface_y_f = base_rock_level_2d(m_seed, p2d);
2248 /*// Experimental stuff
2250 float a = highlands_level_2d(m_seed, p2d);
2255 // Convert to integer
2256 s16 surface_y = (s16)surface_y_f;
2259 if(surface_y > stone_surface_max_y)
2260 stone_surface_max_y = surface_y;
2263 Fill ground with stone
2266 // Use fast index incrementing
2267 v3s16 em = vmanip.m_area.getExtent();
2268 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2269 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2271 vmanip.m_data[i].d = CONTENT_STONE;
2273 vmanip.m_area.add_y(em, i, 1);
2281 Randomize some parameters
2284 s32 stone_obstacle_count = 0;
2285 /*s32 stone_obstacle_count =
2286 rangelim((1.0+noise2d(m_seed+897,
2287 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2289 s16 stone_obstacle_max_height = 0;
2290 /*s16 stone_obstacle_max_height =
2291 rangelim((1.0+noise2d(m_seed+5902,
2292 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2295 Loop this part, it will make stuff look older and newer nicely
2297 //for(u32 i_age=0; i_age<1; i_age++)
2298 for(u32 i_age=0; i_age<2; i_age++)
2303 //TimeTaker timer1("stone obstacles");
2306 Add some random stone obstacles
2309 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2311 // Randomize max height so usually stuff will be quite low
2312 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2314 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2315 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2318 myrand_range(5, stone_obstacle_max_size),
2319 myrand_range(0, maxheight_randomized),
2320 myrand_range(5, stone_obstacle_max_size)
2323 // Don't make stupid small rectangle bumps
2328 myrand_range(1+ob_size.X/2+2,
2329 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2330 myrand_range(1+ob_size.Z/2+2,
2331 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2334 // Minimum space left on top of the obstacle
2335 s16 min_head_space = 12;
2337 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2338 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2340 // Node position in 2d
2341 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2343 // Find stone ground level
2344 // (ignore everything else than mud in already generated chunks)
2345 // and mud amount over the stone level
2349 v3s16 em = vmanip.m_area.getExtent();
2350 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2352 // Go to ground level
2353 for(y=y_nodes_max; y>=y_nodes_min; y--)
2355 MapNode *n = &vmanip.m_data[i];
2356 /*if(content_walkable(n.d)
2357 && n.d != CONTENT_MUD
2358 && n.d != CONTENT_GRASS)
2360 if(n->d == CONTENT_STONE)
2363 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2367 Change to mud because otherwise we might
2368 be throwing mud on grass at the next
2374 vmanip.m_area.add_y(em, i, -1);
2376 if(y >= y_nodes_min)
2379 surface_y = y_nodes_min;
2387 v3s16 em = vmanip.m_area.getExtent();
2388 s16 y_start = surface_y+1;
2389 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2393 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2395 MapNode &n = vmanip.m_data[i];
2396 n.d = CONTENT_STONE;
2398 if(y > stone_surface_max_y)
2399 stone_surface_max_y = y;
2402 if(count >= ob_size.Y)
2405 vmanip.m_area.add_y(em, i, 1);
2409 for(; y<=y_nodes_max - min_head_space; y++)
2411 MapNode &n = vmanip.m_data[i];
2414 if(count >= mud_amount)
2417 vmanip.m_area.add_y(em, i, 1);
2427 //TimeTaker timer1("dungeons");
2432 u32 dungeons_count = relative_volume / 600000;
2433 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2434 if(stone_surface_max_y < WATER_LEVEL)
2436 /*u32 dungeons_count = 0;
2437 u32 bruises_count = 0;*/
2438 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2440 s16 min_tunnel_diameter = 2;
2441 s16 max_tunnel_diameter = 6;
2442 u16 tunnel_routepoints = 25;
2444 bool bruise_surface = (jj < bruises_count);
2448 min_tunnel_diameter = 5;
2449 max_tunnel_diameter = myrand_range(10, 20);
2450 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2451 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2453 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2454 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2456 tunnel_routepoints = 5;
2459 // Allowed route area size in nodes
2461 sectorpos_base_size*MAP_BLOCKSIZE,
2462 h_blocks*MAP_BLOCKSIZE,
2463 sectorpos_base_size*MAP_BLOCKSIZE
2466 // Area starting point in nodes
2468 sectorpos_base.X*MAP_BLOCKSIZE,
2469 y_blocks_min*MAP_BLOCKSIZE,
2470 sectorpos_base.Y*MAP_BLOCKSIZE
2474 //(this should be more than the maximum radius of the tunnel)
2475 //s16 insure = 5; // Didn't work with max_d = 20
2477 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2478 ar += v3s16(1,0,1) * more * 2;
2479 of -= v3s16(1,0,1) * more;
2481 s16 route_y_min = 0;
2482 // Allow half a diameter + 7 over stone surface
2483 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2485 /*// If dungeons, don't go through surface too often
2486 if(bruise_surface == false)
2487 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2489 // Limit maximum to area
2490 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2494 /*// Minimum is at y=0
2495 route_y_min = -of.Y - 0;*/
2496 // Minimum is at y=max_tunnel_diameter/4
2497 //route_y_min = -of.Y + max_tunnel_diameter/4;
2498 //s16 min = -of.Y + max_tunnel_diameter/4;
2499 s16 min = -of.Y + 0;
2500 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2501 route_y_min = rangelim(route_y_min, 0, route_y_max);
2504 /*dstream<<"route_y_min = "<<route_y_min
2505 <<", route_y_max = "<<route_y_max<<std::endl;*/
2507 s16 route_start_y_min = route_y_min;
2508 s16 route_start_y_max = route_y_max;
2510 // Start every 2nd dungeon from surface
2511 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2513 if(coming_from_surface)
2515 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2518 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2519 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2521 // Randomize starting position
2523 (float)(myrand()%ar.X)+0.5,
2524 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2525 (float)(myrand()%ar.Z)+0.5
2528 MapNode airnode(CONTENT_AIR);
2531 Generate some tunnel starting from orp
2534 for(u16 j=0; j<tunnel_routepoints; j++)
2537 s16 min_d = min_tunnel_diameter;
2538 s16 max_d = max_tunnel_diameter;
2539 s16 rs = myrand_range(min_d, max_d);
2544 maxlen = v3s16(rs*7,rs*7,rs*7);
2548 maxlen = v3s16(15, myrand_range(1, 20), 15);
2553 if(coming_from_surface && j < 3)
2556 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2557 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2558 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2564 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2565 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2566 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2573 else if(rp.X >= ar.X)
2575 if(rp.Y < route_y_min)
2577 else if(rp.Y >= route_y_max)
2578 rp.Y = route_y_max-1;
2581 else if(rp.Z >= ar.Z)
2585 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2587 v3f fp = orp + vec * f;
2588 v3s16 cp(fp.X, fp.Y, fp.Z);
2591 s16 d1 = d0 + rs - 1;
2592 for(s16 z0=d0; z0<=d1; z0++)
2594 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2595 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
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 - MYMAX(0, maxabsxz-rs/7);
2601 //s16 si2 = rs - abs(x0);
2602 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2608 /*if(isInArea(p, ar) == false)
2610 // Check only height
2611 if(y < 0 || y >= ar.Y)
2615 //assert(vmanip.m_area.contains(p));
2616 if(vmanip.m_area.contains(p) == false)
2618 dstream<<"WARNING: "<<__FUNCTION_NAME
2619 <<":"<<__LINE__<<": "
2620 <<"point not in area"
2625 // Just set it to air, it will be changed to
2627 u32 i = vmanip.m_area.index(p);
2628 vmanip.m_data[i] = airnode;
2630 if(bruise_surface == false)
2633 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2648 //TimeTaker timer1("ore veins");
2653 for(u32 jj=0; jj<relative_volume/1000; jj++)
2655 s16 max_vein_diameter = 3;
2657 // Allowed route area size in nodes
2659 sectorpos_base_size*MAP_BLOCKSIZE,
2660 h_blocks*MAP_BLOCKSIZE,
2661 sectorpos_base_size*MAP_BLOCKSIZE
2664 // Area starting point in nodes
2666 sectorpos_base.X*MAP_BLOCKSIZE,
2667 y_blocks_min*MAP_BLOCKSIZE,
2668 sectorpos_base.Y*MAP_BLOCKSIZE
2672 //(this should be more than the maximum radius of the tunnel)
2674 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2675 ar += v3s16(1,0,1) * more * 2;
2676 of -= v3s16(1,0,1) * more;
2678 // Randomize starting position
2680 (float)(myrand()%ar.X)+0.5,
2681 (float)(myrand()%ar.Y)+0.5,
2682 (float)(myrand()%ar.Z)+0.5
2685 // Randomize mineral
2688 mineral = MINERAL_COAL;
2690 mineral = MINERAL_IRON;
2693 Generate some vein starting from orp
2696 for(u16 j=0; j<2; j++)
2699 (float)(myrand()%ar.X)+0.5,
2700 (float)(myrand()%ar.Y)+0.5,
2701 (float)(myrand()%ar.Z)+0.5
2703 v3f vec = rp - orp;*/
2705 v3s16 maxlen(5, 5, 5);
2707 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2708 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2709 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2714 else if(rp.X >= ar.X)
2718 else if(rp.Y >= ar.Y)
2722 else if(rp.Z >= ar.Z)
2728 s16 max_d = max_vein_diameter;
2729 s16 rs = myrand_range(min_d, max_d);
2731 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2733 v3f fp = orp + vec * f;
2734 v3s16 cp(fp.X, fp.Y, fp.Z);
2736 s16 d1 = d0 + rs - 1;
2737 for(s16 z0=d0; z0<=d1; z0++)
2739 s16 si = rs - abs(z0);
2740 for(s16 x0=-si; x0<=si-1; x0++)
2742 s16 si2 = rs - abs(x0);
2743 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2745 // Don't put mineral to every place
2753 /*if(isInArea(p, ar) == false)
2755 // Check only height
2756 if(y < 0 || y >= ar.Y)
2760 assert(vmanip.m_area.contains(p));
2762 // Just set it to air, it will be changed to
2764 u32 i = vmanip.m_area.index(p);
2765 MapNode *n = &vmanip.m_data[i];
2766 if(n->d == CONTENT_STONE)
2781 //TimeTaker timer1("add mud");
2784 Add mud to the central chunk
2787 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2788 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2790 // Node position in 2d
2791 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2793 // Randomize mud amount
2794 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2795 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2796 m_seed+1, 3, 0.55));
2798 // Find ground level
2799 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2802 If topmost node is grass, change it to mud.
2803 It might be if it was flown to there from a neighboring
2804 chunk and then converted.
2807 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2808 MapNode *n = &vmanip.m_data[i];
2809 if(n->d == CONTENT_GRASS)
2818 v3s16 em = vmanip.m_area.getExtent();
2819 s16 y_start = surface_y+1;
2820 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2821 for(s16 y=y_start; y<=y_nodes_max; y++)
2823 if(mudcount >= mud_add_amount)
2826 MapNode &n = vmanip.m_data[i];
2830 vmanip.m_area.add_y(em, i, 1);
2839 TimeTaker timer1("flow mud");
2842 Flow mud away from steep edges
2845 // Limit area by 1 because mud is flown into neighbors.
2846 s16 mudflow_minpos = 0-max_spread_amount+1;
2847 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2849 // Iterate a few times
2850 for(s16 k=0; k<3; k++)
2853 for(s16 x=mudflow_minpos;
2856 for(s16 z=mudflow_minpos;
2860 // Invert coordinates every 2nd iteration
2863 x = mudflow_maxpos - (x-mudflow_minpos);
2864 z = mudflow_maxpos - (z-mudflow_minpos);
2867 // Node position in 2d
2868 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2870 v3s16 em = vmanip.m_area.getExtent();
2871 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2878 for(; y>=y_nodes_min; y--)
2880 n = &vmanip.m_data[i];
2881 //if(content_walkable(n->d))
2883 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2886 vmanip.m_area.add_y(em, i, -1);
2889 // Stop if out of area
2890 //if(vmanip.m_area.contains(i) == false)
2894 /*// If not mud, do nothing to it
2895 MapNode *n = &vmanip.m_data[i];
2896 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2900 Don't flow it if the stuff under it is not mud
2904 vmanip.m_area.add_y(em, i2, -1);
2905 // Cancel if out of area
2906 if(vmanip.m_area.contains(i2) == false)
2908 MapNode *n2 = &vmanip.m_data[i2];
2909 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2913 // Make it exactly mud
2916 /*s16 recurse_count = 0;
2920 v3s16(0,0,1), // back
2921 v3s16(1,0,0), // right
2922 v3s16(0,0,-1), // front
2923 v3s16(-1,0,0), // left
2926 // Theck that upper is air or doesn't exist.
2927 // Cancel dropping if upper keeps it in place
2929 vmanip.m_area.add_y(em, i3, 1);
2930 if(vmanip.m_area.contains(i3) == true
2931 && content_walkable(vmanip.m_data[i3].d) == true)
2938 for(u32 di=0; di<4; di++)
2940 v3s16 dirp = dirs4[di];
2943 vmanip.m_area.add_p(em, i2, dirp);
2944 // Fail if out of area
2945 if(vmanip.m_area.contains(i2) == false)
2947 // Check that side is air
2948 MapNode *n2 = &vmanip.m_data[i2];
2949 if(content_walkable(n2->d))
2951 // Check that under side is air
2952 vmanip.m_area.add_y(em, i2, -1);
2953 if(vmanip.m_area.contains(i2) == false)
2955 n2 = &vmanip.m_data[i2];
2956 if(content_walkable(n2->d))
2958 /*// Check that under that is air (need a drop of 2)
2959 vmanip.m_area.add_y(em, i2, -1);
2960 if(vmanip.m_area.contains(i2) == false)
2962 n2 = &vmanip.m_data[i2];
2963 if(content_walkable(n2->d))
2965 // Loop further down until not air
2967 vmanip.m_area.add_y(em, i2, -1);
2968 // Fail if out of area
2969 if(vmanip.m_area.contains(i2) == false)
2971 n2 = &vmanip.m_data[i2];
2972 }while(content_walkable(n2->d) == false);
2973 // Loop one up so that we're in air
2974 vmanip.m_area.add_y(em, i2, 1);
2975 n2 = &vmanip.m_data[i2];
2977 // Move mud to new place
2979 // Set old place to be air
2980 *n = MapNode(CONTENT_AIR);
2993 //TimeTaker timer1("add water");
2996 Add water to the central chunk (and a bit more)
2999 for(s16 x=0-max_spread_amount;
3000 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3002 for(s16 z=0-max_spread_amount;
3003 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3006 // Node position in 2d
3007 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3009 // Find ground level
3010 //s16 surface_y = find_ground_level(vmanip, p2d);
3013 If ground level is over water level, skip.
3014 NOTE: This leaves caves near water without water,
3015 which looks especially crappy when the nearby water
3016 won't start flowing either for some reason
3018 /*if(surface_y > WATER_LEVEL)
3025 v3s16 em = vmanip.m_area.getExtent();
3026 u8 light = LIGHT_MAX;
3027 // Start at global water surface level
3028 s16 y_start = WATER_LEVEL;
3029 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3030 MapNode *n = &vmanip.m_data[i];
3032 /*// Add first one to transforming liquid queue, if water
3033 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3035 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3036 m_transforming_liquid.push_back(p);
3039 for(s16 y=y_start; y>=y_nodes_min; y--)
3041 n = &vmanip.m_data[i];
3043 // Stop when there is no water and no air
3044 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3045 && n->d != CONTENT_WATER)
3047 /*// Add bottom one to transforming liquid queue
3048 vmanip.m_area.add_y(em, i, 1);
3049 n = &vmanip.m_data[i];
3050 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3052 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3053 m_transforming_liquid.push_back(p);
3059 // Make water only not in dungeons
3060 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3062 n->d = CONTENT_WATERSOURCE;
3063 //n->setLight(LIGHTBANK_DAY, light);
3065 // Add to transforming liquid queue (in case it'd
3067 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3068 m_transforming_liquid.push_back(p);
3072 vmanip.m_area.add_y(em, i, -1);
3085 //TimeTaker timer1("convert mud to sand");
3091 //s16 mud_add_amount = myrand_range(2, 4);
3092 //s16 mud_add_amount = 0;
3094 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3095 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3096 for(s16 x=0-max_spread_amount+1;
3097 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3099 for(s16 z=0-max_spread_amount+1;
3100 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3103 // Node position in 2d
3104 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3106 // Determine whether to have sand here
3107 double sandnoise = noise2d_perlin(
3108 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3109 m_seed+59420, 3, 0.50);
3111 bool have_sand = (sandnoise > -0.15);
3113 if(have_sand == false)
3116 // Find ground level
3117 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3119 if(surface_y > WATER_LEVEL + 2)
3123 v3s16 em = vmanip.m_area.getExtent();
3124 s16 y_start = surface_y;
3125 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3126 u32 not_sand_counter = 0;
3127 for(s16 y=y_start; y>=y_nodes_min; y--)
3129 MapNode *n = &vmanip.m_data[i];
3130 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3132 n->d = CONTENT_SAND;
3137 if(not_sand_counter > 3)
3141 vmanip.m_area.add_y(em, i, -1);
3150 //TimeTaker timer1("generate trees");
3156 // Divide area into parts
3158 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3159 double area = sidelen * sidelen;
3160 for(s16 x0=0; x0<div; x0++)
3161 for(s16 z0=0; z0<div; z0++)
3163 // Center position of part of division
3165 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3166 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3168 // Minimum edge of part of division
3170 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3171 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3173 // Maximum edge of part of division
3175 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3176 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3179 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3180 // Put trees in random places on part of division
3181 for(u32 i=0; i<tree_count; i++)
3183 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3184 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3185 s16 y = find_ground_level(vmanip, v2s16(x,z));
3186 // Don't make a tree under water level
3191 Trees grow only on mud and grass
3194 u32 i = vmanip.m_area.index(v3s16(p));
3195 MapNode *n = &vmanip.m_data[i];
3196 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3201 make_tree(vmanip, p);
3204 /*u32 tree_max = relative_area / 60;
3205 //u32 count = myrand_range(0, tree_max);
3206 for(u32 i=0; i<count; i++)
3208 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3209 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3210 x += sectorpos_base.X*MAP_BLOCKSIZE;
3211 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3212 s16 y = find_ground_level(vmanip, v2s16(x,z));
3213 // Don't make a tree under water level
3218 make_tree(vmanip, p);
3226 //TimeTaker timer1("grow grass");
3232 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3233 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3234 for(s16 x=0-max_spread_amount;
3235 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3237 for(s16 z=0-max_spread_amount;
3238 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3241 // Node position in 2d
3242 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3245 Find the lowest surface to which enough light ends up
3248 Basically just wait until not air and not leaves.
3252 v3s16 em = vmanip.m_area.getExtent();
3253 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3255 // Go to ground level
3256 for(y=y_nodes_max; y>=y_nodes_min; y--)
3258 MapNode &n = vmanip.m_data[i];
3259 if(n.d != CONTENT_AIR
3260 && n.d != CONTENT_LEAVES)
3262 vmanip.m_area.add_y(em, i, -1);
3264 if(y >= y_nodes_min)
3267 surface_y = y_nodes_min;
3270 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3271 MapNode *n = &vmanip.m_data[i];
3272 if(n->d == CONTENT_MUD)
3273 n->d = CONTENT_GRASS;
3279 Initial lighting (sunlight)
3282 core::map<v3s16, bool> light_sources;
3285 // 750ms @cs=8, can't optimize more
3286 TimeTaker timer1("initial lighting");
3290 Go through the edges and add all nodes that have light to light_sources
3294 for(s16 i=0; i<4; i++)
3296 for(s16 j=lighting_min_d;
3303 if(i == 0 || i == 1)
3305 x = (i==0) ? lighting_min_d : lighting_max_d;
3314 z = (i==0) ? lighting_min_d : lighting_max_d;
3321 // Node position in 2d
3322 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3325 v3s16 em = vmanip.m_area.getExtent();
3326 s16 y_start = y_nodes_max;
3327 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3328 for(s16 y=y_start; y>=y_nodes_min; y--)
3330 MapNode *n = &vmanip.m_data[i];
3331 if(n->getLight(LIGHTBANK_DAY) != 0)
3333 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3335 //NOTE: This is broken, at least the index has to
3344 Go through the edges and apply sunlight to them, not caring
3349 for(s16 i=0; i<4; i++)
3351 for(s16 j=lighting_min_d;
3358 if(i == 0 || i == 1)
3360 x = (i==0) ? lighting_min_d : lighting_max_d;
3369 z = (i==0) ? lighting_min_d : lighting_max_d;
3376 // Node position in 2d
3377 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3379 // Loop from top to down
3381 u8 light = LIGHT_SUN;
3382 v3s16 em = vmanip.m_area.getExtent();
3383 s16 y_start = y_nodes_max;
3384 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3385 for(s16 y=y_start; y>=y_nodes_min; y--)
3387 MapNode *n = &vmanip.m_data[i];
3388 if(light_propagates_content(n->d) == false)
3392 else if(light != LIGHT_SUN
3393 || sunlight_propagates_content(n->d) == false)
3399 n->setLight(LIGHTBANK_DAY, light);
3400 n->setLight(LIGHTBANK_NIGHT, 0);
3404 // Insert light source
3405 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3408 // Increment index by y
3409 vmanip.m_area.add_y(em, i, -1);
3415 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3416 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3417 /*for(s16 x=0-max_spread_amount+1;
3418 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3420 for(s16 z=0-max_spread_amount+1;
3421 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3425 This has to be 1 smaller than the actual area, because
3426 neighboring nodes are checked.
3428 for(s16 x=lighting_min_d+1;
3429 x<=lighting_max_d-1;
3431 for(s16 z=lighting_min_d+1;
3432 z<=lighting_max_d-1;
3435 // Node position in 2d
3436 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3439 Apply initial sunlight
3442 u8 light = LIGHT_SUN;
3443 bool add_to_sources = false;
3444 v3s16 em = vmanip.m_area.getExtent();
3445 s16 y_start = y_nodes_max;
3446 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3447 for(s16 y=y_start; y>=y_nodes_min; y--)
3449 MapNode *n = &vmanip.m_data[i];
3451 if(light_propagates_content(n->d) == false)
3455 else if(light != LIGHT_SUN
3456 || sunlight_propagates_content(n->d) == false)
3462 // This doesn't take much time
3463 if(add_to_sources == false)
3466 Check sides. If side is not air or water, start
3467 adding to light_sources.
3470 v3s16(0,0,1), // back
3471 v3s16(1,0,0), // right
3472 v3s16(0,0,-1), // front
3473 v3s16(-1,0,0), // left
3475 for(u32 di=0; di<4; di++)
3477 v3s16 dirp = dirs4[di];
3479 vmanip.m_area.add_p(em, i2, dirp);
3480 MapNode *n2 = &vmanip.m_data[i2];
3482 n2->d != CONTENT_AIR
3483 && n2->d != CONTENT_WATERSOURCE
3484 && n2->d != CONTENT_WATER
3486 add_to_sources = true;
3492 n->setLight(LIGHTBANK_DAY, light);
3493 n->setLight(LIGHTBANK_NIGHT, 0);
3495 // This doesn't take much time
3496 if(light != 0 && add_to_sources)
3498 // Insert light source
3499 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3502 // Increment index by y
3503 vmanip.m_area.add_y(em, i, -1);
3510 for(s16 x=lighting_min_d+1;
3511 x<=lighting_max_d-1;
3513 for(s16 z=lighting_min_d+1;
3514 z<=lighting_max_d-1;
3517 // Node position in 2d
3518 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3521 Apply initial sunlight
3524 u8 light = LIGHT_SUN;
3525 v3s16 em = vmanip.m_area.getExtent();
3526 s16 y_start = y_nodes_max;
3527 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3528 for(s16 y=y_start; y>=y_nodes_min; y--)
3530 MapNode *n = &vmanip.m_data[i];
3532 if(light_propagates_content(n->d) == false)
3536 else if(light != LIGHT_SUN
3537 || sunlight_propagates_content(n->d) == false)
3543 n->setLight(LIGHTBANK_DAY, light);
3544 n->setLight(LIGHTBANK_NIGHT, 0);
3546 // This doesn't take much time
3549 // Insert light source
3550 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3553 // Increment index by y
3554 vmanip.m_area.add_y(em, i, -1);
3562 // Spread light around
3564 TimeTaker timer("generateChunkRaw() spreadLight");
3565 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3572 timer_generate.stop();
3575 Blit generated stuff to map
3579 //TimeTaker timer("generateChunkRaw() blitBackAll");
3580 vmanip.blitBackAll(&changed_blocks);
3584 Update day/night difference cache of the MapBlocks
3587 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3588 i.atEnd() == false; i++)
3590 MapBlock *block = i.getNode()->getValue();
3591 block->updateDayNightDiff();
3597 Create chunk metadata
3600 for(s16 x=-1; x<=1; x++)
3601 for(s16 y=-1; y<=1; y++)
3603 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3604 // Add chunk meta information
3605 MapChunk *chunk = getChunk(chunkpos0);
3608 chunk = new MapChunk();
3609 m_chunks.insert(chunkpos0, chunk);
3611 //chunk->setIsVolatile(true);
3612 if(chunk->getGenLevel() > GENERATED_PARTLY)
3613 chunk->setGenLevel(GENERATED_PARTLY);
3617 Set central chunk non-volatile
3619 MapChunk *chunk = getChunk(chunkpos);
3622 //chunk->setIsVolatile(false);
3623 chunk->setGenLevel(GENERATED_FULLY);
3626 Save changed parts of map
3631 Return central chunk (which was requested)
3636 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3637 core::map<v3s16, MapBlock*> &changed_blocks)
3639 dstream<<"generateChunk(): Generating chunk "
3640 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3643 /*for(s16 x=-1; x<=1; x++)
3644 for(s16 y=-1; y<=1; y++)*/
3645 for(s16 x=-0; x<=0; x++)
3646 for(s16 y=-0; y<=0; y++)
3648 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3649 MapChunk *chunk = getChunk(chunkpos0);
3650 // Skip if already generated
3651 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3653 generateChunkRaw(chunkpos0, changed_blocks);
3656 assert(chunkNonVolatile(chunkpos1));
3658 MapChunk *chunk = getChunk(chunkpos1);
3662 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3664 DSTACK("%s: p2d=(%d,%d)",
3669 Check if it exists already in memory
3671 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3676 Try to load it from disk (with blocks)
3678 if(loadSectorFull(p2d) == true)
3680 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3683 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3684 throw InvalidPositionException("");
3690 Do not create over-limit
3692 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3693 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3694 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3695 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3696 throw InvalidPositionException("createSector(): pos. over limit");
3699 Generate blank sector
3702 sector = new ServerMapSector(this, p2d);
3704 // Sector position on map in nodes
3705 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3710 m_sectors.insert(p2d, sector);
3715 MapSector * ServerMap::emergeSector(v2s16 p2d,
3716 core::map<v3s16, MapBlock*> &changed_blocks)
3718 DSTACK("%s: p2d=(%d,%d)",
3725 v2s16 chunkpos = sector_to_chunk(p2d);
3726 /*bool chunk_nonvolatile = false;
3727 MapChunk *chunk = getChunk(chunkpos);
3728 if(chunk && chunk->getIsVolatile() == false)
3729 chunk_nonvolatile = true;*/
3730 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3733 If chunk is not fully generated, generate chunk
3735 if(chunk_nonvolatile == false)
3737 // Generate chunk and neighbors
3738 generateChunk(chunkpos, changed_blocks);
3742 Return sector if it exists now
3744 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3749 Try to load it from disk
3751 if(loadSectorFull(p2d) == true)
3753 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3756 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3757 throw InvalidPositionException("");
3763 generateChunk should have generated the sector
3767 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3768 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3772 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3774 return createSector(p2d);
3779 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3782 generateChunkRaw(chunkpos, changed_blocks, true);
3785 Return sector if it exists now
3787 sector = getSectorNoGenerateNoEx(p2d);
3791 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3799 //return generateSector();
3803 NOTE: This is not used for main map generation, only for blocks
3804 that are very high or low
3806 MapBlock * ServerMap::generateBlock(
3808 MapBlock *original_dummy,
3809 ServerMapSector *sector,
3810 core::map<v3s16, MapBlock*> &changed_blocks,
3811 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3814 DSTACK("%s: p=(%d,%d,%d)",
3818 /*dstream<<"generateBlock(): "
3819 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3822 MapBlock *block = original_dummy;
3824 v2s16 p2d(p.X, p.Z);
3828 Do not generate over-limit
3830 if(blockpos_over_limit(p))
3832 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3833 throw InvalidPositionException("generateBlock(): pos. over limit");
3837 If block doesn't exist, create one.
3838 If it exists, it is a dummy. In that case unDummify() it.
3840 NOTE: This already sets the map as the parent of the block
3844 block = sector->createBlankBlockNoInsert(block_y);
3848 // Remove the block so that nobody can get a half-generated one.
3849 sector->removeBlock(block);
3850 // Allocate the block to contain the generated data
3854 u8 water_material = CONTENT_WATERSOURCE;
3856 s32 lowest_ground_y = 32767;
3857 s32 highest_ground_y = -32768;
3859 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3860 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3862 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3866 if(surface_y < lowest_ground_y)
3867 lowest_ground_y = surface_y;
3868 if(surface_y > highest_ground_y)
3869 highest_ground_y = surface_y;
3871 s32 surface_depth = 2;
3873 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3875 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3880 NOTE: If there are some man-made structures above the
3881 newly created block, they won't be taken into account.
3883 if(real_y > surface_y)
3884 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3890 // If node is over heightmap y, it's air or water
3891 if(real_y > surface_y)
3893 // If under water level, it's water
3894 if(real_y < WATER_LEVEL)
3896 n.d = water_material;
3897 n.setLight(LIGHTBANK_DAY,
3898 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3900 Add to transforming liquid queue (in case it'd
3903 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3904 m_transforming_liquid.push_back(real_pos);
3910 // Else it's ground or dungeons (air)
3913 // If it's surface_depth under ground, it's stone
3914 if(real_y <= surface_y - surface_depth)
3916 n.d = CONTENT_STONE;
3920 // It is mud if it is under the first ground
3921 // level or under water
3922 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3928 n.d = CONTENT_GRASS;
3931 //n.d = CONTENT_MUD;
3933 /*// If under water level, it's mud
3934 if(real_y < WATER_LEVEL)
3936 // Only the topmost node is grass
3937 else if(real_y <= surface_y - 1)
3940 n.d = CONTENT_GRASS;*/
3944 block->setNode(v3s16(x0,y0,z0), n);
3949 Calculate some helper variables
3952 // Completely underground if the highest part of block is under lowest
3954 // This has to be very sure; it's probably one too strict now but
3955 // that's just better.
3956 bool completely_underground =
3957 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3959 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3961 bool mostly_underwater_surface = false;
3962 if(highest_ground_y < WATER_LEVEL
3963 && some_part_underground && !completely_underground)
3964 mostly_underwater_surface = true;
3967 Get local attributes
3970 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3972 float caves_amount = 0.5;
3977 NOTE: BEWARE: Too big amount of attribute points slows verything
3979 1 interpolation from 5000 points takes 2-3ms.
3981 //TimeTaker timer("generateBlock() local attribute retrieval");
3982 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3983 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3984 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3988 //dstream<<"generateBlock(): Done"<<std::endl;
3994 // Initialize temporary table
3995 const s32 ued = MAP_BLOCKSIZE;
3996 bool underground_emptiness[ued*ued*ued];
3997 for(s32 i=0; i<ued*ued*ued; i++)
3999 underground_emptiness[i] = 0;
4006 Initialize orp and ors. Try to find if some neighboring
4007 MapBlock has a tunnel ended in its side
4011 (float)(myrand()%ued)+0.5,
4012 (float)(myrand()%ued)+0.5,
4013 (float)(myrand()%ued)+0.5
4016 bool found_existing = false;
4022 for(s16 y=0; y<ued; y++)
4023 for(s16 x=0; x<ued; x++)
4025 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4026 if(getNode(ap).d == CONTENT_AIR)
4028 orp = v3f(x+1,y+1,0);
4029 found_existing = true;
4030 goto continue_generating;
4034 catch(InvalidPositionException &e){}
4040 for(s16 y=0; y<ued; y++)
4041 for(s16 x=0; x<ued; x++)
4043 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4044 if(getNode(ap).d == CONTENT_AIR)
4046 orp = v3f(x+1,y+1,ued-1);
4047 found_existing = true;
4048 goto continue_generating;
4052 catch(InvalidPositionException &e){}
4058 for(s16 y=0; y<ued; y++)
4059 for(s16 z=0; z<ued; z++)
4061 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4062 if(getNode(ap).d == CONTENT_AIR)
4064 orp = v3f(0,y+1,z+1);
4065 found_existing = true;
4066 goto continue_generating;
4070 catch(InvalidPositionException &e){}
4076 for(s16 y=0; y<ued; y++)
4077 for(s16 z=0; z<ued; z++)
4079 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4080 if(getNode(ap).d == CONTENT_AIR)
4082 orp = v3f(ued-1,y+1,z+1);
4083 found_existing = true;
4084 goto continue_generating;
4088 catch(InvalidPositionException &e){}
4094 for(s16 x=0; x<ued; x++)
4095 for(s16 z=0; z<ued; z++)
4097 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4098 if(getNode(ap).d == CONTENT_AIR)
4100 orp = v3f(x+1,0,z+1);
4101 found_existing = true;
4102 goto continue_generating;
4106 catch(InvalidPositionException &e){}
4112 for(s16 x=0; x<ued; x++)
4113 for(s16 z=0; z<ued; z++)
4115 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4116 if(getNode(ap).d == CONTENT_AIR)
4118 orp = v3f(x+1,ued-1,z+1);
4119 found_existing = true;
4120 goto continue_generating;
4124 catch(InvalidPositionException &e){}
4126 continue_generating:
4129 Choose whether to actually generate dungeon
4131 bool do_generate_dungeons = true;
4132 // Don't generate if no part is underground
4133 if(!some_part_underground)
4135 do_generate_dungeons = false;
4137 // Don't generate if mostly underwater surface
4138 /*else if(mostly_underwater_surface)
4140 do_generate_dungeons = false;
4142 // Partly underground = cave
4143 else if(!completely_underground)
4145 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4147 // Found existing dungeon underground
4148 else if(found_existing && completely_underground)
4150 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4152 // Underground and no dungeons found
4155 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4158 if(do_generate_dungeons)
4161 Generate some tunnel starting from orp and ors
4163 for(u16 i=0; i<3; i++)
4166 (float)(myrand()%ued)+0.5,
4167 (float)(myrand()%ued)+0.5,
4168 (float)(myrand()%ued)+0.5
4172 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4176 for(float f=0; f<1.0; f+=0.04)
4178 v3f fp = orp + vec * f;
4179 v3s16 cp(fp.X, fp.Y, fp.Z);
4181 s16 d1 = d0 + rs - 1;
4182 for(s16 z0=d0; z0<=d1; z0++)
4184 s16 si = rs - abs(z0);
4185 for(s16 x0=-si; x0<=si-1; x0++)
4187 s16 si2 = rs - abs(x0);
4188 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4194 if(isInArea(p, ued) == false)
4196 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4208 // Set to true if has caves.
4209 // Set when some non-air is changed to air when making caves.
4210 bool has_dungeons = false;
4213 Apply temporary cave data to block
4216 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4217 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4219 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4221 MapNode n = block->getNode(v3s16(x0,y0,z0));
4224 if(underground_emptiness[
4225 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4226 +ued*(y0*ued/MAP_BLOCKSIZE)
4227 +(x0*ued/MAP_BLOCKSIZE)])
4229 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4232 has_dungeons = true;
4238 block->setNode(v3s16(x0,y0,z0), n);
4243 This is used for guessing whether or not the block should
4244 receive sunlight from the top if the block above doesn't exist
4246 block->setIsUnderground(completely_underground);
4249 Force lighting update if some part of block is partly
4250 underground and has caves.
4252 /*if(some_part_underground && !completely_underground && has_dungeons)
4254 //dstream<<"Half-ground caves"<<std::endl;
4255 lighting_invalidated_blocks[block->getPos()] = block;
4258 // DEBUG: Always update lighting
4259 //lighting_invalidated_blocks[block->getPos()] = block;
4265 if(some_part_underground)
4267 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4272 for(s16 i=0; i<underground_level/4 + 1; i++)
4274 if(myrand()%50 == 0)
4277 (myrand()%(MAP_BLOCKSIZE-2))+1,
4278 (myrand()%(MAP_BLOCKSIZE-2))+1,
4279 (myrand()%(MAP_BLOCKSIZE-2))+1
4285 for(u16 i=0; i<27; i++)
4287 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4289 block->setNode(cp+g_27dirs[i], n);
4297 u16 coal_amount = 30;
4298 u16 coal_rareness = 60 / coal_amount;
4299 if(coal_rareness == 0)
4301 if(myrand()%coal_rareness == 0)
4303 u16 a = myrand() % 16;
4304 u16 amount = coal_amount * a*a*a / 1000;
4305 for(s16 i=0; i<amount; i++)
4308 (myrand()%(MAP_BLOCKSIZE-2))+1,
4309 (myrand()%(MAP_BLOCKSIZE-2))+1,
4310 (myrand()%(MAP_BLOCKSIZE-2))+1
4314 n.d = CONTENT_STONE;
4315 n.param = MINERAL_COAL;
4317 for(u16 i=0; i<27; i++)
4319 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4321 block->setNode(cp+g_27dirs[i], n);
4329 //TODO: change to iron_amount or whatever
4330 u16 iron_amount = 15;
4331 u16 iron_rareness = 60 / iron_amount;
4332 if(iron_rareness == 0)
4334 if(myrand()%iron_rareness == 0)
4336 u16 a = myrand() % 16;
4337 u16 amount = iron_amount * a*a*a / 1000;
4338 for(s16 i=0; i<amount; i++)
4341 (myrand()%(MAP_BLOCKSIZE-2))+1,
4342 (myrand()%(MAP_BLOCKSIZE-2))+1,
4343 (myrand()%(MAP_BLOCKSIZE-2))+1
4347 n.d = CONTENT_STONE;
4348 n.param = MINERAL_IRON;
4350 for(u16 i=0; i<27; i++)
4352 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4354 block->setNode(cp+g_27dirs[i], n);
4361 Create a few rats in empty blocks underground
4363 if(completely_underground)
4365 //for(u16 i=0; i<2; i++)
4368 (myrand()%(MAP_BLOCKSIZE-2))+1,
4369 (myrand()%(MAP_BLOCKSIZE-2))+1,
4370 (myrand()%(MAP_BLOCKSIZE-2))+1
4373 // Check that the place is empty
4374 //if(!is_ground_content(block->getNode(cp).d))
4377 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4378 block->addObject(obj);
4384 Add block to sector.
4386 sector->insertBlock(block);
4388 // Lighting is invalid after generation.
4389 block->setLightingExpired(true);
4396 <<"lighting_invalidated_blocks.size()"
4400 <<" "<<lighting_invalidated_blocks.size()
4401 <<", "<<has_dungeons
4402 <<", "<<completely_underground
4403 <<", "<<some_part_underground
4410 MapBlock * ServerMap::createBlock(v3s16 p)
4412 DSTACK("%s: p=(%d,%d,%d)",
4413 __FUNCTION_NAME, p.X, p.Y, p.Z);
4415 v2s16 p2d(p.X, p.Z);
4418 This will create or load a sector if not found in memory.
4419 If block exists on disk, it will be loaded.
4421 NOTE: On old save formats, this will be slow, as it generates
4422 lighting on blocks for them.
4424 ServerMapSector *sector;
4426 sector = (ServerMapSector*)createSector(p2d);
4427 assert(sector->getId() == MAPSECTOR_SERVER);
4429 /*catch(InvalidPositionException &e)
4431 dstream<<"createBlock: createSector() failed"<<std::endl;
4434 catch(std::exception &e)
4436 dstream<<"createBlock: createSector() failed: "
4437 <<e.what()<<std::endl;
4442 Try to get a block from the sector
4445 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4449 block = sector->createBlankBlock(block_y);
4453 MapBlock * ServerMap::emergeBlock(
4455 bool only_from_disk,
4456 core::map<v3s16, MapBlock*> &changed_blocks,
4457 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4460 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4462 p.X, p.Y, p.Z, only_from_disk);
4464 v2s16 p2d(p.X, p.Z);
4467 This will create or load a sector if not found in memory.
4468 If block exists on disk, it will be loaded.
4470 NOTE: On old save formats, this will be slow, as it generates
4471 lighting on blocks for them.
4473 ServerMapSector *sector;
4475 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4476 assert(sector->getId() == MAPSECTOR_SERVER);
4478 catch(std::exception &e)
4480 dstream<<"emergeBlock: emergeSector() failed: "
4481 <<e.what()<<std::endl;
4486 Try to get a block from the sector
4489 bool does_not_exist = false;
4490 bool lighting_expired = false;
4491 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4495 does_not_exist = true;
4497 else if(block->isDummy() == true)
4499 does_not_exist = true;
4501 else if(block->getLightingExpired())
4503 lighting_expired = true;
4508 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4513 If block was not found on disk and not going to generate a
4514 new one, make sure there is a dummy block in place.
4516 if(only_from_disk && (does_not_exist || lighting_expired))
4518 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4522 // Create dummy block
4523 block = new MapBlock(this, p, true);
4525 // Add block to sector
4526 sector->insertBlock(block);
4532 //dstream<<"Not found on disk, generating."<<std::endl;
4534 //TimeTaker("emergeBlock() generate");
4536 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4539 If the block doesn't exist, generate the block.
4543 block = generateBlock(p, block, sector, changed_blocks,
4544 lighting_invalidated_blocks);
4547 if(lighting_expired)
4549 lighting_invalidated_blocks.insert(p, block);
4553 Initially update sunlight
4557 core::map<v3s16, bool> light_sources;
4558 bool black_air_left = false;
4559 bool bottom_invalid =
4560 block->propagateSunlight(light_sources, true,
4561 &black_air_left, true);
4563 // If sunlight didn't reach everywhere and part of block is
4564 // above ground, lighting has to be properly updated
4565 //if(black_air_left && some_part_underground)
4568 lighting_invalidated_blocks[block->getPos()] = block;
4573 lighting_invalidated_blocks[block->getPos()] = block;
4580 void ServerMap::createDir(std::string path)
4582 if(fs::CreateDir(path) == false)
4584 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4585 <<"\""<<path<<"\""<<std::endl;
4586 throw BaseException("ServerMap failed to create directory");
4590 std::string ServerMap::getSectorSubDir(v2s16 pos)
4593 snprintf(cc, 9, "%.4x%.4x",
4594 (unsigned int)pos.X&0xffff,
4595 (unsigned int)pos.Y&0xffff);
4597 return std::string(cc);
4600 std::string ServerMap::getSectorDir(v2s16 pos)
4602 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4605 v2s16 ServerMap::getSectorPos(std::string dirname)
4607 if(dirname.size() != 8)
4608 throw InvalidFilenameException("Invalid sector directory name");
4610 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4612 throw InvalidFilenameException("Invalid sector directory name");
4613 v2s16 pos((s16)x, (s16)y);
4617 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4619 v2s16 p2d = getSectorPos(sectordir);
4621 if(blockfile.size() != 4){
4622 throw InvalidFilenameException("Invalid block filename");
4625 int r = sscanf(blockfile.c_str(), "%4x", &y);
4627 throw InvalidFilenameException("Invalid block filename");
4628 return v3s16(p2d.X, y, p2d.Y);
4631 void ServerMap::save(bool only_changed)
4633 DSTACK(__FUNCTION_NAME);
4634 if(m_map_saving_enabled == false)
4636 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4640 if(only_changed == false)
4641 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4647 u32 sector_meta_count = 0;
4648 u32 block_count = 0;
4651 JMutexAutoLock lock(m_sector_mutex);
4653 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4654 for(; i.atEnd() == false; i++)
4656 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4657 assert(sector->getId() == MAPSECTOR_SERVER);
4659 if(sector->differs_from_disk || only_changed == false)
4661 saveSectorMeta(sector);
4662 sector_meta_count++;
4664 core::list<MapBlock*> blocks;
4665 sector->getBlocks(blocks);
4666 core::list<MapBlock*>::Iterator j;
4667 for(j=blocks.begin(); j!=blocks.end(); j++)
4669 MapBlock *block = *j;
4670 if(block->getChangedFlag() || only_changed == false)
4675 /*dstream<<"ServerMap: Written block ("
4676 <<block->getPos().X<<","
4677 <<block->getPos().Y<<","
4678 <<block->getPos().Z<<")"
4687 Only print if something happened or saved whole map
4689 if(only_changed == false || sector_meta_count != 0
4690 || block_count != 0)
4692 dstream<<DTIME<<"ServerMap: Written: "
4693 <<sector_meta_count<<" sector metadata files, "
4694 <<block_count<<" block files"
4699 void ServerMap::loadAll()
4701 DSTACK(__FUNCTION_NAME);
4702 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4707 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4709 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4711 JMutexAutoLock lock(m_sector_mutex);
4714 s32 printed_counter = -100000;
4715 s32 count = list.size();
4717 std::vector<fs::DirListNode>::iterator i;
4718 for(i=list.begin(); i!=list.end(); i++)
4720 if(counter > printed_counter + 10)
4722 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4723 printed_counter = counter;
4727 MapSector *sector = NULL;
4729 // We want directories
4733 sector = loadSectorMeta(i->name);
4735 catch(InvalidFilenameException &e)
4737 // This catches unknown crap in directory
4740 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4741 (m_savedir+"/sectors/"+i->name);
4742 std::vector<fs::DirListNode>::iterator i2;
4743 for(i2=list2.begin(); i2!=list2.end(); i2++)
4749 loadBlock(i->name, i2->name, sector);
4751 catch(InvalidFilenameException &e)
4753 // This catches unknown crap in directory
4757 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4761 void ServerMap::saveMasterHeightmap()
4763 DSTACK(__FUNCTION_NAME);
4765 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4767 createDir(m_savedir);
4769 /*std::string fullpath = m_savedir + "/master_heightmap";
4770 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4771 if(o.good() == false)
4772 throw FileNotGoodException("Cannot open master heightmap");*/
4774 // Format used for writing
4775 //u8 version = SER_FMT_VER_HIGHEST;
4778 void ServerMap::loadMasterHeightmap()
4780 DSTACK(__FUNCTION_NAME);
4782 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4784 /*std::string fullpath = m_savedir + "/master_heightmap";
4785 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4786 if(is.good() == false)
4787 throw FileNotGoodException("Cannot open master heightmap");*/
4791 void ServerMap::saveMapMeta()
4793 DSTACK(__FUNCTION_NAME);
4795 dstream<<"INFO: ServerMap::saveMapMeta(): "
4796 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4799 createDir(m_savedir);
4801 std::string fullpath = m_savedir + "/map_meta.txt";
4802 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4803 if(os.good() == false)
4805 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4806 <<"could not open"<<fullpath<<std::endl;
4807 throw FileNotGoodException("Cannot open chunk metadata");
4811 params.setU64("seed", m_seed);
4812 params.setS32("chunksize", m_chunksize);
4814 params.writeLines(os);
4816 os<<"[end_of_params]\n";
4820 void ServerMap::loadMapMeta()
4822 DSTACK(__FUNCTION_NAME);
4824 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4827 std::string fullpath = m_savedir + "/map_meta.txt";
4828 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4829 if(is.good() == false)
4831 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4832 <<"could not open"<<fullpath<<std::endl;
4833 throw FileNotGoodException("Cannot open chunk metadata");
4841 throw SerializationError
4842 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4844 std::getline(is, line);
4845 std::string trimmedline = trim(line);
4846 if(trimmedline == "[end_of_params]")
4848 params.parseConfigLine(line);
4851 m_seed = params.getU64("seed");
4852 m_chunksize = params.getS32("chunksize");
4854 dstream<<"INFO: ServerMap::loadMapMeta(): "
4855 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4859 void ServerMap::saveChunkMeta()
4861 DSTACK(__FUNCTION_NAME);
4863 u32 count = m_chunks.size();
4865 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4866 <<count<<" chunks"<<std::endl;
4868 createDir(m_savedir);
4870 std::string fullpath = m_savedir + "/chunk_meta";
4871 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4872 if(os.good() == false)
4874 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4875 <<"could not open"<<fullpath<<std::endl;
4876 throw FileNotGoodException("Cannot open chunk metadata");
4882 os.write((char*)&version, 1);
4887 writeU32(buf, count);
4888 os.write((char*)buf, 4);
4890 for(core::map<v2s16, MapChunk*>::Iterator
4891 i = m_chunks.getIterator();
4892 i.atEnd()==false; i++)
4894 v2s16 p = i.getNode()->getKey();
4895 MapChunk *chunk = i.getNode()->getValue();
4898 os.write((char*)buf, 4);
4900 chunk->serialize(os, version);
4904 void ServerMap::loadChunkMeta()
4906 DSTACK(__FUNCTION_NAME);
4908 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
4911 std::string fullpath = m_savedir + "/chunk_meta";
4912 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4913 if(is.good() == false)
4915 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
4916 <<"could not open"<<fullpath<<std::endl;
4917 throw FileNotGoodException("Cannot open chunk metadata");
4923 is.read((char*)&version, 1);
4928 is.read((char*)buf, 4);
4929 u32 count = readU32(buf);
4931 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
4932 <<count<<" chunks"<<std::endl;
4934 for(u32 i=0; i<count; i++)
4937 MapChunk *chunk = new MapChunk();
4939 is.read((char*)buf, 4);
4942 chunk->deSerialize(is, version);
4943 m_chunks.insert(p, chunk);
4947 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4949 DSTACK(__FUNCTION_NAME);
4950 // Format used for writing
4951 u8 version = SER_FMT_VER_HIGHEST;
4953 v2s16 pos = sector->getPos();
4954 createDir(m_savedir);
4955 createDir(m_savedir+"/sectors");
4956 std::string dir = getSectorDir(pos);
4959 std::string fullpath = dir + "/meta";
4960 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4961 if(o.good() == false)
4962 throw FileNotGoodException("Cannot open sector metafile");
4964 sector->serialize(o, version);
4966 sector->differs_from_disk = false;
4969 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4971 DSTACK(__FUNCTION_NAME);
4973 v2s16 p2d = getSectorPos(dirname);
4974 std::string dir = m_savedir + "/sectors/" + dirname;
4976 std::string fullpath = dir + "/meta";
4977 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4978 if(is.good() == false)
4979 throw FileNotGoodException("Cannot open sector metafile");
4981 ServerMapSector *sector = ServerMapSector::deSerialize
4982 (is, this, p2d, m_sectors);
4984 sector->differs_from_disk = false;
4989 bool ServerMap::loadSectorFull(v2s16 p2d)
4991 DSTACK(__FUNCTION_NAME);
4992 std::string sectorsubdir = getSectorSubDir(p2d);
4994 MapSector *sector = NULL;
4996 JMutexAutoLock lock(m_sector_mutex);
4999 sector = loadSectorMeta(sectorsubdir);
5001 catch(InvalidFilenameException &e)
5005 catch(FileNotGoodException &e)
5009 catch(std::exception &e)
5017 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5018 (m_savedir+"/sectors/"+sectorsubdir);
5019 std::vector<fs::DirListNode>::iterator i2;
5020 for(i2=list2.begin(); i2!=list2.end(); i2++)
5026 loadBlock(sectorsubdir, i2->name, sector);
5028 catch(InvalidFilenameException &e)
5030 // This catches unknown crap in directory
5037 bool ServerMap::deFlushSector(v2s16 p2d)
5039 DSTACK(__FUNCTION_NAME);
5040 // See if it already exists in memory
5042 MapSector *sector = getSectorNoGenerate(p2d);
5045 catch(InvalidPositionException &e)
5048 Try to load the sector from disk.
5050 if(loadSectorFull(p2d) == true)
5059 void ServerMap::saveBlock(MapBlock *block)
5061 DSTACK(__FUNCTION_NAME);
5063 Dummy blocks are not written
5065 if(block->isDummy())
5067 /*v3s16 p = block->getPos();
5068 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5069 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5073 // Format used for writing
5074 u8 version = SER_FMT_VER_HIGHEST;
5076 v3s16 p3d = block->getPos();
5077 v2s16 p2d(p3d.X, p3d.Z);
5078 createDir(m_savedir);
5079 createDir(m_savedir+"/sectors");
5080 std::string dir = getSectorDir(p2d);
5083 // Block file is map/sectors/xxxxxxxx/xxxx
5085 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5086 std::string fullpath = dir + "/" + cc;
5087 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5088 if(o.good() == false)
5089 throw FileNotGoodException("Cannot open block data");
5092 [0] u8 serialization version
5095 o.write((char*)&version, 1);
5097 block->serialize(o, version);
5100 Versions up from 9 have block objects.
5104 block->serializeObjects(o, version);
5107 // We just wrote it to the disk
5108 block->resetChangedFlag();
5111 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5113 DSTACK(__FUNCTION_NAME);
5117 // Block file is map/sectors/xxxxxxxx/xxxx
5118 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5119 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5120 if(is.good() == false)
5121 throw FileNotGoodException("Cannot open block file");
5123 v3s16 p3d = getBlockPos(sectordir, blockfile);
5124 v2s16 p2d(p3d.X, p3d.Z);
5126 assert(sector->getPos() == p2d);
5128 u8 version = SER_FMT_VER_INVALID;
5129 is.read((char*)&version, 1);
5131 /*u32 block_size = MapBlock::serializedLength(version);
5132 SharedBuffer<u8> data(block_size);
5133 is.read((char*)*data, block_size);*/
5135 // This will always return a sector because we're the server
5136 //MapSector *sector = emergeSector(p2d);
5138 MapBlock *block = NULL;
5139 bool created_new = false;
5141 block = sector->getBlockNoCreate(p3d.Y);
5143 catch(InvalidPositionException &e)
5145 block = sector->createBlankBlockNoInsert(p3d.Y);
5149 // deserialize block data
5150 block->deSerialize(is, version);
5153 Versions up from 9 have block objects.
5157 block->updateObjects(is, version, NULL, 0);
5161 sector->insertBlock(block);
5164 Convert old formats to new and save
5167 // Save old format blocks in new format
5168 if(version < SER_FMT_VER_HIGHEST)
5173 // We just loaded it from the disk, so it's up-to-date.
5174 block->resetChangedFlag();
5177 catch(SerializationError &e)
5179 dstream<<"WARNING: Invalid block data on disk "
5180 "(SerializationError). Ignoring."
5185 // Gets from master heightmap
5186 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5188 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5189 //assert(m_heightmap != NULL);
5197 /*corners[0] = m_heightmap->getGroundHeight
5198 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5199 corners[1] = m_heightmap->getGroundHeight
5200 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5201 corners[2] = m_heightmap->getGroundHeight
5202 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5203 corners[3] = m_heightmap->getGroundHeight
5204 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
5207 void ServerMap::PrintInfo(std::ostream &out)
5218 ClientMap::ClientMap(
5220 MapDrawControl &control,
5221 scene::ISceneNode* parent,
5222 scene::ISceneManager* mgr,
5226 scene::ISceneNode(parent, mgr, id),
5230 //mesh_mutex.Init();
5232 /*m_box = core::aabbox3d<f32>(0,0,0,
5233 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5234 /*m_box = core::aabbox3d<f32>(0,0,0,
5235 map->getSizeNodes().X * BS,
5236 map->getSizeNodes().Y * BS,
5237 map->getSizeNodes().Z * BS);*/
5238 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5239 BS*1000000,BS*1000000,BS*1000000);
5241 //setPosition(v3f(BS,BS,BS));
5244 ClientMap::~ClientMap()
5246 /*JMutexAutoLock lock(mesh_mutex);
5255 MapSector * ClientMap::emergeSector(v2s16 p2d)
5257 DSTACK(__FUNCTION_NAME);
5258 // Check that it doesn't exist already
5260 return getSectorNoGenerate(p2d);
5262 catch(InvalidPositionException &e)
5266 // Create a sector with no heightmaps
5267 ClientMapSector *sector = new ClientMapSector(this, p2d);
5270 JMutexAutoLock lock(m_sector_mutex);
5271 m_sectors.insert(p2d, sector);
5277 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5279 DSTACK(__FUNCTION_NAME);
5280 ClientMapSector *sector = NULL;
5282 JMutexAutoLock lock(m_sector_mutex);
5284 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5288 sector = (ClientMapSector*)n->getValue();
5289 assert(sector->getId() == MAPSECTOR_CLIENT);
5293 sector = new ClientMapSector(this, p2d);
5295 JMutexAutoLock lock(m_sector_mutex);
5296 m_sectors.insert(p2d, sector);
5300 sector->deSerialize(is);
5303 void ClientMap::OnRegisterSceneNode()
5307 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5308 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5311 ISceneNode::OnRegisterSceneNode();
5314 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5316 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5317 DSTACK(__FUNCTION_NAME);
5319 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5322 Get time for measuring timeout.
5324 Measuring time is very useful for long delays when the
5325 machine is swapping a lot.
5327 int time1 = time(0);
5329 u32 daynight_ratio = m_client->getDayNightRatio();
5331 m_camera_mutex.Lock();
5332 v3f camera_position = m_camera_position;
5333 v3f camera_direction = m_camera_direction;
5334 m_camera_mutex.Unlock();
5337 Get all blocks and draw all visible ones
5340 v3s16 cam_pos_nodes(
5341 camera_position.X / BS,
5342 camera_position.Y / BS,
5343 camera_position.Z / BS);
5345 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5347 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5348 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5350 // Take a fair amount as we will be dropping more out later
5352 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5353 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5354 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5356 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5357 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5358 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5360 u32 vertex_count = 0;
5362 // For limiting number of mesh updates per frame
5363 u32 mesh_update_count = 0;
5365 u32 blocks_would_have_drawn = 0;
5366 u32 blocks_drawn = 0;
5368 //NOTE: The sectors map should be locked but we're not doing it
5369 // because it'd cause too much delays
5371 int timecheck_counter = 0;
5372 core::map<v2s16, MapSector*>::Iterator si;
5373 si = m_sectors.getIterator();
5374 for(; si.atEnd() == false; si++)
5377 timecheck_counter++;
5378 if(timecheck_counter > 50)
5380 timecheck_counter = 0;
5381 int time2 = time(0);
5382 if(time2 > time1 + 4)
5384 dstream<<"ClientMap::renderMap(): "
5385 "Rendering takes ages, returning."
5392 MapSector *sector = si.getNode()->getValue();
5393 v2s16 sp = sector->getPos();
5395 if(m_control.range_all == false)
5397 if(sp.X < p_blocks_min.X
5398 || sp.X > p_blocks_max.X
5399 || sp.Y < p_blocks_min.Z
5400 || sp.Y > p_blocks_max.Z)
5404 core::list< MapBlock * > sectorblocks;
5405 sector->getBlocks(sectorblocks);
5411 core::list< MapBlock * >::Iterator i;
5412 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5414 MapBlock *block = *i;
5417 Compare block position to camera position, skip
5418 if not seen on display
5421 float range = 100000 * BS;
5422 if(m_control.range_all == false)
5423 range = m_control.wanted_range * BS;
5426 if(isBlockInSight(block->getPos(), camera_position,
5427 camera_direction, range, &d) == false)
5437 bool mesh_expired = false;
5440 JMutexAutoLock lock(block->mesh_mutex);
5442 mesh_expired = block->getMeshExpired();
5444 // Mesh has not been expired and there is no mesh:
5445 // block has no content
5446 if(block->mesh == NULL && mesh_expired == false)
5450 f32 faraway = BS*50;
5451 //f32 faraway = m_control.wanted_range * BS;
5454 This has to be done with the mesh_mutex unlocked
5456 // Pretty random but this should work somewhat nicely
5457 if(mesh_expired && (
5458 (mesh_update_count < 3
5459 && (d < faraway || mesh_update_count < 2)
5462 (m_control.range_all && mesh_update_count < 20)
5465 /*if(mesh_expired && mesh_update_count < 6
5466 && (d < faraway || mesh_update_count < 3))*/
5468 mesh_update_count++;
5470 // Mesh has been expired: generate new mesh
5471 //block->updateMeshes(daynight_i);
5472 block->updateMesh(daynight_ratio);
5474 mesh_expired = false;
5478 Don't draw an expired mesh that is far away
5480 /*if(mesh_expired && d >= faraway)
5483 // Instead, delete it
5484 JMutexAutoLock lock(block->mesh_mutex);
5487 block->mesh->drop();
5490 // And continue to next block
5495 Draw the faces of the block
5498 JMutexAutoLock lock(block->mesh_mutex);
5500 scene::SMesh *mesh = block->mesh;
5505 blocks_would_have_drawn++;
5506 if(blocks_drawn >= m_control.wanted_max_blocks
5507 && m_control.range_all == false
5508 && d > m_control.wanted_min_range * BS)
5512 u32 c = mesh->getMeshBufferCount();
5514 for(u32 i=0; i<c; i++)
5516 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5517 const video::SMaterial& material = buf->getMaterial();
5518 video::IMaterialRenderer* rnd =
5519 driver->getMaterialRenderer(material.MaterialType);
5520 bool transparent = (rnd && rnd->isTransparent());
5521 // Render transparent on transparent pass and likewise.
5522 if(transparent == is_transparent_pass)
5525 This *shouldn't* hurt too much because Irrlicht
5526 doesn't change opengl textures if the old
5527 material is set again.
5529 driver->setMaterial(buf->getMaterial());
5530 driver->drawMeshBuffer(buf);
5531 vertex_count += buf->getVertexCount();
5535 } // foreach sectorblocks
5538 m_control.blocks_drawn = blocks_drawn;
5539 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5541 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5542 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5545 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5546 core::map<v3s16, MapBlock*> *affected_blocks)
5548 bool changed = false;
5550 Add it to all blocks touching it
5553 v3s16(0,0,0), // this
5554 v3s16(0,0,1), // back
5555 v3s16(0,1,0), // top
5556 v3s16(1,0,0), // right
5557 v3s16(0,0,-1), // front
5558 v3s16(0,-1,0), // bottom
5559 v3s16(-1,0,0), // left
5561 for(u16 i=0; i<7; i++)
5563 v3s16 p2 = p + dirs[i];
5564 // Block position of neighbor (or requested) node
5565 v3s16 blockpos = getNodeBlockPos(p2);
5566 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5567 if(blockref == NULL)
5569 // Relative position of requested node
5570 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5571 if(blockref->setTempMod(relpos, mod))
5576 if(changed && affected_blocks!=NULL)
5578 for(u16 i=0; i<7; i++)
5580 v3s16 p2 = p + dirs[i];
5581 // Block position of neighbor (or requested) node
5582 v3s16 blockpos = getNodeBlockPos(p2);
5583 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5584 if(blockref == NULL)
5586 affected_blocks->insert(blockpos, blockref);
5592 bool ClientMap::clearTempMod(v3s16 p,
5593 core::map<v3s16, MapBlock*> *affected_blocks)
5595 bool changed = false;
5597 v3s16(0,0,0), // this
5598 v3s16(0,0,1), // back
5599 v3s16(0,1,0), // top
5600 v3s16(1,0,0), // right
5601 v3s16(0,0,-1), // front
5602 v3s16(0,-1,0), // bottom
5603 v3s16(-1,0,0), // left
5605 for(u16 i=0; i<7; i++)
5607 v3s16 p2 = p + dirs[i];
5608 // Block position of neighbor (or requested) node
5609 v3s16 blockpos = getNodeBlockPos(p2);
5610 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5611 if(blockref == NULL)
5613 // Relative position of requested node
5614 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5615 if(blockref->clearTempMod(relpos))
5620 if(changed && affected_blocks!=NULL)
5622 for(u16 i=0; i<7; i++)
5624 v3s16 p2 = p + dirs[i];
5625 // Block position of neighbor (or requested) node
5626 v3s16 blockpos = getNodeBlockPos(p2);
5627 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5628 if(blockref == NULL)
5630 affected_blocks->insert(blockpos, blockref);
5636 void ClientMap::PrintInfo(std::ostream &out)
5647 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5652 MapVoxelManipulator::~MapVoxelManipulator()
5654 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5658 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5660 TimeTaker timer1("emerge", &emerge_time);
5662 // Units of these are MapBlocks
5663 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5664 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5666 VoxelArea block_area_nodes
5667 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5669 addArea(block_area_nodes);
5671 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5672 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5673 for(s32 x=p_min.X; x<=p_max.X; x++)
5676 core::map<v3s16, bool>::Node *n;
5677 n = m_loaded_blocks.find(p);
5681 bool block_data_inexistent = false;
5684 TimeTaker timer1("emerge load", &emerge_load_time);
5686 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5687 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5690 dstream<<std::endl;*/
5692 MapBlock *block = m_map->getBlockNoCreate(p);
5693 if(block->isDummy())
5694 block_data_inexistent = true;
5696 block->copyTo(*this);
5698 catch(InvalidPositionException &e)
5700 block_data_inexistent = true;
5703 if(block_data_inexistent)
5705 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5706 // Fill with VOXELFLAG_INEXISTENT
5707 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5708 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5710 s32 i = m_area.index(a.MinEdge.X,y,z);
5711 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5715 m_loaded_blocks.insert(p, !block_data_inexistent);
5718 //dstream<<"emerge done"<<std::endl;
5722 SUGG: Add an option to only update eg. water and air nodes.
5723 This will make it interfere less with important stuff if
5726 void MapVoxelManipulator::blitBack
5727 (core::map<v3s16, MapBlock*> & modified_blocks)
5729 if(m_area.getExtent() == v3s16(0,0,0))
5732 //TimeTaker timer1("blitBack");
5734 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5735 <<m_loaded_blocks.size()<<std::endl;*/
5738 Initialize block cache
5740 v3s16 blockpos_last;
5741 MapBlock *block = NULL;
5742 bool block_checked_in_modified = false;
5744 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5745 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5746 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5750 u8 f = m_flags[m_area.index(p)];
5751 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5754 MapNode &n = m_data[m_area.index(p)];
5756 v3s16 blockpos = getNodeBlockPos(p);
5761 if(block == NULL || blockpos != blockpos_last){
5762 block = m_map->getBlockNoCreate(blockpos);
5763 blockpos_last = blockpos;
5764 block_checked_in_modified = false;
5767 // Calculate relative position in block
5768 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5770 // Don't continue if nothing has changed here
5771 if(block->getNode(relpos) == n)
5774 //m_map->setNode(m_area.MinEdge + p, n);
5775 block->setNode(relpos, n);
5778 Make sure block is in modified_blocks
5780 if(block_checked_in_modified == false)
5782 modified_blocks[blockpos] = block;
5783 block_checked_in_modified = true;
5786 catch(InvalidPositionException &e)
5792 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5793 MapVoxelManipulator(map)
5797 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5801 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5803 // Just create the area so that it can be pointed to
5804 VoxelManipulator::emerge(a, caller_id);
5807 void ManualMapVoxelManipulator::initialEmerge(
5808 v3s16 blockpos_min, v3s16 blockpos_max)
5810 TimeTaker timer1("initialEmerge", &emerge_time);
5812 // Units of these are MapBlocks
5813 v3s16 p_min = blockpos_min;
5814 v3s16 p_max = blockpos_max;
5816 VoxelArea block_area_nodes
5817 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5819 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5822 dstream<<"initialEmerge: area: ";
5823 block_area_nodes.print(dstream);
5824 dstream<<" ("<<size_MB<<"MB)";
5828 addArea(block_area_nodes);
5830 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5831 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5832 for(s32 x=p_min.X; x<=p_max.X; x++)
5835 core::map<v3s16, bool>::Node *n;
5836 n = m_loaded_blocks.find(p);
5840 bool block_data_inexistent = false;
5843 TimeTaker timer1("emerge load", &emerge_load_time);
5845 MapBlock *block = m_map->getBlockNoCreate(p);
5846 if(block->isDummy())
5847 block_data_inexistent = true;
5849 block->copyTo(*this);
5851 catch(InvalidPositionException &e)
5853 block_data_inexistent = true;
5856 if(block_data_inexistent)
5859 Mark area inexistent
5861 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5862 // Fill with VOXELFLAG_INEXISTENT
5863 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5864 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5866 s32 i = m_area.index(a.MinEdge.X,y,z);
5867 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5871 m_loaded_blocks.insert(p, !block_data_inexistent);
5875 void ManualMapVoxelManipulator::blitBackAll(
5876 core::map<v3s16, MapBlock*> * modified_blocks)
5878 if(m_area.getExtent() == v3s16(0,0,0))
5882 Copy data of all blocks
5884 for(core::map<v3s16, bool>::Iterator
5885 i = m_loaded_blocks.getIterator();
5886 i.atEnd() == false; i++)
5888 bool existed = i.getNode()->getValue();
5889 if(existed == false)
5891 v3s16 p = i.getNode()->getKey();
5892 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5895 dstream<<"WARNING: "<<__FUNCTION_NAME
5896 <<": got NULL block "
5897 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5902 block->copyFrom(*this);
5905 modified_blocks->insert(p, block);