3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
35 Map::Map(std::ostream &dout):
37 m_camera_position(0,0,0),
38 m_camera_direction(0,0,1),
41 m_sector_mutex.Init();
42 m_camera_mutex.Init();
43 assert(m_sector_mutex.IsInitialized());
44 assert(m_camera_mutex.IsInitialized());
46 // Get this so that the player can stay on it at first
47 //getSector(v2s16(0,0));
55 /*updater.setRun(false);
56 while(updater.IsRunning())
62 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
63 for(; i.atEnd() == false; i++)
65 MapSector *sector = i.getNode()->getValue();
70 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
72 if(m_sector_cache != NULL && p == m_sector_cache_p){
73 MapSector * sector = m_sector_cache;
74 // Reset inactivity timer
75 sector->usage_timer = 0.0;
79 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
84 MapSector *sector = n->getValue();
86 // Cache the last result
88 m_sector_cache = sector;
90 // Reset inactivity timer
91 sector->usage_timer = 0.0;
95 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
97 JMutexAutoLock lock(m_sector_mutex);
99 return getSectorNoGenerateNoExNoLock(p);
102 MapSector * Map::getSectorNoGenerate(v2s16 p)
104 MapSector *sector = getSectorNoGenerateNoEx(p);
106 throw InvalidPositionException();
111 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
113 v2s16 p2d(p3d.X, p3d.Z);
114 MapSector * sector = getSectorNoGenerate(p2d);
116 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
121 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerate(p2d);
127 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 catch(InvalidPositionException &e)
136 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
138 v2s16 p2d(p3d.X, p3d.Z);
139 MapSector * sector = getSectorCreate(p2d);
141 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
144 block = sector->createBlankBlock(p3d.Y);
148 f32 Map::getGroundHeight(v2s16 p, bool generate)
151 v2s16 sectorpos = getNodeSectorPos(p);
152 MapSector * sref = getSectorNoGenerate(sectorpos);
153 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
154 f32 y = sref->getGroundHeight(relpos);
157 catch(InvalidPositionException &e)
159 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
163 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
165 /*m_dout<<DTIME<<"Map::setGroundHeight(("
167 <<"), "<<y<<")"<<std::endl;*/
168 v2s16 sectorpos = getNodeSectorPos(p);
169 MapSector * sref = getSectorNoGenerate(sectorpos);
170 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
171 //sref->mutex.Lock();
172 sref->setGroundHeight(relpos, y);
173 //sref->mutex.Unlock();
176 bool Map::isNodeUnderground(v3s16 p)
178 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock * block = getBlockNoCreate(blockpos);
181 return block->getIsUnderground();
183 catch(InvalidPositionException &e)
190 Goes recursively through the neighbours of the node.
192 Alters only transparent nodes.
194 If the lighting of the neighbour is lower than the lighting of
195 the node was (before changing it to 0 at the step before), the
196 lighting of the neighbour is set to 0 and then the same stuff
197 repeats for the neighbour.
199 The ending nodes of the routine are stored in light_sources.
200 This is useful when a light is removed. In such case, this
201 routine can be called for the light node and then again for
202 light_sources to re-light the area without the removed light.
204 values of from_nodes are lighting values.
206 void Map::unspreadLight(enum LightBank bank,
207 core::map<v3s16, u8> & from_nodes,
208 core::map<v3s16, bool> & light_sources,
209 core::map<v3s16, MapBlock*> & modified_blocks)
212 v3s16(0,0,1), // back
214 v3s16(1,0,0), // right
215 v3s16(0,0,-1), // front
216 v3s16(0,-1,0), // bottom
217 v3s16(-1,0,0), // left
220 if(from_nodes.size() == 0)
223 u32 blockchangecount = 0;
225 core::map<v3s16, u8> unlighted_nodes;
226 core::map<v3s16, u8>::Iterator j;
227 j = from_nodes.getIterator();
230 Initialize block cache
233 MapBlock *block = NULL;
234 // Cache this a bit, too
235 bool block_checked_in_modified = false;
237 for(; j.atEnd() == false; j++)
239 v3s16 pos = j.getNode()->getKey();
240 v3s16 blockpos = getNodeBlockPos(pos);
242 // Only fetch a new block if the block position has changed
244 if(block == NULL || blockpos != blockpos_last){
245 block = getBlockNoCreate(blockpos);
246 blockpos_last = blockpos;
248 block_checked_in_modified = false;
252 catch(InvalidPositionException &e)
260 // Calculate relative position in block
261 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
263 // Get node straight from the block
264 MapNode n = block->getNode(relpos);
266 u8 oldlight = j.getNode()->getValue();
268 // Loop through 6 neighbors
269 for(u16 i=0; i<6; i++)
271 // Get the position of the neighbor node
272 v3s16 n2pos = pos + dirs[i];
274 // Get the block where the node is located
275 v3s16 blockpos = getNodeBlockPos(n2pos);
279 // Only fetch a new block if the block position has changed
281 if(block == NULL || blockpos != blockpos_last){
282 block = getBlockNoCreate(blockpos);
283 blockpos_last = blockpos;
285 block_checked_in_modified = false;
289 catch(InvalidPositionException &e)
294 // Calculate relative position in block
295 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
296 // Get node straight from the block
297 MapNode n2 = block->getNode(relpos);
299 bool changed = false;
301 //TODO: Optimize output by optimizing light_sources?
304 If the neighbor is dimmer than what was specified
305 as oldlight (the light of the previous node)
307 if(n2.getLight(bank) < oldlight)
310 And the neighbor is transparent and it has some light
312 if(n2.light_propagates() && n2.getLight(bank) != 0)
315 Set light to 0 and add to queue
318 u8 current_light = n2.getLight(bank);
319 n2.setLight(bank, 0);
320 block->setNode(relpos, n2);
322 unlighted_nodes.insert(n2pos, current_light);
326 Remove from light_sources if it is there
327 NOTE: This doesn't happen nearly at all
329 /*if(light_sources.find(n2pos))
331 std::cout<<"Removed from light_sources"<<std::endl;
332 light_sources.remove(n2pos);
337 if(light_sources.find(n2pos) != NULL)
338 light_sources.remove(n2pos);*/
341 light_sources.insert(n2pos, true);
344 // Add to modified_blocks
345 if(changed == true && block_checked_in_modified == false)
347 // If the block is not found in modified_blocks, add.
348 if(modified_blocks.find(blockpos) == NULL)
350 modified_blocks.insert(blockpos, block);
352 block_checked_in_modified = true;
355 catch(InvalidPositionException &e)
362 /*dstream<<"unspreadLight(): Changed block "
363 <<blockchangecount<<" times"
364 <<" for "<<from_nodes.size()<<" nodes"
367 if(unlighted_nodes.size() > 0)
368 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
372 A single-node wrapper of the above
374 void Map::unLightNeighbors(enum LightBank bank,
375 v3s16 pos, u8 lightwas,
376 core::map<v3s16, bool> & light_sources,
377 core::map<v3s16, MapBlock*> & modified_blocks)
379 core::map<v3s16, u8> from_nodes;
380 from_nodes.insert(pos, lightwas);
382 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
386 Lights neighbors of from_nodes, collects all them and then
389 void Map::spreadLight(enum LightBank bank,
390 core::map<v3s16, bool> & from_nodes,
391 core::map<v3s16, MapBlock*> & modified_blocks)
393 const v3s16 dirs[6] = {
394 v3s16(0,0,1), // back
396 v3s16(1,0,0), // right
397 v3s16(0,0,-1), // front
398 v3s16(0,-1,0), // bottom
399 v3s16(-1,0,0), // left
402 if(from_nodes.size() == 0)
405 u32 blockchangecount = 0;
407 core::map<v3s16, bool> lighted_nodes;
408 core::map<v3s16, bool>::Iterator j;
409 j = from_nodes.getIterator();
412 Initialize block cache
415 MapBlock *block = NULL;
416 // Cache this a bit, too
417 bool block_checked_in_modified = false;
419 for(; j.atEnd() == false; j++)
420 //for(; j != from_nodes.end(); j++)
422 v3s16 pos = j.getNode()->getKey();
424 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
425 v3s16 blockpos = getNodeBlockPos(pos);
427 // Only fetch a new block if the block position has changed
429 if(block == NULL || blockpos != blockpos_last){
430 block = getBlockNoCreate(blockpos);
431 blockpos_last = blockpos;
433 block_checked_in_modified = false;
437 catch(InvalidPositionException &e)
445 // Calculate relative position in block
446 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
448 // Get node straight from the block
449 MapNode n = block->getNode(relpos);
451 u8 oldlight = n.getLight(bank);
452 u8 newlight = diminish_light(oldlight);
454 // Loop through 6 neighbors
455 for(u16 i=0; i<6; i++){
456 // Get the position of the neighbor node
457 v3s16 n2pos = pos + dirs[i];
459 // Get the block where the node is located
460 v3s16 blockpos = getNodeBlockPos(n2pos);
464 // Only fetch a new block if the block position has changed
466 if(block == NULL || blockpos != blockpos_last){
467 block = getBlockNoCreate(blockpos);
468 blockpos_last = blockpos;
470 block_checked_in_modified = false;
474 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
481 // Get node straight from the block
482 MapNode n2 = block->getNode(relpos);
484 bool changed = false;
486 If the neighbor is brighter than the current node,
487 add to list (it will light up this node on its turn)
489 if(n2.getLight(bank) > undiminish_light(oldlight))
491 lighted_nodes.insert(n2pos, true);
492 //lighted_nodes.push_back(n2pos);
496 If the neighbor is dimmer than how much light this node
497 would spread on it, add to list
499 if(n2.getLight(bank) < newlight)
501 if(n2.light_propagates())
503 n2.setLight(bank, newlight);
504 block->setNode(relpos, n2);
505 lighted_nodes.insert(n2pos, true);
506 //lighted_nodes.push_back(n2pos);
511 // Add to modified_blocks
512 if(changed == true && block_checked_in_modified == false)
514 // If the block is not found in modified_blocks, add.
515 if(modified_blocks.find(blockpos) == NULL)
517 modified_blocks.insert(blockpos, block);
519 block_checked_in_modified = true;
522 catch(InvalidPositionException &e)
529 /*dstream<<"spreadLight(): Changed block "
530 <<blockchangecount<<" times"
531 <<" for "<<from_nodes.size()<<" nodes"
534 if(lighted_nodes.size() > 0)
535 spreadLight(bank, lighted_nodes, modified_blocks);
539 A single-node source variation of the above.
541 void Map::lightNeighbors(enum LightBank bank,
543 core::map<v3s16, MapBlock*> & modified_blocks)
545 core::map<v3s16, bool> from_nodes;
546 from_nodes.insert(pos, true);
547 spreadLight(bank, from_nodes, modified_blocks);
550 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
553 v3s16(0,0,1), // back
555 v3s16(1,0,0), // right
556 v3s16(0,0,-1), // front
557 v3s16(0,-1,0), // bottom
558 v3s16(-1,0,0), // left
561 u8 brightest_light = 0;
562 v3s16 brightest_pos(0,0,0);
563 bool found_something = false;
565 // Loop through 6 neighbors
566 for(u16 i=0; i<6; i++){
567 // Get the position of the neighbor node
568 v3s16 n2pos = p + dirs[i];
573 catch(InvalidPositionException &e)
577 if(n2.getLight(bank) > brightest_light || found_something == false){
578 brightest_light = n2.getLight(bank);
579 brightest_pos = n2pos;
580 found_something = true;
584 if(found_something == false)
585 throw InvalidPositionException();
587 return brightest_pos;
591 Propagates sunlight down from a node.
592 Starting point gets sunlight.
594 Returns the lowest y value of where the sunlight went.
596 Mud is turned into grass in where the sunlight stops.
598 s16 Map::propagateSunlight(v3s16 start,
599 core::map<v3s16, MapBlock*> & modified_blocks)
604 v3s16 pos(start.X, y, start.Z);
606 v3s16 blockpos = getNodeBlockPos(pos);
609 block = getBlockNoCreate(blockpos);
611 catch(InvalidPositionException &e)
616 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
617 MapNode n = block->getNode(relpos);
619 if(n.sunlight_propagates())
621 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
622 block->setNode(relpos, n);
624 modified_blocks.insert(blockpos, block);
628 // Turn mud into grass
629 if(n.d == CONTENT_MUD)
632 block->setNode(relpos, n);
633 modified_blocks.insert(blockpos, block);
636 // Sunlight goes no further
643 void Map::updateLighting(enum LightBank bank,
644 core::map<v3s16, MapBlock*> & a_blocks,
645 core::map<v3s16, MapBlock*> & modified_blocks)
647 /*m_dout<<DTIME<<"Map::updateLighting(): "
648 <<a_blocks.size()<<" blocks."<<std::endl;*/
650 //TimeTaker timer("updateLighting");
654 //u32 count_was = modified_blocks.size();
656 core::map<v3s16, MapBlock*> blocks_to_update;
658 core::map<v3s16, bool> light_sources;
660 core::map<v3s16, u8> unlight_from;
662 core::map<v3s16, MapBlock*>::Iterator i;
663 i = a_blocks.getIterator();
664 for(; i.atEnd() == false; i++)
666 MapBlock *block = i.getNode()->getValue();
670 // Don't bother with dummy blocks.
674 v3s16 pos = block->getPos();
675 modified_blocks.insert(pos, block);
677 blocks_to_update.insert(pos, block);
680 Clear all light from block
682 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
683 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
684 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
689 MapNode n = block->getNode(v3s16(x,y,z));
690 u8 oldlight = n.getLight(bank);
692 block->setNode(v3s16(x,y,z), n);
694 // Collect borders for unlighting
695 if(x==0 || x == MAP_BLOCKSIZE-1
696 || y==0 || y == MAP_BLOCKSIZE-1
697 || z==0 || z == MAP_BLOCKSIZE-1)
699 v3s16 p_map = p + v3s16(
702 MAP_BLOCKSIZE*pos.Z);
703 unlight_from.insert(p_map, oldlight);
706 catch(InvalidPositionException &e)
709 This would happen when dealing with a
713 dstream<<"updateLighting(): InvalidPositionException"
718 if(bank == LIGHTBANK_DAY)
720 bool bottom_valid = block->propagateSunlight(light_sources);
722 // If bottom is valid, we're done.
726 else if(bank == LIGHTBANK_NIGHT)
728 // For night lighting, sunlight is not propagated
733 // Invalid lighting bank
737 /*dstream<<"Bottom for sunlight-propagated block ("
738 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
741 // Bottom sunlight is not valid; get the block and loop to it
745 block = getBlockNoCreate(pos);
747 catch(InvalidPositionException &e)
757 TimeTaker timer("unspreadLight");
758 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
763 u32 diff = modified_blocks.size() - count_was;
764 count_was = modified_blocks.size();
765 dstream<<"unspreadLight modified "<<diff<<std::endl;
769 TimeTaker timer("spreadLight");
770 spreadLight(bank, light_sources, modified_blocks);
775 u32 diff = modified_blocks.size() - count_was;
776 count_was = modified_blocks.size();
777 dstream<<"spreadLight modified "<<diff<<std::endl;
782 //MapVoxelManipulator vmanip(this);
784 // Make a manual voxel manipulator and load all the blocks
785 // that touch the requested blocks
786 ManualMapVoxelManipulator vmanip(this);
787 core::map<v3s16, MapBlock*>::Iterator i;
788 i = blocks_to_update.getIterator();
789 for(; i.atEnd() == false; i++)
791 MapBlock *block = i.getNode()->getValue();
792 v3s16 p = block->getPos();
794 // Add all surrounding blocks
795 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
798 Add all surrounding blocks that have up-to-date lighting
799 NOTE: This doesn't quite do the job (not everything
800 appropriate is lighted)
802 /*for(s16 z=-1; z<=1; z++)
803 for(s16 y=-1; y<=1; y++)
804 for(s16 x=-1; x<=1; x++)
807 MapBlock *block = getBlockNoCreateNoEx(p);
812 if(block->getLightingExpired())
814 vmanip.initialEmerge(p, p);
817 // Lighting of block will be updated completely
818 block->setLightingExpired(false);
822 //TimeTaker timer("unSpreadLight");
823 vmanip.unspreadLight(bank, unlight_from, light_sources);
826 //TimeTaker timer("spreadLight");
827 vmanip.spreadLight(bank, light_sources);
830 //TimeTaker timer("blitBack");
831 vmanip.blitBack(modified_blocks);
833 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
837 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
840 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
841 core::map<v3s16, MapBlock*> & modified_blocks)
843 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
844 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
847 Update information about whether day and night light differ
849 for(core::map<v3s16, MapBlock*>::Iterator
850 i = modified_blocks.getIterator();
851 i.atEnd() == false; i++)
853 MapBlock *block = i.getNode()->getValue();
854 block->updateDayNightDiff();
859 This is called after changing a node from transparent to opaque.
860 The lighting value of the node should be left as-is after changing
861 other values. This sets the lighting value to 0.
863 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
864 core::map<v3s16, MapBlock*> &modified_blocks)*/
865 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
866 core::map<v3s16, MapBlock*> &modified_blocks)
869 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
870 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
873 From this node to nodes underneath:
874 If lighting is sunlight (1.0), unlight neighbours and
879 v3s16 toppos = p + v3s16(0,1,0);
880 v3s16 bottompos = p + v3s16(0,-1,0);
882 bool node_under_sunlight = true;
883 core::map<v3s16, bool> light_sources;
886 If there is a node at top and it doesn't have sunlight,
887 there has not been any sunlight going down.
889 Otherwise there probably is.
892 MapNode topnode = getNode(toppos);
894 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
895 node_under_sunlight = false;
897 catch(InvalidPositionException &e)
901 if(n.d != CONTENT_TORCH)
904 If there is grass below, change it to mud
907 MapNode bottomnode = getNode(bottompos);
909 if(bottomnode.d == CONTENT_GRASS
910 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
912 bottomnode.d = CONTENT_MUD;
913 setNode(bottompos, bottomnode);
916 catch(InvalidPositionException &e)
921 enum LightBank banks[] =
926 for(s32 i=0; i<2; i++)
928 enum LightBank bank = banks[i];
930 u8 lightwas = getNode(p).getLight(bank);
932 // Add the block of the added node to modified_blocks
933 v3s16 blockpos = getNodeBlockPos(p);
934 MapBlock * block = getBlockNoCreate(blockpos);
935 assert(block != NULL);
936 modified_blocks.insert(blockpos, block);
938 if(isValidPosition(p) == false)
941 // Unlight neighbours of node.
942 // This means setting light of all consequent dimmer nodes
944 // This also collects the nodes at the border which will spread
945 // light again into this.
946 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
954 If node is under sunlight, take all sunlighted nodes under
955 it and clear light from them and from where the light has
957 TODO: This could be optimized by mass-unlighting instead
960 if(node_under_sunlight)
964 //m_dout<<DTIME<<"y="<<y<<std::endl;
965 v3s16 n2pos(p.X, y, p.Z);
971 catch(InvalidPositionException &e)
976 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
978 //m_dout<<DTIME<<"doing"<<std::endl;
979 unLightNeighbors(LIGHTBANK_DAY,
980 n2pos, n2.getLight(LIGHTBANK_DAY),
981 light_sources, modified_blocks);
982 n2.setLight(LIGHTBANK_DAY, 0);
990 for(s32 i=0; i<2; i++)
992 enum LightBank bank = banks[i];
995 Spread light from all nodes that might be capable of doing so
996 TODO: Convert to spreadLight
998 spreadLight(bank, light_sources, modified_blocks);
1002 Update information about whether day and night light differ
1004 for(core::map<v3s16, MapBlock*>::Iterator
1005 i = modified_blocks.getIterator();
1006 i.atEnd() == false; i++)
1008 MapBlock *block = i.getNode()->getValue();
1009 block->updateDayNightDiff();
1013 Add neighboring liquid nodes and the node itself if it is
1014 liquid (=water node was added) to transform queue.
1017 v3s16(0,0,0), // self
1018 v3s16(0,0,1), // back
1019 v3s16(0,1,0), // top
1020 v3s16(1,0,0), // right
1021 v3s16(0,0,-1), // front
1022 v3s16(0,-1,0), // bottom
1023 v3s16(-1,0,0), // left
1025 for(u16 i=0; i<7; i++)
1030 v3s16 p2 = p + dirs[i];
1032 MapNode n2 = getNode(p2);
1033 if(content_liquid(n2.d))
1035 m_transforming_liquid.push_back(p2);
1038 }catch(InvalidPositionException &e)
1046 void Map::removeNodeAndUpdate(v3s16 p,
1047 core::map<v3s16, MapBlock*> &modified_blocks)
1049 /*PrintInfo(m_dout);
1050 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1051 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1053 bool node_under_sunlight = true;
1055 v3s16 toppos = p + v3s16(0,1,0);
1057 // Node will be replaced with this
1058 u8 replace_material = CONTENT_AIR;
1061 If there is a node at top and it doesn't have sunlight,
1062 there will be no sunlight going down.
1065 MapNode topnode = getNode(toppos);
1067 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1068 node_under_sunlight = false;
1070 catch(InvalidPositionException &e)
1074 core::map<v3s16, bool> light_sources;
1076 enum LightBank banks[] =
1081 for(s32 i=0; i<2; i++)
1083 enum LightBank bank = banks[i];
1086 Unlight neighbors (in case the node is a light source)
1088 unLightNeighbors(bank, p,
1089 getNode(p).getLight(bank),
1090 light_sources, modified_blocks);
1095 This also clears the lighting.
1099 n.d = replace_material;
1102 for(s32 i=0; i<2; i++)
1104 enum LightBank bank = banks[i];
1107 Recalculate lighting
1109 spreadLight(bank, light_sources, modified_blocks);
1112 // Add the block of the removed node to modified_blocks
1113 v3s16 blockpos = getNodeBlockPos(p);
1114 MapBlock * block = getBlockNoCreate(blockpos);
1115 assert(block != NULL);
1116 modified_blocks.insert(blockpos, block);
1119 If the removed node was under sunlight, propagate the
1120 sunlight down from it and then light all neighbors
1121 of the propagated blocks.
1123 if(node_under_sunlight)
1125 s16 ybottom = propagateSunlight(p, modified_blocks);
1126 /*m_dout<<DTIME<<"Node was under sunlight. "
1127 "Propagating sunlight";
1128 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1130 for(; y >= ybottom; y--)
1132 v3s16 p2(p.X, y, p.Z);
1133 /*m_dout<<DTIME<<"lighting neighbors of node ("
1134 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1136 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1141 // Set the lighting of this node to 0
1142 // TODO: Is this needed? Lighting is cleared up there already.
1144 MapNode n = getNode(p);
1145 n.setLight(LIGHTBANK_DAY, 0);
1148 catch(InvalidPositionException &e)
1154 for(s32 i=0; i<2; i++)
1156 enum LightBank bank = banks[i];
1158 // Get the brightest neighbour node and propagate light from it
1159 v3s16 n2p = getBrightestNeighbour(bank, p);
1161 MapNode n2 = getNode(n2p);
1162 lightNeighbors(bank, n2p, modified_blocks);
1164 catch(InvalidPositionException &e)
1170 Update information about whether day and night light differ
1172 for(core::map<v3s16, MapBlock*>::Iterator
1173 i = modified_blocks.getIterator();
1174 i.atEnd() == false; i++)
1176 MapBlock *block = i.getNode()->getValue();
1177 block->updateDayNightDiff();
1181 Add neighboring liquid nodes to transform queue.
1184 v3s16(0,0,1), // back
1185 v3s16(0,1,0), // top
1186 v3s16(1,0,0), // right
1187 v3s16(0,0,-1), // front
1188 v3s16(0,-1,0), // bottom
1189 v3s16(-1,0,0), // left
1191 for(u16 i=0; i<6; i++)
1196 v3s16 p2 = p + dirs[i];
1198 MapNode n2 = getNode(p2);
1199 if(content_liquid(n2.d))
1201 m_transforming_liquid.push_back(p2);
1204 }catch(InvalidPositionException &e)
1211 void Map::expireMeshes(bool only_daynight_diffed)
1213 TimeTaker timer("expireMeshes()");
1215 core::map<v2s16, MapSector*>::Iterator si;
1216 si = m_sectors.getIterator();
1217 for(; si.atEnd() == false; si++)
1219 MapSector *sector = si.getNode()->getValue();
1221 core::list< MapBlock * > sectorblocks;
1222 sector->getBlocks(sectorblocks);
1224 core::list< MapBlock * >::Iterator i;
1225 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1227 MapBlock *block = *i;
1229 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1235 JMutexAutoLock lock(block->mesh_mutex);
1236 if(block->mesh != NULL)
1238 /*block->mesh->drop();
1239 block->mesh = NULL;*/
1240 block->setMeshExpired(true);
1247 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1249 assert(mapType() == MAPTYPE_CLIENT);
1252 v3s16 p = blockpos + v3s16(0,0,0);
1253 MapBlock *b = getBlockNoCreate(p);
1254 b->updateMesh(daynight_ratio);
1256 catch(InvalidPositionException &e){}
1259 v3s16 p = blockpos + v3s16(-1,0,0);
1260 MapBlock *b = getBlockNoCreate(p);
1261 b->updateMesh(daynight_ratio);
1263 catch(InvalidPositionException &e){}
1265 v3s16 p = blockpos + v3s16(0,-1,0);
1266 MapBlock *b = getBlockNoCreate(p);
1267 b->updateMesh(daynight_ratio);
1269 catch(InvalidPositionException &e){}
1271 v3s16 p = blockpos + v3s16(0,0,-1);
1272 MapBlock *b = getBlockNoCreate(p);
1273 b->updateMesh(daynight_ratio);
1275 catch(InvalidPositionException &e){}
1278 v3s16 p = blockpos + v3s16(1,0,0);
1279 MapBlock *b = getBlockNoCreate(p);
1280 b->updateMesh(daynight_ratio);
1282 catch(InvalidPositionException &e){}
1284 v3s16 p = blockpos + v3s16(0,1,0);
1285 MapBlock *b = getBlockNoCreate(p);
1286 b->updateMesh(daynight_ratio);
1288 catch(InvalidPositionException &e){}
1290 v3s16 p = blockpos + v3s16(0,0,1);
1291 MapBlock *b = getBlockNoCreate(p);
1292 b->updateMesh(daynight_ratio);
1294 catch(InvalidPositionException &e){}*/
1299 bool Map::dayNightDiffed(v3s16 blockpos)
1302 v3s16 p = blockpos + v3s16(0,0,0);
1303 MapBlock *b = getBlockNoCreate(p);
1304 if(b->dayNightDiffed())
1307 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(-1,0,0);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1317 v3s16 p = blockpos + v3s16(0,-1,0);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1324 v3s16 p = blockpos + v3s16(0,0,-1);
1325 MapBlock *b = getBlockNoCreate(p);
1326 if(b->dayNightDiffed())
1329 catch(InvalidPositionException &e){}
1332 v3s16 p = blockpos + v3s16(1,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1339 v3s16 p = blockpos + v3s16(0,1,0);
1340 MapBlock *b = getBlockNoCreate(p);
1341 if(b->dayNightDiffed())
1344 catch(InvalidPositionException &e){}
1346 v3s16 p = blockpos + v3s16(0,0,1);
1347 MapBlock *b = getBlockNoCreate(p);
1348 if(b->dayNightDiffed())
1351 catch(InvalidPositionException &e){}
1357 Updates usage timers
1359 void Map::timerUpdate(float dtime)
1361 JMutexAutoLock lock(m_sector_mutex);
1363 core::map<v2s16, MapSector*>::Iterator si;
1365 si = m_sectors.getIterator();
1366 for(; si.atEnd() == false; si++)
1368 MapSector *sector = si.getNode()->getValue();
1369 sector->usage_timer += dtime;
1373 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1376 Wait for caches to be removed before continuing.
1378 This disables the existence of caches while locked
1380 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1382 core::list<v2s16>::Iterator j;
1383 for(j=list.begin(); j!=list.end(); j++)
1385 MapSector *sector = m_sectors[*j];
1388 sector->deleteBlocks();
1393 If sector is in sector cache, remove it from there
1395 if(m_sector_cache == sector)
1397 m_sector_cache = NULL;
1400 Remove from map and delete
1402 m_sectors.remove(*j);
1408 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1409 core::list<v3s16> *deleted_blocks)
1411 JMutexAutoLock lock(m_sector_mutex);
1413 core::list<v2s16> sector_deletion_queue;
1414 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1415 for(; i.atEnd() == false; i++)
1417 MapSector *sector = i.getNode()->getValue();
1419 Delete sector from memory if it hasn't been used in a long time
1421 if(sector->usage_timer > timeout)
1423 sector_deletion_queue.push_back(i.getNode()->getKey());
1425 if(deleted_blocks != NULL)
1427 // Collect positions of blocks of sector
1428 MapSector *sector = i.getNode()->getValue();
1429 core::list<MapBlock*> blocks;
1430 sector->getBlocks(blocks);
1431 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1432 i != blocks.end(); i++)
1434 deleted_blocks->push_back((*i)->getPos());
1439 deleteSectors(sector_deletion_queue, only_blocks);
1440 return sector_deletion_queue.getSize();
1443 void Map::PrintInfo(std::ostream &out)
1448 #define WATER_DROP_BOOST 4
1450 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1452 DSTACK(__FUNCTION_NAME);
1453 //TimeTaker timer("transformLiquids()");
1456 u32 initial_size = m_transforming_liquid.size();
1458 /*if(initial_size != 0)
1459 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1461 while(m_transforming_liquid.size() != 0)
1464 Get a queued transforming liquid node
1466 v3s16 p0 = m_transforming_liquid.pop_front();
1468 MapNode n0 = getNode(p0);
1470 // Don't deal with non-liquids
1471 if(content_liquid(n0.d) == false)
1474 bool is_source = !content_flowing_liquid(n0.d);
1476 u8 liquid_level = 8;
1477 if(is_source == false)
1478 liquid_level = n0.param2 & 0x0f;
1480 // Turn possible source into non-source
1481 u8 nonsource_c = make_liquid_flowing(n0.d);
1484 If not source, check that some node flows into this one
1485 and what is the level of liquid in this one
1487 if(is_source == false)
1489 s8 new_liquid_level_max = -1;
1491 v3s16 dirs_from[5] = {
1492 v3s16(0,1,0), // top
1493 v3s16(0,0,1), // back
1494 v3s16(1,0,0), // right
1495 v3s16(0,0,-1), // front
1496 v3s16(-1,0,0), // left
1498 for(u16 i=0; i<5; i++)
1503 bool from_top = (i==0);
1505 v3s16 p2 = p0 + dirs_from[i];
1506 MapNode n2 = getNode(p2);
1508 if(content_liquid(n2.d))
1510 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1511 // Check that the liquids are the same type
1512 if(n2_nonsource_c != nonsource_c)
1514 dstream<<"WARNING: Not handling: different liquids"
1515 " collide"<<std::endl;
1518 bool n2_is_source = !content_flowing_liquid(n2.d);
1519 s8 n2_liquid_level = 8;
1520 if(n2_is_source == false)
1521 n2_liquid_level = n2.param2 & 0x07;
1523 s8 new_liquid_level = -1;
1526 //new_liquid_level = 7;
1527 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1528 new_liquid_level = 7;
1530 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1532 else if(n2_liquid_level > 0)
1534 new_liquid_level = n2_liquid_level - 1;
1537 if(new_liquid_level > new_liquid_level_max)
1538 new_liquid_level_max = new_liquid_level;
1541 }catch(InvalidPositionException &e)
1547 If liquid level should be something else, update it and
1548 add all the neighboring water nodes to the transform queue.
1550 if(new_liquid_level_max != liquid_level)
1552 if(new_liquid_level_max == -1)
1554 // Remove water alltoghether
1561 n0.param2 = new_liquid_level_max;
1565 // Block has been modified
1567 v3s16 blockpos = getNodeBlockPos(p0);
1568 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1570 modified_blocks.insert(blockpos, block);
1574 Add neighboring non-source liquid nodes to transform queue.
1577 v3s16(0,0,1), // back
1578 v3s16(0,1,0), // top
1579 v3s16(1,0,0), // right
1580 v3s16(0,0,-1), // front
1581 v3s16(0,-1,0), // bottom
1582 v3s16(-1,0,0), // left
1584 for(u16 i=0; i<6; i++)
1589 v3s16 p2 = p0 + dirs[i];
1591 MapNode n2 = getNode(p2);
1592 if(content_flowing_liquid(n2.d))
1594 m_transforming_liquid.push_back(p2);
1597 }catch(InvalidPositionException &e)
1604 // Get a new one from queue if the node has turned into non-water
1605 if(content_liquid(n0.d) == false)
1609 Flow water from this node
1611 v3s16 dirs_to[5] = {
1612 v3s16(0,-1,0), // bottom
1613 v3s16(0,0,1), // back
1614 v3s16(1,0,0), // right
1615 v3s16(0,0,-1), // front
1616 v3s16(-1,0,0), // left
1618 for(u16 i=0; i<5; i++)
1623 bool to_bottom = (i == 0);
1625 // If liquid is at lowest possible height, it's not going
1626 // anywhere except down
1627 if(liquid_level == 0 && to_bottom == false)
1630 u8 liquid_next_level = 0;
1631 // If going to bottom
1634 //liquid_next_level = 7;
1635 if(liquid_level >= 7 - WATER_DROP_BOOST)
1636 liquid_next_level = 7;
1638 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1641 liquid_next_level = liquid_level - 1;
1643 bool n2_changed = false;
1644 bool flowed = false;
1646 v3s16 p2 = p0 + dirs_to[i];
1648 MapNode n2 = getNode(p2);
1649 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1651 if(content_liquid(n2.d))
1653 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1654 // Check that the liquids are the same type
1655 if(n2_nonsource_c != nonsource_c)
1657 dstream<<"WARNING: Not handling: different liquids"
1658 " collide"<<std::endl;
1661 bool n2_is_source = !content_flowing_liquid(n2.d);
1662 u8 n2_liquid_level = 8;
1663 if(n2_is_source == false)
1664 n2_liquid_level = n2.param2 & 0x07;
1673 // Just flow into the source, nothing changes.
1674 // n2_changed is not set because destination didn't change
1679 if(liquid_next_level > liquid_level)
1681 n2.param2 = liquid_next_level;
1689 else if(n2.d == CONTENT_AIR)
1692 n2.param2 = liquid_next_level;
1699 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1703 m_transforming_liquid.push_back(p2);
1705 v3s16 blockpos = getNodeBlockPos(p2);
1706 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1708 modified_blocks.insert(blockpos, block);
1711 // If n2_changed to bottom, don't flow anywhere else
1712 if(to_bottom && flowed && !is_source)
1715 }catch(InvalidPositionException &e)
1721 //if(loopcount >= 100000)
1722 if(loopcount >= initial_size * 1)
1725 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1732 ServerMap::ServerMap(std::string savedir):
1738 //m_chunksize = 16; // Too slow
1739 m_chunksize = 8; // Takes a few seconds
1743 // TODO: Save to and load from a file
1744 m_seed = (((u64)(myrand()%0xffff)<<0)
1745 + ((u64)(myrand()%0xffff)<<16)
1746 + ((u64)(myrand()%0xffff)<<32)
1747 + ((u64)(myrand()%0xffff)<<48));
1750 Experimental and debug stuff
1757 Try to load map; if not found, create a new one.
1760 m_savedir = savedir;
1761 m_map_saving_enabled = false;
1765 // If directory exists, check contents and load if possible
1766 if(fs::PathExists(m_savedir))
1768 // If directory is empty, it is safe to save into it.
1769 if(fs::GetDirListing(m_savedir).size() == 0)
1771 dstream<<DTIME<<"Server: Empty save directory is valid."
1773 m_map_saving_enabled = true;
1777 // Load map metadata (seed, chunksize)
1780 // Load chunk metadata
1783 /*// Load sector (0,0) and throw and exception on fail
1784 if(loadSectorFull(v2s16(0,0)) == false)
1785 throw LoadError("Failed to load sector (0,0)");*/
1787 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1788 "metadata and sector (0,0) from "<<savedir<<
1789 ", assuming valid save directory."
1792 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1793 <<"and chunk metadata from "<<savedir
1794 <<", assuming valid save directory."
1797 m_map_saving_enabled = true;
1798 // Map loaded, not creating new one
1802 // If directory doesn't exist, it is safe to save to it
1804 m_map_saving_enabled = true;
1807 catch(std::exception &e)
1809 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1810 <<", exception: "<<e.what()<<std::endl;
1811 dstream<<"Please remove the map or fix it."<<std::endl;
1812 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1815 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1817 // Create zero sector
1818 emergeSector(v2s16(0,0));
1820 // Initially write whole map
1824 ServerMap::~ServerMap()
1828 if(m_map_saving_enabled)
1831 // Save only changed parts
1833 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1837 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1840 catch(std::exception &e)
1842 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1843 <<", exception: "<<e.what()<<std::endl;
1849 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1850 for(; i.atEnd() == false; i++)
1852 MapChunk *chunk = i.getNode()->getValue();
1858 Some helper functions for the map generator
1861 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1863 v3s16 em = vmanip.m_area.getExtent();
1864 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1865 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1866 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1868 for(y=y_nodes_max; y>=y_nodes_min; y--)
1870 MapNode &n = vmanip.m_data[i];
1871 if(content_walkable(n.d))
1874 vmanip.m_area.add_y(em, i, -1);
1876 if(y >= y_nodes_min)
1882 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1884 v3s16 em = vmanip.m_area.getExtent();
1885 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1886 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1887 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1889 for(y=y_nodes_max; y>=y_nodes_min; y--)
1891 MapNode &n = vmanip.m_data[i];
1892 if(content_walkable(n.d)
1893 && n.d != CONTENT_TREE
1894 && n.d != CONTENT_LEAVES)
1897 vmanip.m_area.add_y(em, i, -1);
1899 if(y >= y_nodes_min)
1905 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1907 MapNode treenode(CONTENT_TREE);
1908 MapNode leavesnode(CONTENT_LEAVES);
1910 s16 trunk_h = myrand_range(3, 6);
1912 for(s16 ii=0; ii<trunk_h; ii++)
1914 if(vmanip.m_area.contains(p1))
1915 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1919 // p1 is now the last piece of the trunk
1922 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1923 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1924 Buffer<u8> leaves_d(leaves_a.getVolume());
1925 for(s32 i=0; i<leaves_a.getVolume(); i++)
1928 // Force leaves at near the end of the trunk
1931 for(s16 z=-d; z<=d; z++)
1932 for(s16 y=-d; y<=d; y++)
1933 for(s16 x=-d; x<=d; x++)
1935 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1939 // Add leaves randomly
1940 for(u32 iii=0; iii<7; iii++)
1945 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1946 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1947 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1950 for(s16 z=0; z<=d; z++)
1951 for(s16 y=0; y<=d; y++)
1952 for(s16 x=0; x<=d; x++)
1954 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1958 // Blit leaves to vmanip
1959 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1960 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1961 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1965 if(vmanip.m_area.contains(p) == false)
1967 u32 vi = vmanip.m_area.index(p);
1968 if(vmanip.m_data[vi].d != CONTENT_AIR)
1970 u32 i = leaves_a.index(x,y,z);
1971 if(leaves_d[i] == 1)
1972 vmanip.m_data[vi] = leavesnode;
1977 Noise functions. Make sure seed is mangled differently in each one.
1980 // Amount of trees per area in nodes
1981 double tree_amount_2d(u64 seed, v2s16 p)
1983 double noise = noise2d_perlin(
1984 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1986 double zeroval = -0.3;
1990 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1993 /*double base_rock_level_2d(u64 seed, v2s16 p)
1995 return WATER_LEVEL - 6.0 + 25. * noise2d_perlin(
1996 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2000 /*double highlands_level_2d(u64 seed, v2s16 p)
2002 double a = noise2d_perlin(
2003 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2008 return WATER_LEVEL + 25;
2009 return WATER_LEVEL + 55. * noise2d_perlin(
2010 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2011 seed+85039, 6, 0.69);
2017 double base_rock_level_2d(u64 seed, v2s16 p)
2019 // The base ground level
2020 double base = (double)WATER_LEVEL - 4.0 + 25. * noise2d_perlin(
2021 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2022 (seed>>32)+654879876, 6, 0.6);
2024 /*// A bit hillier one
2025 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2026 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2027 (seed>>27)+90340, 6, 0.69);
2031 // Higher ground level
2032 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2033 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2034 seed+85039, 5, 0.69);
2035 //higher = 30; // For debugging
2037 // Limit higher to at least base
2041 // Steepness factor of cliffs
2042 double b = 1.0 + 1.0 * noise2d_perlin(
2043 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2045 b = rangelim(b, 0.0, 1000.0);
2048 b = rangelim(b, 3.0, 1000.0);
2049 //dstream<<"b="<<b<<std::endl;
2052 // Offset to more low
2053 double a_off = -0.15;
2054 // High/low selector
2055 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2056 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2057 seed-359, 6, 0.7));*/
2058 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2059 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2060 seed-359, 5, 0.60));
2062 a = rangelim(a, 0.0, 1.0);
2064 //dstream<<"a="<<a<<std::endl;
2066 double h = base*(1.0-a) + higher*a;
2073 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2076 This is the main map generation method
2079 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2080 core::map<v3s16, MapBlock*> &changed_blocks)
2082 DSTACK(__FUNCTION_NAME);
2085 Don't generate if already fully generated
2088 MapChunk *chunk = getChunk(chunkpos);
2089 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2091 dstream<<"generateChunkRaw(): Chunk "
2092 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2093 <<" already generated"<<std::endl;
2098 dstream<<"generateChunkRaw(): Generating chunk "
2099 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2102 TimeTaker timer("generateChunkRaw()");
2104 // The distance how far into the neighbors the generator is allowed to go.
2105 s16 max_spread_amount_sectors = 2;
2106 assert(max_spread_amount_sectors <= m_chunksize);
2107 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2109 // Minimum amount of space left on sides for mud to fall in
2110 //s16 min_mud_fall_space = 2;
2112 // Maximum diameter of stone obstacles in X and Z
2113 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2114 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2116 s16 y_blocks_min = -4;
2117 s16 y_blocks_max = 3;
2118 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2119 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2120 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2122 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2123 s16 sectorpos_base_size = m_chunksize;
2125 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2126 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2127 v2s16 sectorpos_bigbase =
2128 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2129 s16 sectorpos_bigbase_size =
2130 sectorpos_base_size + 2 * max_spread_amount_sectors;
2132 v3s16 bigarea_blocks_min(
2133 sectorpos_bigbase.X,
2138 v3s16 bigarea_blocks_max(
2139 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2141 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2144 // Relative values to control amount of stuff in one chunk
2145 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2146 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2147 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2148 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2149 *(u32)h_blocks*MAP_BLOCKSIZE;
2152 The limiting edges of the lighting update, inclusive.
2154 s16 lighting_min_d = 0-max_spread_amount;
2155 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2158 Create the whole area of this and the neighboring chunks
2161 TimeTaker timer("generateChunkRaw() create area");
2163 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2164 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2166 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2167 ServerMapSector *sector = createSector(sectorpos);
2170 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2172 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2173 MapBlock *block = createBlock(blockpos);
2175 // Lighting won't be calculated
2176 //block->setLightingExpired(true);
2177 // Lighting will be calculated
2178 block->setLightingExpired(false);
2181 Block gets sunlight if this is true.
2183 This should be set to true when the top side of a block
2184 is completely exposed to the sky.
2186 Actually this doesn't matter now because the
2187 initial lighting is done here.
2189 block->setIsUnderground(y != y_blocks_max);
2195 Now we have a big empty area.
2197 Make a ManualMapVoxelManipulator that contains this and the
2201 ManualMapVoxelManipulator vmanip(this);
2202 // Add the area we just generated
2204 TimeTaker timer("generateChunkRaw() initialEmerge");
2205 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2209 vmanip.clearFlag(0xff);
2211 TimeTaker timer_generate("generateChunkRaw() generate");
2213 // Maximum height of the stone surface and obstacles.
2214 // This is used to disable dungeon generation from going too high.
2215 s16 stone_surface_max_y = 0;
2218 Generate general ground level to full area
2223 //TimeTaker timer1("ground level");
2225 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2226 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2229 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2232 Skip of already generated
2235 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2236 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2240 // Ground height at this point
2241 float surface_y_f = 0.0;
2243 // Use perlin noise for ground height
2244 surface_y_f = base_rock_level_2d(m_seed, p2d);
2246 /*// Experimental stuff
2248 float a = highlands_level_2d(m_seed, p2d);
2253 // Convert to integer
2254 s16 surface_y = (s16)surface_y_f;
2257 if(surface_y > stone_surface_max_y)
2258 stone_surface_max_y = surface_y;
2261 Fill ground with stone
2264 // Use fast index incrementing
2265 v3s16 em = vmanip.m_area.getExtent();
2266 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2267 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2269 vmanip.m_data[i].d = CONTENT_STONE;
2271 vmanip.m_area.add_y(em, i, 1);
2279 Randomize some parameters
2282 s32 stone_obstacle_count = 0;
2283 /*s32 stone_obstacle_count =
2284 rangelim((1.0+noise2d(m_seed+897,
2285 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2287 s16 stone_obstacle_max_height = 0;
2288 /*s16 stone_obstacle_max_height =
2289 rangelim((1.0+noise2d(m_seed+5902,
2290 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2293 Loop this part, it will make stuff look older and newer nicely
2295 //for(u32 i_age=0; i_age<1; i_age++)
2296 for(u32 i_age=0; i_age<2; i_age++)
2301 //TimeTaker timer1("stone obstacles");
2304 Add some random stone obstacles
2307 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2309 // Randomize max height so usually stuff will be quite low
2310 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2312 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2313 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2316 myrand_range(5, stone_obstacle_max_size),
2317 myrand_range(0, maxheight_randomized),
2318 myrand_range(5, stone_obstacle_max_size)
2321 // Don't make stupid small rectangle bumps
2326 myrand_range(1+ob_size.X/2+2,
2327 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2328 myrand_range(1+ob_size.Z/2+2,
2329 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2332 // Minimum space left on top of the obstacle
2333 s16 min_head_space = 12;
2335 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2336 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2338 // Node position in 2d
2339 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2341 // Find stone ground level
2342 // (ignore everything else than mud in already generated chunks)
2343 // and mud amount over the stone level
2347 v3s16 em = vmanip.m_area.getExtent();
2348 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2350 // Go to ground level
2351 for(y=y_nodes_max; y>=y_nodes_min; y--)
2353 MapNode *n = &vmanip.m_data[i];
2354 /*if(content_walkable(n.d)
2355 && n.d != CONTENT_MUD
2356 && n.d != CONTENT_GRASS)
2358 if(n->d == CONTENT_STONE)
2361 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2365 Change to mud because otherwise we might
2366 be throwing mud on grass at the next
2372 vmanip.m_area.add_y(em, i, -1);
2374 if(y >= y_nodes_min)
2377 surface_y = y_nodes_min;
2385 v3s16 em = vmanip.m_area.getExtent();
2386 s16 y_start = surface_y+1;
2387 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2391 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2393 MapNode &n = vmanip.m_data[i];
2394 n.d = CONTENT_STONE;
2396 if(y > stone_surface_max_y)
2397 stone_surface_max_y = y;
2400 if(count >= ob_size.Y)
2403 vmanip.m_area.add_y(em, i, 1);
2407 for(; y<=y_nodes_max - min_head_space; y++)
2409 MapNode &n = vmanip.m_data[i];
2412 if(count >= mud_amount)
2415 vmanip.m_area.add_y(em, i, 1);
2425 //TimeTaker timer1("dungeons");
2430 u32 dungeons_count = relative_volume / 400000;
2431 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2432 if(stone_surface_max_y < WATER_LEVEL)
2434 /*u32 dungeons_count = 0;
2435 u32 bruises_count = 0;*/
2436 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2438 s16 min_tunnel_diameter = 2;
2439 s16 max_tunnel_diameter = 6;
2440 u16 tunnel_routepoints = 25;
2442 bool bruise_surface = (jj < bruises_count);
2446 min_tunnel_diameter = 5;
2447 max_tunnel_diameter = myrand_range(10, 20);
2448 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2449 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2451 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2452 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2454 tunnel_routepoints = 5;
2457 // Allowed route area size in nodes
2459 sectorpos_base_size*MAP_BLOCKSIZE,
2460 h_blocks*MAP_BLOCKSIZE,
2461 sectorpos_base_size*MAP_BLOCKSIZE
2464 // Area starting point in nodes
2466 sectorpos_base.X*MAP_BLOCKSIZE,
2467 y_blocks_min*MAP_BLOCKSIZE,
2468 sectorpos_base.Y*MAP_BLOCKSIZE
2472 //(this should be more than the maximum radius of the tunnel)
2473 //s16 insure = 5; // Didn't work with max_d = 20
2475 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2476 ar += v3s16(1,0,1) * more * 2;
2477 of -= v3s16(1,0,1) * more;
2479 s16 route_y_min = 0;
2480 // Allow half a diameter + 7 over stone surface
2481 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2483 /*// If dungeons, don't go through surface too often
2484 if(bruise_surface == false)
2485 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2487 // Limit maximum to area
2488 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2492 /*// Minimum is at y=0
2493 route_y_min = -of.Y - 0;*/
2494 // Minimum is at y=max_tunnel_diameter/4
2495 //route_y_min = -of.Y + max_tunnel_diameter/4;
2496 //s16 min = -of.Y + max_tunnel_diameter/4;
2497 s16 min = -of.Y + 0;
2498 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2499 route_y_min = rangelim(route_y_min, 0, route_y_max);
2502 /*dstream<<"route_y_min = "<<route_y_min
2503 <<", route_y_max = "<<route_y_max<<std::endl;*/
2505 s16 route_start_y_min = route_y_min;
2506 s16 route_start_y_max = route_y_max;
2508 // Start every 2nd dungeon from surface
2509 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2511 if(coming_from_surface)
2513 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2516 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2517 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2519 // Randomize starting position
2521 (float)(myrand()%ar.X)+0.5,
2522 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2523 (float)(myrand()%ar.Z)+0.5
2526 MapNode airnode(CONTENT_AIR);
2529 Generate some tunnel starting from orp
2532 for(u16 j=0; j<tunnel_routepoints; j++)
2535 s16 min_d = min_tunnel_diameter;
2536 s16 max_d = max_tunnel_diameter;
2537 s16 rs = myrand_range(min_d, max_d);
2542 maxlen = v3s16(rs*7,rs*7,rs*7);
2546 maxlen = v3s16(15, myrand_range(1, 20), 15);
2551 if(coming_from_surface && j < 3)
2554 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2555 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2556 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2562 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2563 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2564 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2571 else if(rp.X >= ar.X)
2573 if(rp.Y < route_y_min)
2575 else if(rp.Y >= route_y_max)
2576 rp.Y = route_y_max-1;
2579 else if(rp.Z >= ar.Z)
2583 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2585 v3f fp = orp + vec * f;
2586 v3s16 cp(fp.X, fp.Y, fp.Z);
2589 s16 d1 = d0 + rs - 1;
2590 for(s16 z0=d0; z0<=d1; z0++)
2592 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2593 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2594 for(s16 x0=-si; x0<=si-1; x0++)
2596 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2597 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2598 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2599 //s16 si2 = rs - abs(x0);
2600 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2606 /*if(isInArea(p, ar) == false)
2608 // Check only height
2609 if(y < 0 || y >= ar.Y)
2613 //assert(vmanip.m_area.contains(p));
2614 if(vmanip.m_area.contains(p) == false)
2616 dstream<<"WARNING: "<<__FUNCTION_NAME
2617 <<":"<<__LINE__<<": "
2618 <<"point not in area"
2623 // Just set it to air, it will be changed to
2625 u32 i = vmanip.m_area.index(p);
2626 vmanip.m_data[i] = airnode;
2628 if(bruise_surface == false)
2631 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2646 //TimeTaker timer1("ore veins");
2651 for(u32 jj=0; jj<relative_volume/2000; jj++)
2653 s16 max_vein_diameter = 3;
2655 // Allowed route area size in nodes
2657 sectorpos_base_size*MAP_BLOCKSIZE,
2658 h_blocks*MAP_BLOCKSIZE,
2659 sectorpos_base_size*MAP_BLOCKSIZE
2662 // Area starting point in nodes
2664 sectorpos_base.X*MAP_BLOCKSIZE,
2665 y_blocks_min*MAP_BLOCKSIZE,
2666 sectorpos_base.Y*MAP_BLOCKSIZE
2670 //(this should be more than the maximum radius of the tunnel)
2672 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2673 ar += v3s16(1,0,1) * more * 2;
2674 of -= v3s16(1,0,1) * more;
2676 // Randomize starting position
2678 (float)(myrand()%ar.X)+0.5,
2679 (float)(myrand()%ar.Y)+0.5,
2680 (float)(myrand()%ar.Z)+0.5
2683 // Randomize mineral
2684 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2687 Generate some vein starting from orp
2690 for(u16 j=0; j<2; j++)
2693 (float)(myrand()%ar.X)+0.5,
2694 (float)(myrand()%ar.Y)+0.5,
2695 (float)(myrand()%ar.Z)+0.5
2697 v3f vec = rp - orp;*/
2699 v3s16 maxlen(10, 10, 10);
2701 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2702 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2703 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2708 else if(rp.X >= ar.X)
2712 else if(rp.Y >= ar.Y)
2716 else if(rp.Z >= ar.Z)
2722 s16 max_d = max_vein_diameter;
2723 s16 rs = myrand_range(min_d, max_d);
2725 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2727 v3f fp = orp + vec * f;
2728 v3s16 cp(fp.X, fp.Y, fp.Z);
2730 s16 d1 = d0 + rs - 1;
2731 for(s16 z0=d0; z0<=d1; z0++)
2733 s16 si = rs - abs(z0);
2734 for(s16 x0=-si; x0<=si-1; x0++)
2736 s16 si2 = rs - abs(x0);
2737 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2739 // Don't put mineral to every place
2747 /*if(isInArea(p, ar) == false)
2749 // Check only height
2750 if(y < 0 || y >= ar.Y)
2754 assert(vmanip.m_area.contains(p));
2756 // Just set it to air, it will be changed to
2758 u32 i = vmanip.m_area.index(p);
2759 MapNode *n = &vmanip.m_data[i];
2760 if(n->d == CONTENT_STONE)
2775 //TimeTaker timer1("add mud");
2778 Add mud to the central chunk
2781 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2782 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2784 // Node position in 2d
2785 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2787 // Randomize mud amount
2788 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2789 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2790 m_seed+1, 3, 0.55));
2792 // Find ground level
2793 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2796 If topmost node is grass, change it to mud.
2797 It might be if it was flown to there from a neighboring
2798 chunk and then converted.
2801 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2802 MapNode *n = &vmanip.m_data[i];
2803 if(n->d == CONTENT_GRASS)
2812 v3s16 em = vmanip.m_area.getExtent();
2813 s16 y_start = surface_y+1;
2814 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2815 for(s16 y=y_start; y<=y_nodes_max; y++)
2817 if(mudcount >= mud_add_amount)
2820 MapNode &n = vmanip.m_data[i];
2824 vmanip.m_area.add_y(em, i, 1);
2833 TimeTaker timer1("flow mud");
2836 Flow mud away from steep edges
2839 // Limit area by 1 because mud is flown into neighbors.
2840 s16 mudflow_minpos = 0-max_spread_amount+1;
2841 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2843 // Iterate a few times
2844 for(s16 k=0; k<3; k++)
2847 for(s16 x=mudflow_minpos;
2850 for(s16 z=mudflow_minpos;
2854 // Invert coordinates every 2nd iteration
2857 x = mudflow_maxpos - (x-mudflow_minpos);
2858 z = mudflow_maxpos - (z-mudflow_minpos);
2861 // Node position in 2d
2862 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2864 v3s16 em = vmanip.m_area.getExtent();
2865 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2872 for(; y>=y_nodes_min; y--)
2874 n = &vmanip.m_data[i];
2875 //if(content_walkable(n->d))
2877 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2880 vmanip.m_area.add_y(em, i, -1);
2883 // Stop if out of area
2884 //if(vmanip.m_area.contains(i) == false)
2888 /*// If not mud, do nothing to it
2889 MapNode *n = &vmanip.m_data[i];
2890 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2894 Don't flow it if the stuff under it is not mud
2898 vmanip.m_area.add_y(em, i2, -1);
2899 // Cancel if out of area
2900 if(vmanip.m_area.contains(i2) == false)
2902 MapNode *n2 = &vmanip.m_data[i2];
2903 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2907 // Make it exactly mud
2910 /*s16 recurse_count = 0;
2914 v3s16(0,0,1), // back
2915 v3s16(1,0,0), // right
2916 v3s16(0,0,-1), // front
2917 v3s16(-1,0,0), // left
2920 // Theck that upper is air or doesn't exist.
2921 // Cancel dropping if upper keeps it in place
2923 vmanip.m_area.add_y(em, i3, 1);
2924 if(vmanip.m_area.contains(i3) == true
2925 && content_walkable(vmanip.m_data[i3].d) == true)
2932 for(u32 di=0; di<4; di++)
2934 v3s16 dirp = dirs4[di];
2937 vmanip.m_area.add_p(em, i2, dirp);
2938 // Fail if out of area
2939 if(vmanip.m_area.contains(i2) == false)
2941 // Check that side is air
2942 MapNode *n2 = &vmanip.m_data[i2];
2943 if(content_walkable(n2->d))
2945 // Check that under side is air
2946 vmanip.m_area.add_y(em, i2, -1);
2947 if(vmanip.m_area.contains(i2) == false)
2949 n2 = &vmanip.m_data[i2];
2950 if(content_walkable(n2->d))
2952 /*// Check that under that is air (need a drop of 2)
2953 vmanip.m_area.add_y(em, i2, -1);
2954 if(vmanip.m_area.contains(i2) == false)
2956 n2 = &vmanip.m_data[i2];
2957 if(content_walkable(n2->d))
2959 // Loop further down until not air
2961 vmanip.m_area.add_y(em, i2, -1);
2962 // Fail if out of area
2963 if(vmanip.m_area.contains(i2) == false)
2965 n2 = &vmanip.m_data[i2];
2966 }while(content_walkable(n2->d) == false);
2967 // Loop one up so that we're in air
2968 vmanip.m_area.add_y(em, i2, 1);
2969 n2 = &vmanip.m_data[i2];
2971 // Move mud to new place
2973 // Set old place to be air
2974 *n = MapNode(CONTENT_AIR);
2987 //TimeTaker timer1("add water");
2990 Add water to the central chunk (and a bit more)
2993 for(s16 x=0-max_spread_amount;
2994 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2996 for(s16 z=0-max_spread_amount;
2997 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3000 // Node position in 2d
3001 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3003 // Find ground level
3004 //s16 surface_y = find_ground_level(vmanip, p2d);
3007 If ground level is over water level, skip.
3008 NOTE: This leaves caves near water without water,
3009 which looks especially crappy when the nearby water
3010 won't start flowing either for some reason
3012 /*if(surface_y > WATER_LEVEL)
3019 v3s16 em = vmanip.m_area.getExtent();
3020 u8 light = LIGHT_MAX;
3021 // Start at global water surface level
3022 s16 y_start = WATER_LEVEL;
3023 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3024 MapNode *n = &vmanip.m_data[i];
3026 /*// Add first one to transforming liquid queue, if water
3027 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3029 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3030 m_transforming_liquid.push_back(p);
3033 for(s16 y=y_start; y>=y_nodes_min; y--)
3035 n = &vmanip.m_data[i];
3037 // Stop when there is no water and no air
3038 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3039 && n->d != CONTENT_WATER)
3041 /*// Add bottom one to transforming liquid queue
3042 vmanip.m_area.add_y(em, i, 1);
3043 n = &vmanip.m_data[i];
3044 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3046 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3047 m_transforming_liquid.push_back(p);
3053 // Make water only not in dungeons
3054 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3056 n->d = CONTENT_WATERSOURCE;
3057 //n->setLight(LIGHTBANK_DAY, light);
3059 // Add to transforming liquid queue (in case it'd
3061 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3062 m_transforming_liquid.push_back(p);
3066 vmanip.m_area.add_y(em, i, -1);
3079 //TimeTaker timer1("convert mud to sand");
3085 //s16 mud_add_amount = myrand_range(2, 4);
3086 //s16 mud_add_amount = 0;
3088 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3089 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3090 for(s16 x=0-max_spread_amount+1;
3091 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3093 for(s16 z=0-max_spread_amount+1;
3094 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3097 // Node position in 2d
3098 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3100 // Determine whether to have sand here
3101 double sandnoise = noise2d_perlin(
3102 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3103 m_seed+59420, 3, 0.50);
3105 bool have_sand = (sandnoise > -0.15);
3107 if(have_sand == false)
3110 // Find ground level
3111 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3113 if(surface_y > WATER_LEVEL + 2)
3117 v3s16 em = vmanip.m_area.getExtent();
3118 s16 y_start = surface_y;
3119 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3120 u32 not_sand_counter = 0;
3121 for(s16 y=y_start; y>=y_nodes_min; y--)
3123 MapNode *n = &vmanip.m_data[i];
3124 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3126 n->d = CONTENT_SAND;
3131 if(not_sand_counter > 3)
3135 vmanip.m_area.add_y(em, i, -1);
3144 //TimeTaker timer1("generate trees");
3150 // Divide area into parts
3152 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3153 double area = sidelen * sidelen;
3154 for(s16 x0=0; x0<div; x0++)
3155 for(s16 z0=0; z0<div; z0++)
3157 // Center position of part of division
3159 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3160 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3162 // Minimum edge of part of division
3164 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3165 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3167 // Maximum edge of part of division
3169 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3170 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3173 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3174 // Put trees in random places on part of division
3175 for(u32 i=0; i<tree_count; i++)
3177 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3178 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3179 s16 y = find_ground_level(vmanip, v2s16(x,z));
3180 // Don't make a tree under water level
3185 Trees grow only on mud and grass
3188 u32 i = vmanip.m_area.index(v3s16(p));
3189 MapNode *n = &vmanip.m_data[i];
3190 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3195 make_tree(vmanip, p);
3198 /*u32 tree_max = relative_area / 60;
3199 //u32 count = myrand_range(0, tree_max);
3200 for(u32 i=0; i<count; i++)
3202 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3203 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3204 x += sectorpos_base.X*MAP_BLOCKSIZE;
3205 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3206 s16 y = find_ground_level(vmanip, v2s16(x,z));
3207 // Don't make a tree under water level
3212 make_tree(vmanip, p);
3220 //TimeTaker timer1("grow grass");
3226 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3227 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3228 for(s16 x=0-max_spread_amount;
3229 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3231 for(s16 z=0-max_spread_amount;
3232 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3235 // Node position in 2d
3236 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3239 Find the lowest surface to which enough light ends up
3242 Basically just wait until not air and not leaves.
3246 v3s16 em = vmanip.m_area.getExtent();
3247 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3249 // Go to ground level
3250 for(y=y_nodes_max; y>=y_nodes_min; y--)
3252 MapNode &n = vmanip.m_data[i];
3253 if(n.d != CONTENT_AIR
3254 && n.d != CONTENT_LEAVES)
3256 vmanip.m_area.add_y(em, i, -1);
3258 if(y >= y_nodes_min)
3261 surface_y = y_nodes_min;
3264 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3265 MapNode *n = &vmanip.m_data[i];
3266 if(n->d == CONTENT_MUD)
3267 n->d = CONTENT_GRASS;
3273 Initial lighting (sunlight)
3276 core::map<v3s16, bool> light_sources;
3279 // 750ms @cs=8, can't optimize more
3280 TimeTaker timer1("initial lighting");
3284 Go through the edges and add all nodes that have light to light_sources
3288 for(s16 i=0; i<4; i++)
3290 for(s16 j=lighting_min_d;
3297 if(i == 0 || i == 1)
3299 x = (i==0) ? lighting_min_d : lighting_max_d;
3308 z = (i==0) ? lighting_min_d : lighting_max_d;
3315 // Node position in 2d
3316 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3319 v3s16 em = vmanip.m_area.getExtent();
3320 s16 y_start = y_nodes_max;
3321 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3322 for(s16 y=y_start; y>=y_nodes_min; y--)
3324 MapNode *n = &vmanip.m_data[i];
3325 if(n->getLight(LIGHTBANK_DAY) != 0)
3327 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3329 //NOTE: This is broken, at least the index has to
3338 Go through the edges and apply sunlight to them, not caring
3343 for(s16 i=0; i<4; i++)
3345 for(s16 j=lighting_min_d;
3352 if(i == 0 || i == 1)
3354 x = (i==0) ? lighting_min_d : lighting_max_d;
3363 z = (i==0) ? lighting_min_d : lighting_max_d;
3370 // Node position in 2d
3371 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3373 // Loop from top to down
3375 u8 light = LIGHT_SUN;
3376 v3s16 em = vmanip.m_area.getExtent();
3377 s16 y_start = y_nodes_max;
3378 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3379 for(s16 y=y_start; y>=y_nodes_min; y--)
3381 MapNode *n = &vmanip.m_data[i];
3382 if(light_propagates_content(n->d) == false)
3386 else if(light != LIGHT_SUN
3387 || sunlight_propagates_content(n->d) == false)
3393 n->setLight(LIGHTBANK_DAY, light);
3394 n->setLight(LIGHTBANK_NIGHT, 0);
3398 // Insert light source
3399 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3402 // Increment index by y
3403 vmanip.m_area.add_y(em, i, -1);
3409 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3410 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3411 /*for(s16 x=0-max_spread_amount+1;
3412 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3414 for(s16 z=0-max_spread_amount+1;
3415 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3419 This has to be 1 smaller than the actual area, because
3420 neighboring nodes are checked.
3422 for(s16 x=lighting_min_d+1;
3423 x<=lighting_max_d-1;
3425 for(s16 z=lighting_min_d+1;
3426 z<=lighting_max_d-1;
3429 // Node position in 2d
3430 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3433 Apply initial sunlight
3436 u8 light = LIGHT_SUN;
3437 bool add_to_sources = false;
3438 v3s16 em = vmanip.m_area.getExtent();
3439 s16 y_start = y_nodes_max;
3440 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3441 for(s16 y=y_start; y>=y_nodes_min; y--)
3443 MapNode *n = &vmanip.m_data[i];
3445 if(light_propagates_content(n->d) == false)
3449 else if(light != LIGHT_SUN
3450 || sunlight_propagates_content(n->d) == false)
3456 // This doesn't take much time
3457 if(add_to_sources == false)
3460 Check sides. If side is not air or water, start
3461 adding to light_sources.
3464 v3s16(0,0,1), // back
3465 v3s16(1,0,0), // right
3466 v3s16(0,0,-1), // front
3467 v3s16(-1,0,0), // left
3469 for(u32 di=0; di<4; di++)
3471 v3s16 dirp = dirs4[di];
3473 vmanip.m_area.add_p(em, i2, dirp);
3474 MapNode *n2 = &vmanip.m_data[i2];
3476 n2->d != CONTENT_AIR
3477 && n2->d != CONTENT_WATERSOURCE
3478 && n2->d != CONTENT_WATER
3480 add_to_sources = true;
3486 n->setLight(LIGHTBANK_DAY, light);
3487 n->setLight(LIGHTBANK_NIGHT, 0);
3489 // This doesn't take much time
3490 if(light != 0 && add_to_sources)
3492 // Insert light source
3493 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3496 // Increment index by y
3497 vmanip.m_area.add_y(em, i, -1);
3504 for(s16 x=lighting_min_d+1;
3505 x<=lighting_max_d-1;
3507 for(s16 z=lighting_min_d+1;
3508 z<=lighting_max_d-1;
3511 // Node position in 2d
3512 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3515 Apply initial sunlight
3518 u8 light = LIGHT_SUN;
3519 v3s16 em = vmanip.m_area.getExtent();
3520 s16 y_start = y_nodes_max;
3521 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3522 for(s16 y=y_start; y>=y_nodes_min; y--)
3524 MapNode *n = &vmanip.m_data[i];
3526 if(light_propagates_content(n->d) == false)
3530 else if(light != LIGHT_SUN
3531 || sunlight_propagates_content(n->d) == false)
3537 n->setLight(LIGHTBANK_DAY, light);
3538 n->setLight(LIGHTBANK_NIGHT, 0);
3540 // This doesn't take much time
3543 // Insert light source
3544 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3547 // Increment index by y
3548 vmanip.m_area.add_y(em, i, -1);
3556 // Spread light around
3558 TimeTaker timer("generateChunkRaw() spreadLight");
3559 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3566 timer_generate.stop();
3569 Blit generated stuff to map
3573 //TimeTaker timer("generateChunkRaw() blitBackAll");
3574 vmanip.blitBackAll(&changed_blocks);
3578 Update day/night difference cache of the MapBlocks
3581 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3582 i.atEnd() == false; i++)
3584 MapBlock *block = i.getNode()->getValue();
3585 block->updateDayNightDiff();
3591 Create chunk metadata
3594 for(s16 x=-1; x<=1; x++)
3595 for(s16 y=-1; y<=1; y++)
3597 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3598 // Add chunk meta information
3599 MapChunk *chunk = getChunk(chunkpos0);
3602 chunk = new MapChunk();
3603 m_chunks.insert(chunkpos0, chunk);
3605 //chunk->setIsVolatile(true);
3606 if(chunk->getGenLevel() > GENERATED_PARTLY)
3607 chunk->setGenLevel(GENERATED_PARTLY);
3611 Set central chunk non-volatile
3613 MapChunk *chunk = getChunk(chunkpos);
3616 //chunk->setIsVolatile(false);
3617 chunk->setGenLevel(GENERATED_FULLY);
3620 Save changed parts of map
3625 Return central chunk (which was requested)
3630 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3631 core::map<v3s16, MapBlock*> &changed_blocks)
3633 dstream<<"generateChunk(): Generating chunk "
3634 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3637 /*for(s16 x=-1; x<=1; x++)
3638 for(s16 y=-1; y<=1; y++)*/
3639 for(s16 x=-0; x<=0; x++)
3640 for(s16 y=-0; y<=0; y++)
3642 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3643 MapChunk *chunk = getChunk(chunkpos0);
3644 // Skip if already generated
3645 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3647 generateChunkRaw(chunkpos0, changed_blocks);
3650 assert(chunkNonVolatile(chunkpos1));
3652 MapChunk *chunk = getChunk(chunkpos1);
3656 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3658 DSTACK("%s: p2d=(%d,%d)",
3663 Check if it exists already in memory
3665 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3670 Try to load it from disk (with blocks)
3672 if(loadSectorFull(p2d) == true)
3674 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3677 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3678 throw InvalidPositionException("");
3684 Do not create over-limit
3686 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3687 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3688 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3689 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3690 throw InvalidPositionException("createSector(): pos. over limit");
3693 Generate blank sector
3696 sector = new ServerMapSector(this, p2d);
3698 // Sector position on map in nodes
3699 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3704 m_sectors.insert(p2d, sector);
3709 MapSector * ServerMap::emergeSector(v2s16 p2d,
3710 core::map<v3s16, MapBlock*> &changed_blocks)
3712 DSTACK("%s: p2d=(%d,%d)",
3719 v2s16 chunkpos = sector_to_chunk(p2d);
3720 /*bool chunk_nonvolatile = false;
3721 MapChunk *chunk = getChunk(chunkpos);
3722 if(chunk && chunk->getIsVolatile() == false)
3723 chunk_nonvolatile = true;*/
3724 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3727 If chunk is not fully generated, generate chunk
3729 if(chunk_nonvolatile == false)
3731 // Generate chunk and neighbors
3732 generateChunk(chunkpos, changed_blocks);
3736 Return sector if it exists now
3738 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3743 Try to load it from disk
3745 if(loadSectorFull(p2d) == true)
3747 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3750 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3751 throw InvalidPositionException("");
3757 generateChunk should have generated the sector
3764 //return generateSector();
3768 NOTE: This is not used for main map generation, only for blocks
3769 that are very high or low
3771 MapBlock * ServerMap::generateBlock(
3773 MapBlock *original_dummy,
3774 ServerMapSector *sector,
3775 core::map<v3s16, MapBlock*> &changed_blocks,
3776 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3779 DSTACK("%s: p=(%d,%d,%d)",
3783 /*dstream<<"generateBlock(): "
3784 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3787 MapBlock *block = original_dummy;
3789 v2s16 p2d(p.X, p.Z);
3793 Do not generate over-limit
3795 if(blockpos_over_limit(p))
3797 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3798 throw InvalidPositionException("generateBlock(): pos. over limit");
3802 If block doesn't exist, create one.
3803 If it exists, it is a dummy. In that case unDummify() it.
3805 NOTE: This already sets the map as the parent of the block
3809 block = sector->createBlankBlockNoInsert(block_y);
3813 // Remove the block so that nobody can get a half-generated one.
3814 sector->removeBlock(block);
3815 // Allocate the block to contain the generated data
3819 u8 water_material = CONTENT_WATERSOURCE;
3821 s32 lowest_ground_y = 32767;
3822 s32 highest_ground_y = -32768;
3824 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3825 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3827 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3831 if(surface_y < lowest_ground_y)
3832 lowest_ground_y = surface_y;
3833 if(surface_y > highest_ground_y)
3834 highest_ground_y = surface_y;
3836 s32 surface_depth = 2;
3838 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3840 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3845 NOTE: If there are some man-made structures above the
3846 newly created block, they won't be taken into account.
3848 if(real_y > surface_y)
3849 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3855 // If node is over heightmap y, it's air or water
3856 if(real_y > surface_y)
3858 // If under water level, it's water
3859 if(real_y < WATER_LEVEL)
3861 n.d = water_material;
3862 n.setLight(LIGHTBANK_DAY,
3863 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3865 Add to transforming liquid queue (in case it'd
3868 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3869 m_transforming_liquid.push_back(real_pos);
3875 // Else it's ground or dungeons (air)
3878 // If it's surface_depth under ground, it's stone
3879 if(real_y <= surface_y - surface_depth)
3881 n.d = CONTENT_STONE;
3885 // It is mud if it is under the first ground
3886 // level or under water
3887 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3893 n.d = CONTENT_GRASS;
3896 //n.d = CONTENT_MUD;
3898 /*// If under water level, it's mud
3899 if(real_y < WATER_LEVEL)
3901 // Only the topmost node is grass
3902 else if(real_y <= surface_y - 1)
3905 n.d = CONTENT_GRASS;*/
3909 block->setNode(v3s16(x0,y0,z0), n);
3914 Calculate some helper variables
3917 // Completely underground if the highest part of block is under lowest
3919 // This has to be very sure; it's probably one too strict now but
3920 // that's just better.
3921 bool completely_underground =
3922 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3924 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3926 bool mostly_underwater_surface = false;
3927 if(highest_ground_y < WATER_LEVEL
3928 && some_part_underground && !completely_underground)
3929 mostly_underwater_surface = true;
3932 Get local attributes
3935 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3937 float caves_amount = 0.5;
3942 NOTE: BEWARE: Too big amount of attribute points slows verything
3944 1 interpolation from 5000 points takes 2-3ms.
3946 //TimeTaker timer("generateBlock() local attribute retrieval");
3947 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3948 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3949 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3953 //dstream<<"generateBlock(): Done"<<std::endl;
3959 // Initialize temporary table
3960 const s32 ued = MAP_BLOCKSIZE;
3961 bool underground_emptiness[ued*ued*ued];
3962 for(s32 i=0; i<ued*ued*ued; i++)
3964 underground_emptiness[i] = 0;
3971 Initialize orp and ors. Try to find if some neighboring
3972 MapBlock has a tunnel ended in its side
3976 (float)(myrand()%ued)+0.5,
3977 (float)(myrand()%ued)+0.5,
3978 (float)(myrand()%ued)+0.5
3981 bool found_existing = false;
3987 for(s16 y=0; y<ued; y++)
3988 for(s16 x=0; x<ued; x++)
3990 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3991 if(getNode(ap).d == CONTENT_AIR)
3993 orp = v3f(x+1,y+1,0);
3994 found_existing = true;
3995 goto continue_generating;
3999 catch(InvalidPositionException &e){}
4005 for(s16 y=0; y<ued; y++)
4006 for(s16 x=0; x<ued; x++)
4008 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4009 if(getNode(ap).d == CONTENT_AIR)
4011 orp = v3f(x+1,y+1,ued-1);
4012 found_existing = true;
4013 goto continue_generating;
4017 catch(InvalidPositionException &e){}
4023 for(s16 y=0; y<ued; y++)
4024 for(s16 z=0; z<ued; z++)
4026 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4027 if(getNode(ap).d == CONTENT_AIR)
4029 orp = v3f(0,y+1,z+1);
4030 found_existing = true;
4031 goto continue_generating;
4035 catch(InvalidPositionException &e){}
4041 for(s16 y=0; y<ued; y++)
4042 for(s16 z=0; z<ued; z++)
4044 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4045 if(getNode(ap).d == CONTENT_AIR)
4047 orp = v3f(ued-1,y+1,z+1);
4048 found_existing = true;
4049 goto continue_generating;
4053 catch(InvalidPositionException &e){}
4059 for(s16 x=0; x<ued; x++)
4060 for(s16 z=0; z<ued; z++)
4062 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4063 if(getNode(ap).d == CONTENT_AIR)
4065 orp = v3f(x+1,0,z+1);
4066 found_existing = true;
4067 goto continue_generating;
4071 catch(InvalidPositionException &e){}
4077 for(s16 x=0; x<ued; x++)
4078 for(s16 z=0; z<ued; z++)
4080 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4081 if(getNode(ap).d == CONTENT_AIR)
4083 orp = v3f(x+1,ued-1,z+1);
4084 found_existing = true;
4085 goto continue_generating;
4089 catch(InvalidPositionException &e){}
4091 continue_generating:
4094 Choose whether to actually generate dungeon
4096 bool do_generate_dungeons = true;
4097 // Don't generate if no part is underground
4098 if(!some_part_underground)
4100 do_generate_dungeons = false;
4102 // Don't generate if mostly underwater surface
4103 /*else if(mostly_underwater_surface)
4105 do_generate_dungeons = false;
4107 // Partly underground = cave
4108 else if(!completely_underground)
4110 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4112 // Found existing dungeon underground
4113 else if(found_existing && completely_underground)
4115 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4117 // Underground and no dungeons found
4120 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4123 if(do_generate_dungeons)
4126 Generate some tunnel starting from orp and ors
4128 for(u16 i=0; i<3; i++)
4131 (float)(myrand()%ued)+0.5,
4132 (float)(myrand()%ued)+0.5,
4133 (float)(myrand()%ued)+0.5
4137 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4141 for(float f=0; f<1.0; f+=0.04)
4143 v3f fp = orp + vec * f;
4144 v3s16 cp(fp.X, fp.Y, fp.Z);
4146 s16 d1 = d0 + rs - 1;
4147 for(s16 z0=d0; z0<=d1; z0++)
4149 s16 si = rs - abs(z0);
4150 for(s16 x0=-si; x0<=si-1; x0++)
4152 s16 si2 = rs - abs(x0);
4153 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4159 if(isInArea(p, ued) == false)
4161 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4173 // Set to true if has caves.
4174 // Set when some non-air is changed to air when making caves.
4175 bool has_dungeons = false;
4178 Apply temporary cave data to block
4181 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4182 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4184 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4186 MapNode n = block->getNode(v3s16(x0,y0,z0));
4189 if(underground_emptiness[
4190 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4191 +ued*(y0*ued/MAP_BLOCKSIZE)
4192 +(x0*ued/MAP_BLOCKSIZE)])
4194 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4197 has_dungeons = true;
4203 block->setNode(v3s16(x0,y0,z0), n);
4208 This is used for guessing whether or not the block should
4209 receive sunlight from the top if the block above doesn't exist
4211 block->setIsUnderground(completely_underground);
4214 Force lighting update if some part of block is partly
4215 underground and has caves.
4217 /*if(some_part_underground && !completely_underground && has_dungeons)
4219 //dstream<<"Half-ground caves"<<std::endl;
4220 lighting_invalidated_blocks[block->getPos()] = block;
4223 // DEBUG: Always update lighting
4224 //lighting_invalidated_blocks[block->getPos()] = block;
4230 if(some_part_underground)
4232 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4237 for(s16 i=0; i<underground_level/4 + 1; i++)
4239 if(myrand()%50 == 0)
4242 (myrand()%(MAP_BLOCKSIZE-2))+1,
4243 (myrand()%(MAP_BLOCKSIZE-2))+1,
4244 (myrand()%(MAP_BLOCKSIZE-2))+1
4250 for(u16 i=0; i<27; i++)
4252 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4254 block->setNode(cp+g_27dirs[i], n);
4262 u16 coal_amount = 30;
4263 u16 coal_rareness = 60 / coal_amount;
4264 if(coal_rareness == 0)
4266 if(myrand()%coal_rareness == 0)
4268 u16 a = myrand() % 16;
4269 u16 amount = coal_amount * a*a*a / 1000;
4270 for(s16 i=0; i<amount; i++)
4273 (myrand()%(MAP_BLOCKSIZE-2))+1,
4274 (myrand()%(MAP_BLOCKSIZE-2))+1,
4275 (myrand()%(MAP_BLOCKSIZE-2))+1
4279 n.d = CONTENT_STONE;
4280 n.param = MINERAL_COAL;
4282 for(u16 i=0; i<27; i++)
4284 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4286 block->setNode(cp+g_27dirs[i], n);
4294 //TODO: change to iron_amount or whatever
4295 u16 iron_amount = 15;
4296 u16 iron_rareness = 60 / iron_amount;
4297 if(iron_rareness == 0)
4299 if(myrand()%iron_rareness == 0)
4301 u16 a = myrand() % 16;
4302 u16 amount = iron_amount * a*a*a / 1000;
4303 for(s16 i=0; i<amount; i++)
4306 (myrand()%(MAP_BLOCKSIZE-2))+1,
4307 (myrand()%(MAP_BLOCKSIZE-2))+1,
4308 (myrand()%(MAP_BLOCKSIZE-2))+1
4312 n.d = CONTENT_STONE;
4313 n.param = MINERAL_IRON;
4315 for(u16 i=0; i<27; i++)
4317 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4319 block->setNode(cp+g_27dirs[i], n);
4326 Create a few rats in empty blocks underground
4328 if(completely_underground)
4330 //for(u16 i=0; i<2; i++)
4333 (myrand()%(MAP_BLOCKSIZE-2))+1,
4334 (myrand()%(MAP_BLOCKSIZE-2))+1,
4335 (myrand()%(MAP_BLOCKSIZE-2))+1
4338 // Check that the place is empty
4339 //if(!is_ground_content(block->getNode(cp).d))
4342 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4343 block->addObject(obj);
4349 Add block to sector.
4351 sector->insertBlock(block);
4353 // Lighting is invalid after generation.
4354 block->setLightingExpired(true);
4361 <<"lighting_invalidated_blocks.size()"
4365 <<" "<<lighting_invalidated_blocks.size()
4366 <<", "<<has_dungeons
4367 <<", "<<completely_underground
4368 <<", "<<some_part_underground
4375 MapBlock * ServerMap::createBlock(v3s16 p)
4377 DSTACK("%s: p=(%d,%d,%d)",
4378 __FUNCTION_NAME, p.X, p.Y, p.Z);
4380 v2s16 p2d(p.X, p.Z);
4383 This will create or load a sector if not found in memory.
4384 If block exists on disk, it will be loaded.
4386 NOTE: On old save formats, this will be slow, as it generates
4387 lighting on blocks for them.
4389 ServerMapSector *sector;
4391 sector = (ServerMapSector*)createSector(p2d);
4392 assert(sector->getId() == MAPSECTOR_SERVER);
4394 /*catch(InvalidPositionException &e)
4396 dstream<<"createBlock: createSector() failed"<<std::endl;
4399 catch(std::exception &e)
4401 dstream<<"createBlock: createSector() failed: "
4402 <<e.what()<<std::endl;
4407 Try to get a block from the sector
4410 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4414 block = sector->createBlankBlock(block_y);
4418 MapBlock * ServerMap::emergeBlock(
4420 bool only_from_disk,
4421 core::map<v3s16, MapBlock*> &changed_blocks,
4422 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4425 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4427 p.X, p.Y, p.Z, only_from_disk);
4429 v2s16 p2d(p.X, p.Z);
4432 This will create or load a sector if not found in memory.
4433 If block exists on disk, it will be loaded.
4435 NOTE: On old save formats, this will be slow, as it generates
4436 lighting on blocks for them.
4438 ServerMapSector *sector;
4440 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4441 assert(sector->getId() == MAPSECTOR_SERVER);
4443 catch(std::exception &e)
4445 dstream<<"emergeBlock: emergeSector() failed: "
4446 <<e.what()<<std::endl;
4451 Try to get a block from the sector
4454 bool does_not_exist = false;
4455 bool lighting_expired = false;
4456 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4460 does_not_exist = true;
4462 else if(block->isDummy() == true)
4464 does_not_exist = true;
4466 else if(block->getLightingExpired())
4468 lighting_expired = true;
4473 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4478 If block was not found on disk and not going to generate a
4479 new one, make sure there is a dummy block in place.
4481 if(only_from_disk && (does_not_exist || lighting_expired))
4483 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4487 // Create dummy block
4488 block = new MapBlock(this, p, true);
4490 // Add block to sector
4491 sector->insertBlock(block);
4497 //dstream<<"Not found on disk, generating."<<std::endl;
4499 //TimeTaker("emergeBlock() generate");
4501 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4504 If the block doesn't exist, generate the block.
4508 block = generateBlock(p, block, sector, changed_blocks,
4509 lighting_invalidated_blocks);
4512 if(lighting_expired)
4514 lighting_invalidated_blocks.insert(p, block);
4518 Initially update sunlight
4522 core::map<v3s16, bool> light_sources;
4523 bool black_air_left = false;
4524 bool bottom_invalid =
4525 block->propagateSunlight(light_sources, true,
4526 &black_air_left, true);
4528 // If sunlight didn't reach everywhere and part of block is
4529 // above ground, lighting has to be properly updated
4530 //if(black_air_left && some_part_underground)
4533 lighting_invalidated_blocks[block->getPos()] = block;
4538 lighting_invalidated_blocks[block->getPos()] = block;
4545 void ServerMap::createDir(std::string path)
4547 if(fs::CreateDir(path) == false)
4549 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4550 <<"\""<<path<<"\""<<std::endl;
4551 throw BaseException("ServerMap failed to create directory");
4555 std::string ServerMap::getSectorSubDir(v2s16 pos)
4558 snprintf(cc, 9, "%.4x%.4x",
4559 (unsigned int)pos.X&0xffff,
4560 (unsigned int)pos.Y&0xffff);
4562 return std::string(cc);
4565 std::string ServerMap::getSectorDir(v2s16 pos)
4567 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4570 v2s16 ServerMap::getSectorPos(std::string dirname)
4572 if(dirname.size() != 8)
4573 throw InvalidFilenameException("Invalid sector directory name");
4575 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4577 throw InvalidFilenameException("Invalid sector directory name");
4578 v2s16 pos((s16)x, (s16)y);
4582 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4584 v2s16 p2d = getSectorPos(sectordir);
4586 if(blockfile.size() != 4){
4587 throw InvalidFilenameException("Invalid block filename");
4590 int r = sscanf(blockfile.c_str(), "%4x", &y);
4592 throw InvalidFilenameException("Invalid block filename");
4593 return v3s16(p2d.X, y, p2d.Y);
4596 void ServerMap::save(bool only_changed)
4598 DSTACK(__FUNCTION_NAME);
4599 if(m_map_saving_enabled == false)
4601 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4605 if(only_changed == false)
4606 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4612 u32 sector_meta_count = 0;
4613 u32 block_count = 0;
4616 JMutexAutoLock lock(m_sector_mutex);
4618 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4619 for(; i.atEnd() == false; i++)
4621 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4622 assert(sector->getId() == MAPSECTOR_SERVER);
4624 if(sector->differs_from_disk || only_changed == false)
4626 saveSectorMeta(sector);
4627 sector_meta_count++;
4629 core::list<MapBlock*> blocks;
4630 sector->getBlocks(blocks);
4631 core::list<MapBlock*>::Iterator j;
4632 for(j=blocks.begin(); j!=blocks.end(); j++)
4634 MapBlock *block = *j;
4635 if(block->getChangedFlag() || only_changed == false)
4640 /*dstream<<"ServerMap: Written block ("
4641 <<block->getPos().X<<","
4642 <<block->getPos().Y<<","
4643 <<block->getPos().Z<<")"
4652 Only print if something happened or saved whole map
4654 if(only_changed == false || sector_meta_count != 0
4655 || block_count != 0)
4657 dstream<<DTIME<<"ServerMap: Written: "
4658 <<sector_meta_count<<" sector metadata files, "
4659 <<block_count<<" block files"
4664 void ServerMap::loadAll()
4666 DSTACK(__FUNCTION_NAME);
4667 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4672 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4674 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4676 JMutexAutoLock lock(m_sector_mutex);
4679 s32 printed_counter = -100000;
4680 s32 count = list.size();
4682 std::vector<fs::DirListNode>::iterator i;
4683 for(i=list.begin(); i!=list.end(); i++)
4685 if(counter > printed_counter + 10)
4687 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4688 printed_counter = counter;
4692 MapSector *sector = NULL;
4694 // We want directories
4698 sector = loadSectorMeta(i->name);
4700 catch(InvalidFilenameException &e)
4702 // This catches unknown crap in directory
4705 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4706 (m_savedir+"/sectors/"+i->name);
4707 std::vector<fs::DirListNode>::iterator i2;
4708 for(i2=list2.begin(); i2!=list2.end(); i2++)
4714 loadBlock(i->name, i2->name, sector);
4716 catch(InvalidFilenameException &e)
4718 // This catches unknown crap in directory
4722 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4726 void ServerMap::saveMasterHeightmap()
4728 DSTACK(__FUNCTION_NAME);
4730 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4732 createDir(m_savedir);
4734 /*std::string fullpath = m_savedir + "/master_heightmap";
4735 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4736 if(o.good() == false)
4737 throw FileNotGoodException("Cannot open master heightmap");*/
4739 // Format used for writing
4740 //u8 version = SER_FMT_VER_HIGHEST;
4743 void ServerMap::loadMasterHeightmap()
4745 DSTACK(__FUNCTION_NAME);
4747 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4749 /*std::string fullpath = m_savedir + "/master_heightmap";
4750 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4751 if(is.good() == false)
4752 throw FileNotGoodException("Cannot open master heightmap");*/
4756 void ServerMap::saveMapMeta()
4758 DSTACK(__FUNCTION_NAME);
4760 dstream<<"INFO: ServerMap::saveMapMeta(): "
4761 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4764 createDir(m_savedir);
4766 std::string fullpath = m_savedir + "/meta.txt";
4767 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4768 if(os.good() == false)
4770 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4771 <<"could not open"<<fullpath<<std::endl;
4772 throw FileNotGoodException("Cannot open chunk metadata");
4776 params.setU64("seed", m_seed);
4777 params.setS32("chunksize", m_chunksize);
4779 params.writeLines(os);
4781 os<<"[end_of_params]\n";
4785 void ServerMap::loadMapMeta()
4787 DSTACK(__FUNCTION_NAME);
4789 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4792 std::string fullpath = m_savedir + "/meta.txt";
4793 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4794 if(is.good() == false)
4796 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4797 <<"could not open"<<fullpath<<std::endl;
4798 throw FileNotGoodException("Cannot open chunk metadata");
4806 throw SerializationError
4807 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4809 std::getline(is, line);
4810 std::string trimmedline = trim(line);
4811 if(trimmedline == "[end_of_params]")
4813 params.parseConfigLine(line);
4816 m_seed = params.getU64("seed");
4817 m_chunksize = params.getS32("chunksize");
4819 dstream<<"INFO: ServerMap::loadMapMeta(): "
4820 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4824 void ServerMap::saveChunkMeta()
4826 DSTACK(__FUNCTION_NAME);
4828 u32 count = m_chunks.size();
4830 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4831 <<count<<" chunks"<<std::endl;
4833 createDir(m_savedir);
4835 std::string fullpath = m_savedir + "/chunk_meta";
4836 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4837 if(os.good() == false)
4839 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4840 <<"could not open"<<fullpath<<std::endl;
4841 throw FileNotGoodException("Cannot open chunk metadata");
4847 os.write((char*)&version, 1);
4852 writeU32(buf, count);
4853 os.write((char*)buf, 4);
4855 for(core::map<v2s16, MapChunk*>::Iterator
4856 i = m_chunks.getIterator();
4857 i.atEnd()==false; i++)
4859 v2s16 p = i.getNode()->getKey();
4860 MapChunk *chunk = i.getNode()->getValue();
4863 os.write((char*)buf, 4);
4865 chunk->serialize(os, version);
4869 void ServerMap::loadChunkMeta()
4871 DSTACK(__FUNCTION_NAME);
4873 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
4876 std::string fullpath = m_savedir + "/chunk_meta";
4877 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4878 if(is.good() == false)
4880 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
4881 <<"could not open"<<fullpath<<std::endl;
4882 throw FileNotGoodException("Cannot open chunk metadata");
4888 is.read((char*)&version, 1);
4893 is.read((char*)buf, 4);
4894 u32 count = readU32(buf);
4896 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
4897 <<count<<" chunks"<<std::endl;
4899 for(u32 i=0; i<count; i++)
4902 MapChunk *chunk = new MapChunk();
4904 is.read((char*)buf, 4);
4907 chunk->deSerialize(is, version);
4908 m_chunks.insert(p, chunk);
4912 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4914 DSTACK(__FUNCTION_NAME);
4915 // Format used for writing
4916 u8 version = SER_FMT_VER_HIGHEST;
4918 v2s16 pos = sector->getPos();
4919 createDir(m_savedir);
4920 createDir(m_savedir+"/sectors");
4921 std::string dir = getSectorDir(pos);
4924 std::string fullpath = dir + "/heightmap";
4925 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4926 if(o.good() == false)
4927 throw FileNotGoodException("Cannot open master heightmap");
4929 sector->serialize(o, version);
4931 sector->differs_from_disk = false;
4934 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4936 DSTACK(__FUNCTION_NAME);
4938 v2s16 p2d = getSectorPos(dirname);
4939 std::string dir = m_savedir + "/sectors/" + dirname;
4941 std::string fullpath = dir + "/heightmap";
4942 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4943 if(is.good() == false)
4944 throw FileNotGoodException("Cannot open sector heightmap");
4946 ServerMapSector *sector = ServerMapSector::deSerialize
4947 (is, this, p2d, m_sectors);
4949 sector->differs_from_disk = false;
4954 bool ServerMap::loadSectorFull(v2s16 p2d)
4956 DSTACK(__FUNCTION_NAME);
4957 std::string sectorsubdir = getSectorSubDir(p2d);
4959 MapSector *sector = NULL;
4961 JMutexAutoLock lock(m_sector_mutex);
4964 sector = loadSectorMeta(sectorsubdir);
4966 catch(InvalidFilenameException &e)
4970 catch(FileNotGoodException &e)
4974 catch(std::exception &e)
4979 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4980 (m_savedir+"/sectors/"+sectorsubdir);
4981 std::vector<fs::DirListNode>::iterator i2;
4982 for(i2=list2.begin(); i2!=list2.end(); i2++)
4988 loadBlock(sectorsubdir, i2->name, sector);
4990 catch(InvalidFilenameException &e)
4992 // This catches unknown crap in directory
4999 bool ServerMap::deFlushSector(v2s16 p2d)
5001 DSTACK(__FUNCTION_NAME);
5002 // See if it already exists in memory
5004 MapSector *sector = getSectorNoGenerate(p2d);
5007 catch(InvalidPositionException &e)
5010 Try to load the sector from disk.
5012 if(loadSectorFull(p2d) == true)
5021 void ServerMap::saveBlock(MapBlock *block)
5023 DSTACK(__FUNCTION_NAME);
5025 Dummy blocks are not written
5027 if(block->isDummy())
5029 /*v3s16 p = block->getPos();
5030 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5031 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5035 // Format used for writing
5036 u8 version = SER_FMT_VER_HIGHEST;
5038 v3s16 p3d = block->getPos();
5039 v2s16 p2d(p3d.X, p3d.Z);
5040 createDir(m_savedir);
5041 createDir(m_savedir+"/sectors");
5042 std::string dir = getSectorDir(p2d);
5045 // Block file is map/sectors/xxxxxxxx/xxxx
5047 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5048 std::string fullpath = dir + "/" + cc;
5049 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5050 if(o.good() == false)
5051 throw FileNotGoodException("Cannot open block data");
5054 [0] u8 serialization version
5057 o.write((char*)&version, 1);
5059 block->serialize(o, version);
5062 Versions up from 9 have block objects.
5066 block->serializeObjects(o, version);
5069 // We just wrote it to the disk
5070 block->resetChangedFlag();
5073 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5075 DSTACK(__FUNCTION_NAME);
5079 // Block file is map/sectors/xxxxxxxx/xxxx
5080 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5081 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5082 if(is.good() == false)
5083 throw FileNotGoodException("Cannot open block file");
5085 v3s16 p3d = getBlockPos(sectordir, blockfile);
5086 v2s16 p2d(p3d.X, p3d.Z);
5088 assert(sector->getPos() == p2d);
5090 u8 version = SER_FMT_VER_INVALID;
5091 is.read((char*)&version, 1);
5093 /*u32 block_size = MapBlock::serializedLength(version);
5094 SharedBuffer<u8> data(block_size);
5095 is.read((char*)*data, block_size);*/
5097 // This will always return a sector because we're the server
5098 //MapSector *sector = emergeSector(p2d);
5100 MapBlock *block = NULL;
5101 bool created_new = false;
5103 block = sector->getBlockNoCreate(p3d.Y);
5105 catch(InvalidPositionException &e)
5107 block = sector->createBlankBlockNoInsert(p3d.Y);
5111 // deserialize block data
5112 block->deSerialize(is, version);
5115 Versions up from 9 have block objects.
5119 block->updateObjects(is, version, NULL, 0);
5123 sector->insertBlock(block);
5126 Convert old formats to new and save
5129 // Save old format blocks in new format
5130 if(version < SER_FMT_VER_HIGHEST)
5135 // We just loaded it from the disk, so it's up-to-date.
5136 block->resetChangedFlag();
5139 catch(SerializationError &e)
5141 dstream<<"WARNING: Invalid block data on disk "
5142 "(SerializationError). Ignoring."
5147 // Gets from master heightmap
5148 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5150 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5151 //assert(m_heightmap != NULL);
5159 /*corners[0] = m_heightmap->getGroundHeight
5160 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5161 corners[1] = m_heightmap->getGroundHeight
5162 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5163 corners[2] = m_heightmap->getGroundHeight
5164 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5165 corners[3] = m_heightmap->getGroundHeight
5166 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
5169 void ServerMap::PrintInfo(std::ostream &out)
5180 ClientMap::ClientMap(
5182 MapDrawControl &control,
5183 scene::ISceneNode* parent,
5184 scene::ISceneManager* mgr,
5188 scene::ISceneNode(parent, mgr, id),
5192 //mesh_mutex.Init();
5194 /*m_box = core::aabbox3d<f32>(0,0,0,
5195 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5196 /*m_box = core::aabbox3d<f32>(0,0,0,
5197 map->getSizeNodes().X * BS,
5198 map->getSizeNodes().Y * BS,
5199 map->getSizeNodes().Z * BS);*/
5200 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5201 BS*1000000,BS*1000000,BS*1000000);
5203 //setPosition(v3f(BS,BS,BS));
5206 ClientMap::~ClientMap()
5208 /*JMutexAutoLock lock(mesh_mutex);
5217 MapSector * ClientMap::emergeSector(v2s16 p2d)
5219 DSTACK(__FUNCTION_NAME);
5220 // Check that it doesn't exist already
5222 return getSectorNoGenerate(p2d);
5224 catch(InvalidPositionException &e)
5228 // Create a sector with no heightmaps
5229 ClientMapSector *sector = new ClientMapSector(this, p2d);
5232 JMutexAutoLock lock(m_sector_mutex);
5233 m_sectors.insert(p2d, sector);
5239 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5241 DSTACK(__FUNCTION_NAME);
5242 ClientMapSector *sector = NULL;
5244 JMutexAutoLock lock(m_sector_mutex);
5246 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5250 sector = (ClientMapSector*)n->getValue();
5251 assert(sector->getId() == MAPSECTOR_CLIENT);
5255 sector = new ClientMapSector(this, p2d);
5257 JMutexAutoLock lock(m_sector_mutex);
5258 m_sectors.insert(p2d, sector);
5262 sector->deSerialize(is);
5265 void ClientMap::OnRegisterSceneNode()
5269 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5270 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5273 ISceneNode::OnRegisterSceneNode();
5276 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5278 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5279 DSTACK(__FUNCTION_NAME);
5281 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5284 Get time for measuring timeout.
5286 Measuring time is very useful for long delays when the
5287 machine is swapping a lot.
5289 int time1 = time(0);
5291 u32 daynight_ratio = m_client->getDayNightRatio();
5293 m_camera_mutex.Lock();
5294 v3f camera_position = m_camera_position;
5295 v3f camera_direction = m_camera_direction;
5296 m_camera_mutex.Unlock();
5299 Get all blocks and draw all visible ones
5302 v3s16 cam_pos_nodes(
5303 camera_position.X / BS,
5304 camera_position.Y / BS,
5305 camera_position.Z / BS);
5307 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5309 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5310 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5312 // Take a fair amount as we will be dropping more out later
5314 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5315 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5316 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5318 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5319 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5320 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5322 u32 vertex_count = 0;
5324 // For limiting number of mesh updates per frame
5325 u32 mesh_update_count = 0;
5327 u32 blocks_would_have_drawn = 0;
5328 u32 blocks_drawn = 0;
5330 //NOTE: The sectors map should be locked but we're not doing it
5331 // because it'd cause too much delays
5333 int timecheck_counter = 0;
5334 core::map<v2s16, MapSector*>::Iterator si;
5335 si = m_sectors.getIterator();
5336 for(; si.atEnd() == false; si++)
5339 timecheck_counter++;
5340 if(timecheck_counter > 50)
5342 timecheck_counter = 0;
5343 int time2 = time(0);
5344 if(time2 > time1 + 4)
5346 dstream<<"ClientMap::renderMap(): "
5347 "Rendering takes ages, returning."
5354 MapSector *sector = si.getNode()->getValue();
5355 v2s16 sp = sector->getPos();
5357 if(m_control.range_all == false)
5359 if(sp.X < p_blocks_min.X
5360 || sp.X > p_blocks_max.X
5361 || sp.Y < p_blocks_min.Z
5362 || sp.Y > p_blocks_max.Z)
5366 core::list< MapBlock * > sectorblocks;
5367 sector->getBlocks(sectorblocks);
5373 core::list< MapBlock * >::Iterator i;
5374 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5376 MapBlock *block = *i;
5379 Compare block position to camera position, skip
5380 if not seen on display
5383 float range = 100000 * BS;
5384 if(m_control.range_all == false)
5385 range = m_control.wanted_range * BS;
5388 if(isBlockInSight(block->getPos(), camera_position,
5389 camera_direction, range, &d) == false)
5399 bool mesh_expired = false;
5402 JMutexAutoLock lock(block->mesh_mutex);
5404 mesh_expired = block->getMeshExpired();
5406 // Mesh has not been expired and there is no mesh:
5407 // block has no content
5408 if(block->mesh == NULL && mesh_expired == false)
5412 f32 faraway = BS*50;
5413 //f32 faraway = m_control.wanted_range * BS;
5416 This has to be done with the mesh_mutex unlocked
5418 // Pretty random but this should work somewhat nicely
5419 if(mesh_expired && (
5420 (mesh_update_count < 3
5421 && (d < faraway || mesh_update_count < 2)
5424 (m_control.range_all && mesh_update_count < 20)
5427 /*if(mesh_expired && mesh_update_count < 6
5428 && (d < faraway || mesh_update_count < 3))*/
5430 mesh_update_count++;
5432 // Mesh has been expired: generate new mesh
5433 //block->updateMeshes(daynight_i);
5434 block->updateMesh(daynight_ratio);
5436 mesh_expired = false;
5440 Don't draw an expired mesh that is far away
5442 /*if(mesh_expired && d >= faraway)
5445 // Instead, delete it
5446 JMutexAutoLock lock(block->mesh_mutex);
5449 block->mesh->drop();
5452 // And continue to next block
5457 Draw the faces of the block
5460 JMutexAutoLock lock(block->mesh_mutex);
5462 scene::SMesh *mesh = block->mesh;
5467 blocks_would_have_drawn++;
5468 if(blocks_drawn >= m_control.wanted_max_blocks
5469 && m_control.range_all == false
5470 && d > m_control.wanted_min_range * BS)
5474 u32 c = mesh->getMeshBufferCount();
5476 for(u32 i=0; i<c; i++)
5478 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5479 const video::SMaterial& material = buf->getMaterial();
5480 video::IMaterialRenderer* rnd =
5481 driver->getMaterialRenderer(material.MaterialType);
5482 bool transparent = (rnd && rnd->isTransparent());
5483 // Render transparent on transparent pass and likewise.
5484 if(transparent == is_transparent_pass)
5487 This *shouldn't* hurt too much because Irrlicht
5488 doesn't change opengl textures if the old
5489 material is set again.
5491 driver->setMaterial(buf->getMaterial());
5492 driver->drawMeshBuffer(buf);
5493 vertex_count += buf->getVertexCount();
5497 } // foreach sectorblocks
5500 m_control.blocks_drawn = blocks_drawn;
5501 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5503 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5504 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5507 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5508 core::map<v3s16, MapBlock*> *affected_blocks)
5510 bool changed = false;
5512 Add it to all blocks touching it
5515 v3s16(0,0,0), // this
5516 v3s16(0,0,1), // back
5517 v3s16(0,1,0), // top
5518 v3s16(1,0,0), // right
5519 v3s16(0,0,-1), // front
5520 v3s16(0,-1,0), // bottom
5521 v3s16(-1,0,0), // left
5523 for(u16 i=0; i<7; i++)
5525 v3s16 p2 = p + dirs[i];
5526 // Block position of neighbor (or requested) node
5527 v3s16 blockpos = getNodeBlockPos(p2);
5528 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5529 if(blockref == NULL)
5531 // Relative position of requested node
5532 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5533 if(blockref->setTempMod(relpos, mod))
5538 if(changed && affected_blocks!=NULL)
5540 for(u16 i=0; i<7; i++)
5542 v3s16 p2 = p + dirs[i];
5543 // Block position of neighbor (or requested) node
5544 v3s16 blockpos = getNodeBlockPos(p2);
5545 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5546 if(blockref == NULL)
5548 affected_blocks->insert(blockpos, blockref);
5554 bool ClientMap::clearTempMod(v3s16 p,
5555 core::map<v3s16, MapBlock*> *affected_blocks)
5557 bool changed = false;
5559 v3s16(0,0,0), // this
5560 v3s16(0,0,1), // back
5561 v3s16(0,1,0), // top
5562 v3s16(1,0,0), // right
5563 v3s16(0,0,-1), // front
5564 v3s16(0,-1,0), // bottom
5565 v3s16(-1,0,0), // left
5567 for(u16 i=0; i<7; i++)
5569 v3s16 p2 = p + dirs[i];
5570 // Block position of neighbor (or requested) node
5571 v3s16 blockpos = getNodeBlockPos(p2);
5572 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5573 if(blockref == NULL)
5575 // Relative position of requested node
5576 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5577 if(blockref->clearTempMod(relpos))
5582 if(changed && affected_blocks!=NULL)
5584 for(u16 i=0; i<7; i++)
5586 v3s16 p2 = p + dirs[i];
5587 // Block position of neighbor (or requested) node
5588 v3s16 blockpos = getNodeBlockPos(p2);
5589 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5590 if(blockref == NULL)
5592 affected_blocks->insert(blockpos, blockref);
5598 void ClientMap::PrintInfo(std::ostream &out)
5609 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5614 MapVoxelManipulator::~MapVoxelManipulator()
5616 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5620 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5622 TimeTaker timer1("emerge", &emerge_time);
5624 // Units of these are MapBlocks
5625 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5626 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5628 VoxelArea block_area_nodes
5629 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5631 addArea(block_area_nodes);
5633 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5634 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5635 for(s32 x=p_min.X; x<=p_max.X; x++)
5638 core::map<v3s16, bool>::Node *n;
5639 n = m_loaded_blocks.find(p);
5643 bool block_data_inexistent = false;
5646 TimeTaker timer1("emerge load", &emerge_load_time);
5648 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5649 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5652 dstream<<std::endl;*/
5654 MapBlock *block = m_map->getBlockNoCreate(p);
5655 if(block->isDummy())
5656 block_data_inexistent = true;
5658 block->copyTo(*this);
5660 catch(InvalidPositionException &e)
5662 block_data_inexistent = true;
5665 if(block_data_inexistent)
5667 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5668 // Fill with VOXELFLAG_INEXISTENT
5669 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5670 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5672 s32 i = m_area.index(a.MinEdge.X,y,z);
5673 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5677 m_loaded_blocks.insert(p, !block_data_inexistent);
5680 //dstream<<"emerge done"<<std::endl;
5684 SUGG: Add an option to only update eg. water and air nodes.
5685 This will make it interfere less with important stuff if
5688 void MapVoxelManipulator::blitBack
5689 (core::map<v3s16, MapBlock*> & modified_blocks)
5691 if(m_area.getExtent() == v3s16(0,0,0))
5694 //TimeTaker timer1("blitBack");
5696 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5697 <<m_loaded_blocks.size()<<std::endl;*/
5700 Initialize block cache
5702 v3s16 blockpos_last;
5703 MapBlock *block = NULL;
5704 bool block_checked_in_modified = false;
5706 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5707 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5708 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5712 u8 f = m_flags[m_area.index(p)];
5713 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5716 MapNode &n = m_data[m_area.index(p)];
5718 v3s16 blockpos = getNodeBlockPos(p);
5723 if(block == NULL || blockpos != blockpos_last){
5724 block = m_map->getBlockNoCreate(blockpos);
5725 blockpos_last = blockpos;
5726 block_checked_in_modified = false;
5729 // Calculate relative position in block
5730 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5732 // Don't continue if nothing has changed here
5733 if(block->getNode(relpos) == n)
5736 //m_map->setNode(m_area.MinEdge + p, n);
5737 block->setNode(relpos, n);
5740 Make sure block is in modified_blocks
5742 if(block_checked_in_modified == false)
5744 modified_blocks[blockpos] = block;
5745 block_checked_in_modified = true;
5748 catch(InvalidPositionException &e)
5754 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5755 MapVoxelManipulator(map)
5759 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5763 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5765 // Just create the area so that it can be pointed to
5766 VoxelManipulator::emerge(a, caller_id);
5769 void ManualMapVoxelManipulator::initialEmerge(
5770 v3s16 blockpos_min, v3s16 blockpos_max)
5772 TimeTaker timer1("initialEmerge", &emerge_time);
5774 // Units of these are MapBlocks
5775 v3s16 p_min = blockpos_min;
5776 v3s16 p_max = blockpos_max;
5778 VoxelArea block_area_nodes
5779 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5781 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5784 dstream<<"initialEmerge: area: ";
5785 block_area_nodes.print(dstream);
5786 dstream<<" ("<<size_MB<<"MB)";
5790 addArea(block_area_nodes);
5792 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5793 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5794 for(s32 x=p_min.X; x<=p_max.X; x++)
5797 core::map<v3s16, bool>::Node *n;
5798 n = m_loaded_blocks.find(p);
5802 bool block_data_inexistent = false;
5805 TimeTaker timer1("emerge load", &emerge_load_time);
5807 MapBlock *block = m_map->getBlockNoCreate(p);
5808 if(block->isDummy())
5809 block_data_inexistent = true;
5811 block->copyTo(*this);
5813 catch(InvalidPositionException &e)
5815 block_data_inexistent = true;
5818 if(block_data_inexistent)
5821 Mark area inexistent
5823 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5824 // Fill with VOXELFLAG_INEXISTENT
5825 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5826 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5828 s32 i = m_area.index(a.MinEdge.X,y,z);
5829 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5833 m_loaded_blocks.insert(p, !block_data_inexistent);
5837 void ManualMapVoxelManipulator::blitBackAll(
5838 core::map<v3s16, MapBlock*> * modified_blocks)
5840 if(m_area.getExtent() == v3s16(0,0,0))
5844 Copy data of all blocks
5846 for(core::map<v3s16, bool>::Iterator
5847 i = m_loaded_blocks.getIterator();
5848 i.atEnd() == false; i++)
5850 bool existed = i.getNode()->getValue();
5851 if(existed == false)
5853 v3s16 p = i.getNode()->getKey();
5854 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5857 dstream<<"WARNING: "<<__FUNCTION_NAME
5858 <<": got NULL block "
5859 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5864 block->copyFrom(*this);
5867 modified_blocks->insert(p, block);