3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
34 Map::Map(std::ostream &dout):
36 m_camera_position(0,0,0),
37 m_camera_direction(0,0,1),
41 m_sector_mutex.Init();
42 m_camera_mutex.Init();
43 assert(m_sector_mutex.IsInitialized());
44 assert(m_camera_mutex.IsInitialized());
46 // Get this so that the player can stay on it at first
47 //getSector(v2s16(0,0));
55 /*updater.setRun(false);
56 while(updater.IsRunning())
62 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
63 for(; i.atEnd() == false; i++)
65 MapSector *sector = i.getNode()->getValue();
70 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
72 if(m_sector_cache != NULL && p == m_sector_cache_p){
73 MapSector * sector = m_sector_cache;
74 // Reset inactivity timer
75 sector->usage_timer = 0.0;
79 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
84 MapSector *sector = n->getValue();
86 // Cache the last result
88 m_sector_cache = sector;
90 // Reset inactivity timer
91 sector->usage_timer = 0.0;
95 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
97 JMutexAutoLock lock(m_sector_mutex);
99 return getSectorNoGenerateNoExNoLock(p);
102 MapSector * Map::getSectorNoGenerate(v2s16 p)
104 MapSector *sector = getSectorNoGenerateNoEx(p);
106 throw InvalidPositionException();
111 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
113 v2s16 p2d(p3d.X, p3d.Z);
114 MapSector * sector = getSectorNoGenerate(p2d);
116 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
121 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerate(p2d);
127 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 catch(InvalidPositionException &e)
136 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
138 v2s16 p2d(p3d.X, p3d.Z);
139 MapSector * sector = getSectorCreate(p2d);
141 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
144 block = sector->createBlankBlock(p3d.Y);
148 f32 Map::getGroundHeight(v2s16 p, bool generate)
151 v2s16 sectorpos = getNodeSectorPos(p);
152 MapSector * sref = getSectorNoGenerate(sectorpos);
153 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
154 f32 y = sref->getGroundHeight(relpos);
157 catch(InvalidPositionException &e)
159 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
163 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
165 /*m_dout<<DTIME<<"Map::setGroundHeight(("
167 <<"), "<<y<<")"<<std::endl;*/
168 v2s16 sectorpos = getNodeSectorPos(p);
169 MapSector * sref = getSectorNoGenerate(sectorpos);
170 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
171 //sref->mutex.Lock();
172 sref->setGroundHeight(relpos, y);
173 //sref->mutex.Unlock();
176 bool Map::isNodeUnderground(v3s16 p)
178 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock * block = getBlockNoCreate(blockpos);
181 return block->getIsUnderground();
183 catch(InvalidPositionException &e)
190 Goes recursively through the neighbours of the node.
192 Alters only transparent nodes.
194 If the lighting of the neighbour is lower than the lighting of
195 the node was (before changing it to 0 at the step before), the
196 lighting of the neighbour is set to 0 and then the same stuff
197 repeats for the neighbour.
199 The ending nodes of the routine are stored in light_sources.
200 This is useful when a light is removed. In such case, this
201 routine can be called for the light node and then again for
202 light_sources to re-light the area without the removed light.
204 values of from_nodes are lighting values.
206 void Map::unspreadLight(enum LightBank bank,
207 core::map<v3s16, u8> & from_nodes,
208 core::map<v3s16, bool> & light_sources,
209 core::map<v3s16, MapBlock*> & modified_blocks)
212 v3s16(0,0,1), // back
214 v3s16(1,0,0), // right
215 v3s16(0,0,-1), // front
216 v3s16(0,-1,0), // bottom
217 v3s16(-1,0,0), // left
220 if(from_nodes.size() == 0)
223 u32 blockchangecount = 0;
225 core::map<v3s16, u8> unlighted_nodes;
226 core::map<v3s16, u8>::Iterator j;
227 j = from_nodes.getIterator();
230 Initialize block cache
233 MapBlock *block = NULL;
234 // Cache this a bit, too
235 bool block_checked_in_modified = false;
237 for(; j.atEnd() == false; j++)
239 v3s16 pos = j.getNode()->getKey();
240 v3s16 blockpos = getNodeBlockPos(pos);
242 // Only fetch a new block if the block position has changed
244 if(block == NULL || blockpos != blockpos_last){
245 block = getBlockNoCreate(blockpos);
246 blockpos_last = blockpos;
248 block_checked_in_modified = false;
252 catch(InvalidPositionException &e)
260 // Calculate relative position in block
261 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
263 // Get node straight from the block
264 MapNode n = block->getNode(relpos);
266 u8 oldlight = j.getNode()->getValue();
268 // Loop through 6 neighbors
269 for(u16 i=0; i<6; i++)
271 // Get the position of the neighbor node
272 v3s16 n2pos = pos + dirs[i];
274 // Get the block where the node is located
275 v3s16 blockpos = getNodeBlockPos(n2pos);
279 // Only fetch a new block if the block position has changed
281 if(block == NULL || blockpos != blockpos_last){
282 block = getBlockNoCreate(blockpos);
283 blockpos_last = blockpos;
285 block_checked_in_modified = false;
289 catch(InvalidPositionException &e)
294 // Calculate relative position in block
295 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
296 // Get node straight from the block
297 MapNode n2 = block->getNode(relpos);
299 bool changed = false;
301 //TODO: Optimize output by optimizing light_sources?
304 If the neighbor is dimmer than what was specified
305 as oldlight (the light of the previous node)
307 if(n2.getLight(bank) < oldlight)
310 And the neighbor is transparent and it has some light
312 if(n2.light_propagates() && n2.getLight(bank) != 0)
315 Set light to 0 and add to queue
318 u8 current_light = n2.getLight(bank);
319 n2.setLight(bank, 0);
320 block->setNode(relpos, n2);
322 unlighted_nodes.insert(n2pos, current_light);
326 Remove from light_sources if it is there
327 NOTE: This doesn't happen nearly at all
329 /*if(light_sources.find(n2pos))
331 std::cout<<"Removed from light_sources"<<std::endl;
332 light_sources.remove(n2pos);
337 if(light_sources.find(n2pos) != NULL)
338 light_sources.remove(n2pos);*/
341 light_sources.insert(n2pos, true);
344 // Add to modified_blocks
345 if(changed == true && block_checked_in_modified == false)
347 // If the block is not found in modified_blocks, add.
348 if(modified_blocks.find(blockpos) == NULL)
350 modified_blocks.insert(blockpos, block);
352 block_checked_in_modified = true;
355 catch(InvalidPositionException &e)
362 /*dstream<<"unspreadLight(): Changed block "
363 <<blockchangecount<<" times"
364 <<" for "<<from_nodes.size()<<" nodes"
367 if(unlighted_nodes.size() > 0)
368 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
372 A single-node wrapper of the above
374 void Map::unLightNeighbors(enum LightBank bank,
375 v3s16 pos, u8 lightwas,
376 core::map<v3s16, bool> & light_sources,
377 core::map<v3s16, MapBlock*> & modified_blocks)
379 core::map<v3s16, u8> from_nodes;
380 from_nodes.insert(pos, lightwas);
382 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
386 Lights neighbors of from_nodes, collects all them and then
389 void Map::spreadLight(enum LightBank bank,
390 core::map<v3s16, bool> & from_nodes,
391 core::map<v3s16, MapBlock*> & modified_blocks)
393 const v3s16 dirs[6] = {
394 v3s16(0,0,1), // back
396 v3s16(1,0,0), // right
397 v3s16(0,0,-1), // front
398 v3s16(0,-1,0), // bottom
399 v3s16(-1,0,0), // left
402 if(from_nodes.size() == 0)
405 u32 blockchangecount = 0;
407 core::map<v3s16, bool> lighted_nodes;
408 core::map<v3s16, bool>::Iterator j;
409 j = from_nodes.getIterator();
412 Initialize block cache
415 MapBlock *block = NULL;
416 // Cache this a bit, too
417 bool block_checked_in_modified = false;
419 for(; j.atEnd() == false; j++)
420 //for(; j != from_nodes.end(); j++)
422 v3s16 pos = j.getNode()->getKey();
424 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
425 v3s16 blockpos = getNodeBlockPos(pos);
427 // Only fetch a new block if the block position has changed
429 if(block == NULL || blockpos != blockpos_last){
430 block = getBlockNoCreate(blockpos);
431 blockpos_last = blockpos;
433 block_checked_in_modified = false;
437 catch(InvalidPositionException &e)
445 // Calculate relative position in block
446 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
448 // Get node straight from the block
449 MapNode n = block->getNode(relpos);
451 u8 oldlight = n.getLight(bank);
452 u8 newlight = diminish_light(oldlight);
454 // Loop through 6 neighbors
455 for(u16 i=0; i<6; i++){
456 // Get the position of the neighbor node
457 v3s16 n2pos = pos + dirs[i];
459 // Get the block where the node is located
460 v3s16 blockpos = getNodeBlockPos(n2pos);
464 // Only fetch a new block if the block position has changed
466 if(block == NULL || blockpos != blockpos_last){
467 block = getBlockNoCreate(blockpos);
468 blockpos_last = blockpos;
470 block_checked_in_modified = false;
474 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
481 // Get node straight from the block
482 MapNode n2 = block->getNode(relpos);
484 bool changed = false;
486 If the neighbor is brighter than the current node,
487 add to list (it will light up this node on its turn)
489 if(n2.getLight(bank) > undiminish_light(oldlight))
491 lighted_nodes.insert(n2pos, true);
492 //lighted_nodes.push_back(n2pos);
496 If the neighbor is dimmer than how much light this node
497 would spread on it, add to list
499 if(n2.getLight(bank) < newlight)
501 if(n2.light_propagates())
503 n2.setLight(bank, newlight);
504 block->setNode(relpos, n2);
505 lighted_nodes.insert(n2pos, true);
506 //lighted_nodes.push_back(n2pos);
511 // Add to modified_blocks
512 if(changed == true && block_checked_in_modified == false)
514 // If the block is not found in modified_blocks, add.
515 if(modified_blocks.find(blockpos) == NULL)
517 modified_blocks.insert(blockpos, block);
519 block_checked_in_modified = true;
522 catch(InvalidPositionException &e)
529 /*dstream<<"spreadLight(): Changed block "
530 <<blockchangecount<<" times"
531 <<" for "<<from_nodes.size()<<" nodes"
534 if(lighted_nodes.size() > 0)
535 spreadLight(bank, lighted_nodes, modified_blocks);
539 A single-node source variation of the above.
541 void Map::lightNeighbors(enum LightBank bank,
543 core::map<v3s16, MapBlock*> & modified_blocks)
545 core::map<v3s16, bool> from_nodes;
546 from_nodes.insert(pos, true);
547 spreadLight(bank, from_nodes, modified_blocks);
550 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
553 v3s16(0,0,1), // back
555 v3s16(1,0,0), // right
556 v3s16(0,0,-1), // front
557 v3s16(0,-1,0), // bottom
558 v3s16(-1,0,0), // left
561 u8 brightest_light = 0;
562 v3s16 brightest_pos(0,0,0);
563 bool found_something = false;
565 // Loop through 6 neighbors
566 for(u16 i=0; i<6; i++){
567 // Get the position of the neighbor node
568 v3s16 n2pos = p + dirs[i];
573 catch(InvalidPositionException &e)
577 if(n2.getLight(bank) > brightest_light || found_something == false){
578 brightest_light = n2.getLight(bank);
579 brightest_pos = n2pos;
580 found_something = true;
584 if(found_something == false)
585 throw InvalidPositionException();
587 return brightest_pos;
591 Propagates sunlight down from a node.
592 Starting point gets sunlight.
594 Returns the lowest y value of where the sunlight went.
596 Mud is turned into grass in where the sunlight stops.
598 s16 Map::propagateSunlight(v3s16 start,
599 core::map<v3s16, MapBlock*> & modified_blocks)
604 v3s16 pos(start.X, y, start.Z);
606 v3s16 blockpos = getNodeBlockPos(pos);
609 block = getBlockNoCreate(blockpos);
611 catch(InvalidPositionException &e)
616 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
617 MapNode n = block->getNode(relpos);
619 if(n.sunlight_propagates())
621 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
622 block->setNode(relpos, n);
624 modified_blocks.insert(blockpos, block);
628 // Turn mud into grass
629 if(n.d == CONTENT_MUD)
632 block->setNode(relpos, n);
633 modified_blocks.insert(blockpos, block);
636 // Sunlight goes no further
643 void Map::updateLighting(enum LightBank bank,
644 core::map<v3s16, MapBlock*> & a_blocks,
645 core::map<v3s16, MapBlock*> & modified_blocks)
647 /*m_dout<<DTIME<<"Map::updateLighting(): "
648 <<a_blocks.size()<<" blocks."<<std::endl;*/
650 //TimeTaker timer("updateLighting");
654 //u32 count_was = modified_blocks.size();
656 core::map<v3s16, MapBlock*> blocks_to_update;
658 core::map<v3s16, bool> light_sources;
660 core::map<v3s16, u8> unlight_from;
662 core::map<v3s16, MapBlock*>::Iterator i;
663 i = a_blocks.getIterator();
664 for(; i.atEnd() == false; i++)
666 MapBlock *block = i.getNode()->getValue();
670 // Don't bother with dummy blocks.
674 v3s16 pos = block->getPos();
675 modified_blocks.insert(pos, block);
677 blocks_to_update.insert(pos, block);
680 Clear all light from block
682 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
683 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
684 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
689 MapNode n = block->getNode(v3s16(x,y,z));
690 u8 oldlight = n.getLight(bank);
692 block->setNode(v3s16(x,y,z), n);
694 // Collect borders for unlighting
695 if(x==0 || x == MAP_BLOCKSIZE-1
696 || y==0 || y == MAP_BLOCKSIZE-1
697 || z==0 || z == MAP_BLOCKSIZE-1)
699 v3s16 p_map = p + v3s16(
702 MAP_BLOCKSIZE*pos.Z);
703 unlight_from.insert(p_map, oldlight);
706 catch(InvalidPositionException &e)
709 This would happen when dealing with a
713 dstream<<"updateLighting(): InvalidPositionException"
718 if(bank == LIGHTBANK_DAY)
720 bool bottom_valid = block->propagateSunlight(light_sources);
722 // If bottom is valid, we're done.
726 else if(bank == LIGHTBANK_NIGHT)
728 // For night lighting, sunlight is not propagated
733 // Invalid lighting bank
737 /*dstream<<"Bottom for sunlight-propagated block ("
738 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
741 // Bottom sunlight is not valid; get the block and loop to it
745 block = getBlockNoCreate(pos);
747 catch(InvalidPositionException &e)
757 TimeTaker timer("unspreadLight");
758 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
763 u32 diff = modified_blocks.size() - count_was;
764 count_was = modified_blocks.size();
765 dstream<<"unspreadLight modified "<<diff<<std::endl;
769 TimeTaker timer("spreadLight");
770 spreadLight(bank, light_sources, modified_blocks);
775 u32 diff = modified_blocks.size() - count_was;
776 count_was = modified_blocks.size();
777 dstream<<"spreadLight modified "<<diff<<std::endl;
782 //MapVoxelManipulator vmanip(this);
784 // Make a manual voxel manipulator and load all the blocks
785 // that touch the requested blocks
786 ManualMapVoxelManipulator vmanip(this);
787 core::map<v3s16, MapBlock*>::Iterator i;
788 i = blocks_to_update.getIterator();
789 for(; i.atEnd() == false; i++)
791 MapBlock *block = i.getNode()->getValue();
792 v3s16 p = block->getPos();
794 // Add all surrounding blocks
795 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
798 Add all surrounding blocks that have up-to-date lighting
799 NOTE: This doesn't quite do the job (not everything
800 appropriate is lighted)
802 /*for(s16 z=-1; z<=1; z++)
803 for(s16 y=-1; y<=1; y++)
804 for(s16 x=-1; x<=1; x++)
807 MapBlock *block = getBlockNoCreateNoEx(p);
812 if(block->getLightingExpired())
814 vmanip.initialEmerge(p, p);
817 // Lighting of block will be updated completely
818 block->setLightingExpired(false);
822 //TimeTaker timer("unSpreadLight");
823 vmanip.unspreadLight(bank, unlight_from, light_sources);
826 //TimeTaker timer("spreadLight");
827 vmanip.spreadLight(bank, light_sources);
830 //TimeTaker timer("blitBack");
831 vmanip.blitBack(modified_blocks);
833 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
837 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
840 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
841 core::map<v3s16, MapBlock*> & modified_blocks)
843 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
844 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
847 Update information about whether day and night light differ
849 for(core::map<v3s16, MapBlock*>::Iterator
850 i = modified_blocks.getIterator();
851 i.atEnd() == false; i++)
853 MapBlock *block = i.getNode()->getValue();
854 block->updateDayNightDiff();
859 This is called after changing a node from transparent to opaque.
860 The lighting value of the node should be left as-is after changing
861 other values. This sets the lighting value to 0.
863 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
864 core::map<v3s16, MapBlock*> &modified_blocks)*/
865 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
866 core::map<v3s16, MapBlock*> &modified_blocks)
869 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
870 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
873 From this node to nodes underneath:
874 If lighting is sunlight (1.0), unlight neighbours and
879 v3s16 toppos = p + v3s16(0,1,0);
880 v3s16 bottompos = p + v3s16(0,-1,0);
882 bool node_under_sunlight = true;
883 core::map<v3s16, bool> light_sources;
886 If there is a node at top and it doesn't have sunlight,
887 there has not been any sunlight going down.
889 Otherwise there probably is.
892 MapNode topnode = getNode(toppos);
894 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
895 node_under_sunlight = false;
897 catch(InvalidPositionException &e)
901 if(n.d != CONTENT_TORCH)
904 If there is grass below, change it to mud
907 MapNode bottomnode = getNode(bottompos);
909 if(bottomnode.d == CONTENT_GRASS
910 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
912 bottomnode.d = CONTENT_MUD;
913 setNode(bottompos, bottomnode);
916 catch(InvalidPositionException &e)
921 enum LightBank banks[] =
926 for(s32 i=0; i<2; i++)
928 enum LightBank bank = banks[i];
930 u8 lightwas = getNode(p).getLight(bank);
932 // Add the block of the added node to modified_blocks
933 v3s16 blockpos = getNodeBlockPos(p);
934 MapBlock * block = getBlockNoCreate(blockpos);
935 assert(block != NULL);
936 modified_blocks.insert(blockpos, block);
938 if(isValidPosition(p) == false)
941 // Unlight neighbours of node.
942 // This means setting light of all consequent dimmer nodes
944 // This also collects the nodes at the border which will spread
945 // light again into this.
946 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
954 If node is under sunlight, take all sunlighted nodes under
955 it and clear light from them and from where the light has
957 TODO: This could be optimized by mass-unlighting instead
960 if(node_under_sunlight)
964 //m_dout<<DTIME<<"y="<<y<<std::endl;
965 v3s16 n2pos(p.X, y, p.Z);
971 catch(InvalidPositionException &e)
976 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
978 //m_dout<<DTIME<<"doing"<<std::endl;
979 unLightNeighbors(LIGHTBANK_DAY,
980 n2pos, n2.getLight(LIGHTBANK_DAY),
981 light_sources, modified_blocks);
982 n2.setLight(LIGHTBANK_DAY, 0);
990 for(s32 i=0; i<2; i++)
992 enum LightBank bank = banks[i];
995 Spread light from all nodes that might be capable of doing so
996 TODO: Convert to spreadLight
998 spreadLight(bank, light_sources, modified_blocks);
1002 Update information about whether day and night light differ
1004 for(core::map<v3s16, MapBlock*>::Iterator
1005 i = modified_blocks.getIterator();
1006 i.atEnd() == false; i++)
1008 MapBlock *block = i.getNode()->getValue();
1009 block->updateDayNightDiff();
1013 Add neighboring liquid nodes and the node itself if it is
1014 liquid (=water node was added) to transform queue.
1017 v3s16(0,0,0), // self
1018 v3s16(0,0,1), // back
1019 v3s16(0,1,0), // top
1020 v3s16(1,0,0), // right
1021 v3s16(0,0,-1), // front
1022 v3s16(0,-1,0), // bottom
1023 v3s16(-1,0,0), // left
1025 for(u16 i=0; i<7; i++)
1030 v3s16 p2 = p + dirs[i];
1032 MapNode n2 = getNode(p2);
1033 if(content_liquid(n2.d))
1035 m_transforming_liquid.push_back(p2);
1038 }catch(InvalidPositionException &e)
1046 void Map::removeNodeAndUpdate(v3s16 p,
1047 core::map<v3s16, MapBlock*> &modified_blocks)
1049 /*PrintInfo(m_dout);
1050 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1051 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1053 bool node_under_sunlight = true;
1055 v3s16 toppos = p + v3s16(0,1,0);
1057 // Node will be replaced with this
1058 u8 replace_material = CONTENT_AIR;
1061 If there is a node at top and it doesn't have sunlight,
1062 there will be no sunlight going down.
1065 MapNode topnode = getNode(toppos);
1067 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1068 node_under_sunlight = false;
1070 catch(InvalidPositionException &e)
1074 core::map<v3s16, bool> light_sources;
1076 enum LightBank banks[] =
1081 for(s32 i=0; i<2; i++)
1083 enum LightBank bank = banks[i];
1086 Unlight neighbors (in case the node is a light source)
1088 unLightNeighbors(bank, p,
1089 getNode(p).getLight(bank),
1090 light_sources, modified_blocks);
1095 This also clears the lighting.
1099 n.d = replace_material;
1102 for(s32 i=0; i<2; i++)
1104 enum LightBank bank = banks[i];
1107 Recalculate lighting
1109 spreadLight(bank, light_sources, modified_blocks);
1112 // Add the block of the removed node to modified_blocks
1113 v3s16 blockpos = getNodeBlockPos(p);
1114 MapBlock * block = getBlockNoCreate(blockpos);
1115 assert(block != NULL);
1116 modified_blocks.insert(blockpos, block);
1119 If the removed node was under sunlight, propagate the
1120 sunlight down from it and then light all neighbors
1121 of the propagated blocks.
1123 if(node_under_sunlight)
1125 s16 ybottom = propagateSunlight(p, modified_blocks);
1126 /*m_dout<<DTIME<<"Node was under sunlight. "
1127 "Propagating sunlight";
1128 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1130 for(; y >= ybottom; y--)
1132 v3s16 p2(p.X, y, p.Z);
1133 /*m_dout<<DTIME<<"lighting neighbors of node ("
1134 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1136 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1141 // Set the lighting of this node to 0
1142 // TODO: Is this needed? Lighting is cleared up there already.
1144 MapNode n = getNode(p);
1145 n.setLight(LIGHTBANK_DAY, 0);
1148 catch(InvalidPositionException &e)
1154 for(s32 i=0; i<2; i++)
1156 enum LightBank bank = banks[i];
1158 // Get the brightest neighbour node and propagate light from it
1159 v3s16 n2p = getBrightestNeighbour(bank, p);
1161 MapNode n2 = getNode(n2p);
1162 lightNeighbors(bank, n2p, modified_blocks);
1164 catch(InvalidPositionException &e)
1170 Update information about whether day and night light differ
1172 for(core::map<v3s16, MapBlock*>::Iterator
1173 i = modified_blocks.getIterator();
1174 i.atEnd() == false; i++)
1176 MapBlock *block = i.getNode()->getValue();
1177 block->updateDayNightDiff();
1181 Add neighboring liquid nodes to transform queue.
1184 v3s16(0,0,1), // back
1185 v3s16(0,1,0), // top
1186 v3s16(1,0,0), // right
1187 v3s16(0,0,-1), // front
1188 v3s16(0,-1,0), // bottom
1189 v3s16(-1,0,0), // left
1191 for(u16 i=0; i<6; i++)
1196 v3s16 p2 = p + dirs[i];
1198 MapNode n2 = getNode(p2);
1199 if(content_liquid(n2.d))
1201 m_transforming_liquid.push_back(p2);
1204 }catch(InvalidPositionException &e)
1211 void Map::expireMeshes(bool only_daynight_diffed)
1213 TimeTaker timer("expireMeshes()");
1215 core::map<v2s16, MapSector*>::Iterator si;
1216 si = m_sectors.getIterator();
1217 for(; si.atEnd() == false; si++)
1219 MapSector *sector = si.getNode()->getValue();
1221 core::list< MapBlock * > sectorblocks;
1222 sector->getBlocks(sectorblocks);
1224 core::list< MapBlock * >::Iterator i;
1225 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1227 MapBlock *block = *i;
1229 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1235 JMutexAutoLock lock(block->mesh_mutex);
1236 if(block->mesh != NULL)
1238 /*block->mesh->drop();
1239 block->mesh = NULL;*/
1240 block->setMeshExpired(true);
1247 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1249 assert(mapType() == MAPTYPE_CLIENT);
1252 v3s16 p = blockpos + v3s16(0,0,0);
1253 MapBlock *b = getBlockNoCreate(p);
1254 b->updateMesh(daynight_ratio);
1256 catch(InvalidPositionException &e){}
1259 v3s16 p = blockpos + v3s16(-1,0,0);
1260 MapBlock *b = getBlockNoCreate(p);
1261 b->updateMesh(daynight_ratio);
1263 catch(InvalidPositionException &e){}
1265 v3s16 p = blockpos + v3s16(0,-1,0);
1266 MapBlock *b = getBlockNoCreate(p);
1267 b->updateMesh(daynight_ratio);
1269 catch(InvalidPositionException &e){}
1271 v3s16 p = blockpos + v3s16(0,0,-1);
1272 MapBlock *b = getBlockNoCreate(p);
1273 b->updateMesh(daynight_ratio);
1275 catch(InvalidPositionException &e){}
1278 v3s16 p = blockpos + v3s16(1,0,0);
1279 MapBlock *b = getBlockNoCreate(p);
1280 b->updateMesh(daynight_ratio);
1282 catch(InvalidPositionException &e){}
1284 v3s16 p = blockpos + v3s16(0,1,0);
1285 MapBlock *b = getBlockNoCreate(p);
1286 b->updateMesh(daynight_ratio);
1288 catch(InvalidPositionException &e){}
1290 v3s16 p = blockpos + v3s16(0,0,1);
1291 MapBlock *b = getBlockNoCreate(p);
1292 b->updateMesh(daynight_ratio);
1294 catch(InvalidPositionException &e){}*/
1299 bool Map::dayNightDiffed(v3s16 blockpos)
1302 v3s16 p = blockpos + v3s16(0,0,0);
1303 MapBlock *b = getBlockNoCreate(p);
1304 if(b->dayNightDiffed())
1307 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(-1,0,0);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1317 v3s16 p = blockpos + v3s16(0,-1,0);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1324 v3s16 p = blockpos + v3s16(0,0,-1);
1325 MapBlock *b = getBlockNoCreate(p);
1326 if(b->dayNightDiffed())
1329 catch(InvalidPositionException &e){}
1332 v3s16 p = blockpos + v3s16(1,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1339 v3s16 p = blockpos + v3s16(0,1,0);
1340 MapBlock *b = getBlockNoCreate(p);
1341 if(b->dayNightDiffed())
1344 catch(InvalidPositionException &e){}
1346 v3s16 p = blockpos + v3s16(0,0,1);
1347 MapBlock *b = getBlockNoCreate(p);
1348 if(b->dayNightDiffed())
1351 catch(InvalidPositionException &e){}
1357 Updates usage timers
1359 void Map::timerUpdate(float dtime)
1361 JMutexAutoLock lock(m_sector_mutex);
1363 core::map<v2s16, MapSector*>::Iterator si;
1365 si = m_sectors.getIterator();
1366 for(; si.atEnd() == false; si++)
1368 MapSector *sector = si.getNode()->getValue();
1369 sector->usage_timer += dtime;
1373 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1376 Wait for caches to be removed before continuing.
1378 This disables the existence of caches while locked
1380 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1382 core::list<v2s16>::Iterator j;
1383 for(j=list.begin(); j!=list.end(); j++)
1385 MapSector *sector = m_sectors[*j];
1388 sector->deleteBlocks();
1393 If sector is in sector cache, remove it from there
1395 if(m_sector_cache == sector)
1397 m_sector_cache = NULL;
1400 Remove from map and delete
1402 m_sectors.remove(*j);
1408 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1409 core::list<v3s16> *deleted_blocks)
1411 JMutexAutoLock lock(m_sector_mutex);
1413 core::list<v2s16> sector_deletion_queue;
1414 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1415 for(; i.atEnd() == false; i++)
1417 MapSector *sector = i.getNode()->getValue();
1419 Delete sector from memory if it hasn't been used in a long time
1421 if(sector->usage_timer > timeout)
1423 sector_deletion_queue.push_back(i.getNode()->getKey());
1425 if(deleted_blocks != NULL)
1427 // Collect positions of blocks of sector
1428 MapSector *sector = i.getNode()->getValue();
1429 core::list<MapBlock*> blocks;
1430 sector->getBlocks(blocks);
1431 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1432 i != blocks.end(); i++)
1434 deleted_blocks->push_back((*i)->getPos());
1439 deleteSectors(sector_deletion_queue, only_blocks);
1440 return sector_deletion_queue.getSize();
1443 void Map::PrintInfo(std::ostream &out)
1448 #define WATER_DROP_BOOST 4
1450 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1452 DSTACK(__FUNCTION_NAME);
1453 //TimeTaker timer("transformLiquids()");
1456 u32 initial_size = m_transforming_liquid.size();
1458 if(initial_size != 0)
1459 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1461 while(m_transforming_liquid.size() != 0)
1464 Get a queued transforming liquid node
1466 v3s16 p0 = m_transforming_liquid.pop_front();
1468 MapNode n0 = getNode(p0);
1470 // Don't deal with non-liquids
1471 if(content_liquid(n0.d) == false)
1474 bool is_source = !content_flowing_liquid(n0.d);
1476 u8 liquid_level = 8;
1477 if(is_source == false)
1478 liquid_level = n0.param2 & 0x0f;
1480 // Turn possible source into non-source
1481 u8 nonsource_c = make_liquid_flowing(n0.d);
1484 If not source, check that some node flows into this one
1485 and what is the level of liquid in this one
1487 if(is_source == false)
1489 s8 new_liquid_level_max = -1;
1491 v3s16 dirs_from[5] = {
1492 v3s16(0,1,0), // top
1493 v3s16(0,0,1), // back
1494 v3s16(1,0,0), // right
1495 v3s16(0,0,-1), // front
1496 v3s16(-1,0,0), // left
1498 for(u16 i=0; i<5; i++)
1503 bool from_top = (i==0);
1505 v3s16 p2 = p0 + dirs_from[i];
1506 MapNode n2 = getNode(p2);
1508 if(content_liquid(n2.d))
1510 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1511 // Check that the liquids are the same type
1512 if(n2_nonsource_c != nonsource_c)
1514 dstream<<"WARNING: Not handling: different liquids"
1515 " collide"<<std::endl;
1518 bool n2_is_source = !content_flowing_liquid(n2.d);
1519 s8 n2_liquid_level = 8;
1520 if(n2_is_source == false)
1521 n2_liquid_level = n2.param2 & 0x07;
1523 s8 new_liquid_level = -1;
1526 //new_liquid_level = 7;
1527 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1528 new_liquid_level = 7;
1530 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1532 else if(n2_liquid_level > 0)
1534 new_liquid_level = n2_liquid_level - 1;
1537 if(new_liquid_level > new_liquid_level_max)
1538 new_liquid_level_max = new_liquid_level;
1541 }catch(InvalidPositionException &e)
1547 If liquid level should be something else, update it and
1548 add all the neighboring water nodes to the transform queue.
1550 if(new_liquid_level_max != liquid_level)
1552 if(new_liquid_level_max == -1)
1554 // Remove water alltoghether
1561 n0.param2 = new_liquid_level_max;
1565 // Block has been modified
1567 v3s16 blockpos = getNodeBlockPos(p0);
1568 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1570 modified_blocks.insert(blockpos, block);
1574 Add neighboring non-source liquid nodes to transform queue.
1577 v3s16(0,0,1), // back
1578 v3s16(0,1,0), // top
1579 v3s16(1,0,0), // right
1580 v3s16(0,0,-1), // front
1581 v3s16(0,-1,0), // bottom
1582 v3s16(-1,0,0), // left
1584 for(u16 i=0; i<6; i++)
1589 v3s16 p2 = p0 + dirs[i];
1591 MapNode n2 = getNode(p2);
1592 if(content_flowing_liquid(n2.d))
1594 m_transforming_liquid.push_back(p2);
1597 }catch(InvalidPositionException &e)
1604 // Get a new one from queue if the node has turned into non-water
1605 if(content_liquid(n0.d) == false)
1609 Flow water from this node
1611 v3s16 dirs_to[5] = {
1612 v3s16(0,-1,0), // bottom
1613 v3s16(0,0,1), // back
1614 v3s16(1,0,0), // right
1615 v3s16(0,0,-1), // front
1616 v3s16(-1,0,0), // left
1618 for(u16 i=0; i<5; i++)
1623 bool to_bottom = (i == 0);
1625 // If liquid is at lowest possible height, it's not going
1626 // anywhere except down
1627 if(liquid_level == 0 && to_bottom == false)
1630 u8 liquid_next_level = 0;
1631 // If going to bottom
1634 //liquid_next_level = 7;
1635 if(liquid_level >= 7 - WATER_DROP_BOOST)
1636 liquid_next_level = 7;
1638 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1641 liquid_next_level = liquid_level - 1;
1643 bool n2_changed = false;
1644 bool flowed = false;
1646 v3s16 p2 = p0 + dirs_to[i];
1648 MapNode n2 = getNode(p2);
1649 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1651 if(content_liquid(n2.d))
1653 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1654 // Check that the liquids are the same type
1655 if(n2_nonsource_c != nonsource_c)
1657 dstream<<"WARNING: Not handling: different liquids"
1658 " collide"<<std::endl;
1661 bool n2_is_source = !content_flowing_liquid(n2.d);
1662 u8 n2_liquid_level = 8;
1663 if(n2_is_source == false)
1664 n2_liquid_level = n2.param2 & 0x07;
1673 // Just flow into the source, nothing changes.
1674 // n2_changed is not set because destination didn't change
1679 if(liquid_next_level > liquid_level)
1681 n2.param2 = liquid_next_level;
1689 else if(n2.d == CONTENT_AIR)
1692 n2.param2 = liquid_next_level;
1699 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1703 m_transforming_liquid.push_back(p2);
1705 v3s16 blockpos = getNodeBlockPos(p2);
1706 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1708 modified_blocks.insert(blockpos, block);
1711 // If n2_changed to bottom, don't flow anywhere else
1712 if(to_bottom && flowed && !is_source)
1715 }catch(InvalidPositionException &e)
1721 //if(loopcount >= 100000)
1722 if(loopcount >= initial_size * 1)
1725 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1732 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1744 Experimental and debug stuff
1748 dstream<<"Generating map point attribute lists"<<std::endl;
1750 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1751 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1752 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1753 //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1754 //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1758 NOTE: BEWARE: Too big amount of these will make map generation
1759 slow. Especially those that are read by every block emerge.
1767 for(u32 i=0; i<500; i++)
1769 /*u32 lim = MAP_GENERATION_LIMIT;
1773 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1776 -lim + myrand()%(lim*2),
1778 -lim + myrand()%(lim*2)
1780 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1781 plants_amount = pow(plants_amount, 5);
1782 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1784 float plants_amount = 0;
1787 plants_amount = 1.5;
1789 else if(myrand()%4 == 0)
1791 plants_amount = 0.5;
1793 else if(myrand()%2 == 0)
1795 plants_amount = 0.03;
1799 plants_amount = 0.0;
1803 list_plants_amount->addPoint(p, Attribute(plants_amount));
1806 for(u32 i=0; i<500; i++)
1808 /*u32 lim = MAP_GENERATION_LIMIT;
1812 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1815 -lim + myrand()%(lim*2),
1817 -lim + myrand()%(lim*2)
1820 float caves_amount = 0;
1825 else if(myrand()%3 == 0)
1831 caves_amount = 0.05;
1834 list_caves_amount->addPoint(p, Attribute(caves_amount));
1837 for(u32 i=0; i<500; i++)
1839 /*u32 lim = MAP_GENERATION_LIMIT;
1843 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1846 -lim + (myrand()%(lim*2)),
1848 -lim + (myrand()%(lim*2))
1851 /*s32 bh_i = (myrand()%200) - 50;
1852 float baseheight = (float)bh_i;
1856 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1857 randmax = pow(randmax, e);
1859 //float randmax = (float)(myrand()%60);
1860 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1862 float baseheight = 0;
1864 float randfactor = 0;
1866 /*if(myrand()%5 == 0)
1872 else if(myrand()%6 == 0)
1878 else if(myrand()%4 == 0)
1884 else if(myrand()%3 == 0)
1910 list_baseheight->addPoint(p, Attribute(baseheight));
1911 list_randmax->addPoint(p, Attribute(randmax));
1912 list_randfactor->addPoint(p, Attribute(randfactor));
1916 // Add only one entry
1917 list_baseheight->addPoint(v3s16(0,0,0), Attribute(-4));
1918 list_randmax->addPoint(v3s16(0,0,0), Attribute(22));
1919 //list_randmax->addPoint(v3s16(0,0,0), Attribute(0));
1920 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1923 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1924 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1925 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1929 Try to load map; if not found, create a new one.
1932 m_savedir = savedir;
1933 m_map_saving_enabled = false;
1937 // If directory exists, check contents and load if possible
1938 if(fs::PathExists(m_savedir))
1940 // If directory is empty, it is safe to save into it.
1941 if(fs::GetDirListing(m_savedir).size() == 0)
1943 dstream<<DTIME<<"Server: Empty save directory is valid."
1945 m_map_saving_enabled = true;
1949 // Load master heightmap
1950 loadMasterHeightmap();
1952 // Load sector (0,0) and throw and exception on fail
1953 if(loadSectorFull(v2s16(0,0)) == false)
1954 throw LoadError("Failed to load sector (0,0)");
1956 dstream<<DTIME<<"Server: Successfully loaded master "
1957 "heightmap and sector (0,0) from "<<savedir<<
1958 ", assuming valid save directory."
1961 m_map_saving_enabled = true;
1962 // Map loaded, not creating new one
1966 // If directory doesn't exist, it is safe to save to it
1968 m_map_saving_enabled = true;
1971 catch(std::exception &e)
1973 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1974 <<", exception: "<<e.what()<<std::endl;
1975 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1976 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1979 dstream<<DTIME<<"Initializing new map."<<std::endl;
1981 // Create master heightmap
1982 m_heightmap = new UnlimitedHeightmap
1985 // Set map parameters
1988 // Create zero sector
1989 emergeSector(v2s16(0,0));
1991 // Initially write whole map
1995 ServerMap::~ServerMap()
1999 if(m_map_saving_enabled)
2002 // Save only changed parts
2004 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2008 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2011 catch(std::exception &e)
2013 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2014 <<", exception: "<<e.what()<<std::endl;
2017 if(m_heightmap != NULL)
2023 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2024 for(; i.atEnd() == false; i++)
2026 MapChunk *chunk = i.getNode()->getValue();
2032 Some helper functions
2035 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2037 v3s16 em = vmanip.m_area.getExtent();
2038 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2039 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2040 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2042 for(y=y_nodes_max; y>=y_nodes_min; y--)
2044 MapNode &n = vmanip.m_data[i];
2045 if(content_walkable(n.d))
2048 vmanip.m_area.add_y(em, i, -1);
2050 if(y >= y_nodes_min)
2056 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2058 MapNode treenode(CONTENT_TREE);
2059 MapNode leavesnode(CONTENT_LEAVES);
2061 s16 trunk_h = myrand_range(2, 6);
2063 for(s16 ii=0; ii<trunk_h; ii++)
2065 if(vmanip.m_area.contains(p1))
2066 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2070 // p1 is now the last piece of the trunk
2073 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2074 SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2075 for(s32 i=0; i<leaves_a.getVolume(); i++)
2078 // Force leaves at near the end of the trunk
2081 for(s16 z=-d; z<=d; z++)
2082 for(s16 y=-d; y<=d; y++)
2083 for(s16 x=-d; x<=d; x++)
2085 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2089 // Add leaves randomly
2090 for(u32 iii=0; iii<7; iii++)
2095 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2096 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2097 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2100 for(s16 z=0; z<=d; z++)
2101 for(s16 y=0; y<=d; y++)
2102 for(s16 x=0; x<=d; x++)
2104 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2108 // Blit leaves to vmanip
2109 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2110 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2111 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2115 if(vmanip.m_area.contains(p) == false)
2117 u32 vi = vmanip.m_area.index(p);
2118 if(vmanip.m_data[vi].d != CONTENT_AIR)
2120 u32 i = leaves_a.index(x,y,z);
2121 if(leaves_d[i] == 1)
2122 vmanip.m_data[vi] = leavesnode;
2126 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2127 core::map<v3s16, MapBlock*> &changed_blocks)
2130 Don't generate if already fully generated
2133 MapChunk *chunk = getChunk(chunkpos);
2134 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2136 dstream<<"generateChunkRaw(): Chunk "
2137 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2138 <<" already generated"<<std::endl;
2143 dstream<<"generateChunkRaw(): Generating chunk "
2144 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2147 TimeTaker timer("generateChunkRaw()");
2149 // The distance how far into the neighbors the generator is allowed to go.
2150 s16 max_spread_amount_sectors = 2;
2151 assert(max_spread_amount_sectors <= m_chunksize);
2152 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2153 // Minimum amount of space left on sides for mud to fall in
2154 s16 min_mud_fall_space = 2;
2155 // Maximum diameter of stone obstacles in X and Z
2156 s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2157 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2159 s16 y_blocks_min = -4;
2160 s16 y_blocks_max = 3;
2161 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2162 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2163 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2165 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2166 s16 sectorpos_base_size = m_chunksize;
2168 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2169 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2170 v2s16 sectorpos_bigbase =
2171 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2172 s16 sectorpos_bigbase_size =
2173 sectorpos_base_size + 2 * max_spread_amount_sectors;
2175 v3s16 bigarea_blocks_min(
2176 sectorpos_bigbase.X,
2181 v3s16 bigarea_blocks_max(
2182 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2184 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2187 // Relative values to control amount of stuff in one chunk
2188 u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2189 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;
2190 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2191 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2192 *(u32)h_blocks*MAP_BLOCKSIZE;
2195 The limiting edges of the lighting update, inclusive.
2197 s16 lighting_min_d = 0-max_spread_amount;
2198 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2201 Create the whole area of this and the neighboring chunks
2204 TimeTaker timer("generateChunkRaw() create area");
2206 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2207 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2209 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2210 ServerMapSector *sector = createSector(sectorpos);
2213 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2215 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2216 MapBlock *block = createBlock(blockpos);
2218 // Lighting won't be calculated
2219 //block->setLightingExpired(true);
2220 // Lighting will be calculated
2221 block->setLightingExpired(false);
2224 Block gets sunlight if this is true.
2226 This should be set to true when the top side of a block
2227 is completely exposed to the sky.
2229 Actually this doesn't matter now because the
2230 initial lighting is done here.
2232 block->setIsUnderground(y != y_blocks_max);
2238 Clear all light emitted
2241 core::map<v3s16, u8> unlight_from;
2244 Now we have a big empty area.
2246 Make a ManualMapVoxelManipulator that contains this and the
2250 ManualMapVoxelManipulator vmanip(this);
2251 // Add the area we just generated
2253 TimeTaker timer("generateChunkRaw() initialEmerge");
2254 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2257 TimeTaker timer_generate("generateChunkRaw() generate");
2260 Generate general ground level to full area
2265 //TimeTaker timer1("ground level");
2267 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2268 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2271 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2274 Skip of already generated
2277 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2278 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2282 // Ground height at this point
2283 float surface_y_f = 0.0;
2285 A hack to get the ground height from the sector.
2289 v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
2290 v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
2291 MapSector *sector = getSectorNoGenerate(sectorpos);
2293 float h = sector->getGroundHeight(sector_relpos);
2294 if(h > GROUNDHEIGHT_VALID_MINVALUE)
2297 dstream<<"WARNING: "<<__FUNCTION_NAME
2298 <<": sector->getGroundHeight returned bad height"<<std::endl;
2300 // Convert to integer
2301 s16 surface_y = (s16)surface_y_f;
2304 Fill ground with stone
2307 // Use fast index incrementing
2308 v3s16 em = vmanip.m_area.getExtent();
2309 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2310 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2312 vmanip.m_data[i].d = CONTENT_STONE;
2314 vmanip.m_area.add_y(em, i, 1);
2322 Randomize some parameters
2325 u32 stone_obstacle_amount = 0;
2326 if(myrand() % 2 == 0)
2327 stone_obstacle_amount = myrand_range(0, myrand_range(20, 150));
2329 stone_obstacle_amount = myrand_range(0, myrand_range(20, 50));
2330 //u32 stone_obstacle_amount =
2331 // myrand_range(0, myrand_range(20, myrand_range(80,150)));
2334 Loop this part, it will make stuff look older and newer nicely
2337 for(u32 i_age=0; i_age<2; i_age++)
2340 // This is set during the next operation.
2341 // Maximum height of the stone surface and obstacles.
2342 // This is used to disable dungeon generation from going too high.
2343 s16 stone_surface_max_y = 0;
2347 //TimeTaker timer1("stone obstacles");
2350 Add some random stone obstacles
2353 for(u32 ri=0; ri<stone_obstacle_amount/3; ri++)
2354 //for(u32 ri=0; ri<7; ri++)
2357 // Randomize max height so usually stuff will be quite low
2358 //s16 maxheight_randomized = myrand_range(0, 25);
2359 s16 maxheight_randomized = myrand_range(0, stone_obstacle_amount/3);
2361 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2363 myrand_range(5, stone_obstacle_max_size),
2364 myrand_range(0, maxheight_randomized),
2365 myrand_range(5, stone_obstacle_max_size)
2368 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2369 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2372 Limit by 1 to not obstruct sunlight at borders, because
2373 it would fuck up lighting in some places because we're
2374 leaving out removing light from the borders for optimization
2378 myrand_range(1, sectorpos_base_size*MAP_BLOCKSIZE-1-1),
2379 myrand_range(1, sectorpos_base_size*MAP_BLOCKSIZE-1-1)
2382 // Minimum space left on top of the obstacle
2383 s16 min_head_space = 12;
2385 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2386 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2388 // Node position in 2d
2389 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2391 // Find stone ground level
2392 // (ignore everything else than mud in already generated chunks)
2393 // and mud amount over the stone level
2397 v3s16 em = vmanip.m_area.getExtent();
2398 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2400 // Go to ground level
2401 for(y=y_nodes_max; y>=y_nodes_min; y--)
2403 MapNode *n = &vmanip.m_data[i];
2404 /*if(content_walkable(n.d)
2405 && n.d != CONTENT_MUD
2406 && n.d != CONTENT_GRASS)
2408 if(n->d == CONTENT_STONE)
2411 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2415 Change to mud because otherwise we might
2416 be throwing mud on grass at the next
2422 vmanip.m_area.add_y(em, i, -1);
2424 if(y >= y_nodes_min)
2427 surface_y = y_nodes_min;
2435 v3s16 em = vmanip.m_area.getExtent();
2436 s16 y_start = surface_y+1;
2437 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2441 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2443 MapNode &n = vmanip.m_data[i];
2444 n.d = CONTENT_STONE;
2446 if(y > stone_surface_max_y)
2447 stone_surface_max_y = y;
2450 if(count >= ob_size.Y)
2453 vmanip.m_area.add_y(em, i, 1);
2457 for(; y<=y_nodes_max - min_head_space; y++)
2459 MapNode &n = vmanip.m_data[i];
2462 if(count >= mud_amount)
2465 vmanip.m_area.add_y(em, i, 1);
2475 //TimeTaker timer1("dungeons");
2480 u32 dungeons_count = relative_volume / 200000;
2481 u32 bruises_count = relative_volume * stone_surface_max_y / 200000 / 50;
2482 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2484 s16 min_tunnel_diameter = 3;
2485 s16 max_tunnel_diameter = 6;
2486 u16 tunnel_routepoints = 15;
2488 bool bruise_surface = (jj < bruises_count);
2492 min_tunnel_diameter = 5;
2493 max_tunnel_diameter = myrand_range(10, 20);
2494 tunnel_routepoints = 3;
2497 // Allowed route area size in nodes
2499 sectorpos_base_size*MAP_BLOCKSIZE,
2500 h_blocks*MAP_BLOCKSIZE,
2501 sectorpos_base_size*MAP_BLOCKSIZE
2504 // Area starting point in nodes
2506 sectorpos_base.X*MAP_BLOCKSIZE,
2507 y_blocks_min*MAP_BLOCKSIZE,
2508 sectorpos_base.Y*MAP_BLOCKSIZE
2512 //(this should be more than the maximum radius of the tunnel)
2513 //s16 insure = 5; // Didn't work with max_d = 20
2515 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2516 ar += v3s16(1,0,1) * more * 2;
2517 of -= v3s16(1,0,1) * more;
2519 s16 route_y_min = 0;
2520 //s16 route_y_max = ar.Y-1;
2521 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
2522 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2526 /*// Minimum is at y=0
2527 route_y_min = -of.Y - 0;*/
2528 // Minimum is at y=max_tunnel_diameter/4
2529 //route_y_min = -of.Y + max_tunnel_diameter/4;
2530 //s16 min = -of.Y + max_tunnel_diameter/4;
2531 s16 min = -of.Y + 0;
2532 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2533 route_y_min = rangelim(route_y_min, 0, route_y_max);
2536 /*dstream<<"route_y_min = "<<route_y_min
2537 <<", route_y_max = "<<route_y_max<<std::endl;*/
2539 // Randomize starting position
2541 (float)(myrand()%ar.X)+0.5,
2542 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2543 (float)(myrand()%ar.Z)+0.5
2546 MapNode airnode(CONTENT_AIR);
2549 Generate some tunnel starting from orp
2552 for(u16 j=0; j<tunnel_routepoints; j++)
2554 v3s16 maxlen(20, 10, 20);
2558 maxlen = v3s16(60,60,60);
2562 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2563 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2564 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2569 else if(rp.X >= ar.X)
2571 if(rp.Y < route_y_min)
2573 else if(rp.Y >= route_y_max)
2574 rp.Y = route_y_max-1;
2577 else if(rp.Z >= ar.Z)
2582 s16 min_d = min_tunnel_diameter;
2583 s16 max_d = max_tunnel_diameter;
2584 s16 rs = myrand_range(min_d, max_d);
2586 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2588 v3f fp = orp + vec * f;
2589 v3s16 cp(fp.X, fp.Y, fp.Z);
2592 s16 d1 = d0 + rs - 1;
2593 for(s16 z0=d0; z0<=d1; z0++)
2595 s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2596 for(s16 x0=-si; x0<=si-1; x0++)
2598 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2599 s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2600 //s16 si2 = rs - abs(x0);
2601 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2607 /*if(isInArea(p, ar) == false)
2609 // Check only height
2610 if(y < 0 || y >= ar.Y)
2614 //assert(vmanip.m_area.contains(p));
2615 if(vmanip.m_area.contains(p) == false)
2617 dstream<<"WARNING: "<<__FUNCTION_NAME
2618 <<":"<<__LINE__<<": "
2619 <<"point not in area"
2624 // Just set it to air, it will be changed to
2626 u32 i = vmanip.m_area.index(p);
2627 vmanip.m_data[i] = airnode;
2641 //TimeTaker timer1("ore veins");
2646 for(u32 jj=0; jj<relative_volume/2000; jj++)
2648 s16 max_vein_diameter = 3;
2650 // Allowed route area size in nodes
2652 sectorpos_base_size*MAP_BLOCKSIZE,
2653 h_blocks*MAP_BLOCKSIZE,
2654 sectorpos_base_size*MAP_BLOCKSIZE
2657 // Area starting point in nodes
2659 sectorpos_base.X*MAP_BLOCKSIZE,
2660 y_blocks_min*MAP_BLOCKSIZE,
2661 sectorpos_base.Y*MAP_BLOCKSIZE
2665 //(this should be more than the maximum radius of the tunnel)
2667 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2668 ar += v3s16(1,0,1) * more * 2;
2669 of -= v3s16(1,0,1) * more;
2671 // Randomize starting position
2673 (float)(myrand()%ar.X)+0.5,
2674 (float)(myrand()%ar.Y)+0.5,
2675 (float)(myrand()%ar.Z)+0.5
2678 // Randomize mineral
2679 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2682 Generate some vein starting from orp
2685 for(u16 j=0; j<2; j++)
2688 (float)(myrand()%ar.X)+0.5,
2689 (float)(myrand()%ar.Y)+0.5,
2690 (float)(myrand()%ar.Z)+0.5
2692 v3f vec = rp - orp;*/
2694 v3s16 maxlen(10, 10, 10);
2696 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2697 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2698 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2703 else if(rp.X >= ar.X)
2707 else if(rp.Y >= ar.Y)
2711 else if(rp.Z >= ar.Z)
2717 s16 max_d = max_vein_diameter;
2718 s16 rs = myrand_range(min_d, max_d);
2720 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2722 v3f fp = orp + vec * f;
2723 v3s16 cp(fp.X, fp.Y, fp.Z);
2725 s16 d1 = d0 + rs - 1;
2726 for(s16 z0=d0; z0<=d1; z0++)
2728 s16 si = rs - abs(z0);
2729 for(s16 x0=-si; x0<=si-1; x0++)
2731 s16 si2 = rs - abs(x0);
2732 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2734 // Don't put mineral to every place
2742 /*if(isInArea(p, ar) == false)
2744 // Check only height
2745 if(y < 0 || y >= ar.Y)
2749 assert(vmanip.m_area.contains(p));
2751 // Just set it to air, it will be changed to
2753 u32 i = vmanip.m_area.index(p);
2754 MapNode *n = &vmanip.m_data[i];
2755 if(n->d == CONTENT_STONE)
2770 //TimeTaker timer1("add mud");
2773 Add mud to the central chunk
2776 s16 mud_add_amount = myrand_range(1, 5);
2778 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2779 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2781 // Node position in 2d
2782 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2784 // Find ground level
2785 s16 surface_y = find_ground_level(vmanip, p2d);
2788 If topmost node is grass, change it to mud.
2789 It might be if it was flown to there from a neighboring
2790 chunk and then converted.
2793 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2794 MapNode *n = &vmanip.m_data[i];
2795 if(n->d == CONTENT_GRASS)
2804 v3s16 em = vmanip.m_area.getExtent();
2805 s16 y_start = surface_y+1;
2806 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2807 for(s16 y=y_start; y<=y_nodes_max; y++)
2809 MapNode &n = vmanip.m_data[i];
2812 if(mudcount >= mud_add_amount)
2815 vmanip.m_area.add_y(em, i, 1);
2824 //TimeTaker timer1("flow mud");
2827 Flow mud away from steep edges
2830 // Iterate a few times
2831 for(s16 k=0; k<4; k++)
2834 /*for(s16 x=0-max_spread_amount+1;
2835 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2837 for(s16 z=0-max_spread_amount+1;
2838 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2842 Firstly, limit area by 1 because mud is flown into neighbors.
2843 Secondly, limit by 1 more to not obstruct sunlight at borders,
2844 because it would fuck up lighting in some places because we're
2845 leaving out removing light from the borders for optimization
2848 /*for(s16 x=0-max_spread_amount+2;
2849 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2851 for(s16 z=0-max_spread_amount+2;
2852 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2854 for(s16 x=0-max_spread_amount+1;
2855 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2857 for(s16 z=0-max_spread_amount+1;
2858 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2861 // Node position in 2d
2862 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2864 v3s16 em = vmanip.m_area.getExtent();
2865 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2872 for(; y>=y_nodes_min; y--)
2874 n = &vmanip.m_data[i];
2875 //if(content_walkable(n->d))
2877 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2880 vmanip.m_area.add_y(em, i, -1);
2883 // Stop if out of area
2884 //if(vmanip.m_area.contains(i) == false)
2888 /*// If not mud, do nothing to it
2889 MapNode *n = &vmanip.m_data[i];
2890 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2893 // Make it exactly mud
2896 /*s16 recurse_count = 0;
2900 v3s16(0,0,1), // back
2901 v3s16(1,0,0), // right
2902 v3s16(0,0,-1), // front
2903 v3s16(-1,0,0), // left
2908 for(u32 di=0; di<4; di++)
2910 v3s16 dirp = dirs4[di];
2913 vmanip.m_area.add_p(em, i2, dirp);
2914 // Fail if out of area
2915 if(vmanip.m_area.contains(i2) == false)
2917 // Check that side is air
2918 MapNode *n2 = &vmanip.m_data[i2];
2919 if(content_walkable(n2->d))
2921 // Check that under side is air
2922 vmanip.m_area.add_y(em, i2, -1);
2923 // Fail if out of area
2924 if(vmanip.m_area.contains(i2) == false)
2926 n2 = &vmanip.m_data[i2];
2927 if(content_walkable(n2->d))
2929 // Loop further down until not air
2931 vmanip.m_area.add_y(em, i2, -1);
2932 // Fail if out of area
2933 if(vmanip.m_area.contains(i2) == false)
2935 n2 = &vmanip.m_data[i2];
2936 }while(content_walkable(n2->d) == false);
2937 // Loop one up so that we're in air
2938 vmanip.m_area.add_y(em, i2, 1);
2939 n2 = &vmanip.m_data[i2];
2941 // Move mud to new place
2943 // Set old place to be air
2944 *n = MapNode(CONTENT_AIR);
2950 // Continue from next y
2960 //TimeTaker timer1("add water");
2963 Add water to the central chunk (and a bit more)
2966 for(s16 x=0-max_spread_amount;
2967 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2969 for(s16 z=0-max_spread_amount;
2970 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2973 // Node position in 2d
2974 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2976 // Find ground level
2977 //s16 surface_y = find_ground_level(vmanip, p2d);
2980 If ground level is over water level, skip.
2981 NOTE: This leaves caves near water without water,
2982 which looks especially crappy when the nearby water
2983 won't start flowing either for some reason
2985 /*if(surface_y > WATER_LEVEL)
2992 v3s16 em = vmanip.m_area.getExtent();
2993 s16 y_start = WATER_LEVEL;
2994 u8 light = LIGHT_MAX;
2995 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2996 MapNode *n = &vmanip.m_data[i];
2998 Add first one to transforming liquid queue
3000 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3002 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3003 m_transforming_liquid.push_back(p);
3005 for(s16 y=y_start; y>=y_nodes_min; y--)
3007 n = &vmanip.m_data[i];
3009 // Stop when there is no water and no air
3010 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3011 && n->d != CONTENT_WATER)
3014 Add bottom one to transforming liquid queue
3016 vmanip.m_area.add_y(em, i, 1);
3017 n = &vmanip.m_data[i];
3018 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3020 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3021 m_transforming_liquid.push_back(p);
3027 n->d = CONTENT_WATERSOURCE;
3028 n->setLight(LIGHTBANK_DAY, light);
3030 /*// Add to transforming liquid queue (in case it'd
3032 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3033 m_transforming_liquid.push_back(p);*/
3036 vmanip.m_area.add_y(em, i, -1);
3050 //TimeTaker timer1("plant trees");
3056 u32 tree_max = relative_area / 60;
3058 u32 count = myrand_range(0, tree_max);
3059 for(u32 i=0; i<count; i++)
3061 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3062 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3063 x += sectorpos_base.X*MAP_BLOCKSIZE;
3064 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3065 s16 y = find_ground_level(vmanip, v2s16(x,z));
3066 // Don't make a tree under water level
3071 make_tree(vmanip, p);
3079 //TimeTaker timer1("grow grass");
3085 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3086 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3087 for(s16 x=0-max_spread_amount;
3088 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3090 for(s16 z=0-max_spread_amount;
3091 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3094 // Node position in 2d
3095 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3098 Find the lowest surface to which enough light ends up
3101 Basically just wait until not air and not leaves.
3105 v3s16 em = vmanip.m_area.getExtent();
3106 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3108 // Go to ground level
3109 for(y=y_nodes_max; y>=y_nodes_min; y--)
3111 MapNode &n = vmanip.m_data[i];
3112 if(n.d != CONTENT_AIR
3113 && n.d != CONTENT_LEAVES)
3115 vmanip.m_area.add_y(em, i, -1);
3117 if(y >= y_nodes_min)
3120 surface_y = y_nodes_min;
3123 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3124 MapNode *n = &vmanip.m_data[i];
3125 if(n->d == CONTENT_MUD)
3126 n->d = CONTENT_GRASS;
3135 core::map<v3s16, bool> light_sources;
3138 // 750ms @cs=8, can't optimize more
3139 //TimeTaker timer1("initial lighting");
3143 Go through the edges and add all nodes that have light to light_sources
3147 for(s16 i=0; i<4; i++)
3149 for(s16 j=lighting_min_d;
3156 if(i == 0 || i == 1)
3158 x = (i==0) ? lighting_min_d : lighting_max_d;
3167 z = (i==0) ? lighting_min_d : lighting_max_d;
3174 // Node position in 2d
3175 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3178 v3s16 em = vmanip.m_area.getExtent();
3179 s16 y_start = y_nodes_max;
3180 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3181 for(s16 y=y_start; y>=y_nodes_min; y--)
3183 MapNode *n = &vmanip.m_data[i];
3184 if(n->getLight(LIGHTBANK_DAY) != 0)
3186 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3193 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3194 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3195 /*for(s16 x=0-max_spread_amount+1;
3196 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3198 for(s16 z=0-max_spread_amount+1;
3199 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3203 This has to be 1 smaller than the actual area, because
3204 neighboring nodes are checked.
3206 for(s16 x=lighting_min_d+1;
3207 x<=lighting_max_d-1;
3209 for(s16 z=lighting_min_d+1;
3210 z<=lighting_max_d-1;
3213 // Node position in 2d
3214 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3217 Apply initial sunlight
3220 u8 light = LIGHT_SUN;
3221 bool add_to_sources = false;
3222 v3s16 em = vmanip.m_area.getExtent();
3223 s16 y_start = y_nodes_max;
3224 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3225 for(s16 y=y_start; y>=y_nodes_min; y--)
3227 MapNode *n = &vmanip.m_data[i];
3229 if(light_propagates_content(n->d) == false)
3233 else if(light != LIGHT_SUN
3234 || sunlight_propagates_content(n->d) == false)
3240 // This doesn't take much time
3241 if(add_to_sources == false)
3244 Check sides. If side is not air or water, start
3245 adding to light_sources.
3248 v3s16(0,0,1), // back
3249 v3s16(1,0,0), // right
3250 v3s16(0,0,-1), // front
3251 v3s16(-1,0,0), // left
3253 for(u32 di=0; di<4; di++)
3255 v3s16 dirp = dirs4[di];
3257 vmanip.m_area.add_p(em, i2, dirp);
3258 MapNode *n2 = &vmanip.m_data[i2];
3260 n2->d != CONTENT_AIR
3261 && n2->d != CONTENT_WATERSOURCE
3262 && n2->d != CONTENT_WATER
3264 add_to_sources = true;
3270 n->setLight(LIGHTBANK_DAY, light);
3271 n->setLight(LIGHTBANK_NIGHT, 0);
3273 // This doesn't take much time
3274 if(light != 0 && add_to_sources)
3276 // Insert light source
3277 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3280 // Increment index by y
3281 vmanip.m_area.add_y(em, i, -1);
3288 // Spread light around
3290 TimeTaker timer("generateChunkRaw() spreadLight");
3291 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3298 timer_generate.stop();
3301 Blit generated stuff to map
3305 //TimeTaker timer("generateChunkRaw() blitBackAll");
3306 vmanip.blitBackAll(&changed_blocks);
3309 Update day/night difference cache of the MapBlocks
3312 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3313 i.atEnd() == false; i++)
3315 MapBlock *block = i.getNode()->getValue();
3316 block->updateDayNightDiff();
3322 Create chunk metadata
3325 for(s16 x=-1; x<=1; x++)
3326 for(s16 y=-1; y<=1; y++)
3328 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3329 // Add chunk meta information
3330 MapChunk *chunk = getChunk(chunkpos0);
3333 chunk = new MapChunk();
3334 m_chunks.insert(chunkpos0, chunk);
3336 //chunk->setIsVolatile(true);
3337 if(chunk->getGenLevel() > GENERATED_PARTLY)
3338 chunk->setGenLevel(GENERATED_PARTLY);
3342 Set central chunk non-volatile and return it
3344 MapChunk *chunk = getChunk(chunkpos);
3347 //chunk->setIsVolatile(false);
3348 chunk->setGenLevel(GENERATED_FULLY);
3353 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3354 core::map<v3s16, MapBlock*> &changed_blocks)
3356 dstream<<"generateChunk(): Generating chunk "
3357 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3360 /*for(s16 x=-1; x<=1; x++)
3361 for(s16 y=-1; y<=1; y++)*/
3362 for(s16 x=-0; x<=0; x++)
3363 for(s16 y=-0; y<=0; y++)
3365 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3366 MapChunk *chunk = getChunk(chunkpos0);
3367 // Skip if already generated
3368 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3370 generateChunkRaw(chunkpos0, changed_blocks);
3373 assert(chunkNonVolatile(chunkpos1));
3375 MapChunk *chunk = getChunk(chunkpos1);
3379 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3381 DSTACK("%s: p2d=(%d,%d)",
3386 Check if it exists already in memory
3388 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3393 Try to load it from disk (with blocks)
3395 if(loadSectorFull(p2d) == true)
3397 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3400 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3401 throw InvalidPositionException("");
3407 If there is no master heightmap, throw.
3409 if(m_heightmap == NULL)
3411 throw InvalidPositionException("createSector(): no heightmap");
3415 Do not create over-limit
3417 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3418 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3419 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3420 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3421 throw InvalidPositionException("createSector(): pos. over limit");
3424 Generate blank sector
3427 // Number of heightmaps in sector in each direction
3428 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3430 // Heightmap side width
3431 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3433 sector = new ServerMapSector(this, p2d, hm_split);
3435 // Sector position on map in nodes
3436 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3438 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3439 " heightmaps and objects"<<std::endl;*/
3442 Generate sector heightmap
3445 v2s16 mhm_p = p2d * hm_split;
3446 /*f32 corners[4] = {
3447 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3448 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3449 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3450 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3453 // Loop through sub-heightmaps
3454 for(s16 y=0; y<hm_split; y++)
3455 for(s16 x=0; x<hm_split; x++)
3457 v2s16 p_in_sector = v2s16(x,y);
3458 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3460 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3461 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3462 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3463 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3466 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3467 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3470 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3472 sector->setHeightmap(p_in_sector, hm);
3474 //hm->generateContinued(1.0, 0.5, corners);
3475 hm->generateContinued(0.5, 0.5, corners);
3480 // Add dummy objects
3481 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3482 sector->setObjects(objects);
3487 m_sectors.insert(p2d, sector);
3492 MapSector * ServerMap::emergeSector(v2s16 p2d,
3493 core::map<v3s16, MapBlock*> &changed_blocks)
3495 DSTACK("%s: p2d=(%d,%d)",
3502 v2s16 chunkpos = sector_to_chunk(p2d);
3503 /*bool chunk_nonvolatile = false;
3504 MapChunk *chunk = getChunk(chunkpos);
3505 if(chunk && chunk->getIsVolatile() == false)
3506 chunk_nonvolatile = true;*/
3507 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3510 If chunk is not fully generated, generate chunk
3512 if(chunk_nonvolatile == false)
3514 // Generate chunk and neighbors
3515 generateChunk(chunkpos, changed_blocks);
3519 Return sector if it exists now
3521 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3526 Try to load it from disk
3528 if(loadSectorFull(p2d) == true)
3530 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3533 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3534 throw InvalidPositionException("");
3540 generateChunk should have generated the sector
3547 //return generateSector();
3551 NOTE: This is not used for main map generation, only for blocks
3552 that are very high or low
3554 MapBlock * ServerMap::generateBlock(
3556 MapBlock *original_dummy,
3557 ServerMapSector *sector,
3558 core::map<v3s16, MapBlock*> &changed_blocks,
3559 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3562 DSTACK("%s: p=(%d,%d,%d)",
3566 /*dstream<<"generateBlock(): "
3567 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3570 MapBlock *block = original_dummy;
3572 v2s16 p2d(p.X, p.Z);
3576 Do not generate over-limit
3578 if(blockpos_over_limit(p))
3580 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3581 throw InvalidPositionException("generateBlock(): pos. over limit");
3585 If block doesn't exist, create one.
3586 If it exists, it is a dummy. In that case unDummify() it.
3588 NOTE: This already sets the map as the parent of the block
3592 block = sector->createBlankBlockNoInsert(block_y);
3596 // Remove the block so that nobody can get a half-generated one.
3597 sector->removeBlock(block);
3598 // Allocate the block to contain the generated data
3602 /*u8 water_material = CONTENT_WATER;
3603 if(g_settings.getBool("endless_water"))
3604 water_material = CONTENT_WATERSOURCE;*/
3605 u8 water_material = CONTENT_WATERSOURCE;
3607 s32 lowest_ground_y = 32767;
3608 s32 highest_ground_y = -32768;
3611 //sector->printHeightmaps();
3613 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3614 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3616 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3618 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3619 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3620 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3622 dstream<<"WARNING: Surface height not found in sector "
3623 "for block that is being emerged"<<std::endl;
3627 s16 surface_y = surface_y_f;
3628 //avg_ground_y += surface_y;
3629 if(surface_y < lowest_ground_y)
3630 lowest_ground_y = surface_y;
3631 if(surface_y > highest_ground_y)
3632 highest_ground_y = surface_y;
3634 s32 surface_depth = 0;
3636 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3638 //float min_slope = 0.45;
3639 //float max_slope = 0.85;
3640 float min_slope = 0.60;
3641 float max_slope = 1.20;
3642 float min_slope_depth = 5.0;
3643 float max_slope_depth = 0;
3645 if(slope < min_slope)
3646 surface_depth = min_slope_depth;
3647 else if(slope > max_slope)
3648 surface_depth = max_slope_depth;
3650 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3652 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3654 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3659 NOTE: If there are some man-made structures above the
3660 newly created block, they won't be taken into account.
3662 if(real_y > surface_y)
3663 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3669 // If node is over heightmap y, it's air or water
3670 if(real_y > surface_y)
3672 // If under water level, it's water
3673 if(real_y < WATER_LEVEL)
3675 n.d = water_material;
3676 n.setLight(LIGHTBANK_DAY,
3677 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3679 Add to transforming liquid queue (in case it'd
3682 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3683 m_transforming_liquid.push_back(real_pos);
3689 // Else it's ground or dungeons (air)
3692 // If it's surface_depth under ground, it's stone
3693 if(real_y <= surface_y - surface_depth)
3695 n.d = CONTENT_STONE;
3699 // It is mud if it is under the first ground
3700 // level or under water
3701 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3707 n.d = CONTENT_GRASS;
3710 //n.d = CONTENT_MUD;
3712 /*// If under water level, it's mud
3713 if(real_y < WATER_LEVEL)
3715 // Only the topmost node is grass
3716 else if(real_y <= surface_y - 1)
3719 n.d = CONTENT_GRASS;*/
3723 block->setNode(v3s16(x0,y0,z0), n);
3728 Calculate some helper variables
3731 // Completely underground if the highest part of block is under lowest
3733 // This has to be very sure; it's probably one too strict now but
3734 // that's just better.
3735 bool completely_underground =
3736 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3738 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3740 bool mostly_underwater_surface = false;
3741 if(highest_ground_y < WATER_LEVEL
3742 && some_part_underground && !completely_underground)
3743 mostly_underwater_surface = true;
3746 Get local attributes
3749 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3751 float caves_amount = 0.5;
3756 NOTE: BEWARE: Too big amount of attribute points slows verything
3758 1 interpolation from 5000 points takes 2-3ms.
3760 //TimeTaker timer("generateBlock() local attribute retrieval");
3761 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3762 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3763 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3767 //dstream<<"generateBlock(): Done"<<std::endl;
3773 // Initialize temporary table
3774 const s32 ued = MAP_BLOCKSIZE;
3775 bool underground_emptiness[ued*ued*ued];
3776 for(s32 i=0; i<ued*ued*ued; i++)
3778 underground_emptiness[i] = 0;
3785 Initialize orp and ors. Try to find if some neighboring
3786 MapBlock has a tunnel ended in its side
3790 (float)(myrand()%ued)+0.5,
3791 (float)(myrand()%ued)+0.5,
3792 (float)(myrand()%ued)+0.5
3795 bool found_existing = false;
3801 for(s16 y=0; y<ued; y++)
3802 for(s16 x=0; x<ued; x++)
3804 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3805 if(getNode(ap).d == CONTENT_AIR)
3807 orp = v3f(x+1,y+1,0);
3808 found_existing = true;
3809 goto continue_generating;
3813 catch(InvalidPositionException &e){}
3819 for(s16 y=0; y<ued; y++)
3820 for(s16 x=0; x<ued; x++)
3822 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3823 if(getNode(ap).d == CONTENT_AIR)
3825 orp = v3f(x+1,y+1,ued-1);
3826 found_existing = true;
3827 goto continue_generating;
3831 catch(InvalidPositionException &e){}
3837 for(s16 y=0; y<ued; y++)
3838 for(s16 z=0; z<ued; z++)
3840 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3841 if(getNode(ap).d == CONTENT_AIR)
3843 orp = v3f(0,y+1,z+1);
3844 found_existing = true;
3845 goto continue_generating;
3849 catch(InvalidPositionException &e){}
3855 for(s16 y=0; y<ued; y++)
3856 for(s16 z=0; z<ued; z++)
3858 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3859 if(getNode(ap).d == CONTENT_AIR)
3861 orp = v3f(ued-1,y+1,z+1);
3862 found_existing = true;
3863 goto continue_generating;
3867 catch(InvalidPositionException &e){}
3873 for(s16 x=0; x<ued; x++)
3874 for(s16 z=0; z<ued; z++)
3876 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3877 if(getNode(ap).d == CONTENT_AIR)
3879 orp = v3f(x+1,0,z+1);
3880 found_existing = true;
3881 goto continue_generating;
3885 catch(InvalidPositionException &e){}
3891 for(s16 x=0; x<ued; x++)
3892 for(s16 z=0; z<ued; z++)
3894 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3895 if(getNode(ap).d == CONTENT_AIR)
3897 orp = v3f(x+1,ued-1,z+1);
3898 found_existing = true;
3899 goto continue_generating;
3903 catch(InvalidPositionException &e){}
3905 continue_generating:
3908 Choose whether to actually generate dungeon
3910 bool do_generate_dungeons = true;
3911 // Don't generate if no part is underground
3912 if(!some_part_underground)
3914 do_generate_dungeons = false;
3916 // Don't generate if mostly underwater surface
3917 /*else if(mostly_underwater_surface)
3919 do_generate_dungeons = false;
3921 // Partly underground = cave
3922 else if(!completely_underground)
3924 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3926 // Found existing dungeon underground
3927 else if(found_existing && completely_underground)
3929 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3931 // Underground and no dungeons found
3934 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3937 if(do_generate_dungeons)
3940 Generate some tunnel starting from orp and ors
3942 for(u16 i=0; i<3; i++)
3945 (float)(myrand()%ued)+0.5,
3946 (float)(myrand()%ued)+0.5,
3947 (float)(myrand()%ued)+0.5
3951 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3955 for(float f=0; f<1.0; f+=0.04)
3957 v3f fp = orp + vec * f;
3958 v3s16 cp(fp.X, fp.Y, fp.Z);
3960 s16 d1 = d0 + rs - 1;
3961 for(s16 z0=d0; z0<=d1; z0++)
3963 s16 si = rs - abs(z0);
3964 for(s16 x0=-si; x0<=si-1; x0++)
3966 s16 si2 = rs - abs(x0);
3967 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3973 if(isInArea(p, ued) == false)
3975 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3987 // Set to true if has caves.
3988 // Set when some non-air is changed to air when making caves.
3989 bool has_dungeons = false;
3992 Apply temporary cave data to block
3995 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3996 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3998 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4000 MapNode n = block->getNode(v3s16(x0,y0,z0));
4003 if(underground_emptiness[
4004 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4005 +ued*(y0*ued/MAP_BLOCKSIZE)
4006 +(x0*ued/MAP_BLOCKSIZE)])
4008 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4011 has_dungeons = true;
4017 block->setNode(v3s16(x0,y0,z0), n);
4022 This is used for guessing whether or not the block should
4023 receive sunlight from the top if the block above doesn't exist
4025 block->setIsUnderground(completely_underground);
4028 Force lighting update if some part of block is partly
4029 underground and has caves.
4031 /*if(some_part_underground && !completely_underground && has_dungeons)
4033 //dstream<<"Half-ground caves"<<std::endl;
4034 lighting_invalidated_blocks[block->getPos()] = block;
4037 // DEBUG: Always update lighting
4038 //lighting_invalidated_blocks[block->getPos()] = block;
4044 if(some_part_underground)
4046 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4051 for(s16 i=0; i<underground_level/4 + 1; i++)
4053 if(myrand()%50 == 0)
4056 (myrand()%(MAP_BLOCKSIZE-2))+1,
4057 (myrand()%(MAP_BLOCKSIZE-2))+1,
4058 (myrand()%(MAP_BLOCKSIZE-2))+1
4064 for(u16 i=0; i<27; i++)
4066 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4068 block->setNode(cp+g_27dirs[i], n);
4076 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
4077 u16 coal_rareness = 60 / coal_amount;
4078 if(coal_rareness == 0)
4080 if(myrand()%coal_rareness == 0)
4082 u16 a = myrand() % 16;
4083 u16 amount = coal_amount * a*a*a / 1000;
4084 for(s16 i=0; i<amount; i++)
4087 (myrand()%(MAP_BLOCKSIZE-2))+1,
4088 (myrand()%(MAP_BLOCKSIZE-2))+1,
4089 (myrand()%(MAP_BLOCKSIZE-2))+1
4093 n.d = CONTENT_STONE;
4094 n.param = MINERAL_COAL;
4096 for(u16 i=0; i<27; i++)
4098 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4100 block->setNode(cp+g_27dirs[i], n);
4108 //TODO: change to iron_amount or whatever
4109 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
4110 u16 iron_rareness = 60 / iron_amount;
4111 if(iron_rareness == 0)
4113 if(myrand()%iron_rareness == 0)
4115 u16 a = myrand() % 16;
4116 u16 amount = iron_amount * a*a*a / 1000;
4117 for(s16 i=0; i<amount; i++)
4120 (myrand()%(MAP_BLOCKSIZE-2))+1,
4121 (myrand()%(MAP_BLOCKSIZE-2))+1,
4122 (myrand()%(MAP_BLOCKSIZE-2))+1
4126 n.d = CONTENT_STONE;
4127 n.param = MINERAL_IRON;
4129 for(u16 i=0; i<27; i++)
4131 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4133 block->setNode(cp+g_27dirs[i], n);
4140 Create a few rats in empty blocks underground
4142 if(completely_underground)
4144 //for(u16 i=0; i<2; i++)
4147 (myrand()%(MAP_BLOCKSIZE-2))+1,
4148 (myrand()%(MAP_BLOCKSIZE-2))+1,
4149 (myrand()%(MAP_BLOCKSIZE-2))+1
4152 // Check that the place is empty
4153 //if(!is_ground_content(block->getNode(cp).d))
4156 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4157 block->addObject(obj);
4163 Add block to sector.
4165 sector->insertBlock(block);
4171 // An y-wise container of changed blocks
4172 core::map<s16, MapBlock*> changed_blocks_sector;
4175 Check if any sector's objects can be placed now.
4178 core::map<v3s16, u8> *objects = sector->getObjects();
4179 core::list<v3s16> objects_to_remove;
4180 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
4181 i.atEnd() == false; i++)
4183 v3s16 p = i.getNode()->getKey();
4185 u8 d = i.getNode()->getValue();
4187 // Ground level point (user for stuff that is on ground)
4189 bool ground_found = true;
4191 // Search real ground level
4195 MapNode n = sector->getNode(gp);
4197 // If not air, go one up and continue to placing the tree
4198 if(n.d != CONTENT_AIR)
4204 // If air, go one down
4205 gp += v3s16(0,-1,0);
4207 }catch(InvalidPositionException &e)
4209 // Ground not found.
4210 ground_found = false;
4211 // This is most close to ground
4218 if(d == SECTOR_OBJECT_TEST)
4220 if(sector->isValidArea(p + v3s16(0,0,0),
4221 p + v3s16(0,0,0), &changed_blocks_sector))
4224 n.d = CONTENT_TORCH;
4225 sector->setNode(p, n);
4226 objects_to_remove.push_back(p);
4229 else if(d == SECTOR_OBJECT_TREE_1)
4231 if(ground_found == false)
4234 v3s16 p_min = gp + v3s16(-1,0,-1);
4235 v3s16 p_max = gp + v3s16(1,5,1);
4236 if(sector->isValidArea(p_min, p_max,
4237 &changed_blocks_sector))
4241 sector->setNode(gp+v3s16(0,0,0), n);
4242 sector->setNode(gp+v3s16(0,1,0), n);
4243 sector->setNode(gp+v3s16(0,2,0), n);
4244 sector->setNode(gp+v3s16(0,3,0), n);
4246 n.d = CONTENT_LEAVES;
4248 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4250 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4251 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4252 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4253 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4254 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4255 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4256 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4257 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4259 sector->setNode(gp+v3s16(0,4,0), n);
4261 sector->setNode(gp+v3s16(-1,4,0), n);
4262 sector->setNode(gp+v3s16(1,4,0), n);
4263 sector->setNode(gp+v3s16(0,4,-1), n);
4264 sector->setNode(gp+v3s16(0,4,1), n);
4265 sector->setNode(gp+v3s16(1,4,1), n);
4266 sector->setNode(gp+v3s16(-1,4,1), n);
4267 sector->setNode(gp+v3s16(-1,4,-1), n);
4268 sector->setNode(gp+v3s16(1,4,-1), n);
4270 sector->setNode(gp+v3s16(-1,3,0), n);
4271 sector->setNode(gp+v3s16(1,3,0), n);
4272 sector->setNode(gp+v3s16(0,3,-1), n);
4273 sector->setNode(gp+v3s16(0,3,1), n);
4274 sector->setNode(gp+v3s16(1,3,1), n);
4275 sector->setNode(gp+v3s16(-1,3,1), n);
4276 sector->setNode(gp+v3s16(-1,3,-1), n);
4277 sector->setNode(gp+v3s16(1,3,-1), n);
4279 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4280 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4281 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4282 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4283 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4284 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4285 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4286 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4288 // Objects are identified by wanted position
4289 objects_to_remove.push_back(p);
4291 // Lighting has to be recalculated for this one.
4292 sector->getBlocksInArea(p_min, p_max,
4293 lighting_invalidated_blocks);
4296 else if(d == SECTOR_OBJECT_BUSH_1)
4298 if(ground_found == false)
4301 if(sector->isValidArea(gp + v3s16(0,0,0),
4302 gp + v3s16(0,0,0), &changed_blocks_sector))
4305 n.d = CONTENT_LEAVES;
4306 sector->setNode(gp+v3s16(0,0,0), n);
4308 // Objects are identified by wanted position
4309 objects_to_remove.push_back(p);
4312 else if(d == SECTOR_OBJECT_RAVINE)
4315 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4316 v3s16 p_max = p + v3s16(6,6,6);
4317 if(sector->isValidArea(p_min, p_max,
4318 &changed_blocks_sector))
4321 n.d = CONTENT_STONE;
4324 s16 depth = maxdepth + (myrand()%10);
4326 s16 minz = -6 - (-2);
4328 for(s16 x=-6; x<=6; x++)
4330 z += -1 + (myrand()%3);
4335 for(s16 y=depth+(myrand()%2); y<=6; y++)
4337 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4340 v3s16 p2 = p + v3s16(x,y,z-2);
4341 //if(is_ground_content(sector->getNode(p2).d))
4342 if(content_features(sector->getNode(p2).d).walkable)
4343 sector->setNode(p2, n);
4346 v3s16 p2 = p + v3s16(x,y,z-1);
4347 if(content_features(sector->getNode(p2).d).walkable)
4348 sector->setNode(p2, n2);
4351 v3s16 p2 = p + v3s16(x,y,z+0);
4352 if(content_features(sector->getNode(p2).d).walkable)
4353 sector->setNode(p2, n2);
4356 v3s16 p2 = p + v3s16(x,y,z+1);
4357 if(content_features(sector->getNode(p2).d).walkable)
4358 sector->setNode(p2, n);
4361 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4362 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4366 objects_to_remove.push_back(p);
4368 // Lighting has to be recalculated for this one.
4369 sector->getBlocksInArea(p_min, p_max,
4370 lighting_invalidated_blocks);
4375 dstream<<"ServerMap::generateBlock(): "
4376 "Invalid heightmap object"
4381 catch(InvalidPositionException &e)
4383 dstream<<"WARNING: "<<__FUNCTION_NAME
4384 <<": while inserting object "<<(int)d
4385 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4386 <<" InvalidPositionException.what()="
4387 <<e.what()<<std::endl;
4388 // This is not too fatal and seems to happen sometimes.
4393 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4394 i != objects_to_remove.end(); i++)
4396 objects->remove(*i);
4400 Translate sector's changed blocks to global changed blocks
4403 for(core::map<s16, MapBlock*>::Iterator
4404 i = changed_blocks_sector.getIterator();
4405 i.atEnd() == false; i++)
4407 MapBlock *block = i.getNode()->getValue();
4409 changed_blocks.insert(block->getPos(), block);
4412 block->setLightingExpired(true);
4419 <<"lighting_invalidated_blocks.size()"
4423 <<" "<<lighting_invalidated_blocks.size()
4424 <<", "<<has_dungeons
4425 <<", "<<completely_underground
4426 <<", "<<some_part_underground
4433 MapBlock * ServerMap::createBlock(v3s16 p)
4435 DSTACK("%s: p=(%d,%d,%d)",
4436 __FUNCTION_NAME, p.X, p.Y, p.Z);
4438 v2s16 p2d(p.X, p.Z);
4441 This will create or load a sector if not found in memory.
4442 If block exists on disk, it will be loaded.
4444 NOTE: On old save formats, this will be slow, as it generates
4445 lighting on blocks for them.
4447 ServerMapSector *sector;
4449 sector = (ServerMapSector*)createSector(p2d);
4450 assert(sector->getId() == MAPSECTOR_SERVER);
4452 /*catch(InvalidPositionException &e)
4454 dstream<<"createBlock: createSector() failed"<<std::endl;
4457 catch(std::exception &e)
4459 dstream<<"createBlock: createSector() failed: "
4460 <<e.what()<<std::endl;
4465 Try to get a block from the sector
4468 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4472 block = sector->createBlankBlock(block_y);
4476 MapBlock * ServerMap::emergeBlock(
4478 bool only_from_disk,
4479 core::map<v3s16, MapBlock*> &changed_blocks,
4480 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4483 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4485 p.X, p.Y, p.Z, only_from_disk);
4487 v2s16 p2d(p.X, p.Z);
4490 This will create or load a sector if not found in memory.
4491 If block exists on disk, it will be loaded.
4493 NOTE: On old save formats, this will be slow, as it generates
4494 lighting on blocks for them.
4496 ServerMapSector *sector;
4498 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4499 assert(sector->getId() == MAPSECTOR_SERVER);
4501 catch(std::exception &e)
4503 dstream<<"emergeBlock: emergeSector() failed: "
4504 <<e.what()<<std::endl;
4509 Try to get a block from the sector
4512 bool does_not_exist = false;
4513 bool lighting_expired = false;
4514 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4518 does_not_exist = true;
4520 else if(block->isDummy() == true)
4522 does_not_exist = true;
4524 else if(block->getLightingExpired())
4526 lighting_expired = true;
4531 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4536 If block was not found on disk and not going to generate a
4537 new one, make sure there is a dummy block in place.
4539 if(only_from_disk && (does_not_exist || lighting_expired))
4541 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4545 // Create dummy block
4546 block = new MapBlock(this, p, true);
4548 // Add block to sector
4549 sector->insertBlock(block);
4555 //dstream<<"Not found on disk, generating."<<std::endl;
4557 //TimeTaker("emergeBlock() generate");
4559 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4562 If the block doesn't exist, generate the block.
4566 block = generateBlock(p, block, sector, changed_blocks,
4567 lighting_invalidated_blocks);
4570 if(lighting_expired)
4572 lighting_invalidated_blocks.insert(p, block);
4576 Initially update sunlight
4580 core::map<v3s16, bool> light_sources;
4581 bool black_air_left = false;
4582 bool bottom_invalid =
4583 block->propagateSunlight(light_sources, true,
4584 &black_air_left, true);
4586 // If sunlight didn't reach everywhere and part of block is
4587 // above ground, lighting has to be properly updated
4588 //if(black_air_left && some_part_underground)
4591 lighting_invalidated_blocks[block->getPos()] = block;
4596 lighting_invalidated_blocks[block->getPos()] = block;
4601 Debug mode operation
4603 bool haxmode = g_settings.getBool("haxmode");
4606 // Don't calculate lighting at all
4607 //lighting_invalidated_blocks.clear();
4613 void ServerMap::createDir(std::string path)
4615 if(fs::CreateDir(path) == false)
4617 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4618 <<"\""<<path<<"\""<<std::endl;
4619 throw BaseException("ServerMap failed to create directory");
4623 std::string ServerMap::getSectorSubDir(v2s16 pos)
4626 snprintf(cc, 9, "%.4x%.4x",
4627 (unsigned int)pos.X&0xffff,
4628 (unsigned int)pos.Y&0xffff);
4630 return std::string(cc);
4633 std::string ServerMap::getSectorDir(v2s16 pos)
4635 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4638 v2s16 ServerMap::getSectorPos(std::string dirname)
4640 if(dirname.size() != 8)
4641 throw InvalidFilenameException("Invalid sector directory name");
4643 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4645 throw InvalidFilenameException("Invalid sector directory name");
4646 v2s16 pos((s16)x, (s16)y);
4650 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4652 v2s16 p2d = getSectorPos(sectordir);
4654 if(blockfile.size() != 4){
4655 throw InvalidFilenameException("Invalid block filename");
4658 int r = sscanf(blockfile.c_str(), "%4x", &y);
4660 throw InvalidFilenameException("Invalid block filename");
4661 return v3s16(p2d.X, y, p2d.Y);
4665 #define ENABLE_SECTOR_SAVING 1
4666 #define ENABLE_SECTOR_LOADING 1
4667 #define ENABLE_BLOCK_SAVING 1
4668 #define ENABLE_BLOCK_LOADING 1
4670 void ServerMap::save(bool only_changed)
4672 DSTACK(__FUNCTION_NAME);
4673 if(m_map_saving_enabled == false)
4675 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4679 if(only_changed == false)
4680 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4683 saveMasterHeightmap();
4685 u32 sector_meta_count = 0;
4686 u32 block_count = 0;
4689 JMutexAutoLock lock(m_sector_mutex);
4691 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4692 for(; i.atEnd() == false; i++)
4694 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4695 assert(sector->getId() == MAPSECTOR_SERVER);
4697 if(ENABLE_SECTOR_SAVING)
4699 if(sector->differs_from_disk || only_changed == false)
4701 saveSectorMeta(sector);
4702 sector_meta_count++;
4705 if(ENABLE_BLOCK_SAVING)
4707 core::list<MapBlock*> blocks;
4708 sector->getBlocks(blocks);
4709 core::list<MapBlock*>::Iterator j;
4710 for(j=blocks.begin(); j!=blocks.end(); j++)
4712 MapBlock *block = *j;
4713 if(block->getChangedFlag() || only_changed == false)
4718 /*dstream<<"ServerMap: Written block ("
4719 <<block->getPos().X<<","
4720 <<block->getPos().Y<<","
4721 <<block->getPos().Z<<")"
4731 Only print if something happened or saved whole map
4733 if(only_changed == false || sector_meta_count != 0
4734 || block_count != 0)
4736 dstream<<DTIME<<"ServerMap: Written: "
4737 <<sector_meta_count<<" sector metadata files, "
4738 <<block_count<<" block files"
4743 void ServerMap::loadAll()
4745 DSTACK(__FUNCTION_NAME);
4746 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4748 loadMasterHeightmap();
4750 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4752 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4754 JMutexAutoLock lock(m_sector_mutex);
4757 s32 printed_counter = -100000;
4758 s32 count = list.size();
4760 std::vector<fs::DirListNode>::iterator i;
4761 for(i=list.begin(); i!=list.end(); i++)
4763 if(counter > printed_counter + 10)
4765 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4766 printed_counter = counter;
4770 MapSector *sector = NULL;
4772 // We want directories
4776 sector = loadSectorMeta(i->name);
4778 catch(InvalidFilenameException &e)
4780 // This catches unknown crap in directory
4783 if(ENABLE_BLOCK_LOADING)
4785 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4786 (m_savedir+"/sectors/"+i->name);
4787 std::vector<fs::DirListNode>::iterator i2;
4788 for(i2=list2.begin(); i2!=list2.end(); i2++)
4794 loadBlock(i->name, i2->name, sector);
4796 catch(InvalidFilenameException &e)
4798 // This catches unknown crap in directory
4803 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4806 void ServerMap::saveMasterHeightmap()
4808 DSTACK(__FUNCTION_NAME);
4809 createDir(m_savedir);
4811 std::string fullpath = m_savedir + "/master_heightmap";
4812 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4813 if(o.good() == false)
4814 throw FileNotGoodException("Cannot open master heightmap");
4816 // Format used for writing
4817 u8 version = SER_FMT_VER_HIGHEST;
4820 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4822 [0] u8 serialization version
4823 [1] X master heightmap
4825 u32 fullsize = 1 + hmdata.getSize();
4826 SharedBuffer<u8> data(fullsize);
4829 memcpy(&data[1], *hmdata, hmdata.getSize());
4831 o.write((const char*)*data, fullsize);
4834 m_heightmap->serialize(o, version);
4837 void ServerMap::loadMasterHeightmap()
4839 DSTACK(__FUNCTION_NAME);
4840 std::string fullpath = m_savedir + "/master_heightmap";
4841 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4842 if(is.good() == false)
4843 throw FileNotGoodException("Cannot open master heightmap");
4845 if(m_heightmap != NULL)
4848 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4851 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4853 DSTACK(__FUNCTION_NAME);
4854 // Format used for writing
4855 u8 version = SER_FMT_VER_HIGHEST;
4857 v2s16 pos = sector->getPos();
4858 createDir(m_savedir);
4859 createDir(m_savedir+"/sectors");
4860 std::string dir = getSectorDir(pos);
4863 std::string fullpath = dir + "/heightmap";
4864 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4865 if(o.good() == false)
4866 throw FileNotGoodException("Cannot open master heightmap");
4868 sector->serialize(o, version);
4870 sector->differs_from_disk = false;
4873 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4875 DSTACK(__FUNCTION_NAME);
4877 v2s16 p2d = getSectorPos(dirname);
4878 std::string dir = m_savedir + "/sectors/" + dirname;
4880 std::string fullpath = dir + "/heightmap";
4881 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4882 if(is.good() == false)
4883 throw FileNotGoodException("Cannot open sector heightmap");
4885 ServerMapSector *sector = ServerMapSector::deSerialize
4886 (is, this, p2d, &m_hwrapper, m_sectors);
4888 sector->differs_from_disk = false;
4893 bool ServerMap::loadSectorFull(v2s16 p2d)
4895 DSTACK(__FUNCTION_NAME);
4896 std::string sectorsubdir = getSectorSubDir(p2d);
4898 MapSector *sector = NULL;
4900 JMutexAutoLock lock(m_sector_mutex);
4903 sector = loadSectorMeta(sectorsubdir);
4905 catch(InvalidFilenameException &e)
4909 catch(FileNotGoodException &e)
4913 catch(std::exception &e)
4918 if(ENABLE_BLOCK_LOADING)
4920 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4921 (m_savedir+"/sectors/"+sectorsubdir);
4922 std::vector<fs::DirListNode>::iterator i2;
4923 for(i2=list2.begin(); i2!=list2.end(); i2++)
4929 loadBlock(sectorsubdir, i2->name, sector);
4931 catch(InvalidFilenameException &e)
4933 // This catches unknown crap in directory
4941 bool ServerMap::deFlushSector(v2s16 p2d)
4943 DSTACK(__FUNCTION_NAME);
4944 // See if it already exists in memory
4946 MapSector *sector = getSectorNoGenerate(p2d);
4949 catch(InvalidPositionException &e)
4952 Try to load the sector from disk.
4954 if(loadSectorFull(p2d) == true)
4963 void ServerMap::saveBlock(MapBlock *block)
4965 DSTACK(__FUNCTION_NAME);
4967 Dummy blocks are not written
4969 if(block->isDummy())
4971 /*v3s16 p = block->getPos();
4972 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4973 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4977 // Format used for writing
4978 u8 version = SER_FMT_VER_HIGHEST;
4980 v3s16 p3d = block->getPos();
4981 v2s16 p2d(p3d.X, p3d.Z);
4982 createDir(m_savedir);
4983 createDir(m_savedir+"/sectors");
4984 std::string dir = getSectorDir(p2d);
4987 // Block file is map/sectors/xxxxxxxx/xxxx
4989 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4990 std::string fullpath = dir + "/" + cc;
4991 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4992 if(o.good() == false)
4993 throw FileNotGoodException("Cannot open block data");
4996 [0] u8 serialization version
4999 o.write((char*)&version, 1);
5001 block->serialize(o, version);
5004 Versions up from 9 have block objects.
5008 block->serializeObjects(o, version);
5011 // We just wrote it to the disk
5012 block->resetChangedFlag();
5015 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5017 DSTACK(__FUNCTION_NAME);
5021 // Block file is map/sectors/xxxxxxxx/xxxx
5022 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5023 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5024 if(is.good() == false)
5025 throw FileNotGoodException("Cannot open block file");
5027 v3s16 p3d = getBlockPos(sectordir, blockfile);
5028 v2s16 p2d(p3d.X, p3d.Z);
5030 assert(sector->getPos() == p2d);
5032 u8 version = SER_FMT_VER_INVALID;
5033 is.read((char*)&version, 1);
5035 /*u32 block_size = MapBlock::serializedLength(version);
5036 SharedBuffer<u8> data(block_size);
5037 is.read((char*)*data, block_size);*/
5039 // This will always return a sector because we're the server
5040 //MapSector *sector = emergeSector(p2d);
5042 MapBlock *block = NULL;
5043 bool created_new = false;
5045 block = sector->getBlockNoCreate(p3d.Y);
5047 catch(InvalidPositionException &e)
5049 block = sector->createBlankBlockNoInsert(p3d.Y);
5053 // deserialize block data
5054 block->deSerialize(is, version);
5057 Versions up from 9 have block objects.
5061 block->updateObjects(is, version, NULL, 0);
5065 sector->insertBlock(block);
5068 Convert old formats to new and save
5071 // Save old format blocks in new format
5072 if(version < SER_FMT_VER_HIGHEST)
5077 // We just loaded it from the disk, so it's up-to-date.
5078 block->resetChangedFlag();
5081 catch(SerializationError &e)
5083 dstream<<"WARNING: Invalid block data on disk "
5084 "(SerializationError). Ignoring."
5089 // Gets from master heightmap
5090 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5092 assert(m_heightmap != NULL);
5100 corners[0] = m_heightmap->getGroundHeight
5101 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5102 corners[1] = m_heightmap->getGroundHeight
5103 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5104 corners[2] = m_heightmap->getGroundHeight
5105 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5106 corners[3] = m_heightmap->getGroundHeight
5107 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
5110 void ServerMap::PrintInfo(std::ostream &out)
5121 ClientMap::ClientMap(
5123 MapDrawControl &control,
5124 scene::ISceneNode* parent,
5125 scene::ISceneManager* mgr,
5129 scene::ISceneNode(parent, mgr, id),
5136 /*m_box = core::aabbox3d<f32>(0,0,0,
5137 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5138 /*m_box = core::aabbox3d<f32>(0,0,0,
5139 map->getSizeNodes().X * BS,
5140 map->getSizeNodes().Y * BS,
5141 map->getSizeNodes().Z * BS);*/
5142 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5143 BS*1000000,BS*1000000,BS*1000000);
5145 //setPosition(v3f(BS,BS,BS));
5148 ClientMap::~ClientMap()
5150 JMutexAutoLock lock(mesh_mutex);
5159 MapSector * ClientMap::emergeSector(v2s16 p2d)
5161 DSTACK(__FUNCTION_NAME);
5162 // Check that it doesn't exist already
5164 return getSectorNoGenerate(p2d);
5166 catch(InvalidPositionException &e)
5170 // Create a sector with no heightmaps
5171 ClientMapSector *sector = new ClientMapSector(this, p2d);
5174 JMutexAutoLock lock(m_sector_mutex);
5175 m_sectors.insert(p2d, sector);
5181 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5183 DSTACK(__FUNCTION_NAME);
5184 ClientMapSector *sector = NULL;
5186 JMutexAutoLock lock(m_sector_mutex);
5188 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5192 sector = (ClientMapSector*)n->getValue();
5193 assert(sector->getId() == MAPSECTOR_CLIENT);
5197 sector = new ClientMapSector(this, p2d);
5199 JMutexAutoLock lock(m_sector_mutex);
5200 m_sectors.insert(p2d, sector);
5204 sector->deSerialize(is);
5207 void ClientMap::OnRegisterSceneNode()
5211 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5212 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5215 ISceneNode::OnRegisterSceneNode();
5218 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5220 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5221 DSTACK(__FUNCTION_NAME);
5223 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5226 Get time for measuring timeout.
5228 Measuring time is very useful for long delays when the
5229 machine is swapping a lot.
5231 int time1 = time(0);
5233 u32 daynight_ratio = m_client->getDayNightRatio();
5235 m_camera_mutex.Lock();
5236 v3f camera_position = m_camera_position;
5237 v3f camera_direction = m_camera_direction;
5238 m_camera_mutex.Unlock();
5241 Get all blocks and draw all visible ones
5244 v3s16 cam_pos_nodes(
5245 camera_position.X / BS,
5246 camera_position.Y / BS,
5247 camera_position.Z / BS);
5249 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5251 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5252 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5254 // Take a fair amount as we will be dropping more out later
5256 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5257 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5258 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5260 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5261 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5262 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5264 u32 vertex_count = 0;
5266 // For limiting number of mesh updates per frame
5267 u32 mesh_update_count = 0;
5269 u32 blocks_would_have_drawn = 0;
5270 u32 blocks_drawn = 0;
5272 //NOTE: The sectors map should be locked but we're not doing it
5273 // because it'd cause too much delays
5275 int timecheck_counter = 0;
5276 core::map<v2s16, MapSector*>::Iterator si;
5277 si = m_sectors.getIterator();
5278 for(; si.atEnd() == false; si++)
5281 timecheck_counter++;
5282 if(timecheck_counter > 50)
5284 int time2 = time(0);
5285 if(time2 > time1 + 4)
5287 dstream<<"ClientMap::renderMap(): "
5288 "Rendering takes ages, returning."
5295 MapSector *sector = si.getNode()->getValue();
5296 v2s16 sp = sector->getPos();
5298 if(m_control.range_all == false)
5300 if(sp.X < p_blocks_min.X
5301 || sp.X > p_blocks_max.X
5302 || sp.Y < p_blocks_min.Z
5303 || sp.Y > p_blocks_max.Z)
5307 core::list< MapBlock * > sectorblocks;
5308 sector->getBlocks(sectorblocks);
5314 core::list< MapBlock * >::Iterator i;
5315 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5317 MapBlock *block = *i;
5320 Compare block position to camera position, skip
5321 if not seen on display
5324 float range = 100000 * BS;
5325 if(m_control.range_all == false)
5326 range = m_control.wanted_range * BS;
5328 if(isBlockInSight(block->getPos(), camera_position,
5329 camera_direction, range) == false)
5335 v3s16 blockpos_nodes = block->getPosRelative();
5337 // Block center position
5339 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5340 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5341 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5344 // Block position relative to camera
5345 v3f blockpos_relative = blockpos - camera_position;
5347 // Distance in camera direction (+=front, -=back)
5348 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5351 f32 d = blockpos_relative.getLength();
5353 if(m_control.range_all == false)
5355 // If block is far away, don't draw it
5356 if(d > m_control.wanted_range * BS)
5360 // Maximum radius of a block
5361 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5363 // If block is (nearly) touching the camera, don't
5364 // bother validating further (that is, render it anyway)
5365 if(d > block_max_radius * 1.5)
5367 // Cosine of the angle between the camera direction
5368 // and the block direction (camera_direction is an unit vector)
5369 f32 cosangle = dforward / d;
5371 // Compensate for the size of the block
5372 // (as the block has to be shown even if it's a bit off FOV)
5373 // This is an estimate.
5374 cosangle += block_max_radius / dforward;
5376 // If block is not in the field of view, skip it
5377 //if(cosangle < cos(FOV_ANGLE/2))
5378 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5383 v3s16 blockpos_nodes = block->getPosRelative();
5385 // Block center position
5387 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5388 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5389 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5392 // Block position relative to camera
5393 v3f blockpos_relative = blockpos - camera_position;
5396 f32 d = blockpos_relative.getLength();
5403 bool mesh_expired = false;
5406 JMutexAutoLock lock(block->mesh_mutex);
5408 mesh_expired = block->getMeshExpired();
5410 // Mesh has not been expired and there is no mesh:
5411 // block has no content
5412 if(block->mesh == NULL && mesh_expired == false)
5416 f32 faraway = BS*50;
5417 //f32 faraway = m_control.wanted_range * BS;
5420 This has to be done with the mesh_mutex unlocked
5422 // Pretty random but this should work somewhat nicely
5423 if(mesh_expired && (
5424 (mesh_update_count < 3
5425 && (d < faraway || mesh_update_count < 2)
5428 (m_control.range_all && mesh_update_count < 20)
5431 /*if(mesh_expired && mesh_update_count < 6
5432 && (d < faraway || mesh_update_count < 3))*/
5434 mesh_update_count++;
5436 // Mesh has been expired: generate new mesh
5437 //block->updateMeshes(daynight_i);
5438 block->updateMesh(daynight_ratio);
5440 mesh_expired = false;
5444 Don't draw an expired mesh that is far away
5446 /*if(mesh_expired && d >= faraway)
5449 // Instead, delete it
5450 JMutexAutoLock lock(block->mesh_mutex);
5453 block->mesh->drop();
5456 // And continue to next block
5461 Draw the faces of the block
5464 JMutexAutoLock lock(block->mesh_mutex);
5466 scene::SMesh *mesh = block->mesh;
5471 blocks_would_have_drawn++;
5472 if(blocks_drawn >= m_control.wanted_max_blocks
5473 && m_control.range_all == false
5474 && d > m_control.wanted_min_range * BS)
5478 u32 c = mesh->getMeshBufferCount();
5480 for(u32 i=0; i<c; i++)
5482 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5483 const video::SMaterial& material = buf->getMaterial();
5484 video::IMaterialRenderer* rnd =
5485 driver->getMaterialRenderer(material.MaterialType);
5486 bool transparent = (rnd && rnd->isTransparent());
5487 // Render transparent on transparent pass and likewise.
5488 if(transparent == is_transparent_pass)
5490 driver->setMaterial(buf->getMaterial());
5491 driver->drawMeshBuffer(buf);
5492 vertex_count += buf->getVertexCount();
5496 } // foreach sectorblocks
5499 m_control.blocks_drawn = blocks_drawn;
5500 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5502 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5503 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5506 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5507 core::map<v3s16, MapBlock*> *affected_blocks)
5509 bool changed = false;
5511 Add it to all blocks touching it
5514 v3s16(0,0,0), // this
5515 v3s16(0,0,1), // back
5516 v3s16(0,1,0), // top
5517 v3s16(1,0,0), // right
5518 v3s16(0,0,-1), // front
5519 v3s16(0,-1,0), // bottom
5520 v3s16(-1,0,0), // left
5522 for(u16 i=0; i<7; i++)
5524 v3s16 p2 = p + dirs[i];
5525 // Block position of neighbor (or requested) node
5526 v3s16 blockpos = getNodeBlockPos(p2);
5527 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5528 if(blockref == NULL)
5530 // Relative position of requested node
5531 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5532 if(blockref->setTempMod(relpos, mod))
5537 if(changed && affected_blocks!=NULL)
5539 for(u16 i=0; i<7; i++)
5541 v3s16 p2 = p + dirs[i];
5542 // Block position of neighbor (or requested) node
5543 v3s16 blockpos = getNodeBlockPos(p2);
5544 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5545 if(blockref == NULL)
5547 affected_blocks->insert(blockpos, blockref);
5553 bool ClientMap::clearTempMod(v3s16 p,
5554 core::map<v3s16, MapBlock*> *affected_blocks)
5556 bool changed = false;
5558 v3s16(0,0,0), // this
5559 v3s16(0,0,1), // back
5560 v3s16(0,1,0), // top
5561 v3s16(1,0,0), // right
5562 v3s16(0,0,-1), // front
5563 v3s16(0,-1,0), // bottom
5564 v3s16(-1,0,0), // left
5566 for(u16 i=0; i<7; i++)
5568 v3s16 p2 = p + dirs[i];
5569 // Block position of neighbor (or requested) node
5570 v3s16 blockpos = getNodeBlockPos(p2);
5571 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5572 if(blockref == NULL)
5574 // Relative position of requested node
5575 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5576 if(blockref->clearTempMod(relpos))
5581 if(changed && affected_blocks!=NULL)
5583 for(u16 i=0; i<7; i++)
5585 v3s16 p2 = p + dirs[i];
5586 // Block position of neighbor (or requested) node
5587 v3s16 blockpos = getNodeBlockPos(p2);
5588 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5589 if(blockref == NULL)
5591 affected_blocks->insert(blockpos, blockref);
5597 void ClientMap::PrintInfo(std::ostream &out)
5608 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5613 MapVoxelManipulator::~MapVoxelManipulator()
5615 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5619 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5621 TimeTaker timer1("emerge", &emerge_time);
5623 // Units of these are MapBlocks
5624 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5625 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5627 VoxelArea block_area_nodes
5628 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5630 addArea(block_area_nodes);
5632 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5633 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5634 for(s32 x=p_min.X; x<=p_max.X; x++)
5637 core::map<v3s16, bool>::Node *n;
5638 n = m_loaded_blocks.find(p);
5642 bool block_data_inexistent = false;
5645 TimeTaker timer1("emerge load", &emerge_load_time);
5647 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5648 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5651 dstream<<std::endl;*/
5653 MapBlock *block = m_map->getBlockNoCreate(p);
5654 if(block->isDummy())
5655 block_data_inexistent = true;
5657 block->copyTo(*this);
5659 catch(InvalidPositionException &e)
5661 block_data_inexistent = true;
5664 if(block_data_inexistent)
5666 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5667 // Fill with VOXELFLAG_INEXISTENT
5668 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5669 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5671 s32 i = m_area.index(a.MinEdge.X,y,z);
5672 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5676 m_loaded_blocks.insert(p, !block_data_inexistent);
5679 //dstream<<"emerge done"<<std::endl;
5683 SUGG: Add an option to only update eg. water and air nodes.
5684 This will make it interfere less with important stuff if
5687 void MapVoxelManipulator::blitBack
5688 (core::map<v3s16, MapBlock*> & modified_blocks)
5690 if(m_area.getExtent() == v3s16(0,0,0))
5693 //TimeTaker timer1("blitBack");
5695 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5696 <<m_loaded_blocks.size()<<std::endl;*/
5699 Initialize block cache
5701 v3s16 blockpos_last;
5702 MapBlock *block = NULL;
5703 bool block_checked_in_modified = false;
5705 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5706 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5707 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5711 u8 f = m_flags[m_area.index(p)];
5712 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5715 MapNode &n = m_data[m_area.index(p)];
5717 v3s16 blockpos = getNodeBlockPos(p);
5722 if(block == NULL || blockpos != blockpos_last){
5723 block = m_map->getBlockNoCreate(blockpos);
5724 blockpos_last = blockpos;
5725 block_checked_in_modified = false;
5728 // Calculate relative position in block
5729 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5731 // Don't continue if nothing has changed here
5732 if(block->getNode(relpos) == n)
5735 //m_map->setNode(m_area.MinEdge + p, n);
5736 block->setNode(relpos, n);
5739 Make sure block is in modified_blocks
5741 if(block_checked_in_modified == false)
5743 modified_blocks[blockpos] = block;
5744 block_checked_in_modified = true;
5747 catch(InvalidPositionException &e)
5753 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5754 MapVoxelManipulator(map)
5758 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5762 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5764 // Just create the area so that it can be pointed to
5765 VoxelManipulator::emerge(a, caller_id);
5768 void ManualMapVoxelManipulator::initialEmerge(
5769 v3s16 blockpos_min, v3s16 blockpos_max)
5771 TimeTaker timer1("initialEmerge", &emerge_time);
5773 // Units of these are MapBlocks
5774 v3s16 p_min = blockpos_min;
5775 v3s16 p_max = blockpos_max;
5777 VoxelArea block_area_nodes
5778 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5780 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5783 dstream<<"initialEmerge: area: ";
5784 block_area_nodes.print(dstream);
5785 dstream<<" ("<<size_MB<<"MB)";
5789 addArea(block_area_nodes);
5791 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5792 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5793 for(s32 x=p_min.X; x<=p_max.X; x++)
5796 core::map<v3s16, bool>::Node *n;
5797 n = m_loaded_blocks.find(p);
5801 bool block_data_inexistent = false;
5804 TimeTaker timer1("emerge load", &emerge_load_time);
5806 MapBlock *block = m_map->getBlockNoCreate(p);
5807 if(block->isDummy())
5808 block_data_inexistent = true;
5810 block->copyTo(*this);
5812 catch(InvalidPositionException &e)
5814 block_data_inexistent = true;
5817 if(block_data_inexistent)
5820 Mark area inexistent
5822 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5823 // Fill with VOXELFLAG_INEXISTENT
5824 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5825 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5827 s32 i = m_area.index(a.MinEdge.X,y,z);
5828 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5832 m_loaded_blocks.insert(p, !block_data_inexistent);
5836 void ManualMapVoxelManipulator::blitBackAll(
5837 core::map<v3s16, MapBlock*> * modified_blocks)
5839 if(m_area.getExtent() == v3s16(0,0,0))
5843 Copy data of all blocks
5845 for(core::map<v3s16, bool>::Iterator
5846 i = m_loaded_blocks.getIterator();
5847 i.atEnd() == false; i++)
5849 bool existed = i.getNode()->getValue();
5850 if(existed == false)
5852 v3s16 p = i.getNode()->getKey();
5853 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5856 dstream<<"WARNING: "<<__FUNCTION_NAME
5857 <<": got NULL block "
5858 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5863 block->copyFrom(*this);
5866 modified_blocks->insert(p, block);