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/500., 0.5+(float)p.Y/500.,
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.2;
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;
4482 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4484 <<"You could try to delete it."<<std::endl;
4489 Try to get a block from the sector
4492 bool does_not_exist = false;
4493 bool lighting_expired = false;
4494 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4498 does_not_exist = true;
4500 else if(block->isDummy() == true)
4502 does_not_exist = true;
4504 else if(block->getLightingExpired())
4506 lighting_expired = true;
4511 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4516 If block was not found on disk and not going to generate a
4517 new one, make sure there is a dummy block in place.
4519 if(only_from_disk && (does_not_exist || lighting_expired))
4521 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4525 // Create dummy block
4526 block = new MapBlock(this, p, true);
4528 // Add block to sector
4529 sector->insertBlock(block);
4535 //dstream<<"Not found on disk, generating."<<std::endl;
4537 //TimeTaker("emergeBlock() generate");
4539 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4542 If the block doesn't exist, generate the block.
4546 block = generateBlock(p, block, sector, changed_blocks,
4547 lighting_invalidated_blocks);
4550 if(lighting_expired)
4552 lighting_invalidated_blocks.insert(p, block);
4556 Initially update sunlight
4560 core::map<v3s16, bool> light_sources;
4561 bool black_air_left = false;
4562 bool bottom_invalid =
4563 block->propagateSunlight(light_sources, true,
4564 &black_air_left, true);
4566 // If sunlight didn't reach everywhere and part of block is
4567 // above ground, lighting has to be properly updated
4568 //if(black_air_left && some_part_underground)
4571 lighting_invalidated_blocks[block->getPos()] = block;
4576 lighting_invalidated_blocks[block->getPos()] = block;
4583 void ServerMap::createDir(std::string path)
4585 if(fs::CreateDir(path) == false)
4587 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4588 <<"\""<<path<<"\""<<std::endl;
4589 throw BaseException("ServerMap failed to create directory");
4593 std::string ServerMap::getSectorSubDir(v2s16 pos)
4596 snprintf(cc, 9, "%.4x%.4x",
4597 (unsigned int)pos.X&0xffff,
4598 (unsigned int)pos.Y&0xffff);
4600 return std::string(cc);
4603 std::string ServerMap::getSectorDir(v2s16 pos)
4605 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4608 v2s16 ServerMap::getSectorPos(std::string dirname)
4610 if(dirname.size() != 8)
4611 throw InvalidFilenameException("Invalid sector directory name");
4613 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4615 throw InvalidFilenameException("Invalid sector directory name");
4616 v2s16 pos((s16)x, (s16)y);
4620 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4622 v2s16 p2d = getSectorPos(sectordir);
4624 if(blockfile.size() != 4){
4625 throw InvalidFilenameException("Invalid block filename");
4628 int r = sscanf(blockfile.c_str(), "%4x", &y);
4630 throw InvalidFilenameException("Invalid block filename");
4631 return v3s16(p2d.X, y, p2d.Y);
4634 void ServerMap::save(bool only_changed)
4636 DSTACK(__FUNCTION_NAME);
4637 if(m_map_saving_enabled == false)
4639 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4643 if(only_changed == false)
4644 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4650 u32 sector_meta_count = 0;
4651 u32 block_count = 0;
4654 JMutexAutoLock lock(m_sector_mutex);
4656 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4657 for(; i.atEnd() == false; i++)
4659 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4660 assert(sector->getId() == MAPSECTOR_SERVER);
4662 if(sector->differs_from_disk || only_changed == false)
4664 saveSectorMeta(sector);
4665 sector_meta_count++;
4667 core::list<MapBlock*> blocks;
4668 sector->getBlocks(blocks);
4669 core::list<MapBlock*>::Iterator j;
4670 for(j=blocks.begin(); j!=blocks.end(); j++)
4672 MapBlock *block = *j;
4673 if(block->getChangedFlag() || only_changed == false)
4678 /*dstream<<"ServerMap: Written block ("
4679 <<block->getPos().X<<","
4680 <<block->getPos().Y<<","
4681 <<block->getPos().Z<<")"
4690 Only print if something happened or saved whole map
4692 if(only_changed == false || sector_meta_count != 0
4693 || block_count != 0)
4695 dstream<<DTIME<<"ServerMap: Written: "
4696 <<sector_meta_count<<" sector metadata files, "
4697 <<block_count<<" block files"
4702 void ServerMap::loadAll()
4704 DSTACK(__FUNCTION_NAME);
4705 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4710 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4712 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4714 JMutexAutoLock lock(m_sector_mutex);
4717 s32 printed_counter = -100000;
4718 s32 count = list.size();
4720 std::vector<fs::DirListNode>::iterator i;
4721 for(i=list.begin(); i!=list.end(); i++)
4723 if(counter > printed_counter + 10)
4725 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4726 printed_counter = counter;
4730 MapSector *sector = NULL;
4732 // We want directories
4736 sector = loadSectorMeta(i->name);
4738 catch(InvalidFilenameException &e)
4740 // This catches unknown crap in directory
4743 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4744 (m_savedir+"/sectors/"+i->name);
4745 std::vector<fs::DirListNode>::iterator i2;
4746 for(i2=list2.begin(); i2!=list2.end(); i2++)
4752 loadBlock(i->name, i2->name, sector);
4754 catch(InvalidFilenameException &e)
4756 // This catches unknown crap in directory
4760 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4764 void ServerMap::saveMasterHeightmap()
4766 DSTACK(__FUNCTION_NAME);
4768 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4770 createDir(m_savedir);
4772 /*std::string fullpath = m_savedir + "/master_heightmap";
4773 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4774 if(o.good() == false)
4775 throw FileNotGoodException("Cannot open master heightmap");*/
4777 // Format used for writing
4778 //u8 version = SER_FMT_VER_HIGHEST;
4781 void ServerMap::loadMasterHeightmap()
4783 DSTACK(__FUNCTION_NAME);
4785 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4787 /*std::string fullpath = m_savedir + "/master_heightmap";
4788 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4789 if(is.good() == false)
4790 throw FileNotGoodException("Cannot open master heightmap");*/
4794 void ServerMap::saveMapMeta()
4796 DSTACK(__FUNCTION_NAME);
4798 dstream<<"INFO: ServerMap::saveMapMeta(): "
4799 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4802 createDir(m_savedir);
4804 std::string fullpath = m_savedir + "/map_meta.txt";
4805 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4806 if(os.good() == false)
4808 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4809 <<"could not open"<<fullpath<<std::endl;
4810 throw FileNotGoodException("Cannot open chunk metadata");
4814 params.setU64("seed", m_seed);
4815 params.setS32("chunksize", m_chunksize);
4817 params.writeLines(os);
4819 os<<"[end_of_params]\n";
4823 void ServerMap::loadMapMeta()
4825 DSTACK(__FUNCTION_NAME);
4827 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4830 std::string fullpath = m_savedir + "/map_meta.txt";
4831 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4832 if(is.good() == false)
4834 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4835 <<"could not open"<<fullpath<<std::endl;
4836 throw FileNotGoodException("Cannot open chunk metadata");
4844 throw SerializationError
4845 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4847 std::getline(is, line);
4848 std::string trimmedline = trim(line);
4849 if(trimmedline == "[end_of_params]")
4851 params.parseConfigLine(line);
4854 m_seed = params.getU64("seed");
4855 m_chunksize = params.getS32("chunksize");
4857 dstream<<"INFO: ServerMap::loadMapMeta(): "
4858 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4862 void ServerMap::saveChunkMeta()
4864 DSTACK(__FUNCTION_NAME);
4866 u32 count = m_chunks.size();
4868 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4869 <<count<<" chunks"<<std::endl;
4871 createDir(m_savedir);
4873 std::string fullpath = m_savedir + "/chunk_meta";
4874 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4875 if(os.good() == false)
4877 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4878 <<"could not open"<<fullpath<<std::endl;
4879 throw FileNotGoodException("Cannot open chunk metadata");
4885 os.write((char*)&version, 1);
4890 writeU32(buf, count);
4891 os.write((char*)buf, 4);
4893 for(core::map<v2s16, MapChunk*>::Iterator
4894 i = m_chunks.getIterator();
4895 i.atEnd()==false; i++)
4897 v2s16 p = i.getNode()->getKey();
4898 MapChunk *chunk = i.getNode()->getValue();
4901 os.write((char*)buf, 4);
4903 chunk->serialize(os, version);
4907 void ServerMap::loadChunkMeta()
4909 DSTACK(__FUNCTION_NAME);
4911 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
4914 std::string fullpath = m_savedir + "/chunk_meta";
4915 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4916 if(is.good() == false)
4918 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
4919 <<"could not open"<<fullpath<<std::endl;
4920 throw FileNotGoodException("Cannot open chunk metadata");
4926 is.read((char*)&version, 1);
4931 is.read((char*)buf, 4);
4932 u32 count = readU32(buf);
4934 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
4935 <<count<<" chunks"<<std::endl;
4937 for(u32 i=0; i<count; i++)
4940 MapChunk *chunk = new MapChunk();
4942 is.read((char*)buf, 4);
4945 chunk->deSerialize(is, version);
4946 m_chunks.insert(p, chunk);
4950 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4952 DSTACK(__FUNCTION_NAME);
4953 // Format used for writing
4954 u8 version = SER_FMT_VER_HIGHEST;
4956 v2s16 pos = sector->getPos();
4957 createDir(m_savedir);
4958 createDir(m_savedir+"/sectors");
4959 std::string dir = getSectorDir(pos);
4962 std::string fullpath = dir + "/meta";
4963 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4964 if(o.good() == false)
4965 throw FileNotGoodException("Cannot open sector metafile");
4967 sector->serialize(o, version);
4969 sector->differs_from_disk = false;
4972 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4974 DSTACK(__FUNCTION_NAME);
4976 v2s16 p2d = getSectorPos(dirname);
4977 std::string dir = m_savedir + "/sectors/" + dirname;
4979 std::string fullpath = dir + "/meta";
4980 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4981 if(is.good() == false)
4982 throw FileNotGoodException("Cannot open sector metafile");
4984 ServerMapSector *sector = ServerMapSector::deSerialize
4985 (is, this, p2d, m_sectors);
4987 sector->differs_from_disk = false;
4992 bool ServerMap::loadSectorFull(v2s16 p2d)
4994 DSTACK(__FUNCTION_NAME);
4995 std::string sectorsubdir = getSectorSubDir(p2d);
4997 MapSector *sector = NULL;
4999 JMutexAutoLock lock(m_sector_mutex);
5002 sector = loadSectorMeta(sectorsubdir);
5004 catch(InvalidFilenameException &e)
5008 catch(FileNotGoodException &e)
5012 catch(std::exception &e)
5020 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5021 (m_savedir+"/sectors/"+sectorsubdir);
5022 std::vector<fs::DirListNode>::iterator i2;
5023 for(i2=list2.begin(); i2!=list2.end(); i2++)
5029 loadBlock(sectorsubdir, i2->name, sector);
5031 catch(InvalidFilenameException &e)
5033 // This catches unknown crap in directory
5040 bool ServerMap::deFlushSector(v2s16 p2d)
5042 DSTACK(__FUNCTION_NAME);
5043 // See if it already exists in memory
5045 MapSector *sector = getSectorNoGenerate(p2d);
5048 catch(InvalidPositionException &e)
5051 Try to load the sector from disk.
5053 if(loadSectorFull(p2d) == true)
5062 void ServerMap::saveBlock(MapBlock *block)
5064 DSTACK(__FUNCTION_NAME);
5066 Dummy blocks are not written
5068 if(block->isDummy())
5070 /*v3s16 p = block->getPos();
5071 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5072 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5076 // Format used for writing
5077 u8 version = SER_FMT_VER_HIGHEST;
5079 v3s16 p3d = block->getPos();
5080 v2s16 p2d(p3d.X, p3d.Z);
5081 createDir(m_savedir);
5082 createDir(m_savedir+"/sectors");
5083 std::string dir = getSectorDir(p2d);
5086 // Block file is map/sectors/xxxxxxxx/xxxx
5088 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5089 std::string fullpath = dir + "/" + cc;
5090 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5091 if(o.good() == false)
5092 throw FileNotGoodException("Cannot open block data");
5095 [0] u8 serialization version
5098 o.write((char*)&version, 1);
5100 block->serialize(o, version);
5103 Versions up from 9 have block objects.
5107 block->serializeObjects(o, version);
5110 // We just wrote it to the disk
5111 block->resetChangedFlag();
5114 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5116 DSTACK(__FUNCTION_NAME);
5120 // Block file is map/sectors/xxxxxxxx/xxxx
5121 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5122 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5123 if(is.good() == false)
5124 throw FileNotGoodException("Cannot open block file");
5126 v3s16 p3d = getBlockPos(sectordir, blockfile);
5127 v2s16 p2d(p3d.X, p3d.Z);
5129 assert(sector->getPos() == p2d);
5131 u8 version = SER_FMT_VER_INVALID;
5132 is.read((char*)&version, 1);
5134 /*u32 block_size = MapBlock::serializedLength(version);
5135 SharedBuffer<u8> data(block_size);
5136 is.read((char*)*data, block_size);*/
5138 // This will always return a sector because we're the server
5139 //MapSector *sector = emergeSector(p2d);
5141 MapBlock *block = NULL;
5142 bool created_new = false;
5144 block = sector->getBlockNoCreate(p3d.Y);
5146 catch(InvalidPositionException &e)
5148 block = sector->createBlankBlockNoInsert(p3d.Y);
5152 // deserialize block data
5153 block->deSerialize(is, version);
5156 Versions up from 9 have block objects.
5160 block->updateObjects(is, version, NULL, 0);
5164 sector->insertBlock(block);
5167 Convert old formats to new and save
5170 // Save old format blocks in new format
5171 if(version < SER_FMT_VER_HIGHEST)
5176 // We just loaded it from the disk, so it's up-to-date.
5177 block->resetChangedFlag();
5180 catch(SerializationError &e)
5182 dstream<<"WARNING: Invalid block data on disk "
5183 "(SerializationError). Ignoring."
5188 // Gets from master heightmap
5189 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5191 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5192 //assert(m_heightmap != NULL);
5200 /*corners[0] = m_heightmap->getGroundHeight
5201 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5202 corners[1] = m_heightmap->getGroundHeight
5203 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5204 corners[2] = m_heightmap->getGroundHeight
5205 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5206 corners[3] = m_heightmap->getGroundHeight
5207 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
5210 void ServerMap::PrintInfo(std::ostream &out)
5221 ClientMap::ClientMap(
5223 MapDrawControl &control,
5224 scene::ISceneNode* parent,
5225 scene::ISceneManager* mgr,
5229 scene::ISceneNode(parent, mgr, id),
5233 //mesh_mutex.Init();
5235 /*m_box = core::aabbox3d<f32>(0,0,0,
5236 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5237 /*m_box = core::aabbox3d<f32>(0,0,0,
5238 map->getSizeNodes().X * BS,
5239 map->getSizeNodes().Y * BS,
5240 map->getSizeNodes().Z * BS);*/
5241 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5242 BS*1000000,BS*1000000,BS*1000000);
5244 //setPosition(v3f(BS,BS,BS));
5247 ClientMap::~ClientMap()
5249 /*JMutexAutoLock lock(mesh_mutex);
5258 MapSector * ClientMap::emergeSector(v2s16 p2d)
5260 DSTACK(__FUNCTION_NAME);
5261 // Check that it doesn't exist already
5263 return getSectorNoGenerate(p2d);
5265 catch(InvalidPositionException &e)
5269 // Create a sector with no heightmaps
5270 ClientMapSector *sector = new ClientMapSector(this, p2d);
5273 JMutexAutoLock lock(m_sector_mutex);
5274 m_sectors.insert(p2d, sector);
5280 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5282 DSTACK(__FUNCTION_NAME);
5283 ClientMapSector *sector = NULL;
5285 JMutexAutoLock lock(m_sector_mutex);
5287 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5291 sector = (ClientMapSector*)n->getValue();
5292 assert(sector->getId() == MAPSECTOR_CLIENT);
5296 sector = new ClientMapSector(this, p2d);
5298 JMutexAutoLock lock(m_sector_mutex);
5299 m_sectors.insert(p2d, sector);
5303 sector->deSerialize(is);
5306 void ClientMap::OnRegisterSceneNode()
5310 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5311 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5314 ISceneNode::OnRegisterSceneNode();
5317 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5319 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5320 DSTACK(__FUNCTION_NAME);
5322 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5325 Get time for measuring timeout.
5327 Measuring time is very useful for long delays when the
5328 machine is swapping a lot.
5330 int time1 = time(0);
5332 u32 daynight_ratio = m_client->getDayNightRatio();
5334 m_camera_mutex.Lock();
5335 v3f camera_position = m_camera_position;
5336 v3f camera_direction = m_camera_direction;
5337 m_camera_mutex.Unlock();
5340 Get all blocks and draw all visible ones
5343 v3s16 cam_pos_nodes(
5344 camera_position.X / BS,
5345 camera_position.Y / BS,
5346 camera_position.Z / BS);
5348 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5350 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5351 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5353 // Take a fair amount as we will be dropping more out later
5355 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5356 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5357 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5359 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5360 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5361 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5363 u32 vertex_count = 0;
5365 // For limiting number of mesh updates per frame
5366 u32 mesh_update_count = 0;
5368 u32 blocks_would_have_drawn = 0;
5369 u32 blocks_drawn = 0;
5371 //NOTE: The sectors map should be locked but we're not doing it
5372 // because it'd cause too much delays
5374 int timecheck_counter = 0;
5375 core::map<v2s16, MapSector*>::Iterator si;
5376 si = m_sectors.getIterator();
5377 for(; si.atEnd() == false; si++)
5380 timecheck_counter++;
5381 if(timecheck_counter > 50)
5383 timecheck_counter = 0;
5384 int time2 = time(0);
5385 if(time2 > time1 + 4)
5387 dstream<<"ClientMap::renderMap(): "
5388 "Rendering takes ages, returning."
5395 MapSector *sector = si.getNode()->getValue();
5396 v2s16 sp = sector->getPos();
5398 if(m_control.range_all == false)
5400 if(sp.X < p_blocks_min.X
5401 || sp.X > p_blocks_max.X
5402 || sp.Y < p_blocks_min.Z
5403 || sp.Y > p_blocks_max.Z)
5407 core::list< MapBlock * > sectorblocks;
5408 sector->getBlocks(sectorblocks);
5414 core::list< MapBlock * >::Iterator i;
5415 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5417 MapBlock *block = *i;
5420 Compare block position to camera position, skip
5421 if not seen on display
5424 float range = 100000 * BS;
5425 if(m_control.range_all == false)
5426 range = m_control.wanted_range * BS;
5429 if(isBlockInSight(block->getPos(), camera_position,
5430 camera_direction, range, &d) == false)
5440 bool mesh_expired = false;
5443 JMutexAutoLock lock(block->mesh_mutex);
5445 mesh_expired = block->getMeshExpired();
5447 // Mesh has not been expired and there is no mesh:
5448 // block has no content
5449 if(block->mesh == NULL && mesh_expired == false)
5453 f32 faraway = BS*50;
5454 //f32 faraway = m_control.wanted_range * BS;
5457 This has to be done with the mesh_mutex unlocked
5459 // Pretty random but this should work somewhat nicely
5460 if(mesh_expired && (
5461 (mesh_update_count < 3
5462 && (d < faraway || mesh_update_count < 2)
5465 (m_control.range_all && mesh_update_count < 20)
5468 /*if(mesh_expired && mesh_update_count < 6
5469 && (d < faraway || mesh_update_count < 3))*/
5471 mesh_update_count++;
5473 // Mesh has been expired: generate new mesh
5474 //block->updateMeshes(daynight_i);
5475 block->updateMesh(daynight_ratio);
5477 mesh_expired = false;
5481 Don't draw an expired mesh that is far away
5483 /*if(mesh_expired && d >= faraway)
5486 // Instead, delete it
5487 JMutexAutoLock lock(block->mesh_mutex);
5490 block->mesh->drop();
5493 // And continue to next block
5498 Draw the faces of the block
5501 JMutexAutoLock lock(block->mesh_mutex);
5503 scene::SMesh *mesh = block->mesh;
5508 blocks_would_have_drawn++;
5509 if(blocks_drawn >= m_control.wanted_max_blocks
5510 && m_control.range_all == false
5511 && d > m_control.wanted_min_range * BS)
5515 u32 c = mesh->getMeshBufferCount();
5517 for(u32 i=0; i<c; i++)
5519 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5520 const video::SMaterial& material = buf->getMaterial();
5521 video::IMaterialRenderer* rnd =
5522 driver->getMaterialRenderer(material.MaterialType);
5523 bool transparent = (rnd && rnd->isTransparent());
5524 // Render transparent on transparent pass and likewise.
5525 if(transparent == is_transparent_pass)
5528 This *shouldn't* hurt too much because Irrlicht
5529 doesn't change opengl textures if the old
5530 material is set again.
5532 driver->setMaterial(buf->getMaterial());
5533 driver->drawMeshBuffer(buf);
5534 vertex_count += buf->getVertexCount();
5538 } // foreach sectorblocks
5541 m_control.blocks_drawn = blocks_drawn;
5542 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5544 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5545 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5548 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5549 core::map<v3s16, MapBlock*> *affected_blocks)
5551 bool changed = false;
5553 Add it to all blocks touching it
5556 v3s16(0,0,0), // this
5557 v3s16(0,0,1), // back
5558 v3s16(0,1,0), // top
5559 v3s16(1,0,0), // right
5560 v3s16(0,0,-1), // front
5561 v3s16(0,-1,0), // bottom
5562 v3s16(-1,0,0), // left
5564 for(u16 i=0; i<7; i++)
5566 v3s16 p2 = p + dirs[i];
5567 // Block position of neighbor (or requested) node
5568 v3s16 blockpos = getNodeBlockPos(p2);
5569 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5570 if(blockref == NULL)
5572 // Relative position of requested node
5573 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5574 if(blockref->setTempMod(relpos, mod))
5579 if(changed && affected_blocks!=NULL)
5581 for(u16 i=0; i<7; i++)
5583 v3s16 p2 = p + dirs[i];
5584 // Block position of neighbor (or requested) node
5585 v3s16 blockpos = getNodeBlockPos(p2);
5586 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5587 if(blockref == NULL)
5589 affected_blocks->insert(blockpos, blockref);
5595 bool ClientMap::clearTempMod(v3s16 p,
5596 core::map<v3s16, MapBlock*> *affected_blocks)
5598 bool changed = false;
5600 v3s16(0,0,0), // this
5601 v3s16(0,0,1), // back
5602 v3s16(0,1,0), // top
5603 v3s16(1,0,0), // right
5604 v3s16(0,0,-1), // front
5605 v3s16(0,-1,0), // bottom
5606 v3s16(-1,0,0), // left
5608 for(u16 i=0; i<7; i++)
5610 v3s16 p2 = p + dirs[i];
5611 // Block position of neighbor (or requested) node
5612 v3s16 blockpos = getNodeBlockPos(p2);
5613 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5614 if(blockref == NULL)
5616 // Relative position of requested node
5617 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5618 if(blockref->clearTempMod(relpos))
5623 if(changed && affected_blocks!=NULL)
5625 for(u16 i=0; i<7; i++)
5627 v3s16 p2 = p + dirs[i];
5628 // Block position of neighbor (or requested) node
5629 v3s16 blockpos = getNodeBlockPos(p2);
5630 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5631 if(blockref == NULL)
5633 affected_blocks->insert(blockpos, blockref);
5639 void ClientMap::PrintInfo(std::ostream &out)
5650 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5655 MapVoxelManipulator::~MapVoxelManipulator()
5657 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5661 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5663 TimeTaker timer1("emerge", &emerge_time);
5665 // Units of these are MapBlocks
5666 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5667 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5669 VoxelArea block_area_nodes
5670 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5672 addArea(block_area_nodes);
5674 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5675 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5676 for(s32 x=p_min.X; x<=p_max.X; x++)
5679 core::map<v3s16, bool>::Node *n;
5680 n = m_loaded_blocks.find(p);
5684 bool block_data_inexistent = false;
5687 TimeTaker timer1("emerge load", &emerge_load_time);
5689 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5690 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5693 dstream<<std::endl;*/
5695 MapBlock *block = m_map->getBlockNoCreate(p);
5696 if(block->isDummy())
5697 block_data_inexistent = true;
5699 block->copyTo(*this);
5701 catch(InvalidPositionException &e)
5703 block_data_inexistent = true;
5706 if(block_data_inexistent)
5708 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5709 // Fill with VOXELFLAG_INEXISTENT
5710 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5711 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5713 s32 i = m_area.index(a.MinEdge.X,y,z);
5714 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5718 m_loaded_blocks.insert(p, !block_data_inexistent);
5721 //dstream<<"emerge done"<<std::endl;
5725 SUGG: Add an option to only update eg. water and air nodes.
5726 This will make it interfere less with important stuff if
5729 void MapVoxelManipulator::blitBack
5730 (core::map<v3s16, MapBlock*> & modified_blocks)
5732 if(m_area.getExtent() == v3s16(0,0,0))
5735 //TimeTaker timer1("blitBack");
5737 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5738 <<m_loaded_blocks.size()<<std::endl;*/
5741 Initialize block cache
5743 v3s16 blockpos_last;
5744 MapBlock *block = NULL;
5745 bool block_checked_in_modified = false;
5747 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5748 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5749 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5753 u8 f = m_flags[m_area.index(p)];
5754 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5757 MapNode &n = m_data[m_area.index(p)];
5759 v3s16 blockpos = getNodeBlockPos(p);
5764 if(block == NULL || blockpos != blockpos_last){
5765 block = m_map->getBlockNoCreate(blockpos);
5766 blockpos_last = blockpos;
5767 block_checked_in_modified = false;
5770 // Calculate relative position in block
5771 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5773 // Don't continue if nothing has changed here
5774 if(block->getNode(relpos) == n)
5777 //m_map->setNode(m_area.MinEdge + p, n);
5778 block->setNode(relpos, n);
5781 Make sure block is in modified_blocks
5783 if(block_checked_in_modified == false)
5785 modified_blocks[blockpos] = block;
5786 block_checked_in_modified = true;
5789 catch(InvalidPositionException &e)
5795 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5796 MapVoxelManipulator(map)
5800 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5804 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5806 // Just create the area so that it can be pointed to
5807 VoxelManipulator::emerge(a, caller_id);
5810 void ManualMapVoxelManipulator::initialEmerge(
5811 v3s16 blockpos_min, v3s16 blockpos_max)
5813 TimeTaker timer1("initialEmerge", &emerge_time);
5815 // Units of these are MapBlocks
5816 v3s16 p_min = blockpos_min;
5817 v3s16 p_max = blockpos_max;
5819 VoxelArea block_area_nodes
5820 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5822 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5825 dstream<<"initialEmerge: area: ";
5826 block_area_nodes.print(dstream);
5827 dstream<<" ("<<size_MB<<"MB)";
5831 addArea(block_area_nodes);
5833 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5834 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5835 for(s32 x=p_min.X; x<=p_max.X; x++)
5838 core::map<v3s16, bool>::Node *n;
5839 n = m_loaded_blocks.find(p);
5843 bool block_data_inexistent = false;
5846 TimeTaker timer1("emerge load", &emerge_load_time);
5848 MapBlock *block = m_map->getBlockNoCreate(p);
5849 if(block->isDummy())
5850 block_data_inexistent = true;
5852 block->copyTo(*this);
5854 catch(InvalidPositionException &e)
5856 block_data_inexistent = true;
5859 if(block_data_inexistent)
5862 Mark area inexistent
5864 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5865 // Fill with VOXELFLAG_INEXISTENT
5866 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5867 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5869 s32 i = m_area.index(a.MinEdge.X,y,z);
5870 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5874 m_loaded_blocks.insert(p, !block_data_inexistent);
5878 void ManualMapVoxelManipulator::blitBackAll(
5879 core::map<v3s16, MapBlock*> * modified_blocks)
5881 if(m_area.getExtent() == v3s16(0,0,0))
5885 Copy data of all blocks
5887 for(core::map<v3s16, bool>::Iterator
5888 i = m_loaded_blocks.getIterator();
5889 i.atEnd() == false; i++)
5891 bool existed = i.getNode()->getValue();
5892 if(existed == false)
5894 v3s16 p = i.getNode()->getKey();
5895 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5898 dstream<<"WARNING: "<<__FUNCTION_NAME
5899 <<": got NULL block "
5900 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5905 block->copyFrom(*this);
5908 modified_blocks->insert(p, block);