3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
34 Map::Map(std::ostream &dout):
36 m_camera_position(0,0,0),
37 m_camera_direction(0,0,1),
41 m_sector_mutex.Init();
42 m_camera_mutex.Init();
43 assert(m_sector_mutex.IsInitialized());
44 assert(m_camera_mutex.IsInitialized());
46 // Get this so that the player can stay on it at first
47 //getSector(v2s16(0,0));
55 /*updater.setRun(false);
56 while(updater.IsRunning())
62 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
63 for(; i.atEnd() == false; i++)
65 MapSector *sector = i.getNode()->getValue();
70 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
72 if(m_sector_cache != NULL && p == m_sector_cache_p){
73 MapSector * sector = m_sector_cache;
74 // Reset inactivity timer
75 sector->usage_timer = 0.0;
79 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
84 MapSector *sector = n->getValue();
86 // Cache the last result
88 m_sector_cache = sector;
90 // Reset inactivity timer
91 sector->usage_timer = 0.0;
95 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
97 JMutexAutoLock lock(m_sector_mutex);
99 return getSectorNoGenerateNoExNoLock(p);
102 MapSector * Map::getSectorNoGenerate(v2s16 p)
104 MapSector *sector = getSectorNoGenerateNoEx(p);
106 throw InvalidPositionException();
111 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
113 v2s16 p2d(p3d.X, p3d.Z);
114 MapSector * sector = getSectorNoGenerate(p2d);
116 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
121 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerate(p2d);
127 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 catch(InvalidPositionException &e)
136 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
138 v2s16 p2d(p3d.X, p3d.Z);
139 MapSector * sector = getSectorCreate(p2d);
141 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
144 block = sector->createBlankBlock(p3d.Y);
148 f32 Map::getGroundHeight(v2s16 p, bool generate)
151 v2s16 sectorpos = getNodeSectorPos(p);
152 MapSector * sref = getSectorNoGenerate(sectorpos);
153 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
154 f32 y = sref->getGroundHeight(relpos);
157 catch(InvalidPositionException &e)
159 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
163 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
165 /*m_dout<<DTIME<<"Map::setGroundHeight(("
167 <<"), "<<y<<")"<<std::endl;*/
168 v2s16 sectorpos = getNodeSectorPos(p);
169 MapSector * sref = getSectorNoGenerate(sectorpos);
170 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
171 //sref->mutex.Lock();
172 sref->setGroundHeight(relpos, y);
173 //sref->mutex.Unlock();
176 bool Map::isNodeUnderground(v3s16 p)
178 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock * block = getBlockNoCreate(blockpos);
181 return block->getIsUnderground();
183 catch(InvalidPositionException &e)
190 Goes recursively through the neighbours of the node.
192 Alters only transparent nodes.
194 If the lighting of the neighbour is lower than the lighting of
195 the node was (before changing it to 0 at the step before), the
196 lighting of the neighbour is set to 0 and then the same stuff
197 repeats for the neighbour.
199 The ending nodes of the routine are stored in light_sources.
200 This is useful when a light is removed. In such case, this
201 routine can be called for the light node and then again for
202 light_sources to re-light the area without the removed light.
204 values of from_nodes are lighting values.
206 void Map::unspreadLight(enum LightBank bank,
207 core::map<v3s16, u8> & from_nodes,
208 core::map<v3s16, bool> & light_sources,
209 core::map<v3s16, MapBlock*> & modified_blocks)
212 v3s16(0,0,1), // back
214 v3s16(1,0,0), // right
215 v3s16(0,0,-1), // front
216 v3s16(0,-1,0), // bottom
217 v3s16(-1,0,0), // left
220 if(from_nodes.size() == 0)
223 u32 blockchangecount = 0;
225 core::map<v3s16, u8> unlighted_nodes;
226 core::map<v3s16, u8>::Iterator j;
227 j = from_nodes.getIterator();
230 Initialize block cache
233 MapBlock *block = NULL;
234 // Cache this a bit, too
235 bool block_checked_in_modified = false;
237 for(; j.atEnd() == false; j++)
239 v3s16 pos = j.getNode()->getKey();
240 v3s16 blockpos = getNodeBlockPos(pos);
242 // Only fetch a new block if the block position has changed
244 if(block == NULL || blockpos != blockpos_last){
245 block = getBlockNoCreate(blockpos);
246 blockpos_last = blockpos;
248 block_checked_in_modified = false;
252 catch(InvalidPositionException &e)
260 // Calculate relative position in block
261 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
263 // Get node straight from the block
264 MapNode n = block->getNode(relpos);
266 u8 oldlight = j.getNode()->getValue();
268 // Loop through 6 neighbors
269 for(u16 i=0; i<6; i++)
271 // Get the position of the neighbor node
272 v3s16 n2pos = pos + dirs[i];
274 // Get the block where the node is located
275 v3s16 blockpos = getNodeBlockPos(n2pos);
279 // Only fetch a new block if the block position has changed
281 if(block == NULL || blockpos != blockpos_last){
282 block = getBlockNoCreate(blockpos);
283 blockpos_last = blockpos;
285 block_checked_in_modified = false;
289 catch(InvalidPositionException &e)
294 // Calculate relative position in block
295 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
296 // Get node straight from the block
297 MapNode n2 = block->getNode(relpos);
299 bool changed = false;
301 //TODO: Optimize output by optimizing light_sources?
304 If the neighbor is dimmer than what was specified
305 as oldlight (the light of the previous node)
307 if(n2.getLight(bank) < oldlight)
310 And the neighbor is transparent and it has some light
312 if(n2.light_propagates() && n2.getLight(bank) != 0)
315 Set light to 0 and add to queue
318 u8 current_light = n2.getLight(bank);
319 n2.setLight(bank, 0);
320 block->setNode(relpos, n2);
322 unlighted_nodes.insert(n2pos, current_light);
326 Remove from light_sources if it is there
327 NOTE: This doesn't happen nearly at all
329 /*if(light_sources.find(n2pos))
331 std::cout<<"Removed from light_sources"<<std::endl;
332 light_sources.remove(n2pos);
337 if(light_sources.find(n2pos) != NULL)
338 light_sources.remove(n2pos);*/
341 light_sources.insert(n2pos, true);
344 // Add to modified_blocks
345 if(changed == true && block_checked_in_modified == false)
347 // If the block is not found in modified_blocks, add.
348 if(modified_blocks.find(blockpos) == NULL)
350 modified_blocks.insert(blockpos, block);
352 block_checked_in_modified = true;
355 catch(InvalidPositionException &e)
362 /*dstream<<"unspreadLight(): Changed block "
363 <<blockchangecount<<" times"
364 <<" for "<<from_nodes.size()<<" nodes"
367 if(unlighted_nodes.size() > 0)
368 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
372 A single-node wrapper of the above
374 void Map::unLightNeighbors(enum LightBank bank,
375 v3s16 pos, u8 lightwas,
376 core::map<v3s16, bool> & light_sources,
377 core::map<v3s16, MapBlock*> & modified_blocks)
379 core::map<v3s16, u8> from_nodes;
380 from_nodes.insert(pos, lightwas);
382 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
386 Lights neighbors of from_nodes, collects all them and then
389 void Map::spreadLight(enum LightBank bank,
390 core::map<v3s16, bool> & from_nodes,
391 core::map<v3s16, MapBlock*> & modified_blocks)
393 const v3s16 dirs[6] = {
394 v3s16(0,0,1), // back
396 v3s16(1,0,0), // right
397 v3s16(0,0,-1), // front
398 v3s16(0,-1,0), // bottom
399 v3s16(-1,0,0), // left
402 if(from_nodes.size() == 0)
405 u32 blockchangecount = 0;
407 core::map<v3s16, bool> lighted_nodes;
408 core::map<v3s16, bool>::Iterator j;
409 j = from_nodes.getIterator();
412 Initialize block cache
415 MapBlock *block = NULL;
416 // Cache this a bit, too
417 bool block_checked_in_modified = false;
419 for(; j.atEnd() == false; j++)
420 //for(; j != from_nodes.end(); j++)
422 v3s16 pos = j.getNode()->getKey();
424 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
425 v3s16 blockpos = getNodeBlockPos(pos);
427 // Only fetch a new block if the block position has changed
429 if(block == NULL || blockpos != blockpos_last){
430 block = getBlockNoCreate(blockpos);
431 blockpos_last = blockpos;
433 block_checked_in_modified = false;
437 catch(InvalidPositionException &e)
445 // Calculate relative position in block
446 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
448 // Get node straight from the block
449 MapNode n = block->getNode(relpos);
451 u8 oldlight = n.getLight(bank);
452 u8 newlight = diminish_light(oldlight);
454 // Loop through 6 neighbors
455 for(u16 i=0; i<6; i++){
456 // Get the position of the neighbor node
457 v3s16 n2pos = pos + dirs[i];
459 // Get the block where the node is located
460 v3s16 blockpos = getNodeBlockPos(n2pos);
464 // Only fetch a new block if the block position has changed
466 if(block == NULL || blockpos != blockpos_last){
467 block = getBlockNoCreate(blockpos);
468 blockpos_last = blockpos;
470 block_checked_in_modified = false;
474 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
481 // Get node straight from the block
482 MapNode n2 = block->getNode(relpos);
484 bool changed = false;
486 If the neighbor is brighter than the current node,
487 add to list (it will light up this node on its turn)
489 if(n2.getLight(bank) > undiminish_light(oldlight))
491 lighted_nodes.insert(n2pos, true);
492 //lighted_nodes.push_back(n2pos);
496 If the neighbor is dimmer than how much light this node
497 would spread on it, add to list
499 if(n2.getLight(bank) < newlight)
501 if(n2.light_propagates())
503 n2.setLight(bank, newlight);
504 block->setNode(relpos, n2);
505 lighted_nodes.insert(n2pos, true);
506 //lighted_nodes.push_back(n2pos);
511 // Add to modified_blocks
512 if(changed == true && block_checked_in_modified == false)
514 // If the block is not found in modified_blocks, add.
515 if(modified_blocks.find(blockpos) == NULL)
517 modified_blocks.insert(blockpos, block);
519 block_checked_in_modified = true;
522 catch(InvalidPositionException &e)
529 /*dstream<<"spreadLight(): Changed block "
530 <<blockchangecount<<" times"
531 <<" for "<<from_nodes.size()<<" nodes"
534 if(lighted_nodes.size() > 0)
535 spreadLight(bank, lighted_nodes, modified_blocks);
539 A single-node source variation of the above.
541 void Map::lightNeighbors(enum LightBank bank,
543 core::map<v3s16, MapBlock*> & modified_blocks)
545 core::map<v3s16, bool> from_nodes;
546 from_nodes.insert(pos, true);
547 spreadLight(bank, from_nodes, modified_blocks);
550 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
553 v3s16(0,0,1), // back
555 v3s16(1,0,0), // right
556 v3s16(0,0,-1), // front
557 v3s16(0,-1,0), // bottom
558 v3s16(-1,0,0), // left
561 u8 brightest_light = 0;
562 v3s16 brightest_pos(0,0,0);
563 bool found_something = false;
565 // Loop through 6 neighbors
566 for(u16 i=0; i<6; i++){
567 // Get the position of the neighbor node
568 v3s16 n2pos = p + dirs[i];
573 catch(InvalidPositionException &e)
577 if(n2.getLight(bank) > brightest_light || found_something == false){
578 brightest_light = n2.getLight(bank);
579 brightest_pos = n2pos;
580 found_something = true;
584 if(found_something == false)
585 throw InvalidPositionException();
587 return brightest_pos;
591 Propagates sunlight down from a node.
592 Starting point gets sunlight.
594 Returns the lowest y value of where the sunlight went.
596 Mud is turned into grass in where the sunlight stops.
598 s16 Map::propagateSunlight(v3s16 start,
599 core::map<v3s16, MapBlock*> & modified_blocks)
604 v3s16 pos(start.X, y, start.Z);
606 v3s16 blockpos = getNodeBlockPos(pos);
609 block = getBlockNoCreate(blockpos);
611 catch(InvalidPositionException &e)
616 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
617 MapNode n = block->getNode(relpos);
619 if(n.sunlight_propagates())
621 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
622 block->setNode(relpos, n);
624 modified_blocks.insert(blockpos, block);
628 // Turn mud into grass
629 if(n.d == CONTENT_MUD)
632 block->setNode(relpos, n);
633 modified_blocks.insert(blockpos, block);
636 // Sunlight goes no further
643 void Map::updateLighting(enum LightBank bank,
644 core::map<v3s16, MapBlock*> & a_blocks,
645 core::map<v3s16, MapBlock*> & modified_blocks)
647 /*m_dout<<DTIME<<"Map::updateLighting(): "
648 <<a_blocks.size()<<" blocks."<<std::endl;*/
650 //TimeTaker timer("updateLighting");
654 //u32 count_was = modified_blocks.size();
656 core::map<v3s16, MapBlock*> blocks_to_update;
658 core::map<v3s16, bool> light_sources;
660 core::map<v3s16, u8> unlight_from;
662 core::map<v3s16, MapBlock*>::Iterator i;
663 i = a_blocks.getIterator();
664 for(; i.atEnd() == false; i++)
666 MapBlock *block = i.getNode()->getValue();
670 // Don't bother with dummy blocks.
674 v3s16 pos = block->getPos();
675 modified_blocks.insert(pos, block);
677 blocks_to_update.insert(pos, block);
680 Clear all light from block
682 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
683 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
684 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
689 MapNode n = block->getNode(v3s16(x,y,z));
690 u8 oldlight = n.getLight(bank);
692 block->setNode(v3s16(x,y,z), n);
694 // Collect borders for unlighting
695 if(x==0 || x == MAP_BLOCKSIZE-1
696 || y==0 || y == MAP_BLOCKSIZE-1
697 || z==0 || z == MAP_BLOCKSIZE-1)
699 v3s16 p_map = p + v3s16(
702 MAP_BLOCKSIZE*pos.Z);
703 unlight_from.insert(p_map, oldlight);
706 catch(InvalidPositionException &e)
709 This would happen when dealing with a
713 dstream<<"updateLighting(): InvalidPositionException"
718 if(bank == LIGHTBANK_DAY)
720 bool bottom_valid = block->propagateSunlight(light_sources);
722 // If bottom is valid, we're done.
726 else if(bank == LIGHTBANK_NIGHT)
728 // For night lighting, sunlight is not propagated
733 // Invalid lighting bank
737 /*dstream<<"Bottom for sunlight-propagated block ("
738 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
741 // Bottom sunlight is not valid; get the block and loop to it
745 block = getBlockNoCreate(pos);
747 catch(InvalidPositionException &e)
757 TimeTaker timer("unspreadLight");
758 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
763 u32 diff = modified_blocks.size() - count_was;
764 count_was = modified_blocks.size();
765 dstream<<"unspreadLight modified "<<diff<<std::endl;
769 TimeTaker timer("spreadLight");
770 spreadLight(bank, light_sources, modified_blocks);
775 u32 diff = modified_blocks.size() - count_was;
776 count_was = modified_blocks.size();
777 dstream<<"spreadLight modified "<<diff<<std::endl;
782 //MapVoxelManipulator vmanip(this);
784 // Make a manual voxel manipulator and load all the blocks
785 // that touch the requested blocks
786 ManualMapVoxelManipulator vmanip(this);
787 core::map<v3s16, MapBlock*>::Iterator i;
788 i = blocks_to_update.getIterator();
789 for(; i.atEnd() == false; i++)
791 MapBlock *block = i.getNode()->getValue();
792 v3s16 p = block->getPos();
794 // Add all surrounding blocks
795 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
798 Add all surrounding blocks that have up-to-date lighting
799 NOTE: This doesn't quite do the job (not everything
800 appropriate is lighted)
802 /*for(s16 z=-1; z<=1; z++)
803 for(s16 y=-1; y<=1; y++)
804 for(s16 x=-1; x<=1; x++)
807 MapBlock *block = getBlockNoCreateNoEx(p);
812 if(block->getLightingExpired())
814 vmanip.initialEmerge(p, p);
817 // Lighting of block will be updated completely
818 block->setLightingExpired(false);
822 //TimeTaker timer("unSpreadLight");
823 vmanip.unspreadLight(bank, unlight_from, light_sources);
826 //TimeTaker timer("spreadLight");
827 vmanip.spreadLight(bank, light_sources);
830 //TimeTaker timer("blitBack");
831 vmanip.blitBack(modified_blocks);
833 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
837 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
840 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
841 core::map<v3s16, MapBlock*> & modified_blocks)
843 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
844 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
847 Update information about whether day and night light differ
849 for(core::map<v3s16, MapBlock*>::Iterator
850 i = modified_blocks.getIterator();
851 i.atEnd() == false; i++)
853 MapBlock *block = i.getNode()->getValue();
854 block->updateDayNightDiff();
859 This is called after changing a node from transparent to opaque.
860 The lighting value of the node should be left as-is after changing
861 other values. This sets the lighting value to 0.
863 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
864 core::map<v3s16, MapBlock*> &modified_blocks)*/
865 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
866 core::map<v3s16, MapBlock*> &modified_blocks)
869 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
870 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
873 From this node to nodes underneath:
874 If lighting is sunlight (1.0), unlight neighbours and
879 v3s16 toppos = p + v3s16(0,1,0);
880 v3s16 bottompos = p + v3s16(0,-1,0);
882 bool node_under_sunlight = true;
883 core::map<v3s16, bool> light_sources;
886 If there is a node at top and it doesn't have sunlight,
887 there has not been any sunlight going down.
889 Otherwise there probably is.
892 MapNode topnode = getNode(toppos);
894 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
895 node_under_sunlight = false;
897 catch(InvalidPositionException &e)
901 if(n.d != CONTENT_TORCH)
904 If there is grass below, change it to mud
907 MapNode bottomnode = getNode(bottompos);
909 if(bottomnode.d == CONTENT_GRASS
910 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
912 bottomnode.d = CONTENT_MUD;
913 setNode(bottompos, bottomnode);
916 catch(InvalidPositionException &e)
921 enum LightBank banks[] =
926 for(s32 i=0; i<2; i++)
928 enum LightBank bank = banks[i];
930 u8 lightwas = getNode(p).getLight(bank);
932 // Add the block of the added node to modified_blocks
933 v3s16 blockpos = getNodeBlockPos(p);
934 MapBlock * block = getBlockNoCreate(blockpos);
935 assert(block != NULL);
936 modified_blocks.insert(blockpos, block);
938 if(isValidPosition(p) == false)
941 // Unlight neighbours of node.
942 // This means setting light of all consequent dimmer nodes
944 // This also collects the nodes at the border which will spread
945 // light again into this.
946 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
954 If node is under sunlight, take all sunlighted nodes under
955 it and clear light from them and from where the light has
957 TODO: This could be optimized by mass-unlighting instead
960 if(node_under_sunlight)
964 //m_dout<<DTIME<<"y="<<y<<std::endl;
965 v3s16 n2pos(p.X, y, p.Z);
971 catch(InvalidPositionException &e)
976 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
978 //m_dout<<DTIME<<"doing"<<std::endl;
979 unLightNeighbors(LIGHTBANK_DAY,
980 n2pos, n2.getLight(LIGHTBANK_DAY),
981 light_sources, modified_blocks);
982 n2.setLight(LIGHTBANK_DAY, 0);
990 for(s32 i=0; i<2; i++)
992 enum LightBank bank = banks[i];
995 Spread light from all nodes that might be capable of doing so
996 TODO: Convert to spreadLight
998 spreadLight(bank, light_sources, modified_blocks);
1002 Update information about whether day and night light differ
1004 for(core::map<v3s16, MapBlock*>::Iterator
1005 i = modified_blocks.getIterator();
1006 i.atEnd() == false; i++)
1008 MapBlock *block = i.getNode()->getValue();
1009 block->updateDayNightDiff();
1013 Add neighboring liquid nodes and the node itself if it is
1014 liquid (=water node was added) to transform queue.
1017 v3s16(0,0,0), // self
1018 v3s16(0,0,1), // back
1019 v3s16(0,1,0), // top
1020 v3s16(1,0,0), // right
1021 v3s16(0,0,-1), // front
1022 v3s16(0,-1,0), // bottom
1023 v3s16(-1,0,0), // left
1025 for(u16 i=0; i<7; i++)
1030 v3s16 p2 = p + dirs[i];
1032 MapNode n2 = getNode(p2);
1033 if(content_liquid(n2.d))
1035 m_transforming_liquid.push_back(p2);
1038 }catch(InvalidPositionException &e)
1046 void Map::removeNodeAndUpdate(v3s16 p,
1047 core::map<v3s16, MapBlock*> &modified_blocks)
1049 /*PrintInfo(m_dout);
1050 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1051 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1053 bool node_under_sunlight = true;
1055 v3s16 toppos = p + v3s16(0,1,0);
1057 // Node will be replaced with this
1058 u8 replace_material = CONTENT_AIR;
1061 If there is a node at top and it doesn't have sunlight,
1062 there will be no sunlight going down.
1065 MapNode topnode = getNode(toppos);
1067 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1068 node_under_sunlight = false;
1070 catch(InvalidPositionException &e)
1074 core::map<v3s16, bool> light_sources;
1076 enum LightBank banks[] =
1081 for(s32 i=0; i<2; i++)
1083 enum LightBank bank = banks[i];
1086 Unlight neighbors (in case the node is a light source)
1088 unLightNeighbors(bank, p,
1089 getNode(p).getLight(bank),
1090 light_sources, modified_blocks);
1095 This also clears the lighting.
1099 n.d = replace_material;
1102 for(s32 i=0; i<2; i++)
1104 enum LightBank bank = banks[i];
1107 Recalculate lighting
1109 spreadLight(bank, light_sources, modified_blocks);
1112 // Add the block of the removed node to modified_blocks
1113 v3s16 blockpos = getNodeBlockPos(p);
1114 MapBlock * block = getBlockNoCreate(blockpos);
1115 assert(block != NULL);
1116 modified_blocks.insert(blockpos, block);
1119 If the removed node was under sunlight, propagate the
1120 sunlight down from it and then light all neighbors
1121 of the propagated blocks.
1123 if(node_under_sunlight)
1125 s16 ybottom = propagateSunlight(p, modified_blocks);
1126 /*m_dout<<DTIME<<"Node was under sunlight. "
1127 "Propagating sunlight";
1128 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1130 for(; y >= ybottom; y--)
1132 v3s16 p2(p.X, y, p.Z);
1133 /*m_dout<<DTIME<<"lighting neighbors of node ("
1134 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1136 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1141 // Set the lighting of this node to 0
1142 // TODO: Is this needed? Lighting is cleared up there already.
1144 MapNode n = getNode(p);
1145 n.setLight(LIGHTBANK_DAY, 0);
1148 catch(InvalidPositionException &e)
1154 for(s32 i=0; i<2; i++)
1156 enum LightBank bank = banks[i];
1158 // Get the brightest neighbour node and propagate light from it
1159 v3s16 n2p = getBrightestNeighbour(bank, p);
1161 MapNode n2 = getNode(n2p);
1162 lightNeighbors(bank, n2p, modified_blocks);
1164 catch(InvalidPositionException &e)
1170 Update information about whether day and night light differ
1172 for(core::map<v3s16, MapBlock*>::Iterator
1173 i = modified_blocks.getIterator();
1174 i.atEnd() == false; i++)
1176 MapBlock *block = i.getNode()->getValue();
1177 block->updateDayNightDiff();
1181 Add neighboring liquid nodes to transform queue.
1184 v3s16(0,0,1), // back
1185 v3s16(0,1,0), // top
1186 v3s16(1,0,0), // right
1187 v3s16(0,0,-1), // front
1188 v3s16(0,-1,0), // bottom
1189 v3s16(-1,0,0), // left
1191 for(u16 i=0; i<6; i++)
1196 v3s16 p2 = p + dirs[i];
1198 MapNode n2 = getNode(p2);
1199 if(content_liquid(n2.d))
1201 m_transforming_liquid.push_back(p2);
1204 }catch(InvalidPositionException &e)
1211 void Map::expireMeshes(bool only_daynight_diffed)
1213 TimeTaker timer("expireMeshes()");
1215 core::map<v2s16, MapSector*>::Iterator si;
1216 si = m_sectors.getIterator();
1217 for(; si.atEnd() == false; si++)
1219 MapSector *sector = si.getNode()->getValue();
1221 core::list< MapBlock * > sectorblocks;
1222 sector->getBlocks(sectorblocks);
1224 core::list< MapBlock * >::Iterator i;
1225 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1227 MapBlock *block = *i;
1229 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1235 JMutexAutoLock lock(block->mesh_mutex);
1236 if(block->mesh != NULL)
1238 /*block->mesh->drop();
1239 block->mesh = NULL;*/
1240 block->setMeshExpired(true);
1247 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1249 assert(mapType() == MAPTYPE_CLIENT);
1252 v3s16 p = blockpos + v3s16(0,0,0);
1253 MapBlock *b = getBlockNoCreate(p);
1254 b->updateMesh(daynight_ratio);
1256 catch(InvalidPositionException &e){}
1259 v3s16 p = blockpos + v3s16(-1,0,0);
1260 MapBlock *b = getBlockNoCreate(p);
1261 b->updateMesh(daynight_ratio);
1263 catch(InvalidPositionException &e){}
1265 v3s16 p = blockpos + v3s16(0,-1,0);
1266 MapBlock *b = getBlockNoCreate(p);
1267 b->updateMesh(daynight_ratio);
1269 catch(InvalidPositionException &e){}
1271 v3s16 p = blockpos + v3s16(0,0,-1);
1272 MapBlock *b = getBlockNoCreate(p);
1273 b->updateMesh(daynight_ratio);
1275 catch(InvalidPositionException &e){}
1278 v3s16 p = blockpos + v3s16(1,0,0);
1279 MapBlock *b = getBlockNoCreate(p);
1280 b->updateMesh(daynight_ratio);
1282 catch(InvalidPositionException &e){}
1284 v3s16 p = blockpos + v3s16(0,1,0);
1285 MapBlock *b = getBlockNoCreate(p);
1286 b->updateMesh(daynight_ratio);
1288 catch(InvalidPositionException &e){}
1290 v3s16 p = blockpos + v3s16(0,0,1);
1291 MapBlock *b = getBlockNoCreate(p);
1292 b->updateMesh(daynight_ratio);
1294 catch(InvalidPositionException &e){}*/
1299 bool Map::dayNightDiffed(v3s16 blockpos)
1302 v3s16 p = blockpos + v3s16(0,0,0);
1303 MapBlock *b = getBlockNoCreate(p);
1304 if(b->dayNightDiffed())
1307 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(-1,0,0);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1317 v3s16 p = blockpos + v3s16(0,-1,0);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1324 v3s16 p = blockpos + v3s16(0,0,-1);
1325 MapBlock *b = getBlockNoCreate(p);
1326 if(b->dayNightDiffed())
1329 catch(InvalidPositionException &e){}
1332 v3s16 p = blockpos + v3s16(1,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1339 v3s16 p = blockpos + v3s16(0,1,0);
1340 MapBlock *b = getBlockNoCreate(p);
1341 if(b->dayNightDiffed())
1344 catch(InvalidPositionException &e){}
1346 v3s16 p = blockpos + v3s16(0,0,1);
1347 MapBlock *b = getBlockNoCreate(p);
1348 if(b->dayNightDiffed())
1351 catch(InvalidPositionException &e){}
1357 Updates usage timers
1359 void Map::timerUpdate(float dtime)
1361 JMutexAutoLock lock(m_sector_mutex);
1363 core::map<v2s16, MapSector*>::Iterator si;
1365 si = m_sectors.getIterator();
1366 for(; si.atEnd() == false; si++)
1368 MapSector *sector = si.getNode()->getValue();
1369 sector->usage_timer += dtime;
1373 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1376 Wait for caches to be removed before continuing.
1378 This disables the existence of caches while locked
1380 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1382 core::list<v2s16>::Iterator j;
1383 for(j=list.begin(); j!=list.end(); j++)
1385 MapSector *sector = m_sectors[*j];
1388 sector->deleteBlocks();
1393 If sector is in sector cache, remove it from there
1395 if(m_sector_cache == sector)
1397 m_sector_cache = NULL;
1400 Remove from map and delete
1402 m_sectors.remove(*j);
1408 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1409 core::list<v3s16> *deleted_blocks)
1411 JMutexAutoLock lock(m_sector_mutex);
1413 core::list<v2s16> sector_deletion_queue;
1414 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1415 for(; i.atEnd() == false; i++)
1417 MapSector *sector = i.getNode()->getValue();
1419 Delete sector from memory if it hasn't been used in a long time
1421 if(sector->usage_timer > timeout)
1423 sector_deletion_queue.push_back(i.getNode()->getKey());
1425 if(deleted_blocks != NULL)
1427 // Collect positions of blocks of sector
1428 MapSector *sector = i.getNode()->getValue();
1429 core::list<MapBlock*> blocks;
1430 sector->getBlocks(blocks);
1431 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1432 i != blocks.end(); i++)
1434 deleted_blocks->push_back((*i)->getPos());
1439 deleteSectors(sector_deletion_queue, only_blocks);
1440 return sector_deletion_queue.getSize();
1443 void Map::PrintInfo(std::ostream &out)
1448 #define WATER_DROP_BOOST 4
1450 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1452 DSTACK(__FUNCTION_NAME);
1453 //TimeTaker timer("transformLiquids()");
1456 u32 initial_size = m_transforming_liquid.size();
1458 //dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1460 while(m_transforming_liquid.size() != 0)
1463 Get a queued transforming liquid node
1465 v3s16 p0 = m_transforming_liquid.pop_front();
1467 MapNode n0 = getNode(p0);
1469 // Don't deal with non-liquids
1470 if(content_liquid(n0.d) == false)
1473 bool is_source = !content_flowing_liquid(n0.d);
1475 u8 liquid_level = 8;
1476 if(is_source == false)
1477 liquid_level = n0.param2 & 0x0f;
1479 // Turn possible source into non-source
1480 u8 nonsource_c = make_liquid_flowing(n0.d);
1483 If not source, check that some node flows into this one
1484 and what is the level of liquid in this one
1486 if(is_source == false)
1488 s8 new_liquid_level_max = -1;
1490 v3s16 dirs_from[5] = {
1491 v3s16(0,1,0), // top
1492 v3s16(0,0,1), // back
1493 v3s16(1,0,0), // right
1494 v3s16(0,0,-1), // front
1495 v3s16(-1,0,0), // left
1497 for(u16 i=0; i<5; i++)
1502 bool from_top = (i==0);
1504 v3s16 p2 = p0 + dirs_from[i];
1505 MapNode n2 = getNode(p2);
1507 if(content_liquid(n2.d))
1509 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1510 // Check that the liquids are the same type
1511 if(n2_nonsource_c != nonsource_c)
1513 dstream<<"WARNING: Not handling: different liquids"
1514 " collide"<<std::endl;
1517 bool n2_is_source = !content_flowing_liquid(n2.d);
1518 s8 n2_liquid_level = 8;
1519 if(n2_is_source == false)
1520 n2_liquid_level = n2.param2 & 0x07;
1522 s8 new_liquid_level = -1;
1525 //new_liquid_level = 7;
1526 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1527 new_liquid_level = 7;
1529 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1531 else if(n2_liquid_level > 0)
1533 new_liquid_level = n2_liquid_level - 1;
1536 if(new_liquid_level > new_liquid_level_max)
1537 new_liquid_level_max = new_liquid_level;
1540 }catch(InvalidPositionException &e)
1546 If liquid level should be something else, update it and
1547 add all the neighboring water nodes to the transform queue.
1549 if(new_liquid_level_max != liquid_level)
1551 if(new_liquid_level_max == -1)
1553 // Remove water alltoghether
1560 n0.param2 = new_liquid_level_max;
1564 // Block has been modified
1566 v3s16 blockpos = getNodeBlockPos(p0);
1567 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1569 modified_blocks.insert(blockpos, block);
1573 Add neighboring non-source liquid nodes to transform queue.
1576 v3s16(0,0,1), // back
1577 v3s16(0,1,0), // top
1578 v3s16(1,0,0), // right
1579 v3s16(0,0,-1), // front
1580 v3s16(0,-1,0), // bottom
1581 v3s16(-1,0,0), // left
1583 for(u16 i=0; i<6; i++)
1588 v3s16 p2 = p0 + dirs[i];
1590 MapNode n2 = getNode(p2);
1591 if(content_flowing_liquid(n2.d))
1593 m_transforming_liquid.push_back(p2);
1596 }catch(InvalidPositionException &e)
1603 // Get a new one from queue if the node has turned into non-water
1604 if(content_liquid(n0.d) == false)
1608 Flow water from this node
1610 v3s16 dirs_to[5] = {
1611 v3s16(0,-1,0), // bottom
1612 v3s16(0,0,1), // back
1613 v3s16(1,0,0), // right
1614 v3s16(0,0,-1), // front
1615 v3s16(-1,0,0), // left
1617 for(u16 i=0; i<5; i++)
1622 bool to_bottom = (i == 0);
1624 // If liquid is at lowest possible height, it's not going
1625 // anywhere except down
1626 if(liquid_level == 0 && to_bottom == false)
1629 u8 liquid_next_level = 0;
1630 // If going to bottom
1633 //liquid_next_level = 7;
1634 if(liquid_level >= 7 - WATER_DROP_BOOST)
1635 liquid_next_level = 7;
1637 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1640 liquid_next_level = liquid_level - 1;
1642 bool n2_changed = false;
1643 bool flowed = false;
1645 v3s16 p2 = p0 + dirs_to[i];
1647 MapNode n2 = getNode(p2);
1648 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1650 if(content_liquid(n2.d))
1652 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1653 // Check that the liquids are the same type
1654 if(n2_nonsource_c != nonsource_c)
1656 dstream<<"WARNING: Not handling: different liquids"
1657 " collide"<<std::endl;
1660 bool n2_is_source = !content_flowing_liquid(n2.d);
1661 u8 n2_liquid_level = 8;
1662 if(n2_is_source == false)
1663 n2_liquid_level = n2.param2 & 0x07;
1672 // Just flow into the source, nothing changes.
1673 // n2_changed is not set because destination didn't change
1678 if(liquid_next_level > liquid_level)
1680 n2.param2 = liquid_next_level;
1688 else if(n2.d == CONTENT_AIR)
1691 n2.param2 = liquid_next_level;
1698 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1702 m_transforming_liquid.push_back(p2);
1704 v3s16 blockpos = getNodeBlockPos(p2);
1705 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1707 modified_blocks.insert(blockpos, block);
1710 // If n2_changed to bottom, don't flow anywhere else
1711 if(to_bottom && flowed && !is_source)
1714 }catch(InvalidPositionException &e)
1720 //if(loopcount >= 100000)
1721 if(loopcount >= initial_size * 1)
1724 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1731 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1743 Experimental and debug stuff
1747 dstream<<"Generating map point attribute lists"<<std::endl;
1749 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1750 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1751 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1752 //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1753 //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1757 NOTE: BEWARE: Too big amount of these will make map generation
1758 slow. Especially those that are read by every block emerge.
1766 for(u32 i=0; i<500; i++)
1768 /*u32 lim = MAP_GENERATION_LIMIT;
1772 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1775 -lim + myrand()%(lim*2),
1777 -lim + myrand()%(lim*2)
1779 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1780 plants_amount = pow(plants_amount, 5);
1781 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1783 float plants_amount = 0;
1786 plants_amount = 1.5;
1788 else if(myrand()%4 == 0)
1790 plants_amount = 0.5;
1792 else if(myrand()%2 == 0)
1794 plants_amount = 0.03;
1798 plants_amount = 0.0;
1802 list_plants_amount->addPoint(p, Attribute(plants_amount));
1805 for(u32 i=0; i<500; i++)
1807 /*u32 lim = MAP_GENERATION_LIMIT;
1811 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1814 -lim + myrand()%(lim*2),
1816 -lim + myrand()%(lim*2)
1819 float caves_amount = 0;
1824 else if(myrand()%3 == 0)
1830 caves_amount = 0.05;
1833 list_caves_amount->addPoint(p, Attribute(caves_amount));
1836 for(u32 i=0; i<500; i++)
1838 /*u32 lim = MAP_GENERATION_LIMIT;
1842 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1845 -lim + (myrand()%(lim*2)),
1847 -lim + (myrand()%(lim*2))
1850 /*s32 bh_i = (myrand()%200) - 50;
1851 float baseheight = (float)bh_i;
1855 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1856 randmax = pow(randmax, e);
1858 //float randmax = (float)(myrand()%60);
1859 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1861 float baseheight = 0;
1863 float randfactor = 0;
1865 /*if(myrand()%5 == 0)
1871 else if(myrand()%6 == 0)
1877 else if(myrand()%4 == 0)
1883 else if(myrand()%3 == 0)
1909 list_baseheight->addPoint(p, Attribute(baseheight));
1910 list_randmax->addPoint(p, Attribute(randmax));
1911 list_randfactor->addPoint(p, Attribute(randfactor));
1915 // Add only one entry
1916 list_baseheight->addPoint(v3s16(0,0,0), Attribute(-4));
1917 list_randmax->addPoint(v3s16(0,0,0), Attribute(22));
1918 //list_randmax->addPoint(v3s16(0,0,0), Attribute(0));
1919 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1922 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1923 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1924 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1928 Try to load map; if not found, create a new one.
1931 m_savedir = savedir;
1932 m_map_saving_enabled = false;
1936 // If directory exists, check contents and load if possible
1937 if(fs::PathExists(m_savedir))
1939 // If directory is empty, it is safe to save into it.
1940 if(fs::GetDirListing(m_savedir).size() == 0)
1942 dstream<<DTIME<<"Server: Empty save directory is valid."
1944 m_map_saving_enabled = true;
1948 // Load master heightmap
1949 loadMasterHeightmap();
1951 // Load sector (0,0) and throw and exception on fail
1952 if(loadSectorFull(v2s16(0,0)) == false)
1953 throw LoadError("Failed to load sector (0,0)");
1955 dstream<<DTIME<<"Server: Successfully loaded master "
1956 "heightmap and sector (0,0) from "<<savedir<<
1957 ", assuming valid save directory."
1960 m_map_saving_enabled = true;
1961 // Map loaded, not creating new one
1965 // If directory doesn't exist, it is safe to save to it
1967 m_map_saving_enabled = true;
1970 catch(std::exception &e)
1972 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1973 <<", exception: "<<e.what()<<std::endl;
1974 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1975 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1978 dstream<<DTIME<<"Initializing new map."<<std::endl;
1980 // Create master heightmap
1981 m_heightmap = new UnlimitedHeightmap
1984 // Set map parameters
1987 // Create zero sector
1988 emergeSector(v2s16(0,0));
1990 // Initially write whole map
1994 ServerMap::~ServerMap()
1998 if(m_map_saving_enabled)
2001 // Save only changed parts
2003 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2007 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2010 catch(std::exception &e)
2012 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2013 <<", exception: "<<e.what()<<std::endl;
2016 if(m_heightmap != NULL)
2022 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2023 for(; i.atEnd() == false; i++)
2025 MapChunk *chunk = i.getNode()->getValue();
2031 Some helper functions
2034 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2036 v3s16 em = vmanip.m_area.getExtent();
2037 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2038 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2039 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2041 for(y=y_nodes_max; y>=y_nodes_min; y--)
2043 MapNode &n = vmanip.m_data[i];
2044 if(content_walkable(n.d))
2047 vmanip.m_area.add_y(em, i, -1);
2049 if(y >= y_nodes_min)
2055 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2057 MapNode treenode(CONTENT_TREE);
2058 MapNode leavesnode(CONTENT_LEAVES);
2060 s16 trunk_h = myrand_range(2, 6);
2062 for(s16 ii=0; ii<trunk_h; ii++)
2064 if(vmanip.m_area.contains(p1))
2065 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2069 // p1 is now the last piece of the trunk
2072 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2073 SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2074 for(s32 i=0; i<leaves_a.getVolume(); i++)
2077 // Force leaves at near the end of the trunk
2080 for(s16 z=-d; z<=d; z++)
2081 for(s16 y=-d; y<=d; y++)
2082 for(s16 x=-d; x<=d; x++)
2084 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2088 // Add leaves randomly
2089 for(u32 iii=0; iii<7; iii++)
2094 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2095 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2096 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2099 for(s16 z=0; z<=d; z++)
2100 for(s16 y=0; y<=d; y++)
2101 for(s16 x=0; x<=d; x++)
2103 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2107 // Blit leaves to vmanip
2108 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2109 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2110 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2114 if(vmanip.m_area.contains(p) == false)
2116 u32 vi = vmanip.m_area.index(p);
2117 if(vmanip.m_data[vi].d != CONTENT_AIR)
2119 u32 i = leaves_a.index(x,y,z);
2120 if(leaves_d[i] == 1)
2121 vmanip.m_data[vi] = leavesnode;
2125 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2126 core::map<v3s16, MapBlock*> &changed_blocks)
2129 Don't generate if already fully generated
2132 MapChunk *chunk = getChunk(chunkpos);
2133 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2135 dstream<<"generateChunkRaw(): Chunk "
2136 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2137 <<" already generated"<<std::endl;
2142 dstream<<"generateChunkRaw(): Generating chunk "
2143 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2146 TimeTaker timer("generateChunkRaw()");
2148 // The distance how far into the neighbors the generator is allowed to go.
2149 s16 max_spread_amount_sectors = 2;
2150 assert(max_spread_amount_sectors <= m_chunksize);
2151 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2152 // Minimum amount of space left on sides for mud to fall in
2153 s16 min_mud_fall_space = 2;
2154 // Maximum diameter of stone obstacles in X and Z
2155 s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2156 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2158 s16 y_blocks_min = -4;
2159 s16 y_blocks_max = 3;
2160 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2161 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2162 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2164 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2165 s16 sectorpos_base_size = m_chunksize;
2167 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2168 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2169 v2s16 sectorpos_bigbase =
2170 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2171 s16 sectorpos_bigbase_size =
2172 sectorpos_base_size + 2 * max_spread_amount_sectors;
2174 v3s16 bigarea_blocks_min(
2175 sectorpos_bigbase.X,
2180 v3s16 bigarea_blocks_max(
2181 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2183 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2186 // Relative values to control amount of stuff in one chunk
2187 u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2188 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;
2189 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2190 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2191 *(u32)h_blocks*MAP_BLOCKSIZE;
2194 The limiting edges of the lighting update, inclusive.
2196 s16 lighting_min_d = 0-max_spread_amount;
2197 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2200 Create the whole area of this and the neighboring chunks
2203 TimeTaker timer("generateChunkRaw() create area");
2205 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2206 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2208 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2209 ServerMapSector *sector = createSector(sectorpos);
2212 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2214 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2215 MapBlock *block = createBlock(blockpos);
2217 // Lighting won't be calculated
2218 //block->setLightingExpired(true);
2219 // Lighting will be calculated
2220 block->setLightingExpired(false);
2223 Block gets sunlight if this is true.
2225 This should be set to true when the top side of a block
2226 is completely exposed to the sky.
2228 Actually this doesn't matter now because the
2229 initial lighting is done here.
2231 block->setIsUnderground(y != y_blocks_max);
2237 Clear all light emitted
2240 core::map<v3s16, u8> unlight_from;
2243 Now we have a big empty area.
2245 Make a ManualMapVoxelManipulator that contains this and the
2249 ManualMapVoxelManipulator vmanip(this);
2250 // Add the area we just generated
2252 TimeTaker timer("generateChunkRaw() initialEmerge");
2253 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2256 TimeTaker timer_generate("generateChunkRaw() generate");
2259 Generate general ground level to full area
2264 //TimeTaker timer1("ground level");
2266 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2267 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2270 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2273 Skip of already generated
2276 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2277 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2281 // Ground height at this point
2282 float surface_y_f = 0.0;
2284 A hack to get the ground height from the sector.
2288 v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
2289 v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
2290 MapSector *sector = getSectorNoGenerate(sectorpos);
2292 float h = sector->getGroundHeight(sector_relpos);
2293 if(h > GROUNDHEIGHT_VALID_MINVALUE)
2296 dstream<<"WARNING: "<<__FUNCTION_NAME
2297 <<": sector->getGroundHeight returned bad height"<<std::endl;
2299 // Convert to integer
2300 s16 surface_y = (s16)surface_y_f;
2303 Fill ground with stone
2306 // Use fast index incrementing
2307 v3s16 em = vmanip.m_area.getExtent();
2308 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2309 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2311 vmanip.m_data[i].d = CONTENT_STONE;
2313 vmanip.m_area.add_y(em, i, 1);
2321 Randomize some parameters
2324 u32 stone_obstacle_amount =
2325 myrand_range(0, myrand_range(20, 150));
2326 //myrand_range(0, myrand_range(20, myrand_range(80,150)));
2329 Loop this part, it will make stuff look older and newer nicely
2332 for(u32 i_age=0; i_age<2; i_age++)
2335 // This is set during the next operation.
2336 // Maximum height of the stone surface and obstacles.
2337 // This is used to disable dungeon generation from going too high.
2338 s16 stone_surface_max_y = 0;
2342 //TimeTaker timer1("stone obstacles");
2345 Add some random stone obstacles
2348 for(u32 ri=0; ri<stone_obstacle_amount/3; ri++)
2349 //for(u32 ri=0; ri<7; ri++)
2352 // Randomize max height so usually stuff will be quite low
2353 //s16 maxheight_randomized = myrand_range(0, 25);
2354 s16 maxheight_randomized = myrand_range(0, stone_obstacle_amount/3);
2356 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2358 myrand_range(5, stone_obstacle_max_size),
2359 myrand_range(0, maxheight_randomized),
2360 myrand_range(5, stone_obstacle_max_size)
2363 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2364 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2367 Limit by 1 to not obstruct sunlight at borders, because
2368 it would fuck up lighting in some places because we're
2369 leaving out removing light from the borders for optimization
2373 myrand_range(1, sectorpos_base_size*MAP_BLOCKSIZE-1-1),
2374 myrand_range(1, sectorpos_base_size*MAP_BLOCKSIZE-1-1)
2377 // Minimum space left on top of the obstacle
2378 s16 min_head_space = 10;
2380 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2381 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2383 // Node position in 2d
2384 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2386 // Find stone ground level
2387 // (ignore everything else than mud in already generated chunks)
2388 // and mud amount over the stone level
2392 v3s16 em = vmanip.m_area.getExtent();
2393 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2395 // Go to ground level
2396 for(y=y_nodes_max; y>=y_nodes_min; y--)
2398 MapNode *n = &vmanip.m_data[i];
2399 /*if(content_walkable(n.d)
2400 && n.d != CONTENT_MUD
2401 && n.d != CONTENT_GRASS)
2403 if(n->d == CONTENT_STONE)
2406 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2410 Change to mud because otherwise we might
2411 be throwing mud on grass at the next
2417 vmanip.m_area.add_y(em, i, -1);
2419 if(y >= y_nodes_min)
2422 surface_y = y_nodes_min;
2430 v3s16 em = vmanip.m_area.getExtent();
2431 s16 y_start = surface_y+1;
2432 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2436 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2438 MapNode &n = vmanip.m_data[i];
2439 n.d = CONTENT_STONE;
2441 if(y > stone_surface_max_y)
2442 stone_surface_max_y = y;
2445 if(count >= ob_size.Y)
2448 vmanip.m_area.add_y(em, i, 1);
2452 for(; y<=y_nodes_max; y++)
2454 MapNode &n = vmanip.m_data[i];
2457 if(count >= mud_amount)
2460 vmanip.m_area.add_y(em, i, 1);
2470 //TimeTaker timer1("dungeons");
2475 u32 dungeons_count = relative_volume / 200000;
2476 u32 bruises_count = relative_volume * stone_surface_max_y / 200000 / 50;
2477 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2479 s16 min_tunnel_diameter = 3;
2480 s16 max_tunnel_diameter = 6;
2481 u16 tunnel_routepoints = 15;
2483 bool bruise_surface = (jj < bruises_count);
2487 min_tunnel_diameter = 5;
2488 max_tunnel_diameter = myrand_range(10, 20);
2489 tunnel_routepoints = 3;
2492 // Allowed route area size in nodes
2494 sectorpos_base_size*MAP_BLOCKSIZE,
2495 h_blocks*MAP_BLOCKSIZE,
2496 sectorpos_base_size*MAP_BLOCKSIZE
2499 // Area starting point in nodes
2501 sectorpos_base.X*MAP_BLOCKSIZE,
2502 y_blocks_min*MAP_BLOCKSIZE,
2503 sectorpos_base.Y*MAP_BLOCKSIZE
2507 //(this should be more than the maximum radius of the tunnel)
2508 //s16 insure = 5; // Didn't work with max_d = 20
2510 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2511 ar += v3s16(1,0,1) * more * 2;
2512 of -= v3s16(1,0,1) * more;
2514 s16 route_y_min = 0;
2515 //s16 route_y_max = ar.Y-1;
2516 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
2517 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2521 /*// Minimum is at y=0
2522 route_y_min = -of.Y - 0;*/
2523 // Minimum is at y=max_tunnel_diameter/4
2524 //route_y_min = -of.Y + max_tunnel_diameter/4;
2525 //s16 min = -of.Y + max_tunnel_diameter/4;
2526 s16 min = -of.Y + 0;
2527 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2528 route_y_min = rangelim(route_y_min, 0, route_y_max);
2531 /*dstream<<"route_y_min = "<<route_y_min
2532 <<", route_y_max = "<<route_y_max<<std::endl;*/
2534 // Randomize starting position
2536 (float)(myrand()%ar.X)+0.5,
2537 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2538 (float)(myrand()%ar.Z)+0.5
2541 MapNode airnode(CONTENT_AIR);
2544 Generate some tunnel starting from orp
2547 for(u16 j=0; j<tunnel_routepoints; j++)
2549 v3s16 maxlen(20, 10, 20);
2553 maxlen = v3s16(60,60,60);
2557 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2558 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2559 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2564 else if(rp.X >= ar.X)
2566 if(rp.Y < route_y_min)
2568 else if(rp.Y >= route_y_max)
2569 rp.Y = route_y_max-1;
2572 else if(rp.Z >= ar.Z)
2577 s16 min_d = min_tunnel_diameter;
2578 s16 max_d = max_tunnel_diameter;
2579 s16 rs = myrand_range(min_d, max_d);
2581 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2583 v3f fp = orp + vec * f;
2584 v3s16 cp(fp.X, fp.Y, fp.Z);
2587 s16 d1 = d0 + rs - 1;
2588 for(s16 z0=d0; z0<=d1; z0++)
2590 s16 si = rs - abs(z0);
2591 for(s16 x0=-si; x0<=si-1; x0++)
2593 s16 maxabsxz = abs(x0)>abs(z0)?abs(x0):abs(z0);
2594 s16 si2 = rs - maxabsxz;
2595 //s16 si2 = rs - abs(x0);
2596 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2602 /*if(isInArea(p, ar) == false)
2604 // Check only height
2605 if(y < 0 || y >= ar.Y)
2609 //assert(vmanip.m_area.contains(p));
2610 if(vmanip.m_area.contains(p) == false)
2612 dstream<<"WARNING: "<<__FUNCTION_NAME
2613 <<":"<<__LINE__<<": "
2614 <<"point not in area"
2619 // Just set it to air, it will be changed to
2621 u32 i = vmanip.m_area.index(p);
2622 vmanip.m_data[i] = airnode;
2636 //TimeTaker timer1("ore veins");
2641 for(u32 jj=0; jj<relative_volume/524; jj++)
2643 s16 max_vein_diameter = 3;
2645 // Allowed route area size in nodes
2647 sectorpos_base_size*MAP_BLOCKSIZE,
2648 h_blocks*MAP_BLOCKSIZE,
2649 sectorpos_base_size*MAP_BLOCKSIZE
2652 // Area starting point in nodes
2654 sectorpos_base.X*MAP_BLOCKSIZE,
2655 y_blocks_min*MAP_BLOCKSIZE,
2656 sectorpos_base.Y*MAP_BLOCKSIZE
2660 //(this should be more than the maximum radius of the tunnel)
2662 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2663 ar += v3s16(1,0,1) * more * 2;
2664 of -= v3s16(1,0,1) * more;
2666 // Randomize starting position
2668 (float)(myrand()%ar.X)+0.5,
2669 (float)(myrand()%ar.Y)+0.5,
2670 (float)(myrand()%ar.Z)+0.5
2673 // Randomize mineral
2674 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2677 Generate some vein starting from orp
2680 for(u16 j=0; j<2; j++)
2683 (float)(myrand()%ar.X)+0.5,
2684 (float)(myrand()%ar.Y)+0.5,
2685 (float)(myrand()%ar.Z)+0.5
2687 v3f vec = rp - orp;*/
2689 v3s16 maxlen(10, 10, 10);
2691 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2692 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2693 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2698 else if(rp.X >= ar.X)
2702 else if(rp.Y >= ar.Y)
2706 else if(rp.Z >= ar.Z)
2712 s16 max_d = max_vein_diameter;
2713 s16 rs = myrand_range(min_d, max_d);
2715 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2717 v3f fp = orp + vec * f;
2718 v3s16 cp(fp.X, fp.Y, fp.Z);
2720 s16 d1 = d0 + rs - 1;
2721 for(s16 z0=d0; z0<=d1; z0++)
2723 s16 si = rs - abs(z0);
2724 for(s16 x0=-si; x0<=si-1; x0++)
2726 s16 si2 = rs - abs(x0);
2727 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2729 // Don't put mineral to every place
2737 /*if(isInArea(p, ar) == false)
2739 // Check only height
2740 if(y < 0 || y >= ar.Y)
2744 assert(vmanip.m_area.contains(p));
2746 // Just set it to air, it will be changed to
2748 u32 i = vmanip.m_area.index(p);
2749 MapNode *n = &vmanip.m_data[i];
2750 if(n->d == CONTENT_STONE)
2765 //TimeTaker timer1("add mud");
2768 Add mud to the central chunk
2771 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2772 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2774 // Node position in 2d
2775 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2777 // Find ground level
2778 s16 surface_y = find_ground_level(vmanip, p2d);
2781 If topmost node is grass, change it to mud.
2782 It might be if it was flown to there from a neighboring
2783 chunk and then converted.
2786 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2787 MapNode *n = &vmanip.m_data[i];
2788 if(n->d == CONTENT_GRASS)
2797 v3s16 em = vmanip.m_area.getExtent();
2798 s16 y_start = surface_y+1;
2799 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2800 for(s16 y=y_start; y<=y_nodes_max; y++)
2802 MapNode &n = vmanip.m_data[i];
2808 vmanip.m_area.add_y(em, i, 1);
2817 //TimeTaker timer1("flow mud");
2820 Flow mud away from steep edges
2823 // Iterate a few times
2824 for(s16 k=0; k<4; k++)
2827 /*for(s16 x=0-max_spread_amount+1;
2828 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2830 for(s16 z=0-max_spread_amount+1;
2831 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2835 Firstly, limit area by 1 because mud is flown into neighbors.
2836 Secondly, limit by 1 more to not obstruct sunlight at borders,
2837 because it would fuck up lighting in some places because we're
2838 leaving out removing light from the borders for optimization
2841 for(s16 x=0-max_spread_amount+2;
2842 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2844 for(s16 z=0-max_spread_amount+2;
2845 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2848 // Node position in 2d
2849 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2851 v3s16 em = vmanip.m_area.getExtent();
2852 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2854 // Go to ground level
2855 for(y=y_nodes_max; y>=y_nodes_min; y--)
2857 MapNode &n = vmanip.m_data[i];
2858 //if(n.d != CONTENT_AIR)
2859 if(content_walkable(n.d))
2862 vmanip.m_area.add_y(em, i, -1);
2865 // If not mud, do nothing to it
2866 MapNode *n = &vmanip.m_data[i];
2867 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2870 // Make it exactly mud
2874 v3s16(0,0,1), // back
2875 v3s16(1,0,0), // right
2876 v3s16(0,0,-1), // front
2877 v3s16(-1,0,0), // left
2882 for(u32 di=0; di<4; di++)
2884 v3s16 dirp = dirs4[di];
2886 // Check that side is air
2887 vmanip.m_area.add_p(em, i2, dirp);
2888 MapNode *n2 = &vmanip.m_data[i2];
2889 if(content_walkable(n2->d))
2891 // Check that under side is air
2892 vmanip.m_area.add_y(em, i2, -1);
2893 n2 = &vmanip.m_data[i2];
2894 if(content_walkable(n2->d))
2896 // Loop further down until not air
2898 vmanip.m_area.add_y(em, i2, -1);
2899 n2 = &vmanip.m_data[i2];
2900 }while(content_walkable(n2->d) == false);
2901 // Loop one up so that we're in air
2902 vmanip.m_area.add_y(em, i2, 1);
2903 n2 = &vmanip.m_data[i2];
2905 // Move mud to new place
2907 // Set old place to be air
2908 *n = MapNode(CONTENT_AIR);
2911 // Switch mud and other and change mud source to air
2912 //MapNode tempnode = *n2;
2915 // Force old mud position to be air
2929 //TimeTaker timer1("add water");
2932 Add water to the central chunk (and a bit more)
2935 for(s16 x=0-max_spread_amount;
2936 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2938 for(s16 z=0-max_spread_amount;
2939 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2942 // Node position in 2d
2943 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2945 // Find ground level
2946 //s16 surface_y = find_ground_level(vmanip, p2d);
2949 If ground level is over water level, skip.
2950 NOTE: This leaves caves near water without water,
2951 which looks especially crappy when the nearby water
2952 won't start flowing either for some reason
2954 /*if(surface_y > WATER_LEVEL)
2961 v3s16 em = vmanip.m_area.getExtent();
2962 s16 y_start = WATER_LEVEL;
2963 u8 light = LIGHT_MAX;
2964 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2965 MapNode *n = &vmanip.m_data[i];
2967 Add first one to transforming liquid queue
2969 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2971 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2972 m_transforming_liquid.push_back(p);
2974 for(s16 y=y_start; y>=y_nodes_min; y--)
2976 n = &vmanip.m_data[i];
2978 // Stop when there is no water and no air
2979 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2980 && n->d != CONTENT_WATER)
2983 Add bottom one to transforming liquid queue
2985 vmanip.m_area.add_y(em, i, 1);
2986 n = &vmanip.m_data[i];
2987 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2989 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2990 m_transforming_liquid.push_back(p);
2996 n->d = CONTENT_WATERSOURCE;
2997 n->setLight(LIGHTBANK_DAY, light);
2999 /*// Add to transforming liquid queue (in case it'd
3001 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3002 m_transforming_liquid.push_back(p);*/
3005 vmanip.m_area.add_y(em, i, -1);
3019 //TimeTaker timer1("plant trees");
3025 u32 tree_max = relative_area / 60;
3027 u32 count = myrand_range(0, tree_max);
3028 for(u32 i=0; i<count; i++)
3030 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3031 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3032 x += sectorpos_base.X*MAP_BLOCKSIZE;
3033 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3034 s16 y = find_ground_level(vmanip, v2s16(x,z));
3035 // Don't make a tree under water level
3040 make_tree(vmanip, p);
3048 //TimeTaker timer1("grow grass");
3054 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3055 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3056 for(s16 x=0-max_spread_amount;
3057 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3059 for(s16 z=0-max_spread_amount;
3060 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3063 // Node position in 2d
3064 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3067 Find the lowest surface to which enough light ends up
3070 Basically just wait until not air and not leaves.
3074 v3s16 em = vmanip.m_area.getExtent();
3075 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3077 // Go to ground level
3078 for(y=y_nodes_max; y>=y_nodes_min; y--)
3080 MapNode &n = vmanip.m_data[i];
3081 if(n.d != CONTENT_AIR
3082 && n.d != CONTENT_LEAVES)
3084 vmanip.m_area.add_y(em, i, -1);
3086 if(y >= y_nodes_min)
3089 surface_y = y_nodes_min;
3092 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3093 MapNode *n = &vmanip.m_data[i];
3094 if(n->d == CONTENT_MUD)
3095 n->d = CONTENT_GRASS;
3104 core::map<v3s16, bool> light_sources;
3107 // 750ms @cs=8, can't optimize more
3108 //TimeTaker timer1("initial lighting");
3112 Go through the edges and add all nodes that have light to light_sources
3116 for(s16 i=0; i<4; i++)
3118 for(s16 j=lighting_min_d;
3125 if(i == 0 || i == 1)
3127 x = (i==0) ? lighting_min_d : lighting_max_d;
3136 z = (i==0) ? lighting_min_d : lighting_max_d;
3143 // Node position in 2d
3144 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3147 v3s16 em = vmanip.m_area.getExtent();
3148 s16 y_start = y_nodes_max;
3149 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3150 for(s16 y=y_start; y>=y_nodes_min; y--)
3152 MapNode *n = &vmanip.m_data[i];
3153 if(n->getLight(LIGHTBANK_DAY) != 0)
3155 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3162 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3163 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3164 /*for(s16 x=0-max_spread_amount+1;
3165 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3167 for(s16 z=0-max_spread_amount+1;
3168 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3172 This has to be 1 smaller than the actual area, because
3173 neighboring nodes are checked.
3175 for(s16 x=lighting_min_d+1;
3176 x<=lighting_max_d-1;
3178 for(s16 z=lighting_min_d+1;
3179 z<=lighting_max_d-1;
3182 // Node position in 2d
3183 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3186 Apply initial sunlight
3189 u8 light = LIGHT_SUN;
3190 bool add_to_sources = false;
3191 v3s16 em = vmanip.m_area.getExtent();
3192 s16 y_start = y_nodes_max;
3193 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3194 for(s16 y=y_start; y>=y_nodes_min; y--)
3196 MapNode *n = &vmanip.m_data[i];
3198 if(light_propagates_content(n->d) == false)
3202 else if(light != LIGHT_SUN
3203 || sunlight_propagates_content(n->d) == false)
3209 // This doesn't take much time
3210 if(add_to_sources == false)
3213 Check sides. If side is not air or water, start
3214 adding to light_sources.
3217 v3s16(0,0,1), // back
3218 v3s16(1,0,0), // right
3219 v3s16(0,0,-1), // front
3220 v3s16(-1,0,0), // left
3222 for(u32 di=0; di<4; di++)
3224 v3s16 dirp = dirs4[di];
3226 vmanip.m_area.add_p(em, i2, dirp);
3227 MapNode *n2 = &vmanip.m_data[i2];
3229 n2->d != CONTENT_AIR
3230 && n2->d != CONTENT_WATERSOURCE
3231 && n2->d != CONTENT_WATER
3233 add_to_sources = true;
3239 n->setLight(LIGHTBANK_DAY, light);
3240 n->setLight(LIGHTBANK_NIGHT, 0);
3242 // This doesn't take much time
3243 if(light != 0 && add_to_sources)
3245 // Insert light source
3246 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3249 // Increment index by y
3250 vmanip.m_area.add_y(em, i, -1);
3257 // Spread light around
3259 TimeTaker timer("generateChunkRaw() spreadLight");
3260 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3267 timer_generate.stop();
3270 Blit generated stuff to map
3274 //TimeTaker timer("generateChunkRaw() blitBackAll");
3275 vmanip.blitBackAll(&changed_blocks);
3278 Update day/night difference cache of the MapBlocks
3281 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3282 i.atEnd() == false; i++)
3284 MapBlock *block = i.getNode()->getValue();
3285 block->updateDayNightDiff();
3291 Create chunk metadata
3294 for(s16 x=-1; x<=1; x++)
3295 for(s16 y=-1; y<=1; y++)
3297 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3298 // Add chunk meta information
3299 MapChunk *chunk = getChunk(chunkpos0);
3302 chunk = new MapChunk();
3303 m_chunks.insert(chunkpos0, chunk);
3305 //chunk->setIsVolatile(true);
3306 if(chunk->getGenLevel() > GENERATED_PARTLY)
3307 chunk->setGenLevel(GENERATED_PARTLY);
3311 Set central chunk non-volatile and return it
3313 MapChunk *chunk = getChunk(chunkpos);
3316 //chunk->setIsVolatile(false);
3317 chunk->setGenLevel(GENERATED_FULLY);
3322 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3323 core::map<v3s16, MapBlock*> &changed_blocks)
3325 dstream<<"generateChunk(): Generating chunk "
3326 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3329 /*for(s16 x=-1; x<=1; x++)
3330 for(s16 y=-1; y<=1; y++)*/
3331 for(s16 x=-0; x<=0; x++)
3332 for(s16 y=-0; y<=0; y++)
3334 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3335 MapChunk *chunk = getChunk(chunkpos0);
3336 // Skip if already generated
3337 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3339 generateChunkRaw(chunkpos0, changed_blocks);
3342 assert(chunkNonVolatile(chunkpos1));
3344 MapChunk *chunk = getChunk(chunkpos1);
3348 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3350 DSTACK("%s: p2d=(%d,%d)",
3355 Check if it exists already in memory
3357 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3362 Try to load it from disk (with blocks)
3364 if(loadSectorFull(p2d) == true)
3366 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3369 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3370 throw InvalidPositionException("");
3376 If there is no master heightmap, throw.
3378 if(m_heightmap == NULL)
3380 throw InvalidPositionException("createSector(): no heightmap");
3384 Do not create over-limit
3386 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3387 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3388 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3389 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3390 throw InvalidPositionException("createSector(): pos. over limit");
3393 Generate blank sector
3396 // Number of heightmaps in sector in each direction
3397 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3399 // Heightmap side width
3400 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3402 sector = new ServerMapSector(this, p2d, hm_split);
3404 // Sector position on map in nodes
3405 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3407 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3408 " heightmaps and objects"<<std::endl;*/
3411 Generate sector heightmap
3414 v2s16 mhm_p = p2d * hm_split;
3415 /*f32 corners[4] = {
3416 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3417 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3418 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3419 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3422 // Loop through sub-heightmaps
3423 for(s16 y=0; y<hm_split; y++)
3424 for(s16 x=0; x<hm_split; x++)
3426 v2s16 p_in_sector = v2s16(x,y);
3427 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3429 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3430 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3431 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3432 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3435 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3436 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3439 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3441 sector->setHeightmap(p_in_sector, hm);
3443 //hm->generateContinued(1.0, 0.5, corners);
3444 hm->generateContinued(0.5, 0.5, corners);
3449 // Add dummy objects
3450 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3451 sector->setObjects(objects);
3456 m_sectors.insert(p2d, sector);
3461 MapSector * ServerMap::emergeSector(v2s16 p2d,
3462 core::map<v3s16, MapBlock*> &changed_blocks)
3464 DSTACK("%s: p2d=(%d,%d)",
3471 v2s16 chunkpos = sector_to_chunk(p2d);
3472 /*bool chunk_nonvolatile = false;
3473 MapChunk *chunk = getChunk(chunkpos);
3474 if(chunk && chunk->getIsVolatile() == false)
3475 chunk_nonvolatile = true;*/
3476 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3479 If chunk is not fully generated, generate chunk
3481 if(chunk_nonvolatile == false)
3483 // Generate chunk and neighbors
3484 generateChunk(chunkpos, changed_blocks);
3488 Return sector if it exists now
3490 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3495 Try to load it from disk
3497 if(loadSectorFull(p2d) == true)
3499 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3502 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3503 throw InvalidPositionException("");
3509 generateChunk should have generated the sector
3516 //return generateSector();
3520 NOTE: This is not used for main map generation, only for blocks
3521 that are very high or low
3523 MapBlock * ServerMap::generateBlock(
3525 MapBlock *original_dummy,
3526 ServerMapSector *sector,
3527 core::map<v3s16, MapBlock*> &changed_blocks,
3528 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3531 DSTACK("%s: p=(%d,%d,%d)",
3535 /*dstream<<"generateBlock(): "
3536 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3539 MapBlock *block = original_dummy;
3541 v2s16 p2d(p.X, p.Z);
3545 Do not generate over-limit
3547 if(blockpos_over_limit(p))
3549 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3550 throw InvalidPositionException("generateBlock(): pos. over limit");
3554 If block doesn't exist, create one.
3555 If it exists, it is a dummy. In that case unDummify() it.
3557 NOTE: This already sets the map as the parent of the block
3561 block = sector->createBlankBlockNoInsert(block_y);
3565 // Remove the block so that nobody can get a half-generated one.
3566 sector->removeBlock(block);
3567 // Allocate the block to contain the generated data
3571 /*u8 water_material = CONTENT_WATER;
3572 if(g_settings.getBool("endless_water"))
3573 water_material = CONTENT_WATERSOURCE;*/
3574 u8 water_material = CONTENT_WATERSOURCE;
3576 s32 lowest_ground_y = 32767;
3577 s32 highest_ground_y = -32768;
3580 //sector->printHeightmaps();
3582 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3583 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3585 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3587 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3588 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3589 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3591 dstream<<"WARNING: Surface height not found in sector "
3592 "for block that is being emerged"<<std::endl;
3596 s16 surface_y = surface_y_f;
3597 //avg_ground_y += surface_y;
3598 if(surface_y < lowest_ground_y)
3599 lowest_ground_y = surface_y;
3600 if(surface_y > highest_ground_y)
3601 highest_ground_y = surface_y;
3603 s32 surface_depth = 0;
3605 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3607 //float min_slope = 0.45;
3608 //float max_slope = 0.85;
3609 float min_slope = 0.60;
3610 float max_slope = 1.20;
3611 float min_slope_depth = 5.0;
3612 float max_slope_depth = 0;
3614 if(slope < min_slope)
3615 surface_depth = min_slope_depth;
3616 else if(slope > max_slope)
3617 surface_depth = max_slope_depth;
3619 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3621 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3623 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3628 NOTE: If there are some man-made structures above the
3629 newly created block, they won't be taken into account.
3631 if(real_y > surface_y)
3632 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3638 // If node is over heightmap y, it's air or water
3639 if(real_y > surface_y)
3641 // If under water level, it's water
3642 if(real_y < WATER_LEVEL)
3644 n.d = water_material;
3645 n.setLight(LIGHTBANK_DAY,
3646 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3648 Add to transforming liquid queue (in case it'd
3651 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3652 m_transforming_liquid.push_back(real_pos);
3658 // Else it's ground or dungeons (air)
3661 // If it's surface_depth under ground, it's stone
3662 if(real_y <= surface_y - surface_depth)
3664 n.d = CONTENT_STONE;
3668 // It is mud if it is under the first ground
3669 // level or under water
3670 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3676 n.d = CONTENT_GRASS;
3679 //n.d = CONTENT_MUD;
3681 /*// If under water level, it's mud
3682 if(real_y < WATER_LEVEL)
3684 // Only the topmost node is grass
3685 else if(real_y <= surface_y - 1)
3688 n.d = CONTENT_GRASS;*/
3692 block->setNode(v3s16(x0,y0,z0), n);
3697 Calculate some helper variables
3700 // Completely underground if the highest part of block is under lowest
3702 // This has to be very sure; it's probably one too strict now but
3703 // that's just better.
3704 bool completely_underground =
3705 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3707 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3709 bool mostly_underwater_surface = false;
3710 if(highest_ground_y < WATER_LEVEL
3711 && some_part_underground && !completely_underground)
3712 mostly_underwater_surface = true;
3715 Get local attributes
3718 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3720 float caves_amount = 0.5;
3725 NOTE: BEWARE: Too big amount of attribute points slows verything
3727 1 interpolation from 5000 points takes 2-3ms.
3729 //TimeTaker timer("generateBlock() local attribute retrieval");
3730 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3731 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3732 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3736 //dstream<<"generateBlock(): Done"<<std::endl;
3742 // Initialize temporary table
3743 const s32 ued = MAP_BLOCKSIZE;
3744 bool underground_emptiness[ued*ued*ued];
3745 for(s32 i=0; i<ued*ued*ued; i++)
3747 underground_emptiness[i] = 0;
3754 Initialize orp and ors. Try to find if some neighboring
3755 MapBlock has a tunnel ended in its side
3759 (float)(myrand()%ued)+0.5,
3760 (float)(myrand()%ued)+0.5,
3761 (float)(myrand()%ued)+0.5
3764 bool found_existing = false;
3770 for(s16 y=0; y<ued; y++)
3771 for(s16 x=0; x<ued; x++)
3773 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3774 if(getNode(ap).d == CONTENT_AIR)
3776 orp = v3f(x+1,y+1,0);
3777 found_existing = true;
3778 goto continue_generating;
3782 catch(InvalidPositionException &e){}
3788 for(s16 y=0; y<ued; y++)
3789 for(s16 x=0; x<ued; x++)
3791 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3792 if(getNode(ap).d == CONTENT_AIR)
3794 orp = v3f(x+1,y+1,ued-1);
3795 found_existing = true;
3796 goto continue_generating;
3800 catch(InvalidPositionException &e){}
3806 for(s16 y=0; y<ued; y++)
3807 for(s16 z=0; z<ued; z++)
3809 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3810 if(getNode(ap).d == CONTENT_AIR)
3812 orp = v3f(0,y+1,z+1);
3813 found_existing = true;
3814 goto continue_generating;
3818 catch(InvalidPositionException &e){}
3824 for(s16 y=0; y<ued; y++)
3825 for(s16 z=0; z<ued; z++)
3827 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3828 if(getNode(ap).d == CONTENT_AIR)
3830 orp = v3f(ued-1,y+1,z+1);
3831 found_existing = true;
3832 goto continue_generating;
3836 catch(InvalidPositionException &e){}
3842 for(s16 x=0; x<ued; x++)
3843 for(s16 z=0; z<ued; z++)
3845 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3846 if(getNode(ap).d == CONTENT_AIR)
3848 orp = v3f(x+1,0,z+1);
3849 found_existing = true;
3850 goto continue_generating;
3854 catch(InvalidPositionException &e){}
3860 for(s16 x=0; x<ued; x++)
3861 for(s16 z=0; z<ued; z++)
3863 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3864 if(getNode(ap).d == CONTENT_AIR)
3866 orp = v3f(x+1,ued-1,z+1);
3867 found_existing = true;
3868 goto continue_generating;
3872 catch(InvalidPositionException &e){}
3874 continue_generating:
3877 Choose whether to actually generate dungeon
3879 bool do_generate_dungeons = true;
3880 // Don't generate if no part is underground
3881 if(!some_part_underground)
3883 do_generate_dungeons = false;
3885 // Don't generate if mostly underwater surface
3886 /*else if(mostly_underwater_surface)
3888 do_generate_dungeons = false;
3890 // Partly underground = cave
3891 else if(!completely_underground)
3893 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3895 // Found existing dungeon underground
3896 else if(found_existing && completely_underground)
3898 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3900 // Underground and no dungeons found
3903 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3906 if(do_generate_dungeons)
3909 Generate some tunnel starting from orp and ors
3911 for(u16 i=0; i<3; i++)
3914 (float)(myrand()%ued)+0.5,
3915 (float)(myrand()%ued)+0.5,
3916 (float)(myrand()%ued)+0.5
3920 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3924 for(float f=0; f<1.0; f+=0.04)
3926 v3f fp = orp + vec * f;
3927 v3s16 cp(fp.X, fp.Y, fp.Z);
3929 s16 d1 = d0 + rs - 1;
3930 for(s16 z0=d0; z0<=d1; z0++)
3932 s16 si = rs - abs(z0);
3933 for(s16 x0=-si; x0<=si-1; x0++)
3935 s16 si2 = rs - abs(x0);
3936 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3942 if(isInArea(p, ued) == false)
3944 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3956 // Set to true if has caves.
3957 // Set when some non-air is changed to air when making caves.
3958 bool has_dungeons = false;
3961 Apply temporary cave data to block
3964 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3965 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3967 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3969 MapNode n = block->getNode(v3s16(x0,y0,z0));
3972 if(underground_emptiness[
3973 ued*ued*(z0*ued/MAP_BLOCKSIZE)
3974 +ued*(y0*ued/MAP_BLOCKSIZE)
3975 +(x0*ued/MAP_BLOCKSIZE)])
3977 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
3980 has_dungeons = true;
3986 block->setNode(v3s16(x0,y0,z0), n);
3991 This is used for guessing whether or not the block should
3992 receive sunlight from the top if the block above doesn't exist
3994 block->setIsUnderground(completely_underground);
3997 Force lighting update if some part of block is partly
3998 underground and has caves.
4000 /*if(some_part_underground && !completely_underground && has_dungeons)
4002 //dstream<<"Half-ground caves"<<std::endl;
4003 lighting_invalidated_blocks[block->getPos()] = block;
4006 // DEBUG: Always update lighting
4007 //lighting_invalidated_blocks[block->getPos()] = block;
4013 if(some_part_underground)
4015 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4020 for(s16 i=0; i<underground_level/4 + 1; i++)
4022 if(myrand()%50 == 0)
4025 (myrand()%(MAP_BLOCKSIZE-2))+1,
4026 (myrand()%(MAP_BLOCKSIZE-2))+1,
4027 (myrand()%(MAP_BLOCKSIZE-2))+1
4033 for(u16 i=0; i<27; i++)
4035 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4037 block->setNode(cp+g_27dirs[i], n);
4045 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
4046 u16 coal_rareness = 60 / coal_amount;
4047 if(coal_rareness == 0)
4049 if(myrand()%coal_rareness == 0)
4051 u16 a = myrand() % 16;
4052 u16 amount = coal_amount * a*a*a / 1000;
4053 for(s16 i=0; i<amount; i++)
4056 (myrand()%(MAP_BLOCKSIZE-2))+1,
4057 (myrand()%(MAP_BLOCKSIZE-2))+1,
4058 (myrand()%(MAP_BLOCKSIZE-2))+1
4062 n.d = CONTENT_STONE;
4063 n.param = MINERAL_COAL;
4065 for(u16 i=0; i<27; i++)
4067 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4069 block->setNode(cp+g_27dirs[i], n);
4077 //TODO: change to iron_amount or whatever
4078 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
4079 u16 iron_rareness = 60 / iron_amount;
4080 if(iron_rareness == 0)
4082 if(myrand()%iron_rareness == 0)
4084 u16 a = myrand() % 16;
4085 u16 amount = iron_amount * a*a*a / 1000;
4086 for(s16 i=0; i<amount; i++)
4089 (myrand()%(MAP_BLOCKSIZE-2))+1,
4090 (myrand()%(MAP_BLOCKSIZE-2))+1,
4091 (myrand()%(MAP_BLOCKSIZE-2))+1
4095 n.d = CONTENT_STONE;
4096 n.param = MINERAL_IRON;
4098 for(u16 i=0; i<27; i++)
4100 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4102 block->setNode(cp+g_27dirs[i], n);
4109 Create a few rats in empty blocks underground
4111 if(completely_underground)
4113 //for(u16 i=0; i<2; i++)
4116 (myrand()%(MAP_BLOCKSIZE-2))+1,
4117 (myrand()%(MAP_BLOCKSIZE-2))+1,
4118 (myrand()%(MAP_BLOCKSIZE-2))+1
4121 // Check that the place is empty
4122 //if(!is_ground_content(block->getNode(cp).d))
4125 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4126 block->addObject(obj);
4132 Add block to sector.
4134 sector->insertBlock(block);
4140 // An y-wise container of changed blocks
4141 core::map<s16, MapBlock*> changed_blocks_sector;
4144 Check if any sector's objects can be placed now.
4147 core::map<v3s16, u8> *objects = sector->getObjects();
4148 core::list<v3s16> objects_to_remove;
4149 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
4150 i.atEnd() == false; i++)
4152 v3s16 p = i.getNode()->getKey();
4154 u8 d = i.getNode()->getValue();
4156 // Ground level point (user for stuff that is on ground)
4158 bool ground_found = true;
4160 // Search real ground level
4164 MapNode n = sector->getNode(gp);
4166 // If not air, go one up and continue to placing the tree
4167 if(n.d != CONTENT_AIR)
4173 // If air, go one down
4174 gp += v3s16(0,-1,0);
4176 }catch(InvalidPositionException &e)
4178 // Ground not found.
4179 ground_found = false;
4180 // This is most close to ground
4187 if(d == SECTOR_OBJECT_TEST)
4189 if(sector->isValidArea(p + v3s16(0,0,0),
4190 p + v3s16(0,0,0), &changed_blocks_sector))
4193 n.d = CONTENT_TORCH;
4194 sector->setNode(p, n);
4195 objects_to_remove.push_back(p);
4198 else if(d == SECTOR_OBJECT_TREE_1)
4200 if(ground_found == false)
4203 v3s16 p_min = gp + v3s16(-1,0,-1);
4204 v3s16 p_max = gp + v3s16(1,5,1);
4205 if(sector->isValidArea(p_min, p_max,
4206 &changed_blocks_sector))
4210 sector->setNode(gp+v3s16(0,0,0), n);
4211 sector->setNode(gp+v3s16(0,1,0), n);
4212 sector->setNode(gp+v3s16(0,2,0), n);
4213 sector->setNode(gp+v3s16(0,3,0), n);
4215 n.d = CONTENT_LEAVES;
4217 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4219 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4220 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4221 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4222 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4223 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4224 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4225 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4226 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4228 sector->setNode(gp+v3s16(0,4,0), n);
4230 sector->setNode(gp+v3s16(-1,4,0), n);
4231 sector->setNode(gp+v3s16(1,4,0), n);
4232 sector->setNode(gp+v3s16(0,4,-1), n);
4233 sector->setNode(gp+v3s16(0,4,1), n);
4234 sector->setNode(gp+v3s16(1,4,1), n);
4235 sector->setNode(gp+v3s16(-1,4,1), n);
4236 sector->setNode(gp+v3s16(-1,4,-1), n);
4237 sector->setNode(gp+v3s16(1,4,-1), n);
4239 sector->setNode(gp+v3s16(-1,3,0), n);
4240 sector->setNode(gp+v3s16(1,3,0), n);
4241 sector->setNode(gp+v3s16(0,3,-1), n);
4242 sector->setNode(gp+v3s16(0,3,1), n);
4243 sector->setNode(gp+v3s16(1,3,1), n);
4244 sector->setNode(gp+v3s16(-1,3,1), n);
4245 sector->setNode(gp+v3s16(-1,3,-1), n);
4246 sector->setNode(gp+v3s16(1,3,-1), n);
4248 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4249 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4250 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4251 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4252 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4253 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4254 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4255 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4257 // Objects are identified by wanted position
4258 objects_to_remove.push_back(p);
4260 // Lighting has to be recalculated for this one.
4261 sector->getBlocksInArea(p_min, p_max,
4262 lighting_invalidated_blocks);
4265 else if(d == SECTOR_OBJECT_BUSH_1)
4267 if(ground_found == false)
4270 if(sector->isValidArea(gp + v3s16(0,0,0),
4271 gp + v3s16(0,0,0), &changed_blocks_sector))
4274 n.d = CONTENT_LEAVES;
4275 sector->setNode(gp+v3s16(0,0,0), n);
4277 // Objects are identified by wanted position
4278 objects_to_remove.push_back(p);
4281 else if(d == SECTOR_OBJECT_RAVINE)
4284 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4285 v3s16 p_max = p + v3s16(6,6,6);
4286 if(sector->isValidArea(p_min, p_max,
4287 &changed_blocks_sector))
4290 n.d = CONTENT_STONE;
4293 s16 depth = maxdepth + (myrand()%10);
4295 s16 minz = -6 - (-2);
4297 for(s16 x=-6; x<=6; x++)
4299 z += -1 + (myrand()%3);
4304 for(s16 y=depth+(myrand()%2); y<=6; y++)
4306 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4309 v3s16 p2 = p + v3s16(x,y,z-2);
4310 //if(is_ground_content(sector->getNode(p2).d))
4311 if(content_features(sector->getNode(p2).d).walkable)
4312 sector->setNode(p2, n);
4315 v3s16 p2 = p + v3s16(x,y,z-1);
4316 if(content_features(sector->getNode(p2).d).walkable)
4317 sector->setNode(p2, n2);
4320 v3s16 p2 = p + v3s16(x,y,z+0);
4321 if(content_features(sector->getNode(p2).d).walkable)
4322 sector->setNode(p2, n2);
4325 v3s16 p2 = p + v3s16(x,y,z+1);
4326 if(content_features(sector->getNode(p2).d).walkable)
4327 sector->setNode(p2, n);
4330 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4331 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4335 objects_to_remove.push_back(p);
4337 // Lighting has to be recalculated for this one.
4338 sector->getBlocksInArea(p_min, p_max,
4339 lighting_invalidated_blocks);
4344 dstream<<"ServerMap::generateBlock(): "
4345 "Invalid heightmap object"
4350 catch(InvalidPositionException &e)
4352 dstream<<"WARNING: "<<__FUNCTION_NAME
4353 <<": while inserting object "<<(int)d
4354 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4355 <<" InvalidPositionException.what()="
4356 <<e.what()<<std::endl;
4357 // This is not too fatal and seems to happen sometimes.
4362 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4363 i != objects_to_remove.end(); i++)
4365 objects->remove(*i);
4369 Translate sector's changed blocks to global changed blocks
4372 for(core::map<s16, MapBlock*>::Iterator
4373 i = changed_blocks_sector.getIterator();
4374 i.atEnd() == false; i++)
4376 MapBlock *block = i.getNode()->getValue();
4378 changed_blocks.insert(block->getPos(), block);
4381 block->setLightingExpired(true);
4388 <<"lighting_invalidated_blocks.size()"
4392 <<" "<<lighting_invalidated_blocks.size()
4393 <<", "<<has_dungeons
4394 <<", "<<completely_underground
4395 <<", "<<some_part_underground
4402 MapBlock * ServerMap::createBlock(v3s16 p)
4404 DSTACK("%s: p=(%d,%d,%d)",
4405 __FUNCTION_NAME, p.X, p.Y, p.Z);
4407 v2s16 p2d(p.X, p.Z);
4410 This will create or load a sector if not found in memory.
4411 If block exists on disk, it will be loaded.
4413 NOTE: On old save formats, this will be slow, as it generates
4414 lighting on blocks for them.
4416 ServerMapSector *sector;
4418 sector = (ServerMapSector*)createSector(p2d);
4419 assert(sector->getId() == MAPSECTOR_SERVER);
4421 /*catch(InvalidPositionException &e)
4423 dstream<<"createBlock: createSector() failed"<<std::endl;
4426 catch(std::exception &e)
4428 dstream<<"createBlock: createSector() failed: "
4429 <<e.what()<<std::endl;
4434 Try to get a block from the sector
4437 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4441 block = sector->createBlankBlock(block_y);
4445 MapBlock * ServerMap::emergeBlock(
4447 bool only_from_disk,
4448 core::map<v3s16, MapBlock*> &changed_blocks,
4449 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4452 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4454 p.X, p.Y, p.Z, only_from_disk);
4456 v2s16 p2d(p.X, p.Z);
4459 This will create or load a sector if not found in memory.
4460 If block exists on disk, it will be loaded.
4462 NOTE: On old save formats, this will be slow, as it generates
4463 lighting on blocks for them.
4465 ServerMapSector *sector;
4467 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4468 assert(sector->getId() == MAPSECTOR_SERVER);
4470 catch(std::exception &e)
4472 dstream<<"emergeBlock: emergeSector() failed: "
4473 <<e.what()<<std::endl;
4478 Try to get a block from the sector
4481 bool does_not_exist = false;
4482 bool lighting_expired = false;
4483 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4487 does_not_exist = true;
4489 else if(block->isDummy() == true)
4491 does_not_exist = true;
4493 else if(block->getLightingExpired())
4495 lighting_expired = true;
4500 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4505 If block was not found on disk and not going to generate a
4506 new one, make sure there is a dummy block in place.
4508 if(only_from_disk && (does_not_exist || lighting_expired))
4510 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4514 // Create dummy block
4515 block = new MapBlock(this, p, true);
4517 // Add block to sector
4518 sector->insertBlock(block);
4524 //dstream<<"Not found on disk, generating."<<std::endl;
4526 //TimeTaker("emergeBlock() generate");
4528 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4531 If the block doesn't exist, generate the block.
4535 block = generateBlock(p, block, sector, changed_blocks,
4536 lighting_invalidated_blocks);
4539 if(lighting_expired)
4541 lighting_invalidated_blocks.insert(p, block);
4545 Initially update sunlight
4549 core::map<v3s16, bool> light_sources;
4550 bool black_air_left = false;
4551 bool bottom_invalid =
4552 block->propagateSunlight(light_sources, true,
4553 &black_air_left, true);
4555 // If sunlight didn't reach everywhere and part of block is
4556 // above ground, lighting has to be properly updated
4557 //if(black_air_left && some_part_underground)
4560 lighting_invalidated_blocks[block->getPos()] = block;
4565 lighting_invalidated_blocks[block->getPos()] = block;
4570 Debug mode operation
4572 bool haxmode = g_settings.getBool("haxmode");
4575 // Don't calculate lighting at all
4576 //lighting_invalidated_blocks.clear();
4582 void ServerMap::createDir(std::string path)
4584 if(fs::CreateDir(path) == false)
4586 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4587 <<"\""<<path<<"\""<<std::endl;
4588 throw BaseException("ServerMap failed to create directory");
4592 std::string ServerMap::getSectorSubDir(v2s16 pos)
4595 snprintf(cc, 9, "%.4x%.4x",
4596 (unsigned int)pos.X&0xffff,
4597 (unsigned int)pos.Y&0xffff);
4599 return std::string(cc);
4602 std::string ServerMap::getSectorDir(v2s16 pos)
4604 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4607 v2s16 ServerMap::getSectorPos(std::string dirname)
4609 if(dirname.size() != 8)
4610 throw InvalidFilenameException("Invalid sector directory name");
4612 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4614 throw InvalidFilenameException("Invalid sector directory name");
4615 v2s16 pos((s16)x, (s16)y);
4619 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4621 v2s16 p2d = getSectorPos(sectordir);
4623 if(blockfile.size() != 4){
4624 throw InvalidFilenameException("Invalid block filename");
4627 int r = sscanf(blockfile.c_str(), "%4x", &y);
4629 throw InvalidFilenameException("Invalid block filename");
4630 return v3s16(p2d.X, y, p2d.Y);
4634 #define ENABLE_SECTOR_SAVING 1
4635 #define ENABLE_SECTOR_LOADING 1
4636 #define ENABLE_BLOCK_SAVING 1
4637 #define ENABLE_BLOCK_LOADING 1
4639 void ServerMap::save(bool only_changed)
4641 DSTACK(__FUNCTION_NAME);
4642 if(m_map_saving_enabled == false)
4644 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4648 if(only_changed == false)
4649 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4652 saveMasterHeightmap();
4654 u32 sector_meta_count = 0;
4655 u32 block_count = 0;
4658 JMutexAutoLock lock(m_sector_mutex);
4660 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4661 for(; i.atEnd() == false; i++)
4663 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4664 assert(sector->getId() == MAPSECTOR_SERVER);
4666 if(ENABLE_SECTOR_SAVING)
4668 if(sector->differs_from_disk || only_changed == false)
4670 saveSectorMeta(sector);
4671 sector_meta_count++;
4674 if(ENABLE_BLOCK_SAVING)
4676 core::list<MapBlock*> blocks;
4677 sector->getBlocks(blocks);
4678 core::list<MapBlock*>::Iterator j;
4679 for(j=blocks.begin(); j!=blocks.end(); j++)
4681 MapBlock *block = *j;
4682 if(block->getChangedFlag() || only_changed == false)
4694 Only print if something happened or saved whole map
4696 if(only_changed == false || sector_meta_count != 0
4697 || block_count != 0)
4699 dstream<<DTIME<<"ServerMap: Written: "
4700 <<sector_meta_count<<" sector metadata files, "
4701 <<block_count<<" block files"
4706 void ServerMap::loadAll()
4708 DSTACK(__FUNCTION_NAME);
4709 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4711 loadMasterHeightmap();
4713 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4715 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4717 JMutexAutoLock lock(m_sector_mutex);
4720 s32 printed_counter = -100000;
4721 s32 count = list.size();
4723 std::vector<fs::DirListNode>::iterator i;
4724 for(i=list.begin(); i!=list.end(); i++)
4726 if(counter > printed_counter + 10)
4728 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4729 printed_counter = counter;
4733 MapSector *sector = NULL;
4735 // We want directories
4739 sector = loadSectorMeta(i->name);
4741 catch(InvalidFilenameException &e)
4743 // This catches unknown crap in directory
4746 if(ENABLE_BLOCK_LOADING)
4748 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4749 (m_savedir+"/sectors/"+i->name);
4750 std::vector<fs::DirListNode>::iterator i2;
4751 for(i2=list2.begin(); i2!=list2.end(); i2++)
4757 loadBlock(i->name, i2->name, sector);
4759 catch(InvalidFilenameException &e)
4761 // This catches unknown crap in directory
4766 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4769 void ServerMap::saveMasterHeightmap()
4771 DSTACK(__FUNCTION_NAME);
4772 createDir(m_savedir);
4774 std::string fullpath = m_savedir + "/master_heightmap";
4775 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4776 if(o.good() == false)
4777 throw FileNotGoodException("Cannot open master heightmap");
4779 // Format used for writing
4780 u8 version = SER_FMT_VER_HIGHEST;
4783 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4785 [0] u8 serialization version
4786 [1] X master heightmap
4788 u32 fullsize = 1 + hmdata.getSize();
4789 SharedBuffer<u8> data(fullsize);
4792 memcpy(&data[1], *hmdata, hmdata.getSize());
4794 o.write((const char*)*data, fullsize);
4797 m_heightmap->serialize(o, version);
4800 void ServerMap::loadMasterHeightmap()
4802 DSTACK(__FUNCTION_NAME);
4803 std::string fullpath = m_savedir + "/master_heightmap";
4804 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4805 if(is.good() == false)
4806 throw FileNotGoodException("Cannot open master heightmap");
4808 if(m_heightmap != NULL)
4811 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4814 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4816 DSTACK(__FUNCTION_NAME);
4817 // Format used for writing
4818 u8 version = SER_FMT_VER_HIGHEST;
4820 v2s16 pos = sector->getPos();
4821 createDir(m_savedir);
4822 createDir(m_savedir+"/sectors");
4823 std::string dir = getSectorDir(pos);
4826 std::string fullpath = dir + "/heightmap";
4827 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4828 if(o.good() == false)
4829 throw FileNotGoodException("Cannot open master heightmap");
4831 sector->serialize(o, version);
4833 sector->differs_from_disk = false;
4836 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4838 DSTACK(__FUNCTION_NAME);
4840 v2s16 p2d = getSectorPos(dirname);
4841 std::string dir = m_savedir + "/sectors/" + dirname;
4843 std::string fullpath = dir + "/heightmap";
4844 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4845 if(is.good() == false)
4846 throw FileNotGoodException("Cannot open sector heightmap");
4848 ServerMapSector *sector = ServerMapSector::deSerialize
4849 (is, this, p2d, &m_hwrapper, m_sectors);
4851 sector->differs_from_disk = false;
4856 bool ServerMap::loadSectorFull(v2s16 p2d)
4858 DSTACK(__FUNCTION_NAME);
4859 std::string sectorsubdir = getSectorSubDir(p2d);
4861 MapSector *sector = NULL;
4863 JMutexAutoLock lock(m_sector_mutex);
4866 sector = loadSectorMeta(sectorsubdir);
4868 catch(InvalidFilenameException &e)
4872 catch(FileNotGoodException &e)
4876 catch(std::exception &e)
4881 if(ENABLE_BLOCK_LOADING)
4883 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4884 (m_savedir+"/sectors/"+sectorsubdir);
4885 std::vector<fs::DirListNode>::iterator i2;
4886 for(i2=list2.begin(); i2!=list2.end(); i2++)
4892 loadBlock(sectorsubdir, i2->name, sector);
4894 catch(InvalidFilenameException &e)
4896 // This catches unknown crap in directory
4904 bool ServerMap::deFlushSector(v2s16 p2d)
4906 DSTACK(__FUNCTION_NAME);
4907 // See if it already exists in memory
4909 MapSector *sector = getSectorNoGenerate(p2d);
4912 catch(InvalidPositionException &e)
4915 Try to load the sector from disk.
4917 if(loadSectorFull(p2d) == true)
4926 void ServerMap::saveBlock(MapBlock *block)
4928 DSTACK(__FUNCTION_NAME);
4930 Dummy blocks are not written
4932 if(block->isDummy())
4934 /*v3s16 p = block->getPos();
4935 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4936 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4940 // Format used for writing
4941 u8 version = SER_FMT_VER_HIGHEST;
4943 v3s16 p3d = block->getPos();
4944 v2s16 p2d(p3d.X, p3d.Z);
4945 createDir(m_savedir);
4946 createDir(m_savedir+"/sectors");
4947 std::string dir = getSectorDir(p2d);
4950 // Block file is map/sectors/xxxxxxxx/xxxx
4952 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4953 std::string fullpath = dir + "/" + cc;
4954 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4955 if(o.good() == false)
4956 throw FileNotGoodException("Cannot open block data");
4959 [0] u8 serialization version
4962 o.write((char*)&version, 1);
4964 block->serialize(o, version);
4967 Versions up from 9 have block objects.
4971 block->serializeObjects(o, version);
4974 // We just wrote it to the disk
4975 block->resetChangedFlag();
4978 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4980 DSTACK(__FUNCTION_NAME);
4984 // Block file is map/sectors/xxxxxxxx/xxxx
4985 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4986 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4987 if(is.good() == false)
4988 throw FileNotGoodException("Cannot open block file");
4990 v3s16 p3d = getBlockPos(sectordir, blockfile);
4991 v2s16 p2d(p3d.X, p3d.Z);
4993 assert(sector->getPos() == p2d);
4995 u8 version = SER_FMT_VER_INVALID;
4996 is.read((char*)&version, 1);
4998 /*u32 block_size = MapBlock::serializedLength(version);
4999 SharedBuffer<u8> data(block_size);
5000 is.read((char*)*data, block_size);*/
5002 // This will always return a sector because we're the server
5003 //MapSector *sector = emergeSector(p2d);
5005 MapBlock *block = NULL;
5006 bool created_new = false;
5008 block = sector->getBlockNoCreate(p3d.Y);
5010 catch(InvalidPositionException &e)
5012 block = sector->createBlankBlockNoInsert(p3d.Y);
5016 // deserialize block data
5017 block->deSerialize(is, version);
5020 Versions up from 9 have block objects.
5024 block->updateObjects(is, version, NULL, 0);
5028 sector->insertBlock(block);
5031 Convert old formats to new and save
5034 // Save old format blocks in new format
5035 if(version < SER_FMT_VER_HIGHEST)
5040 // We just loaded it from the disk, so it's up-to-date.
5041 block->resetChangedFlag();
5044 catch(SerializationError &e)
5046 dstream<<"WARNING: Invalid block data on disk "
5047 "(SerializationError). Ignoring."
5052 // Gets from master heightmap
5053 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5055 assert(m_heightmap != NULL);
5063 corners[0] = m_heightmap->getGroundHeight
5064 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5065 corners[1] = m_heightmap->getGroundHeight
5066 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5067 corners[2] = m_heightmap->getGroundHeight
5068 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5069 corners[3] = m_heightmap->getGroundHeight
5070 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
5073 void ServerMap::PrintInfo(std::ostream &out)
5084 ClientMap::ClientMap(
5086 MapDrawControl &control,
5087 scene::ISceneNode* parent,
5088 scene::ISceneManager* mgr,
5092 scene::ISceneNode(parent, mgr, id),
5099 /*m_box = core::aabbox3d<f32>(0,0,0,
5100 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5101 /*m_box = core::aabbox3d<f32>(0,0,0,
5102 map->getSizeNodes().X * BS,
5103 map->getSizeNodes().Y * BS,
5104 map->getSizeNodes().Z * BS);*/
5105 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5106 BS*1000000,BS*1000000,BS*1000000);
5108 //setPosition(v3f(BS,BS,BS));
5111 ClientMap::~ClientMap()
5113 JMutexAutoLock lock(mesh_mutex);
5122 MapSector * ClientMap::emergeSector(v2s16 p2d)
5124 DSTACK(__FUNCTION_NAME);
5125 // Check that it doesn't exist already
5127 return getSectorNoGenerate(p2d);
5129 catch(InvalidPositionException &e)
5133 // Create a sector with no heightmaps
5134 ClientMapSector *sector = new ClientMapSector(this, p2d);
5137 JMutexAutoLock lock(m_sector_mutex);
5138 m_sectors.insert(p2d, sector);
5144 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5146 DSTACK(__FUNCTION_NAME);
5147 ClientMapSector *sector = NULL;
5149 JMutexAutoLock lock(m_sector_mutex);
5151 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5155 sector = (ClientMapSector*)n->getValue();
5156 assert(sector->getId() == MAPSECTOR_CLIENT);
5160 sector = new ClientMapSector(this, p2d);
5162 JMutexAutoLock lock(m_sector_mutex);
5163 m_sectors.insert(p2d, sector);
5167 sector->deSerialize(is);
5170 void ClientMap::OnRegisterSceneNode()
5174 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5175 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5178 ISceneNode::OnRegisterSceneNode();
5181 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5183 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5184 DSTACK(__FUNCTION_NAME);
5186 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5189 Get time for measuring timeout.
5191 Measuring time is very useful for long delays when the
5192 machine is swapping a lot.
5194 int time1 = time(0);
5196 u32 daynight_ratio = m_client->getDayNightRatio();
5198 m_camera_mutex.Lock();
5199 v3f camera_position = m_camera_position;
5200 v3f camera_direction = m_camera_direction;
5201 m_camera_mutex.Unlock();
5204 Get all blocks and draw all visible ones
5207 v3s16 cam_pos_nodes(
5208 camera_position.X / BS,
5209 camera_position.Y / BS,
5210 camera_position.Z / BS);
5212 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5214 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5215 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5217 // Take a fair amount as we will be dropping more out later
5219 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5220 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5221 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5223 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5224 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5225 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5227 u32 vertex_count = 0;
5229 // For limiting number of mesh updates per frame
5230 u32 mesh_update_count = 0;
5232 u32 blocks_would_have_drawn = 0;
5233 u32 blocks_drawn = 0;
5235 //NOTE: The sectors map should be locked but we're not doing it
5236 // because it'd cause too much delays
5238 int timecheck_counter = 0;
5239 core::map<v2s16, MapSector*>::Iterator si;
5240 si = m_sectors.getIterator();
5241 for(; si.atEnd() == false; si++)
5244 timecheck_counter++;
5245 if(timecheck_counter > 50)
5247 int time2 = time(0);
5248 if(time2 > time1 + 4)
5250 dstream<<"ClientMap::renderMap(): "
5251 "Rendering takes ages, returning."
5258 MapSector *sector = si.getNode()->getValue();
5259 v2s16 sp = sector->getPos();
5261 if(m_control.range_all == false)
5263 if(sp.X < p_blocks_min.X
5264 || sp.X > p_blocks_max.X
5265 || sp.Y < p_blocks_min.Z
5266 || sp.Y > p_blocks_max.Z)
5270 core::list< MapBlock * > sectorblocks;
5271 sector->getBlocks(sectorblocks);
5277 core::list< MapBlock * >::Iterator i;
5278 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5280 MapBlock *block = *i;
5283 Compare block position to camera position, skip
5284 if not seen on display
5287 float range = 100000 * BS;
5288 if(m_control.range_all == false)
5289 range = m_control.wanted_range * BS;
5291 if(isBlockInSight(block->getPos(), camera_position,
5292 camera_direction, range) == false)
5298 v3s16 blockpos_nodes = block->getPosRelative();
5300 // Block center position
5302 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5303 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5304 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5307 // Block position relative to camera
5308 v3f blockpos_relative = blockpos - camera_position;
5310 // Distance in camera direction (+=front, -=back)
5311 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5314 f32 d = blockpos_relative.getLength();
5316 if(m_control.range_all == false)
5318 // If block is far away, don't draw it
5319 if(d > m_control.wanted_range * BS)
5323 // Maximum radius of a block
5324 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5326 // If block is (nearly) touching the camera, don't
5327 // bother validating further (that is, render it anyway)
5328 if(d > block_max_radius * 1.5)
5330 // Cosine of the angle between the camera direction
5331 // and the block direction (camera_direction is an unit vector)
5332 f32 cosangle = dforward / d;
5334 // Compensate for the size of the block
5335 // (as the block has to be shown even if it's a bit off FOV)
5336 // This is an estimate.
5337 cosangle += block_max_radius / dforward;
5339 // If block is not in the field of view, skip it
5340 //if(cosangle < cos(FOV_ANGLE/2))
5341 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5346 v3s16 blockpos_nodes = block->getPosRelative();
5348 // Block center position
5350 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5351 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5352 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5355 // Block position relative to camera
5356 v3f blockpos_relative = blockpos - camera_position;
5359 f32 d = blockpos_relative.getLength();
5366 bool mesh_expired = false;
5369 JMutexAutoLock lock(block->mesh_mutex);
5371 mesh_expired = block->getMeshExpired();
5373 // Mesh has not been expired and there is no mesh:
5374 // block has no content
5375 if(block->mesh == NULL && mesh_expired == false)
5379 f32 faraway = BS*50;
5380 //f32 faraway = m_control.wanted_range * BS;
5383 This has to be done with the mesh_mutex unlocked
5385 // Pretty random but this should work somewhat nicely
5386 if(mesh_expired && (
5387 (mesh_update_count < 3
5388 && (d < faraway || mesh_update_count < 2)
5391 (m_control.range_all && mesh_update_count < 20)
5394 /*if(mesh_expired && mesh_update_count < 6
5395 && (d < faraway || mesh_update_count < 3))*/
5397 mesh_update_count++;
5399 // Mesh has been expired: generate new mesh
5400 //block->updateMeshes(daynight_i);
5401 block->updateMesh(daynight_ratio);
5403 mesh_expired = false;
5407 Don't draw an expired mesh that is far away
5409 /*if(mesh_expired && d >= faraway)
5412 // Instead, delete it
5413 JMutexAutoLock lock(block->mesh_mutex);
5416 block->mesh->drop();
5419 // And continue to next block
5424 Draw the faces of the block
5427 JMutexAutoLock lock(block->mesh_mutex);
5429 scene::SMesh *mesh = block->mesh;
5434 blocks_would_have_drawn++;
5435 if(blocks_drawn >= m_control.wanted_max_blocks
5436 && m_control.range_all == false
5437 && d > m_control.wanted_min_range * BS)
5441 u32 c = mesh->getMeshBufferCount();
5443 for(u32 i=0; i<c; i++)
5445 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5446 const video::SMaterial& material = buf->getMaterial();
5447 video::IMaterialRenderer* rnd =
5448 driver->getMaterialRenderer(material.MaterialType);
5449 bool transparent = (rnd && rnd->isTransparent());
5450 // Render transparent on transparent pass and likewise.
5451 if(transparent == is_transparent_pass)
5453 driver->setMaterial(buf->getMaterial());
5454 driver->drawMeshBuffer(buf);
5455 vertex_count += buf->getVertexCount();
5459 } // foreach sectorblocks
5462 m_control.blocks_drawn = blocks_drawn;
5463 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5465 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5466 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5469 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5470 core::map<v3s16, MapBlock*> *affected_blocks)
5472 bool changed = false;
5474 Add it to all blocks touching it
5477 v3s16(0,0,0), // this
5478 v3s16(0,0,1), // back
5479 v3s16(0,1,0), // top
5480 v3s16(1,0,0), // right
5481 v3s16(0,0,-1), // front
5482 v3s16(0,-1,0), // bottom
5483 v3s16(-1,0,0), // left
5485 for(u16 i=0; i<7; i++)
5487 v3s16 p2 = p + dirs[i];
5488 // Block position of neighbor (or requested) node
5489 v3s16 blockpos = getNodeBlockPos(p2);
5490 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5491 if(blockref == NULL)
5493 // Relative position of requested node
5494 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5495 if(blockref->setTempMod(relpos, mod))
5500 if(changed && affected_blocks!=NULL)
5502 for(u16 i=0; i<7; i++)
5504 v3s16 p2 = p + dirs[i];
5505 // Block position of neighbor (or requested) node
5506 v3s16 blockpos = getNodeBlockPos(p2);
5507 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5508 if(blockref == NULL)
5510 affected_blocks->insert(blockpos, blockref);
5516 bool ClientMap::clearTempMod(v3s16 p,
5517 core::map<v3s16, MapBlock*> *affected_blocks)
5519 bool changed = false;
5521 v3s16(0,0,0), // this
5522 v3s16(0,0,1), // back
5523 v3s16(0,1,0), // top
5524 v3s16(1,0,0), // right
5525 v3s16(0,0,-1), // front
5526 v3s16(0,-1,0), // bottom
5527 v3s16(-1,0,0), // left
5529 for(u16 i=0; i<7; i++)
5531 v3s16 p2 = p + dirs[i];
5532 // Block position of neighbor (or requested) node
5533 v3s16 blockpos = getNodeBlockPos(p2);
5534 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5535 if(blockref == NULL)
5537 // Relative position of requested node
5538 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5539 if(blockref->clearTempMod(relpos))
5544 if(changed && affected_blocks!=NULL)
5546 for(u16 i=0; i<7; i++)
5548 v3s16 p2 = p + dirs[i];
5549 // Block position of neighbor (or requested) node
5550 v3s16 blockpos = getNodeBlockPos(p2);
5551 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5552 if(blockref == NULL)
5554 affected_blocks->insert(blockpos, blockref);
5560 void ClientMap::PrintInfo(std::ostream &out)
5571 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5576 MapVoxelManipulator::~MapVoxelManipulator()
5578 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5582 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5584 TimeTaker timer1("emerge", &emerge_time);
5586 // Units of these are MapBlocks
5587 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5588 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5590 VoxelArea block_area_nodes
5591 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5593 addArea(block_area_nodes);
5595 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5596 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5597 for(s32 x=p_min.X; x<=p_max.X; x++)
5600 core::map<v3s16, bool>::Node *n;
5601 n = m_loaded_blocks.find(p);
5605 bool block_data_inexistent = false;
5608 TimeTaker timer1("emerge load", &emerge_load_time);
5610 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5611 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5614 dstream<<std::endl;*/
5616 MapBlock *block = m_map->getBlockNoCreate(p);
5617 if(block->isDummy())
5618 block_data_inexistent = true;
5620 block->copyTo(*this);
5622 catch(InvalidPositionException &e)
5624 block_data_inexistent = true;
5627 if(block_data_inexistent)
5629 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5630 // Fill with VOXELFLAG_INEXISTENT
5631 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5632 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5634 s32 i = m_area.index(a.MinEdge.X,y,z);
5635 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5639 m_loaded_blocks.insert(p, !block_data_inexistent);
5642 //dstream<<"emerge done"<<std::endl;
5646 SUGG: Add an option to only update eg. water and air nodes.
5647 This will make it interfere less with important stuff if
5650 void MapVoxelManipulator::blitBack
5651 (core::map<v3s16, MapBlock*> & modified_blocks)
5653 if(m_area.getExtent() == v3s16(0,0,0))
5656 //TimeTaker timer1("blitBack");
5658 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5659 <<m_loaded_blocks.size()<<std::endl;*/
5662 Initialize block cache
5664 v3s16 blockpos_last;
5665 MapBlock *block = NULL;
5666 bool block_checked_in_modified = false;
5668 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5669 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5670 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5674 u8 f = m_flags[m_area.index(p)];
5675 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5678 MapNode &n = m_data[m_area.index(p)];
5680 v3s16 blockpos = getNodeBlockPos(p);
5685 if(block == NULL || blockpos != blockpos_last){
5686 block = m_map->getBlockNoCreate(blockpos);
5687 blockpos_last = blockpos;
5688 block_checked_in_modified = false;
5691 // Calculate relative position in block
5692 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5694 // Don't continue if nothing has changed here
5695 if(block->getNode(relpos) == n)
5698 //m_map->setNode(m_area.MinEdge + p, n);
5699 block->setNode(relpos, n);
5702 Make sure block is in modified_blocks
5704 if(block_checked_in_modified == false)
5706 modified_blocks[blockpos] = block;
5707 block_checked_in_modified = true;
5710 catch(InvalidPositionException &e)
5716 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5717 MapVoxelManipulator(map)
5721 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5725 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5727 // Just create the area so that it can be pointed to
5728 VoxelManipulator::emerge(a, caller_id);
5731 void ManualMapVoxelManipulator::initialEmerge(
5732 v3s16 blockpos_min, v3s16 blockpos_max)
5734 TimeTaker timer1("initialEmerge", &emerge_time);
5736 // Units of these are MapBlocks
5737 v3s16 p_min = blockpos_min;
5738 v3s16 p_max = blockpos_max;
5740 VoxelArea block_area_nodes
5741 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5743 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5746 dstream<<"initialEmerge: area: ";
5747 block_area_nodes.print(dstream);
5748 dstream<<" ("<<size_MB<<"MB)";
5752 addArea(block_area_nodes);
5754 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5755 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5756 for(s32 x=p_min.X; x<=p_max.X; x++)
5759 core::map<v3s16, bool>::Node *n;
5760 n = m_loaded_blocks.find(p);
5764 bool block_data_inexistent = false;
5767 TimeTaker timer1("emerge load", &emerge_load_time);
5769 MapBlock *block = m_map->getBlockNoCreate(p);
5770 if(block->isDummy())
5771 block_data_inexistent = true;
5773 block->copyTo(*this);
5775 catch(InvalidPositionException &e)
5777 block_data_inexistent = true;
5780 if(block_data_inexistent)
5783 Mark area inexistent
5785 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5786 // Fill with VOXELFLAG_INEXISTENT
5787 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5788 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5790 s32 i = m_area.index(a.MinEdge.X,y,z);
5791 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5795 m_loaded_blocks.insert(p, !block_data_inexistent);
5799 void ManualMapVoxelManipulator::blitBackAll(
5800 core::map<v3s16, MapBlock*> * modified_blocks)
5802 if(m_area.getExtent() == v3s16(0,0,0))
5806 Copy data of all blocks
5808 for(core::map<v3s16, bool>::Iterator
5809 i = m_loaded_blocks.getIterator();
5810 i.atEnd() == false; i++)
5812 bool existed = i.getNode()->getValue();
5813 if(existed == false)
5815 v3s16 p = i.getNode()->getKey();
5816 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5819 dstream<<"WARNING: "<<__FUNCTION_NAME
5820 <<": got NULL block "
5821 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5826 block->copyFrom(*this);
5829 modified_blocks->insert(p, block);