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::addNodeAndUpdate(v3s16 p, MapNode n,
864 core::map<v3s16, MapBlock*> &modified_blocks)
867 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
868 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
871 From this node to nodes underneath:
872 If lighting is sunlight (1.0), unlight neighbours and
877 v3s16 toppos = p + v3s16(0,1,0);
878 v3s16 bottompos = p + v3s16(0,-1,0);
880 bool node_under_sunlight = true;
881 core::map<v3s16, bool> light_sources;
884 If there is a node at top and it doesn't have sunlight,
885 there has not been any sunlight going down.
887 Otherwise there probably is.
890 MapNode topnode = getNode(toppos);
892 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
893 node_under_sunlight = false;
895 catch(InvalidPositionException &e)
900 If the new node doesn't propagate sunlight and there is
901 grass below, change it to mud
903 if(content_features(n.d).sunlight_propagates == false)
906 MapNode bottomnode = getNode(bottompos);
908 if(bottomnode.d == CONTENT_GRASS
909 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
911 bottomnode.d = CONTENT_MUD;
912 setNode(bottompos, bottomnode);
915 catch(InvalidPositionException &e)
921 If the new node is mud and it is under sunlight, change it
924 if(n.d == CONTENT_MUD && node_under_sunlight)
930 Remove all light that has come out of this node
933 enum LightBank banks[] =
938 for(s32 i=0; i<2; i++)
940 enum LightBank bank = banks[i];
942 u8 lightwas = getNode(p).getLight(bank);
944 // Add the block of the added node to modified_blocks
945 v3s16 blockpos = getNodeBlockPos(p);
946 MapBlock * block = getBlockNoCreate(blockpos);
947 assert(block != NULL);
948 modified_blocks.insert(blockpos, block);
950 if(isValidPosition(p) == false)
953 // Unlight neighbours of node.
954 // This means setting light of all consequent dimmer nodes
956 // This also collects the nodes at the border which will spread
957 // light again into this.
958 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
964 Set the node on the map
970 If node is under sunlight, take all sunlighted nodes under
971 it and clear light from them and from where the light has
973 TODO: This could be optimized by mass-unlighting instead
976 if(node_under_sunlight)
980 //m_dout<<DTIME<<"y="<<y<<std::endl;
981 v3s16 n2pos(p.X, y, p.Z);
987 catch(InvalidPositionException &e)
992 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
994 unLightNeighbors(LIGHTBANK_DAY,
995 n2pos, n2.getLight(LIGHTBANK_DAY),
996 light_sources, modified_blocks);
997 n2.setLight(LIGHTBANK_DAY, 0);
1005 for(s32 i=0; i<2; i++)
1007 enum LightBank bank = banks[i];
1010 Spread light from all nodes that might be capable of doing so
1012 spreadLight(bank, light_sources, modified_blocks);
1016 Update information about whether day and night light differ
1018 for(core::map<v3s16, MapBlock*>::Iterator
1019 i = modified_blocks.getIterator();
1020 i.atEnd() == false; i++)
1022 MapBlock *block = i.getNode()->getValue();
1023 block->updateDayNightDiff();
1027 Add neighboring liquid nodes and the node itself if it is
1028 liquid (=water node was added) to transform queue.
1031 v3s16(0,0,0), // self
1032 v3s16(0,0,1), // back
1033 v3s16(0,1,0), // top
1034 v3s16(1,0,0), // right
1035 v3s16(0,0,-1), // front
1036 v3s16(0,-1,0), // bottom
1037 v3s16(-1,0,0), // left
1039 for(u16 i=0; i<7; i++)
1044 v3s16 p2 = p + dirs[i];
1046 MapNode n2 = getNode(p2);
1047 if(content_liquid(n2.d))
1049 m_transforming_liquid.push_back(p2);
1052 }catch(InvalidPositionException &e)
1060 void Map::removeNodeAndUpdate(v3s16 p,
1061 core::map<v3s16, MapBlock*> &modified_blocks)
1063 /*PrintInfo(m_dout);
1064 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1065 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1067 bool node_under_sunlight = true;
1069 v3s16 toppos = p + v3s16(0,1,0);
1071 // Node will be replaced with this
1072 u8 replace_material = CONTENT_AIR;
1075 If there is a node at top and it doesn't have sunlight,
1076 there will be no sunlight going down.
1079 MapNode topnode = getNode(toppos);
1081 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1082 node_under_sunlight = false;
1084 catch(InvalidPositionException &e)
1088 core::map<v3s16, bool> light_sources;
1090 enum LightBank banks[] =
1095 for(s32 i=0; i<2; i++)
1097 enum LightBank bank = banks[i];
1100 Unlight neighbors (in case the node is a light source)
1102 unLightNeighbors(bank, p,
1103 getNode(p).getLight(bank),
1104 light_sources, modified_blocks);
1109 This also clears the lighting.
1113 n.d = replace_material;
1116 for(s32 i=0; i<2; i++)
1118 enum LightBank bank = banks[i];
1121 Recalculate lighting
1123 spreadLight(bank, light_sources, modified_blocks);
1126 // Add the block of the removed node to modified_blocks
1127 v3s16 blockpos = getNodeBlockPos(p);
1128 MapBlock * block = getBlockNoCreate(blockpos);
1129 assert(block != NULL);
1130 modified_blocks.insert(blockpos, block);
1133 If the removed node was under sunlight, propagate the
1134 sunlight down from it and then light all neighbors
1135 of the propagated blocks.
1137 if(node_under_sunlight)
1139 s16 ybottom = propagateSunlight(p, modified_blocks);
1140 /*m_dout<<DTIME<<"Node was under sunlight. "
1141 "Propagating sunlight";
1142 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1144 for(; y >= ybottom; y--)
1146 v3s16 p2(p.X, y, p.Z);
1147 /*m_dout<<DTIME<<"lighting neighbors of node ("
1148 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1150 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1155 // Set the lighting of this node to 0
1156 // TODO: Is this needed? Lighting is cleared up there already.
1158 MapNode n = getNode(p);
1159 n.setLight(LIGHTBANK_DAY, 0);
1162 catch(InvalidPositionException &e)
1168 for(s32 i=0; i<2; i++)
1170 enum LightBank bank = banks[i];
1172 // Get the brightest neighbour node and propagate light from it
1173 v3s16 n2p = getBrightestNeighbour(bank, p);
1175 MapNode n2 = getNode(n2p);
1176 lightNeighbors(bank, n2p, modified_blocks);
1178 catch(InvalidPositionException &e)
1184 Update information about whether day and night light differ
1186 for(core::map<v3s16, MapBlock*>::Iterator
1187 i = modified_blocks.getIterator();
1188 i.atEnd() == false; i++)
1190 MapBlock *block = i.getNode()->getValue();
1191 block->updateDayNightDiff();
1195 Add neighboring liquid nodes to transform queue.
1198 v3s16(0,0,1), // back
1199 v3s16(0,1,0), // top
1200 v3s16(1,0,0), // right
1201 v3s16(0,0,-1), // front
1202 v3s16(0,-1,0), // bottom
1203 v3s16(-1,0,0), // left
1205 for(u16 i=0; i<6; i++)
1210 v3s16 p2 = p + dirs[i];
1212 MapNode n2 = getNode(p2);
1213 if(content_liquid(n2.d))
1215 m_transforming_liquid.push_back(p2);
1218 }catch(InvalidPositionException &e)
1225 void Map::expireMeshes(bool only_daynight_diffed)
1227 TimeTaker timer("expireMeshes()");
1229 core::map<v2s16, MapSector*>::Iterator si;
1230 si = m_sectors.getIterator();
1231 for(; si.atEnd() == false; si++)
1233 MapSector *sector = si.getNode()->getValue();
1235 core::list< MapBlock * > sectorblocks;
1236 sector->getBlocks(sectorblocks);
1238 core::list< MapBlock * >::Iterator i;
1239 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1241 MapBlock *block = *i;
1243 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1249 JMutexAutoLock lock(block->mesh_mutex);
1250 if(block->mesh != NULL)
1252 /*block->mesh->drop();
1253 block->mesh = NULL;*/
1254 block->setMeshExpired(true);
1261 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1263 assert(mapType() == MAPTYPE_CLIENT);
1266 v3s16 p = blockpos + v3s16(0,0,0);
1267 MapBlock *b = getBlockNoCreate(p);
1268 b->updateMesh(daynight_ratio);
1270 catch(InvalidPositionException &e){}
1273 v3s16 p = blockpos + v3s16(-1,0,0);
1274 MapBlock *b = getBlockNoCreate(p);
1275 b->updateMesh(daynight_ratio);
1277 catch(InvalidPositionException &e){}
1279 v3s16 p = blockpos + v3s16(0,-1,0);
1280 MapBlock *b = getBlockNoCreate(p);
1281 b->updateMesh(daynight_ratio);
1283 catch(InvalidPositionException &e){}
1285 v3s16 p = blockpos + v3s16(0,0,-1);
1286 MapBlock *b = getBlockNoCreate(p);
1287 b->updateMesh(daynight_ratio);
1289 catch(InvalidPositionException &e){}
1292 v3s16 p = blockpos + v3s16(1,0,0);
1293 MapBlock *b = getBlockNoCreate(p);
1294 b->updateMesh(daynight_ratio);
1296 catch(InvalidPositionException &e){}
1298 v3s16 p = blockpos + v3s16(0,1,0);
1299 MapBlock *b = getBlockNoCreate(p);
1300 b->updateMesh(daynight_ratio);
1302 catch(InvalidPositionException &e){}
1304 v3s16 p = blockpos + v3s16(0,0,1);
1305 MapBlock *b = getBlockNoCreate(p);
1306 b->updateMesh(daynight_ratio);
1308 catch(InvalidPositionException &e){}*/
1313 bool Map::dayNightDiffed(v3s16 blockpos)
1316 v3s16 p = blockpos + v3s16(0,0,0);
1317 MapBlock *b = getBlockNoCreate(p);
1318 if(b->dayNightDiffed())
1321 catch(InvalidPositionException &e){}
1324 v3s16 p = blockpos + v3s16(-1,0,0);
1325 MapBlock *b = getBlockNoCreate(p);
1326 if(b->dayNightDiffed())
1329 catch(InvalidPositionException &e){}
1331 v3s16 p = blockpos + v3s16(0,-1,0);
1332 MapBlock *b = getBlockNoCreate(p);
1333 if(b->dayNightDiffed())
1336 catch(InvalidPositionException &e){}
1338 v3s16 p = blockpos + v3s16(0,0,-1);
1339 MapBlock *b = getBlockNoCreate(p);
1340 if(b->dayNightDiffed())
1343 catch(InvalidPositionException &e){}
1346 v3s16 p = blockpos + v3s16(1,0,0);
1347 MapBlock *b = getBlockNoCreate(p);
1348 if(b->dayNightDiffed())
1351 catch(InvalidPositionException &e){}
1353 v3s16 p = blockpos + v3s16(0,1,0);
1354 MapBlock *b = getBlockNoCreate(p);
1355 if(b->dayNightDiffed())
1358 catch(InvalidPositionException &e){}
1360 v3s16 p = blockpos + v3s16(0,0,1);
1361 MapBlock *b = getBlockNoCreate(p);
1362 if(b->dayNightDiffed())
1365 catch(InvalidPositionException &e){}
1371 Updates usage timers
1373 void Map::timerUpdate(float dtime)
1375 JMutexAutoLock lock(m_sector_mutex);
1377 core::map<v2s16, MapSector*>::Iterator si;
1379 si = m_sectors.getIterator();
1380 for(; si.atEnd() == false; si++)
1382 MapSector *sector = si.getNode()->getValue();
1383 sector->usage_timer += dtime;
1387 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1390 Wait for caches to be removed before continuing.
1392 This disables the existence of caches while locked
1394 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1396 core::list<v2s16>::Iterator j;
1397 for(j=list.begin(); j!=list.end(); j++)
1399 MapSector *sector = m_sectors[*j];
1402 sector->deleteBlocks();
1407 If sector is in sector cache, remove it from there
1409 if(m_sector_cache == sector)
1411 m_sector_cache = NULL;
1414 Remove from map and delete
1416 m_sectors.remove(*j);
1422 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1423 core::list<v3s16> *deleted_blocks)
1425 JMutexAutoLock lock(m_sector_mutex);
1427 core::list<v2s16> sector_deletion_queue;
1428 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1429 for(; i.atEnd() == false; i++)
1431 MapSector *sector = i.getNode()->getValue();
1433 Delete sector from memory if it hasn't been used in a long time
1435 if(sector->usage_timer > timeout)
1437 sector_deletion_queue.push_back(i.getNode()->getKey());
1439 if(deleted_blocks != NULL)
1441 // Collect positions of blocks of sector
1442 MapSector *sector = i.getNode()->getValue();
1443 core::list<MapBlock*> blocks;
1444 sector->getBlocks(blocks);
1445 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1446 i != blocks.end(); i++)
1448 deleted_blocks->push_back((*i)->getPos());
1453 deleteSectors(sector_deletion_queue, only_blocks);
1454 return sector_deletion_queue.getSize();
1457 void Map::PrintInfo(std::ostream &out)
1462 #define WATER_DROP_BOOST 4
1464 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1466 DSTACK(__FUNCTION_NAME);
1467 //TimeTaker timer("transformLiquids()");
1470 u32 initial_size = m_transforming_liquid.size();
1472 /*if(initial_size != 0)
1473 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1475 while(m_transforming_liquid.size() != 0)
1478 Get a queued transforming liquid node
1480 v3s16 p0 = m_transforming_liquid.pop_front();
1482 MapNode n0 = getNode(p0);
1484 // Don't deal with non-liquids
1485 if(content_liquid(n0.d) == false)
1488 bool is_source = !content_flowing_liquid(n0.d);
1490 u8 liquid_level = 8;
1491 if(is_source == false)
1492 liquid_level = n0.param2 & 0x0f;
1494 // Turn possible source into non-source
1495 u8 nonsource_c = make_liquid_flowing(n0.d);
1498 If not source, check that some node flows into this one
1499 and what is the level of liquid in this one
1501 if(is_source == false)
1503 s8 new_liquid_level_max = -1;
1505 v3s16 dirs_from[5] = {
1506 v3s16(0,1,0), // top
1507 v3s16(0,0,1), // back
1508 v3s16(1,0,0), // right
1509 v3s16(0,0,-1), // front
1510 v3s16(-1,0,0), // left
1512 for(u16 i=0; i<5; i++)
1517 bool from_top = (i==0);
1519 v3s16 p2 = p0 + dirs_from[i];
1520 MapNode n2 = getNode(p2);
1522 if(content_liquid(n2.d))
1524 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1525 // Check that the liquids are the same type
1526 if(n2_nonsource_c != nonsource_c)
1528 dstream<<"WARNING: Not handling: different liquids"
1529 " collide"<<std::endl;
1532 bool n2_is_source = !content_flowing_liquid(n2.d);
1533 s8 n2_liquid_level = 8;
1534 if(n2_is_source == false)
1535 n2_liquid_level = n2.param2 & 0x07;
1537 s8 new_liquid_level = -1;
1540 //new_liquid_level = 7;
1541 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1542 new_liquid_level = 7;
1544 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1546 else if(n2_liquid_level > 0)
1548 new_liquid_level = n2_liquid_level - 1;
1551 if(new_liquid_level > new_liquid_level_max)
1552 new_liquid_level_max = new_liquid_level;
1555 }catch(InvalidPositionException &e)
1561 If liquid level should be something else, update it and
1562 add all the neighboring water nodes to the transform queue.
1564 if(new_liquid_level_max != liquid_level)
1566 if(new_liquid_level_max == -1)
1568 // Remove water alltoghether
1575 n0.param2 = new_liquid_level_max;
1579 // Block has been modified
1581 v3s16 blockpos = getNodeBlockPos(p0);
1582 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1584 modified_blocks.insert(blockpos, block);
1588 Add neighboring non-source liquid nodes to transform queue.
1591 v3s16(0,0,1), // back
1592 v3s16(0,1,0), // top
1593 v3s16(1,0,0), // right
1594 v3s16(0,0,-1), // front
1595 v3s16(0,-1,0), // bottom
1596 v3s16(-1,0,0), // left
1598 for(u16 i=0; i<6; i++)
1603 v3s16 p2 = p0 + dirs[i];
1605 MapNode n2 = getNode(p2);
1606 if(content_flowing_liquid(n2.d))
1608 m_transforming_liquid.push_back(p2);
1611 }catch(InvalidPositionException &e)
1618 // Get a new one from queue if the node has turned into non-water
1619 if(content_liquid(n0.d) == false)
1623 Flow water from this node
1625 v3s16 dirs_to[5] = {
1626 v3s16(0,-1,0), // bottom
1627 v3s16(0,0,1), // back
1628 v3s16(1,0,0), // right
1629 v3s16(0,0,-1), // front
1630 v3s16(-1,0,0), // left
1632 for(u16 i=0; i<5; i++)
1637 bool to_bottom = (i == 0);
1639 // If liquid is at lowest possible height, it's not going
1640 // anywhere except down
1641 if(liquid_level == 0 && to_bottom == false)
1644 u8 liquid_next_level = 0;
1645 // If going to bottom
1648 //liquid_next_level = 7;
1649 if(liquid_level >= 7 - WATER_DROP_BOOST)
1650 liquid_next_level = 7;
1652 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1655 liquid_next_level = liquid_level - 1;
1657 bool n2_changed = false;
1658 bool flowed = false;
1660 v3s16 p2 = p0 + dirs_to[i];
1662 MapNode n2 = getNode(p2);
1663 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1665 if(content_liquid(n2.d))
1667 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1668 // Check that the liquids are the same type
1669 if(n2_nonsource_c != nonsource_c)
1671 dstream<<"WARNING: Not handling: different liquids"
1672 " collide"<<std::endl;
1675 bool n2_is_source = !content_flowing_liquid(n2.d);
1676 u8 n2_liquid_level = 8;
1677 if(n2_is_source == false)
1678 n2_liquid_level = n2.param2 & 0x07;
1687 // Just flow into the source, nothing changes.
1688 // n2_changed is not set because destination didn't change
1693 if(liquid_next_level > liquid_level)
1695 n2.param2 = liquid_next_level;
1703 else if(n2.d == CONTENT_AIR)
1706 n2.param2 = liquid_next_level;
1713 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1717 m_transforming_liquid.push_back(p2);
1719 v3s16 blockpos = getNodeBlockPos(p2);
1720 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1722 modified_blocks.insert(blockpos, block);
1725 // If n2_changed to bottom, don't flow anywhere else
1726 if(to_bottom && flowed && !is_source)
1729 }catch(InvalidPositionException &e)
1735 //if(loopcount >= 100000)
1736 if(loopcount >= initial_size * 1)
1739 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1746 ServerMap::ServerMap(std::string savedir):
1752 //m_chunksize = 16; // Too slow
1753 m_chunksize = 8; // Takes a few seconds
1757 // TODO: Save to and load from a file
1758 m_seed = (((u64)(myrand()%0xffff)<<0)
1759 + ((u64)(myrand()%0xffff)<<16)
1760 + ((u64)(myrand()%0xffff)<<32)
1761 + ((u64)(myrand()%0xffff)<<48));
1764 Experimental and debug stuff
1771 Try to load map; if not found, create a new one.
1774 m_savedir = savedir;
1775 m_map_saving_enabled = false;
1779 // If directory exists, check contents and load if possible
1780 if(fs::PathExists(m_savedir))
1782 // If directory is empty, it is safe to save into it.
1783 if(fs::GetDirListing(m_savedir).size() == 0)
1785 dstream<<DTIME<<"Server: Empty save directory is valid."
1787 m_map_saving_enabled = true;
1791 // Load map metadata (seed, chunksize)
1794 // Load chunk metadata
1797 /*// Load sector (0,0) and throw and exception on fail
1798 if(loadSectorFull(v2s16(0,0)) == false)
1799 throw LoadError("Failed to load sector (0,0)");*/
1801 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1802 "metadata and sector (0,0) from "<<savedir<<
1803 ", assuming valid save directory."
1806 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1807 <<"and chunk metadata from "<<savedir
1808 <<", assuming valid save directory."
1811 m_map_saving_enabled = true;
1812 // Map loaded, not creating new one
1816 // If directory doesn't exist, it is safe to save to it
1818 m_map_saving_enabled = true;
1821 catch(std::exception &e)
1823 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1824 <<", exception: "<<e.what()<<std::endl;
1825 dstream<<"Please remove the map or fix it."<<std::endl;
1826 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1829 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1831 // Create zero sector
1832 emergeSector(v2s16(0,0));
1834 // Initially write whole map
1838 ServerMap::~ServerMap()
1842 if(m_map_saving_enabled)
1845 // Save only changed parts
1847 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1851 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1854 catch(std::exception &e)
1856 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1857 <<", exception: "<<e.what()<<std::endl;
1863 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1864 for(; i.atEnd() == false; i++)
1866 MapChunk *chunk = i.getNode()->getValue();
1872 Some helper functions for the map generator
1875 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1877 v3s16 em = vmanip.m_area.getExtent();
1878 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1879 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1880 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1882 for(y=y_nodes_max; y>=y_nodes_min; y--)
1884 MapNode &n = vmanip.m_data[i];
1885 if(content_walkable(n.d))
1888 vmanip.m_area.add_y(em, i, -1);
1890 if(y >= y_nodes_min)
1896 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1898 v3s16 em = vmanip.m_area.getExtent();
1899 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1900 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1901 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1903 for(y=y_nodes_max; y>=y_nodes_min; y--)
1905 MapNode &n = vmanip.m_data[i];
1906 if(content_walkable(n.d)
1907 && n.d != CONTENT_TREE
1908 && n.d != CONTENT_LEAVES)
1911 vmanip.m_area.add_y(em, i, -1);
1913 if(y >= y_nodes_min)
1919 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1921 MapNode treenode(CONTENT_TREE);
1922 MapNode leavesnode(CONTENT_LEAVES);
1924 s16 trunk_h = myrand_range(3, 6);
1926 for(s16 ii=0; ii<trunk_h; ii++)
1928 if(vmanip.m_area.contains(p1))
1929 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1933 // p1 is now the last piece of the trunk
1936 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1937 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1938 Buffer<u8> leaves_d(leaves_a.getVolume());
1939 for(s32 i=0; i<leaves_a.getVolume(); i++)
1942 // Force leaves at near the end of the trunk
1945 for(s16 z=-d; z<=d; z++)
1946 for(s16 y=-d; y<=d; y++)
1947 for(s16 x=-d; x<=d; x++)
1949 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1953 // Add leaves randomly
1954 for(u32 iii=0; iii<7; iii++)
1959 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1960 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1961 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1964 for(s16 z=0; z<=d; z++)
1965 for(s16 y=0; y<=d; y++)
1966 for(s16 x=0; x<=d; x++)
1968 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1972 // Blit leaves to vmanip
1973 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1974 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1975 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1979 if(vmanip.m_area.contains(p) == false)
1981 u32 vi = vmanip.m_area.index(p);
1982 if(vmanip.m_data[vi].d != CONTENT_AIR)
1984 u32 i = leaves_a.index(x,y,z);
1985 if(leaves_d[i] == 1)
1986 vmanip.m_data[vi] = leavesnode;
1991 Noise functions. Make sure seed is mangled differently in each one.
1994 // Amount of trees per area in nodes
1995 double tree_amount_2d(u64 seed, v2s16 p)
1997 double noise = noise2d_perlin(
1998 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2000 double zeroval = -0.3;
2004 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2007 /*double base_rock_level_2d(u64 seed, v2s16 p)
2009 return WATER_LEVEL - 6.0 + 25. * noise2d_perlin(
2010 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2014 /*double highlands_level_2d(u64 seed, v2s16 p)
2016 double a = noise2d_perlin(
2017 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2022 return WATER_LEVEL + 25;
2023 return WATER_LEVEL + 55. * noise2d_perlin(
2024 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2025 seed+85039, 6, 0.69);
2031 double base_rock_level_2d(u64 seed, v2s16 p)
2033 // The base ground level
2034 double base = (double)WATER_LEVEL - 4.0 + 25. * noise2d_perlin(
2035 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2036 (seed>>32)+654879876, 6, 0.6);
2038 /*// A bit hillier one
2039 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2040 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2041 (seed>>27)+90340, 6, 0.69);
2045 // Higher ground level
2046 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2047 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2048 seed+85039, 5, 0.69);
2049 //higher = 30; // For debugging
2051 // Limit higher to at least base
2055 // Steepness factor of cliffs
2056 double b = 1.0 + 1.0 * noise2d_perlin(
2057 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2059 b = rangelim(b, 0.0, 1000.0);
2062 b = rangelim(b, 3.0, 1000.0);
2063 //dstream<<"b="<<b<<std::endl;
2066 // Offset to more low
2067 double a_off = -0.2;
2068 // High/low selector
2069 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2070 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2071 seed-359, 6, 0.7));*/
2072 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2073 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2074 seed-359, 5, 0.60));
2076 a = rangelim(a, 0.0, 1.0);
2078 //dstream<<"a="<<a<<std::endl;
2080 double h = base*(1.0-a) + higher*a;
2087 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2090 This is the main map generation method
2093 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2094 core::map<v3s16, MapBlock*> &changed_blocks,
2097 DSTACK(__FUNCTION_NAME);
2100 Don't generate if already fully generated
2104 MapChunk *chunk = getChunk(chunkpos);
2105 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2107 dstream<<"generateChunkRaw(): Chunk "
2108 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2109 <<" already generated"<<std::endl;
2114 dstream<<"generateChunkRaw(): Generating chunk "
2115 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2118 TimeTaker timer("generateChunkRaw()");
2120 // The distance how far into the neighbors the generator is allowed to go.
2121 s16 max_spread_amount_sectors = 2;
2122 assert(max_spread_amount_sectors <= m_chunksize);
2123 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2125 // Minimum amount of space left on sides for mud to fall in
2126 //s16 min_mud_fall_space = 2;
2128 // Maximum diameter of stone obstacles in X and Z
2129 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2130 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2132 s16 y_blocks_min = -4;
2133 s16 y_blocks_max = 3;
2134 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2135 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2136 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2138 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2139 s16 sectorpos_base_size = m_chunksize;
2141 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2142 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2143 v2s16 sectorpos_bigbase =
2144 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2145 s16 sectorpos_bigbase_size =
2146 sectorpos_base_size + 2 * max_spread_amount_sectors;
2148 v3s16 bigarea_blocks_min(
2149 sectorpos_bigbase.X,
2154 v3s16 bigarea_blocks_max(
2155 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2157 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2160 // Relative values to control amount of stuff in one chunk
2161 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2162 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2163 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2164 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2165 *(u32)h_blocks*MAP_BLOCKSIZE;
2168 The limiting edges of the lighting update, inclusive.
2170 s16 lighting_min_d = 0-max_spread_amount;
2171 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2174 Create the whole area of this and the neighboring chunks
2177 TimeTaker timer("generateChunkRaw() create area");
2179 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2180 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2182 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2183 ServerMapSector *sector = createSector(sectorpos);
2186 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2188 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2189 MapBlock *block = createBlock(blockpos);
2191 // Lighting won't be calculated
2192 //block->setLightingExpired(true);
2193 // Lighting will be calculated
2194 block->setLightingExpired(false);
2197 Block gets sunlight if this is true.
2199 This should be set to true when the top side of a block
2200 is completely exposed to the sky.
2202 Actually this doesn't matter now because the
2203 initial lighting is done here.
2205 block->setIsUnderground(y != y_blocks_max);
2211 Now we have a big empty area.
2213 Make a ManualMapVoxelManipulator that contains this and the
2217 ManualMapVoxelManipulator vmanip(this);
2218 // Add the area we just generated
2220 TimeTaker timer("generateChunkRaw() initialEmerge");
2221 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2225 vmanip.clearFlag(0xff);
2227 TimeTaker timer_generate("generateChunkRaw() generate");
2229 // Maximum height of the stone surface and obstacles.
2230 // This is used to disable dungeon generation from going too high.
2231 s16 stone_surface_max_y = 0;
2234 Generate general ground level to full area
2239 //TimeTaker timer1("ground level");
2241 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2242 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2245 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2248 Skip of already generated
2251 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2252 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2256 // Ground height at this point
2257 float surface_y_f = 0.0;
2259 // Use perlin noise for ground height
2260 surface_y_f = base_rock_level_2d(m_seed, p2d);
2262 /*// Experimental stuff
2264 float a = highlands_level_2d(m_seed, p2d);
2269 // Convert to integer
2270 s16 surface_y = (s16)surface_y_f;
2273 if(surface_y > stone_surface_max_y)
2274 stone_surface_max_y = surface_y;
2277 Fill ground with stone
2280 // Use fast index incrementing
2281 v3s16 em = vmanip.m_area.getExtent();
2282 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2283 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2285 vmanip.m_data[i].d = CONTENT_STONE;
2287 vmanip.m_area.add_y(em, i, 1);
2295 Randomize some parameters
2298 s32 stone_obstacle_count = 0;
2299 /*s32 stone_obstacle_count =
2300 rangelim((1.0+noise2d(m_seed+897,
2301 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2303 s16 stone_obstacle_max_height = 0;
2304 /*s16 stone_obstacle_max_height =
2305 rangelim((1.0+noise2d(m_seed+5902,
2306 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2309 Loop this part, it will make stuff look older and newer nicely
2311 //for(u32 i_age=0; i_age<1; i_age++)
2312 for(u32 i_age=0; i_age<2; i_age++)
2317 //TimeTaker timer1("stone obstacles");
2320 Add some random stone obstacles
2323 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2325 // Randomize max height so usually stuff will be quite low
2326 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2328 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2329 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2332 myrand_range(5, stone_obstacle_max_size),
2333 myrand_range(0, maxheight_randomized),
2334 myrand_range(5, stone_obstacle_max_size)
2337 // Don't make stupid small rectangle bumps
2342 myrand_range(1+ob_size.X/2+2,
2343 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2344 myrand_range(1+ob_size.Z/2+2,
2345 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2348 // Minimum space left on top of the obstacle
2349 s16 min_head_space = 12;
2351 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2352 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2354 // Node position in 2d
2355 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2357 // Find stone ground level
2358 // (ignore everything else than mud in already generated chunks)
2359 // and mud amount over the stone level
2363 v3s16 em = vmanip.m_area.getExtent();
2364 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2366 // Go to ground level
2367 for(y=y_nodes_max; y>=y_nodes_min; y--)
2369 MapNode *n = &vmanip.m_data[i];
2370 /*if(content_walkable(n.d)
2371 && n.d != CONTENT_MUD
2372 && n.d != CONTENT_GRASS)
2374 if(n->d == CONTENT_STONE)
2377 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2381 Change to mud because otherwise we might
2382 be throwing mud on grass at the next
2388 vmanip.m_area.add_y(em, i, -1);
2390 if(y >= y_nodes_min)
2393 surface_y = y_nodes_min;
2401 v3s16 em = vmanip.m_area.getExtent();
2402 s16 y_start = surface_y+1;
2403 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2407 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2409 MapNode &n = vmanip.m_data[i];
2410 n.d = CONTENT_STONE;
2412 if(y > stone_surface_max_y)
2413 stone_surface_max_y = y;
2416 if(count >= ob_size.Y)
2419 vmanip.m_area.add_y(em, i, 1);
2423 for(; y<=y_nodes_max - min_head_space; y++)
2425 MapNode &n = vmanip.m_data[i];
2428 if(count >= mud_amount)
2431 vmanip.m_area.add_y(em, i, 1);
2441 //TimeTaker timer1("dungeons");
2446 u32 dungeons_count = relative_volume / 600000;
2447 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2448 if(stone_surface_max_y < WATER_LEVEL)
2450 /*u32 dungeons_count = 0;
2451 u32 bruises_count = 0;*/
2452 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2454 s16 min_tunnel_diameter = 2;
2455 s16 max_tunnel_diameter = 6;
2456 u16 tunnel_routepoints = 25;
2458 bool bruise_surface = (jj < bruises_count);
2462 min_tunnel_diameter = 5;
2463 max_tunnel_diameter = myrand_range(10, 20);
2464 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2465 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2467 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2468 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2470 tunnel_routepoints = 5;
2473 // Allowed route area size in nodes
2475 sectorpos_base_size*MAP_BLOCKSIZE,
2476 h_blocks*MAP_BLOCKSIZE,
2477 sectorpos_base_size*MAP_BLOCKSIZE
2480 // Area starting point in nodes
2482 sectorpos_base.X*MAP_BLOCKSIZE,
2483 y_blocks_min*MAP_BLOCKSIZE,
2484 sectorpos_base.Y*MAP_BLOCKSIZE
2488 //(this should be more than the maximum radius of the tunnel)
2489 //s16 insure = 5; // Didn't work with max_d = 20
2491 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2492 ar += v3s16(1,0,1) * more * 2;
2493 of -= v3s16(1,0,1) * more;
2495 s16 route_y_min = 0;
2496 // Allow half a diameter + 7 over stone surface
2497 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2499 /*// If dungeons, don't go through surface too often
2500 if(bruise_surface == false)
2501 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2503 // Limit maximum to area
2504 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2508 /*// Minimum is at y=0
2509 route_y_min = -of.Y - 0;*/
2510 // Minimum is at y=max_tunnel_diameter/4
2511 //route_y_min = -of.Y + max_tunnel_diameter/4;
2512 //s16 min = -of.Y + max_tunnel_diameter/4;
2513 s16 min = -of.Y + 0;
2514 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2515 route_y_min = rangelim(route_y_min, 0, route_y_max);
2518 /*dstream<<"route_y_min = "<<route_y_min
2519 <<", route_y_max = "<<route_y_max<<std::endl;*/
2521 s16 route_start_y_min = route_y_min;
2522 s16 route_start_y_max = route_y_max;
2524 // Start every 2nd dungeon from surface
2525 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2527 if(coming_from_surface)
2529 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2532 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2533 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2535 // Randomize starting position
2537 (float)(myrand()%ar.X)+0.5,
2538 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2539 (float)(myrand()%ar.Z)+0.5
2542 MapNode airnode(CONTENT_AIR);
2545 Generate some tunnel starting from orp
2548 for(u16 j=0; j<tunnel_routepoints; j++)
2551 s16 min_d = min_tunnel_diameter;
2552 s16 max_d = max_tunnel_diameter;
2553 s16 rs = myrand_range(min_d, max_d);
2558 maxlen = v3s16(rs*7,rs*7,rs*7);
2562 maxlen = v3s16(15, myrand_range(1, 20), 15);
2567 if(coming_from_surface && j < 3)
2570 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2571 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2572 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2578 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2579 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2580 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2587 else if(rp.X >= ar.X)
2589 if(rp.Y < route_y_min)
2591 else if(rp.Y >= route_y_max)
2592 rp.Y = route_y_max-1;
2595 else if(rp.Z >= ar.Z)
2599 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2601 v3f fp = orp + vec * f;
2602 v3s16 cp(fp.X, fp.Y, fp.Z);
2605 s16 d1 = d0 + rs - 1;
2606 for(s16 z0=d0; z0<=d1; z0++)
2608 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2609 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2610 for(s16 x0=-si; x0<=si-1; x0++)
2612 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2613 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2614 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2615 //s16 si2 = rs - abs(x0);
2616 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2622 /*if(isInArea(p, ar) == false)
2624 // Check only height
2625 if(y < 0 || y >= ar.Y)
2629 //assert(vmanip.m_area.contains(p));
2630 if(vmanip.m_area.contains(p) == false)
2632 dstream<<"WARNING: "<<__FUNCTION_NAME
2633 <<":"<<__LINE__<<": "
2634 <<"point not in area"
2639 // Just set it to air, it will be changed to
2641 u32 i = vmanip.m_area.index(p);
2642 vmanip.m_data[i] = airnode;
2644 if(bruise_surface == false)
2647 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2662 //TimeTaker timer1("ore veins");
2667 for(u32 jj=0; jj<relative_volume/1000; jj++)
2669 s16 max_vein_diameter = 3;
2671 // Allowed route area size in nodes
2673 sectorpos_base_size*MAP_BLOCKSIZE,
2674 h_blocks*MAP_BLOCKSIZE,
2675 sectorpos_base_size*MAP_BLOCKSIZE
2678 // Area starting point in nodes
2680 sectorpos_base.X*MAP_BLOCKSIZE,
2681 y_blocks_min*MAP_BLOCKSIZE,
2682 sectorpos_base.Y*MAP_BLOCKSIZE
2686 //(this should be more than the maximum radius of the tunnel)
2688 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2689 ar += v3s16(1,0,1) * more * 2;
2690 of -= v3s16(1,0,1) * more;
2692 // Randomize starting position
2694 (float)(myrand()%ar.X)+0.5,
2695 (float)(myrand()%ar.Y)+0.5,
2696 (float)(myrand()%ar.Z)+0.5
2699 // Randomize mineral
2702 mineral = MINERAL_COAL;
2704 mineral = MINERAL_IRON;
2707 Generate some vein starting from orp
2710 for(u16 j=0; j<2; j++)
2713 (float)(myrand()%ar.X)+0.5,
2714 (float)(myrand()%ar.Y)+0.5,
2715 (float)(myrand()%ar.Z)+0.5
2717 v3f vec = rp - orp;*/
2719 v3s16 maxlen(5, 5, 5);
2721 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2722 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2723 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2728 else if(rp.X >= ar.X)
2732 else if(rp.Y >= ar.Y)
2736 else if(rp.Z >= ar.Z)
2742 s16 max_d = max_vein_diameter;
2743 s16 rs = myrand_range(min_d, max_d);
2745 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2747 v3f fp = orp + vec * f;
2748 v3s16 cp(fp.X, fp.Y, fp.Z);
2750 s16 d1 = d0 + rs - 1;
2751 for(s16 z0=d0; z0<=d1; z0++)
2753 s16 si = rs - abs(z0);
2754 for(s16 x0=-si; x0<=si-1; x0++)
2756 s16 si2 = rs - abs(x0);
2757 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2759 // Don't put mineral to every place
2767 /*if(isInArea(p, ar) == false)
2769 // Check only height
2770 if(y < 0 || y >= ar.Y)
2774 assert(vmanip.m_area.contains(p));
2776 // Just set it to air, it will be changed to
2778 u32 i = vmanip.m_area.index(p);
2779 MapNode *n = &vmanip.m_data[i];
2780 if(n->d == CONTENT_STONE)
2795 //TimeTaker timer1("add mud");
2798 Add mud to the central chunk
2801 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2802 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2804 // Node position in 2d
2805 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2807 // Randomize mud amount
2808 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2809 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2810 m_seed+1, 3, 0.55));
2812 // Find ground level
2813 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2816 If topmost node is grass, change it to mud.
2817 It might be if it was flown to there from a neighboring
2818 chunk and then converted.
2821 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2822 MapNode *n = &vmanip.m_data[i];
2823 if(n->d == CONTENT_GRASS)
2832 v3s16 em = vmanip.m_area.getExtent();
2833 s16 y_start = surface_y+1;
2834 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2835 for(s16 y=y_start; y<=y_nodes_max; y++)
2837 if(mudcount >= mud_add_amount)
2840 MapNode &n = vmanip.m_data[i];
2844 vmanip.m_area.add_y(em, i, 1);
2853 TimeTaker timer1("flow mud");
2856 Flow mud away from steep edges
2859 // Limit area by 1 because mud is flown into neighbors.
2860 s16 mudflow_minpos = 0-max_spread_amount+1;
2861 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2863 // Iterate a few times
2864 for(s16 k=0; k<3; k++)
2867 for(s16 x=mudflow_minpos;
2870 for(s16 z=mudflow_minpos;
2874 // Invert coordinates every 2nd iteration
2877 x = mudflow_maxpos - (x-mudflow_minpos);
2878 z = mudflow_maxpos - (z-mudflow_minpos);
2881 // Node position in 2d
2882 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2884 v3s16 em = vmanip.m_area.getExtent();
2885 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2892 for(; y>=y_nodes_min; y--)
2894 n = &vmanip.m_data[i];
2895 //if(content_walkable(n->d))
2897 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2900 vmanip.m_area.add_y(em, i, -1);
2903 // Stop if out of area
2904 //if(vmanip.m_area.contains(i) == false)
2908 /*// If not mud, do nothing to it
2909 MapNode *n = &vmanip.m_data[i];
2910 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2914 Don't flow it if the stuff under it is not mud
2918 vmanip.m_area.add_y(em, i2, -1);
2919 // Cancel if out of area
2920 if(vmanip.m_area.contains(i2) == false)
2922 MapNode *n2 = &vmanip.m_data[i2];
2923 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2927 // Make it exactly mud
2930 /*s16 recurse_count = 0;
2934 v3s16(0,0,1), // back
2935 v3s16(1,0,0), // right
2936 v3s16(0,0,-1), // front
2937 v3s16(-1,0,0), // left
2940 // Theck that upper is air or doesn't exist.
2941 // Cancel dropping if upper keeps it in place
2943 vmanip.m_area.add_y(em, i3, 1);
2944 if(vmanip.m_area.contains(i3) == true
2945 && content_walkable(vmanip.m_data[i3].d) == true)
2952 for(u32 di=0; di<4; di++)
2954 v3s16 dirp = dirs4[di];
2957 vmanip.m_area.add_p(em, i2, dirp);
2958 // Fail if out of area
2959 if(vmanip.m_area.contains(i2) == false)
2961 // Check that side is air
2962 MapNode *n2 = &vmanip.m_data[i2];
2963 if(content_walkable(n2->d))
2965 // Check that under side is air
2966 vmanip.m_area.add_y(em, i2, -1);
2967 if(vmanip.m_area.contains(i2) == false)
2969 n2 = &vmanip.m_data[i2];
2970 if(content_walkable(n2->d))
2972 /*// Check that under that is air (need a drop of 2)
2973 vmanip.m_area.add_y(em, i2, -1);
2974 if(vmanip.m_area.contains(i2) == false)
2976 n2 = &vmanip.m_data[i2];
2977 if(content_walkable(n2->d))
2979 // Loop further down until not air
2981 vmanip.m_area.add_y(em, i2, -1);
2982 // Fail if out of area
2983 if(vmanip.m_area.contains(i2) == false)
2985 n2 = &vmanip.m_data[i2];
2986 }while(content_walkable(n2->d) == false);
2987 // Loop one up so that we're in air
2988 vmanip.m_area.add_y(em, i2, 1);
2989 n2 = &vmanip.m_data[i2];
2991 // Move mud to new place
2993 // Set old place to be air
2994 *n = MapNode(CONTENT_AIR);
3007 //TimeTaker timer1("add water");
3010 Add water to the central chunk (and a bit more)
3013 for(s16 x=0-max_spread_amount;
3014 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3016 for(s16 z=0-max_spread_amount;
3017 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3020 // Node position in 2d
3021 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3023 // Find ground level
3024 //s16 surface_y = find_ground_level(vmanip, p2d);
3027 If ground level is over water level, skip.
3028 NOTE: This leaves caves near water without water,
3029 which looks especially crappy when the nearby water
3030 won't start flowing either for some reason
3032 /*if(surface_y > WATER_LEVEL)
3039 v3s16 em = vmanip.m_area.getExtent();
3040 u8 light = LIGHT_MAX;
3041 // Start at global water surface level
3042 s16 y_start = WATER_LEVEL;
3043 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3044 MapNode *n = &vmanip.m_data[i];
3046 /*// Add first one to transforming liquid queue, if water
3047 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3049 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3050 m_transforming_liquid.push_back(p);
3053 for(s16 y=y_start; y>=y_nodes_min; y--)
3055 n = &vmanip.m_data[i];
3057 // Stop when there is no water and no air
3058 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3059 && n->d != CONTENT_WATER)
3061 /*// Add bottom one to transforming liquid queue
3062 vmanip.m_area.add_y(em, i, 1);
3063 n = &vmanip.m_data[i];
3064 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3066 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3067 m_transforming_liquid.push_back(p);
3073 // Make water only not in dungeons
3074 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3076 n->d = CONTENT_WATERSOURCE;
3077 //n->setLight(LIGHTBANK_DAY, light);
3079 // Add to transforming liquid queue (in case it'd
3081 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3082 m_transforming_liquid.push_back(p);
3086 vmanip.m_area.add_y(em, i, -1);
3099 //TimeTaker timer1("convert mud to sand");
3105 //s16 mud_add_amount = myrand_range(2, 4);
3106 //s16 mud_add_amount = 0;
3108 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3109 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3110 for(s16 x=0-max_spread_amount+1;
3111 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3113 for(s16 z=0-max_spread_amount+1;
3114 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3117 // Node position in 2d
3118 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3120 // Determine whether to have sand here
3121 double sandnoise = noise2d_perlin(
3122 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3123 m_seed+59420, 3, 0.50);
3125 bool have_sand = (sandnoise > -0.15);
3127 if(have_sand == false)
3130 // Find ground level
3131 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3133 if(surface_y > WATER_LEVEL + 2)
3137 v3s16 em = vmanip.m_area.getExtent();
3138 s16 y_start = surface_y;
3139 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3140 u32 not_sand_counter = 0;
3141 for(s16 y=y_start; y>=y_nodes_min; y--)
3143 MapNode *n = &vmanip.m_data[i];
3144 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3146 n->d = CONTENT_SAND;
3151 if(not_sand_counter > 3)
3155 vmanip.m_area.add_y(em, i, -1);
3164 //TimeTaker timer1("generate trees");
3170 // Divide area into parts
3172 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3173 double area = sidelen * sidelen;
3174 for(s16 x0=0; x0<div; x0++)
3175 for(s16 z0=0; z0<div; z0++)
3177 // Center position of part of division
3179 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3180 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3182 // Minimum edge of part of division
3184 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3185 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3187 // Maximum edge of part of division
3189 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3190 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3193 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3194 // Put trees in random places on part of division
3195 for(u32 i=0; i<tree_count; i++)
3197 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3198 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3199 s16 y = find_ground_level(vmanip, v2s16(x,z));
3200 // Don't make a tree under water level
3205 Trees grow only on mud and grass
3208 u32 i = vmanip.m_area.index(v3s16(p));
3209 MapNode *n = &vmanip.m_data[i];
3210 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3215 make_tree(vmanip, p);
3218 /*u32 tree_max = relative_area / 60;
3219 //u32 count = myrand_range(0, tree_max);
3220 for(u32 i=0; i<count; i++)
3222 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3223 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3224 x += sectorpos_base.X*MAP_BLOCKSIZE;
3225 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3226 s16 y = find_ground_level(vmanip, v2s16(x,z));
3227 // Don't make a tree under water level
3232 make_tree(vmanip, p);
3240 //TimeTaker timer1("grow grass");
3246 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3247 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3248 for(s16 x=0-max_spread_amount;
3249 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3251 for(s16 z=0-max_spread_amount;
3252 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3255 // Node position in 2d
3256 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3259 Find the lowest surface to which enough light ends up
3262 Basically just wait until not air and not leaves.
3266 v3s16 em = vmanip.m_area.getExtent();
3267 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3269 // Go to ground level
3270 for(y=y_nodes_max; y>=y_nodes_min; y--)
3272 MapNode &n = vmanip.m_data[i];
3273 if(n.d != CONTENT_AIR
3274 && n.d != CONTENT_LEAVES)
3276 vmanip.m_area.add_y(em, i, -1);
3278 if(y >= y_nodes_min)
3281 surface_y = y_nodes_min;
3284 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3285 MapNode *n = &vmanip.m_data[i];
3286 if(n->d == CONTENT_MUD)
3287 n->d = CONTENT_GRASS;
3293 Initial lighting (sunlight)
3296 core::map<v3s16, bool> light_sources;
3299 // 750ms @cs=8, can't optimize more
3300 TimeTaker timer1("initial lighting");
3304 Go through the edges and add all nodes that have light to light_sources
3308 for(s16 i=0; i<4; i++)
3310 for(s16 j=lighting_min_d;
3317 if(i == 0 || i == 1)
3319 x = (i==0) ? lighting_min_d : lighting_max_d;
3328 z = (i==0) ? lighting_min_d : lighting_max_d;
3335 // Node position in 2d
3336 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3339 v3s16 em = vmanip.m_area.getExtent();
3340 s16 y_start = y_nodes_max;
3341 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3342 for(s16 y=y_start; y>=y_nodes_min; y--)
3344 MapNode *n = &vmanip.m_data[i];
3345 if(n->getLight(LIGHTBANK_DAY) != 0)
3347 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3349 //NOTE: This is broken, at least the index has to
3358 Go through the edges and apply sunlight to them, not caring
3363 for(s16 i=0; i<4; i++)
3365 for(s16 j=lighting_min_d;
3372 if(i == 0 || i == 1)
3374 x = (i==0) ? lighting_min_d : lighting_max_d;
3383 z = (i==0) ? lighting_min_d : lighting_max_d;
3390 // Node position in 2d
3391 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3393 // Loop from top to down
3395 u8 light = LIGHT_SUN;
3396 v3s16 em = vmanip.m_area.getExtent();
3397 s16 y_start = y_nodes_max;
3398 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3399 for(s16 y=y_start; y>=y_nodes_min; y--)
3401 MapNode *n = &vmanip.m_data[i];
3402 if(light_propagates_content(n->d) == false)
3406 else if(light != LIGHT_SUN
3407 || sunlight_propagates_content(n->d) == false)
3413 n->setLight(LIGHTBANK_DAY, light);
3414 n->setLight(LIGHTBANK_NIGHT, 0);
3418 // Insert light source
3419 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3422 // Increment index by y
3423 vmanip.m_area.add_y(em, i, -1);
3429 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3430 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3431 /*for(s16 x=0-max_spread_amount+1;
3432 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3434 for(s16 z=0-max_spread_amount+1;
3435 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3439 This has to be 1 smaller than the actual area, because
3440 neighboring nodes are checked.
3442 for(s16 x=lighting_min_d+1;
3443 x<=lighting_max_d-1;
3445 for(s16 z=lighting_min_d+1;
3446 z<=lighting_max_d-1;
3449 // Node position in 2d
3450 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3453 Apply initial sunlight
3456 u8 light = LIGHT_SUN;
3457 bool add_to_sources = false;
3458 v3s16 em = vmanip.m_area.getExtent();
3459 s16 y_start = y_nodes_max;
3460 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3461 for(s16 y=y_start; y>=y_nodes_min; y--)
3463 MapNode *n = &vmanip.m_data[i];
3465 if(light_propagates_content(n->d) == false)
3469 else if(light != LIGHT_SUN
3470 || sunlight_propagates_content(n->d) == false)
3476 // This doesn't take much time
3477 if(add_to_sources == false)
3480 Check sides. If side is not air or water, start
3481 adding to light_sources.
3484 v3s16(0,0,1), // back
3485 v3s16(1,0,0), // right
3486 v3s16(0,0,-1), // front
3487 v3s16(-1,0,0), // left
3489 for(u32 di=0; di<4; di++)
3491 v3s16 dirp = dirs4[di];
3493 vmanip.m_area.add_p(em, i2, dirp);
3494 MapNode *n2 = &vmanip.m_data[i2];
3496 n2->d != CONTENT_AIR
3497 && n2->d != CONTENT_WATERSOURCE
3498 && n2->d != CONTENT_WATER
3500 add_to_sources = true;
3506 n->setLight(LIGHTBANK_DAY, light);
3507 n->setLight(LIGHTBANK_NIGHT, 0);
3509 // This doesn't take much time
3510 if(light != 0 && add_to_sources)
3512 // Insert light source
3513 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3516 // Increment index by y
3517 vmanip.m_area.add_y(em, i, -1);
3524 for(s16 x=lighting_min_d+1;
3525 x<=lighting_max_d-1;
3527 for(s16 z=lighting_min_d+1;
3528 z<=lighting_max_d-1;
3531 // Node position in 2d
3532 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3535 Apply initial sunlight
3538 u8 light = LIGHT_SUN;
3539 v3s16 em = vmanip.m_area.getExtent();
3540 s16 y_start = y_nodes_max;
3541 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3542 for(s16 y=y_start; y>=y_nodes_min; y--)
3544 MapNode *n = &vmanip.m_data[i];
3546 if(light_propagates_content(n->d) == false)
3550 else if(light != LIGHT_SUN
3551 || sunlight_propagates_content(n->d) == false)
3557 n->setLight(LIGHTBANK_DAY, light);
3558 n->setLight(LIGHTBANK_NIGHT, 0);
3560 // This doesn't take much time
3563 // Insert light source
3564 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3567 // Increment index by y
3568 vmanip.m_area.add_y(em, i, -1);
3576 // Spread light around
3578 TimeTaker timer("generateChunkRaw() spreadLight");
3579 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3586 timer_generate.stop();
3589 Blit generated stuff to map
3593 //TimeTaker timer("generateChunkRaw() blitBackAll");
3594 vmanip.blitBackAll(&changed_blocks);
3598 Update day/night difference cache of the MapBlocks
3601 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3602 i.atEnd() == false; i++)
3604 MapBlock *block = i.getNode()->getValue();
3605 block->updateDayNightDiff();
3611 Create chunk metadata
3614 for(s16 x=-1; x<=1; x++)
3615 for(s16 y=-1; y<=1; y++)
3617 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3618 // Add chunk meta information
3619 MapChunk *chunk = getChunk(chunkpos0);
3622 chunk = new MapChunk();
3623 m_chunks.insert(chunkpos0, chunk);
3625 //chunk->setIsVolatile(true);
3626 if(chunk->getGenLevel() > GENERATED_PARTLY)
3627 chunk->setGenLevel(GENERATED_PARTLY);
3631 Set central chunk non-volatile
3633 MapChunk *chunk = getChunk(chunkpos);
3636 //chunk->setIsVolatile(false);
3637 chunk->setGenLevel(GENERATED_FULLY);
3640 Save changed parts of map
3645 Return central chunk (which was requested)
3650 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3651 core::map<v3s16, MapBlock*> &changed_blocks)
3653 dstream<<"generateChunk(): Generating chunk "
3654 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3657 /*for(s16 x=-1; x<=1; x++)
3658 for(s16 y=-1; y<=1; y++)*/
3659 for(s16 x=-0; x<=0; x++)
3660 for(s16 y=-0; y<=0; y++)
3662 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3663 MapChunk *chunk = getChunk(chunkpos0);
3664 // Skip if already generated
3665 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3667 generateChunkRaw(chunkpos0, changed_blocks);
3670 assert(chunkNonVolatile(chunkpos1));
3672 MapChunk *chunk = getChunk(chunkpos1);
3676 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3678 DSTACK("%s: p2d=(%d,%d)",
3683 Check if it exists already in memory
3685 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3690 Try to load it from disk (with blocks)
3692 if(loadSectorFull(p2d) == true)
3694 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3697 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3698 throw InvalidPositionException("");
3704 Do not create over-limit
3706 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3707 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3708 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3709 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3710 throw InvalidPositionException("createSector(): pos. over limit");
3713 Generate blank sector
3716 sector = new ServerMapSector(this, p2d);
3718 // Sector position on map in nodes
3719 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3724 m_sectors.insert(p2d, sector);
3729 MapSector * ServerMap::emergeSector(v2s16 p2d,
3730 core::map<v3s16, MapBlock*> &changed_blocks)
3732 DSTACK("%s: p2d=(%d,%d)",
3739 v2s16 chunkpos = sector_to_chunk(p2d);
3740 /*bool chunk_nonvolatile = false;
3741 MapChunk *chunk = getChunk(chunkpos);
3742 if(chunk && chunk->getIsVolatile() == false)
3743 chunk_nonvolatile = true;*/
3744 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3747 If chunk is not fully generated, generate chunk
3749 if(chunk_nonvolatile == false)
3751 // Generate chunk and neighbors
3752 generateChunk(chunkpos, changed_blocks);
3756 Return sector if it exists now
3758 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3763 Try to load it from disk
3765 if(loadSectorFull(p2d) == true)
3767 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3770 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3771 throw InvalidPositionException("");
3777 generateChunk should have generated the sector
3781 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3782 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3786 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3788 return createSector(p2d);
3793 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3796 generateChunkRaw(chunkpos, changed_blocks, true);
3799 Return sector if it exists now
3801 sector = getSectorNoGenerateNoEx(p2d);
3805 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3813 //return generateSector();
3817 NOTE: This is not used for main map generation, only for blocks
3818 that are very high or low
3820 MapBlock * ServerMap::generateBlock(
3822 MapBlock *original_dummy,
3823 ServerMapSector *sector,
3824 core::map<v3s16, MapBlock*> &changed_blocks,
3825 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3828 DSTACK("%s: p=(%d,%d,%d)",
3832 /*dstream<<"generateBlock(): "
3833 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3836 MapBlock *block = original_dummy;
3838 v2s16 p2d(p.X, p.Z);
3842 Do not generate over-limit
3844 if(blockpos_over_limit(p))
3846 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3847 throw InvalidPositionException("generateBlock(): pos. over limit");
3851 If block doesn't exist, create one.
3852 If it exists, it is a dummy. In that case unDummify() it.
3854 NOTE: This already sets the map as the parent of the block
3858 block = sector->createBlankBlockNoInsert(block_y);
3862 // Remove the block so that nobody can get a half-generated one.
3863 sector->removeBlock(block);
3864 // Allocate the block to contain the generated data
3868 u8 water_material = CONTENT_WATERSOURCE;
3870 s32 lowest_ground_y = 32767;
3871 s32 highest_ground_y = -32768;
3873 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3874 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3876 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3880 if(surface_y < lowest_ground_y)
3881 lowest_ground_y = surface_y;
3882 if(surface_y > highest_ground_y)
3883 highest_ground_y = surface_y;
3885 s32 surface_depth = 2;
3887 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3889 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3894 NOTE: If there are some man-made structures above the
3895 newly created block, they won't be taken into account.
3897 if(real_y > surface_y)
3898 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3904 // If node is over heightmap y, it's air or water
3905 if(real_y > surface_y)
3907 // If under water level, it's water
3908 if(real_y < WATER_LEVEL)
3910 n.d = water_material;
3911 n.setLight(LIGHTBANK_DAY,
3912 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3914 Add to transforming liquid queue (in case it'd
3917 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3918 m_transforming_liquid.push_back(real_pos);
3924 // Else it's ground or dungeons (air)
3927 // If it's surface_depth under ground, it's stone
3928 if(real_y <= surface_y - surface_depth)
3930 n.d = CONTENT_STONE;
3934 // It is mud if it is under the first ground
3935 // level or under water
3936 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3942 n.d = CONTENT_GRASS;
3945 //n.d = CONTENT_MUD;
3947 /*// If under water level, it's mud
3948 if(real_y < WATER_LEVEL)
3950 // Only the topmost node is grass
3951 else if(real_y <= surface_y - 1)
3954 n.d = CONTENT_GRASS;*/
3958 block->setNode(v3s16(x0,y0,z0), n);
3963 Calculate some helper variables
3966 // Completely underground if the highest part of block is under lowest
3968 // This has to be very sure; it's probably one too strict now but
3969 // that's just better.
3970 bool completely_underground =
3971 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3973 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3975 bool mostly_underwater_surface = false;
3976 if(highest_ground_y < WATER_LEVEL
3977 && some_part_underground && !completely_underground)
3978 mostly_underwater_surface = true;
3981 Get local attributes
3984 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3986 float caves_amount = 0.5;
3991 NOTE: BEWARE: Too big amount of attribute points slows verything
3993 1 interpolation from 5000 points takes 2-3ms.
3995 //TimeTaker timer("generateBlock() local attribute retrieval");
3996 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3997 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3998 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4002 //dstream<<"generateBlock(): Done"<<std::endl;
4008 // Initialize temporary table
4009 const s32 ued = MAP_BLOCKSIZE;
4010 bool underground_emptiness[ued*ued*ued];
4011 for(s32 i=0; i<ued*ued*ued; i++)
4013 underground_emptiness[i] = 0;
4020 Initialize orp and ors. Try to find if some neighboring
4021 MapBlock has a tunnel ended in its side
4025 (float)(myrand()%ued)+0.5,
4026 (float)(myrand()%ued)+0.5,
4027 (float)(myrand()%ued)+0.5
4030 bool found_existing = false;
4036 for(s16 y=0; y<ued; y++)
4037 for(s16 x=0; x<ued; x++)
4039 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4040 if(getNode(ap).d == CONTENT_AIR)
4042 orp = v3f(x+1,y+1,0);
4043 found_existing = true;
4044 goto continue_generating;
4048 catch(InvalidPositionException &e){}
4054 for(s16 y=0; y<ued; y++)
4055 for(s16 x=0; x<ued; x++)
4057 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4058 if(getNode(ap).d == CONTENT_AIR)
4060 orp = v3f(x+1,y+1,ued-1);
4061 found_existing = true;
4062 goto continue_generating;
4066 catch(InvalidPositionException &e){}
4072 for(s16 y=0; y<ued; y++)
4073 for(s16 z=0; z<ued; z++)
4075 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4076 if(getNode(ap).d == CONTENT_AIR)
4078 orp = v3f(0,y+1,z+1);
4079 found_existing = true;
4080 goto continue_generating;
4084 catch(InvalidPositionException &e){}
4090 for(s16 y=0; y<ued; y++)
4091 for(s16 z=0; z<ued; z++)
4093 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4094 if(getNode(ap).d == CONTENT_AIR)
4096 orp = v3f(ued-1,y+1,z+1);
4097 found_existing = true;
4098 goto continue_generating;
4102 catch(InvalidPositionException &e){}
4108 for(s16 x=0; x<ued; x++)
4109 for(s16 z=0; z<ued; z++)
4111 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4112 if(getNode(ap).d == CONTENT_AIR)
4114 orp = v3f(x+1,0,z+1);
4115 found_existing = true;
4116 goto continue_generating;
4120 catch(InvalidPositionException &e){}
4126 for(s16 x=0; x<ued; x++)
4127 for(s16 z=0; z<ued; z++)
4129 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4130 if(getNode(ap).d == CONTENT_AIR)
4132 orp = v3f(x+1,ued-1,z+1);
4133 found_existing = true;
4134 goto continue_generating;
4138 catch(InvalidPositionException &e){}
4140 continue_generating:
4143 Choose whether to actually generate dungeon
4145 bool do_generate_dungeons = true;
4146 // Don't generate if no part is underground
4147 if(!some_part_underground)
4149 do_generate_dungeons = false;
4151 // Don't generate if mostly underwater surface
4152 /*else if(mostly_underwater_surface)
4154 do_generate_dungeons = false;
4156 // Partly underground = cave
4157 else if(!completely_underground)
4159 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4161 // Found existing dungeon underground
4162 else if(found_existing && completely_underground)
4164 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4166 // Underground and no dungeons found
4169 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4172 if(do_generate_dungeons)
4175 Generate some tunnel starting from orp and ors
4177 for(u16 i=0; i<3; i++)
4180 (float)(myrand()%ued)+0.5,
4181 (float)(myrand()%ued)+0.5,
4182 (float)(myrand()%ued)+0.5
4186 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4190 for(float f=0; f<1.0; f+=0.04)
4192 v3f fp = orp + vec * f;
4193 v3s16 cp(fp.X, fp.Y, fp.Z);
4195 s16 d1 = d0 + rs - 1;
4196 for(s16 z0=d0; z0<=d1; z0++)
4198 s16 si = rs - abs(z0);
4199 for(s16 x0=-si; x0<=si-1; x0++)
4201 s16 si2 = rs - abs(x0);
4202 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4208 if(isInArea(p, ued) == false)
4210 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4222 // Set to true if has caves.
4223 // Set when some non-air is changed to air when making caves.
4224 bool has_dungeons = false;
4227 Apply temporary cave data to block
4230 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4231 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4233 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4235 MapNode n = block->getNode(v3s16(x0,y0,z0));
4238 if(underground_emptiness[
4239 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4240 +ued*(y0*ued/MAP_BLOCKSIZE)
4241 +(x0*ued/MAP_BLOCKSIZE)])
4243 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4246 has_dungeons = true;
4252 block->setNode(v3s16(x0,y0,z0), n);
4257 This is used for guessing whether or not the block should
4258 receive sunlight from the top if the block above doesn't exist
4260 block->setIsUnderground(completely_underground);
4263 Force lighting update if some part of block is partly
4264 underground and has caves.
4266 /*if(some_part_underground && !completely_underground && has_dungeons)
4268 //dstream<<"Half-ground caves"<<std::endl;
4269 lighting_invalidated_blocks[block->getPos()] = block;
4272 // DEBUG: Always update lighting
4273 //lighting_invalidated_blocks[block->getPos()] = block;
4279 if(some_part_underground)
4281 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4286 for(s16 i=0; i<underground_level/4 + 1; i++)
4288 if(myrand()%50 == 0)
4291 (myrand()%(MAP_BLOCKSIZE-2))+1,
4292 (myrand()%(MAP_BLOCKSIZE-2))+1,
4293 (myrand()%(MAP_BLOCKSIZE-2))+1
4299 for(u16 i=0; i<27; i++)
4301 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4303 block->setNode(cp+g_27dirs[i], n);
4311 u16 coal_amount = 30;
4312 u16 coal_rareness = 60 / coal_amount;
4313 if(coal_rareness == 0)
4315 if(myrand()%coal_rareness == 0)
4317 u16 a = myrand() % 16;
4318 u16 amount = coal_amount * a*a*a / 1000;
4319 for(s16 i=0; i<amount; i++)
4322 (myrand()%(MAP_BLOCKSIZE-2))+1,
4323 (myrand()%(MAP_BLOCKSIZE-2))+1,
4324 (myrand()%(MAP_BLOCKSIZE-2))+1
4328 n.d = CONTENT_STONE;
4329 n.param = MINERAL_COAL;
4331 for(u16 i=0; i<27; i++)
4333 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4335 block->setNode(cp+g_27dirs[i], n);
4343 //TODO: change to iron_amount or whatever
4344 u16 iron_amount = 15;
4345 u16 iron_rareness = 60 / iron_amount;
4346 if(iron_rareness == 0)
4348 if(myrand()%iron_rareness == 0)
4350 u16 a = myrand() % 16;
4351 u16 amount = iron_amount * a*a*a / 1000;
4352 for(s16 i=0; i<amount; i++)
4355 (myrand()%(MAP_BLOCKSIZE-2))+1,
4356 (myrand()%(MAP_BLOCKSIZE-2))+1,
4357 (myrand()%(MAP_BLOCKSIZE-2))+1
4361 n.d = CONTENT_STONE;
4362 n.param = MINERAL_IRON;
4364 for(u16 i=0; i<27; i++)
4366 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4368 block->setNode(cp+g_27dirs[i], n);
4375 Create a few rats in empty blocks underground
4377 if(completely_underground)
4379 //for(u16 i=0; i<2; i++)
4382 (myrand()%(MAP_BLOCKSIZE-2))+1,
4383 (myrand()%(MAP_BLOCKSIZE-2))+1,
4384 (myrand()%(MAP_BLOCKSIZE-2))+1
4387 // Check that the place is empty
4388 //if(!is_ground_content(block->getNode(cp).d))
4391 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4392 block->addObject(obj);
4398 Add block to sector.
4400 sector->insertBlock(block);
4402 // Lighting is invalid after generation.
4403 block->setLightingExpired(true);
4410 <<"lighting_invalidated_blocks.size()"
4414 <<" "<<lighting_invalidated_blocks.size()
4415 <<", "<<has_dungeons
4416 <<", "<<completely_underground
4417 <<", "<<some_part_underground
4424 MapBlock * ServerMap::createBlock(v3s16 p)
4426 DSTACK("%s: p=(%d,%d,%d)",
4427 __FUNCTION_NAME, p.X, p.Y, p.Z);
4430 Do not create over-limit
4432 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4433 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4434 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4435 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4436 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4437 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4438 throw InvalidPositionException("createBlock(): pos. over limit");
4440 v2s16 p2d(p.X, p.Z);
4443 This will create or load a sector if not found in memory.
4444 If block exists on disk, it will be loaded.
4446 NOTE: On old save formats, this will be slow, as it generates
4447 lighting on blocks for them.
4449 ServerMapSector *sector;
4451 sector = (ServerMapSector*)createSector(p2d);
4452 assert(sector->getId() == MAPSECTOR_SERVER);
4454 catch(InvalidPositionException &e)
4456 dstream<<"createBlock: createSector() failed"<<std::endl;
4460 NOTE: This should not be done, or at least the exception
4461 should not be passed on as std::exception, because it
4462 won't be catched at all.
4464 /*catch(std::exception &e)
4466 dstream<<"createBlock: createSector() failed: "
4467 <<e.what()<<std::endl;
4472 Try to get a block from the sector
4475 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4479 block = sector->createBlankBlock(block_y);
4483 MapBlock * ServerMap::emergeBlock(
4485 bool only_from_disk,
4486 core::map<v3s16, MapBlock*> &changed_blocks,
4487 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4490 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4492 p.X, p.Y, p.Z, only_from_disk);
4495 Do not generate over-limit
4497 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4498 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4499 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4500 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4501 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4502 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4503 throw InvalidPositionException("generateBlock(): pos. over limit");
4505 v2s16 p2d(p.X, p.Z);
4508 This will create or load a sector if not found in memory.
4509 If block exists on disk, it will be loaded.
4511 ServerMapSector *sector;
4513 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4514 assert(sector->getId() == MAPSECTOR_SERVER);
4516 catch(InvalidPositionException &e)
4518 dstream<<"emergeBlock: emergeSector() failed: "
4519 <<e.what()<<std::endl;
4520 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4522 <<"You could try to delete it."<<std::endl;
4526 NOTE: This should not be done, or at least the exception
4527 should not be passed on as std::exception, because it
4528 won't be catched at all.
4530 /*catch(std::exception &e)
4532 dstream<<"emergeBlock: emergeSector() failed: "
4533 <<e.what()<<std::endl;
4534 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4536 <<"You could try to delete it."<<std::endl;
4541 Try to get a block from the sector
4544 bool does_not_exist = false;
4545 bool lighting_expired = false;
4546 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4550 does_not_exist = true;
4552 else if(block->isDummy() == true)
4554 does_not_exist = true;
4556 else if(block->getLightingExpired())
4558 lighting_expired = true;
4563 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4568 If block was not found on disk and not going to generate a
4569 new one, make sure there is a dummy block in place.
4571 if(only_from_disk && (does_not_exist || lighting_expired))
4573 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4577 // Create dummy block
4578 block = new MapBlock(this, p, true);
4580 // Add block to sector
4581 sector->insertBlock(block);
4587 //dstream<<"Not found on disk, generating."<<std::endl;
4589 //TimeTaker("emergeBlock() generate");
4591 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4594 If the block doesn't exist, generate the block.
4598 block = generateBlock(p, block, sector, changed_blocks,
4599 lighting_invalidated_blocks);
4602 if(lighting_expired)
4604 lighting_invalidated_blocks.insert(p, block);
4608 Initially update sunlight
4612 core::map<v3s16, bool> light_sources;
4613 bool black_air_left = false;
4614 bool bottom_invalid =
4615 block->propagateSunlight(light_sources, true,
4616 &black_air_left, true);
4618 // If sunlight didn't reach everywhere and part of block is
4619 // above ground, lighting has to be properly updated
4620 //if(black_air_left && some_part_underground)
4623 lighting_invalidated_blocks[block->getPos()] = block;
4628 lighting_invalidated_blocks[block->getPos()] = block;
4635 void ServerMap::createDir(std::string path)
4637 if(fs::CreateDir(path) == false)
4639 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4640 <<"\""<<path<<"\""<<std::endl;
4641 throw BaseException("ServerMap failed to create directory");
4645 std::string ServerMap::getSectorSubDir(v2s16 pos)
4648 snprintf(cc, 9, "%.4x%.4x",
4649 (unsigned int)pos.X&0xffff,
4650 (unsigned int)pos.Y&0xffff);
4652 return std::string(cc);
4655 std::string ServerMap::getSectorDir(v2s16 pos)
4657 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4660 v2s16 ServerMap::getSectorPos(std::string dirname)
4662 if(dirname.size() != 8)
4663 throw InvalidFilenameException("Invalid sector directory name");
4665 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4667 throw InvalidFilenameException("Invalid sector directory name");
4668 v2s16 pos((s16)x, (s16)y);
4672 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4674 v2s16 p2d = getSectorPos(sectordir);
4676 if(blockfile.size() != 4){
4677 throw InvalidFilenameException("Invalid block filename");
4680 int r = sscanf(blockfile.c_str(), "%4x", &y);
4682 throw InvalidFilenameException("Invalid block filename");
4683 return v3s16(p2d.X, y, p2d.Y);
4686 void ServerMap::save(bool only_changed)
4688 DSTACK(__FUNCTION_NAME);
4689 if(m_map_saving_enabled == false)
4691 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4695 if(only_changed == false)
4696 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4702 u32 sector_meta_count = 0;
4703 u32 block_count = 0;
4706 JMutexAutoLock lock(m_sector_mutex);
4708 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4709 for(; i.atEnd() == false; i++)
4711 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4712 assert(sector->getId() == MAPSECTOR_SERVER);
4714 if(sector->differs_from_disk || only_changed == false)
4716 saveSectorMeta(sector);
4717 sector_meta_count++;
4719 core::list<MapBlock*> blocks;
4720 sector->getBlocks(blocks);
4721 core::list<MapBlock*>::Iterator j;
4722 for(j=blocks.begin(); j!=blocks.end(); j++)
4724 MapBlock *block = *j;
4725 if(block->getChangedFlag() || only_changed == false)
4730 /*dstream<<"ServerMap: Written block ("
4731 <<block->getPos().X<<","
4732 <<block->getPos().Y<<","
4733 <<block->getPos().Z<<")"
4742 Only print if something happened or saved whole map
4744 if(only_changed == false || sector_meta_count != 0
4745 || block_count != 0)
4747 dstream<<DTIME<<"ServerMap: Written: "
4748 <<sector_meta_count<<" sector metadata files, "
4749 <<block_count<<" block files"
4754 void ServerMap::loadAll()
4756 DSTACK(__FUNCTION_NAME);
4757 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4762 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4764 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4766 JMutexAutoLock lock(m_sector_mutex);
4769 s32 printed_counter = -100000;
4770 s32 count = list.size();
4772 std::vector<fs::DirListNode>::iterator i;
4773 for(i=list.begin(); i!=list.end(); i++)
4775 if(counter > printed_counter + 10)
4777 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4778 printed_counter = counter;
4782 MapSector *sector = NULL;
4784 // We want directories
4788 sector = loadSectorMeta(i->name);
4790 catch(InvalidFilenameException &e)
4792 // This catches unknown crap in directory
4795 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4796 (m_savedir+"/sectors/"+i->name);
4797 std::vector<fs::DirListNode>::iterator i2;
4798 for(i2=list2.begin(); i2!=list2.end(); i2++)
4804 loadBlock(i->name, i2->name, sector);
4806 catch(InvalidFilenameException &e)
4808 // This catches unknown crap in directory
4812 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4816 void ServerMap::saveMasterHeightmap()
4818 DSTACK(__FUNCTION_NAME);
4820 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4822 createDir(m_savedir);
4824 /*std::string fullpath = m_savedir + "/master_heightmap";
4825 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4826 if(o.good() == false)
4827 throw FileNotGoodException("Cannot open master heightmap");*/
4829 // Format used for writing
4830 //u8 version = SER_FMT_VER_HIGHEST;
4833 void ServerMap::loadMasterHeightmap()
4835 DSTACK(__FUNCTION_NAME);
4837 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4839 /*std::string fullpath = m_savedir + "/master_heightmap";
4840 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4841 if(is.good() == false)
4842 throw FileNotGoodException("Cannot open master heightmap");*/
4846 void ServerMap::saveMapMeta()
4848 DSTACK(__FUNCTION_NAME);
4850 dstream<<"INFO: ServerMap::saveMapMeta(): "
4851 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4854 createDir(m_savedir);
4856 std::string fullpath = m_savedir + "/map_meta.txt";
4857 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4858 if(os.good() == false)
4860 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4861 <<"could not open"<<fullpath<<std::endl;
4862 throw FileNotGoodException("Cannot open chunk metadata");
4866 params.setU64("seed", m_seed);
4867 params.setS32("chunksize", m_chunksize);
4869 params.writeLines(os);
4871 os<<"[end_of_params]\n";
4875 void ServerMap::loadMapMeta()
4877 DSTACK(__FUNCTION_NAME);
4879 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4882 std::string fullpath = m_savedir + "/map_meta.txt";
4883 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4884 if(is.good() == false)
4886 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4887 <<"could not open"<<fullpath<<std::endl;
4888 throw FileNotGoodException("Cannot open chunk metadata");
4896 throw SerializationError
4897 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4899 std::getline(is, line);
4900 std::string trimmedline = trim(line);
4901 if(trimmedline == "[end_of_params]")
4903 params.parseConfigLine(line);
4906 m_seed = params.getU64("seed");
4907 m_chunksize = params.getS32("chunksize");
4909 dstream<<"INFO: ServerMap::loadMapMeta(): "
4910 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4914 void ServerMap::saveChunkMeta()
4916 DSTACK(__FUNCTION_NAME);
4918 u32 count = m_chunks.size();
4920 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4921 <<count<<" chunks"<<std::endl;
4923 createDir(m_savedir);
4925 std::string fullpath = m_savedir + "/chunk_meta";
4926 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4927 if(os.good() == false)
4929 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4930 <<"could not open"<<fullpath<<std::endl;
4931 throw FileNotGoodException("Cannot open chunk metadata");
4937 os.write((char*)&version, 1);
4942 writeU32(buf, count);
4943 os.write((char*)buf, 4);
4945 for(core::map<v2s16, MapChunk*>::Iterator
4946 i = m_chunks.getIterator();
4947 i.atEnd()==false; i++)
4949 v2s16 p = i.getNode()->getKey();
4950 MapChunk *chunk = i.getNode()->getValue();
4953 os.write((char*)buf, 4);
4955 chunk->serialize(os, version);
4959 void ServerMap::loadChunkMeta()
4961 DSTACK(__FUNCTION_NAME);
4963 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
4966 std::string fullpath = m_savedir + "/chunk_meta";
4967 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4968 if(is.good() == false)
4970 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
4971 <<"could not open"<<fullpath<<std::endl;
4972 throw FileNotGoodException("Cannot open chunk metadata");
4978 is.read((char*)&version, 1);
4983 is.read((char*)buf, 4);
4984 u32 count = readU32(buf);
4986 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
4987 <<count<<" chunks"<<std::endl;
4989 for(u32 i=0; i<count; i++)
4992 MapChunk *chunk = new MapChunk();
4994 is.read((char*)buf, 4);
4997 chunk->deSerialize(is, version);
4998 m_chunks.insert(p, chunk);
5002 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5004 DSTACK(__FUNCTION_NAME);
5005 // Format used for writing
5006 u8 version = SER_FMT_VER_HIGHEST;
5008 v2s16 pos = sector->getPos();
5009 createDir(m_savedir);
5010 createDir(m_savedir+"/sectors");
5011 std::string dir = getSectorDir(pos);
5014 std::string fullpath = dir + "/meta";
5015 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5016 if(o.good() == false)
5017 throw FileNotGoodException("Cannot open sector metafile");
5019 sector->serialize(o, version);
5021 sector->differs_from_disk = false;
5024 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5026 DSTACK(__FUNCTION_NAME);
5028 v2s16 p2d = getSectorPos(dirname);
5029 std::string dir = m_savedir + "/sectors/" + dirname;
5031 std::string fullpath = dir + "/meta";
5032 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5033 if(is.good() == false)
5034 throw FileNotGoodException("Cannot open sector metafile");
5036 ServerMapSector *sector = ServerMapSector::deSerialize
5037 (is, this, p2d, m_sectors);
5039 sector->differs_from_disk = false;
5044 bool ServerMap::loadSectorFull(v2s16 p2d)
5046 DSTACK(__FUNCTION_NAME);
5047 std::string sectorsubdir = getSectorSubDir(p2d);
5049 MapSector *sector = NULL;
5051 JMutexAutoLock lock(m_sector_mutex);
5054 sector = loadSectorMeta(sectorsubdir);
5056 catch(InvalidFilenameException &e)
5060 catch(FileNotGoodException &e)
5064 catch(std::exception &e)
5072 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5073 (m_savedir+"/sectors/"+sectorsubdir);
5074 std::vector<fs::DirListNode>::iterator i2;
5075 for(i2=list2.begin(); i2!=list2.end(); i2++)
5081 loadBlock(sectorsubdir, i2->name, sector);
5083 catch(InvalidFilenameException &e)
5085 // This catches unknown crap in directory
5092 bool ServerMap::deFlushSector(v2s16 p2d)
5094 DSTACK(__FUNCTION_NAME);
5095 // See if it already exists in memory
5097 MapSector *sector = getSectorNoGenerate(p2d);
5100 catch(InvalidPositionException &e)
5103 Try to load the sector from disk.
5105 if(loadSectorFull(p2d) == true)
5114 void ServerMap::saveBlock(MapBlock *block)
5116 DSTACK(__FUNCTION_NAME);
5118 Dummy blocks are not written
5120 if(block->isDummy())
5122 /*v3s16 p = block->getPos();
5123 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5124 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5128 // Format used for writing
5129 u8 version = SER_FMT_VER_HIGHEST;
5131 v3s16 p3d = block->getPos();
5132 v2s16 p2d(p3d.X, p3d.Z);
5133 createDir(m_savedir);
5134 createDir(m_savedir+"/sectors");
5135 std::string dir = getSectorDir(p2d);
5138 // Block file is map/sectors/xxxxxxxx/xxxx
5140 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5141 std::string fullpath = dir + "/" + cc;
5142 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5143 if(o.good() == false)
5144 throw FileNotGoodException("Cannot open block data");
5147 [0] u8 serialization version
5150 o.write((char*)&version, 1);
5152 block->serialize(o, version);
5155 Versions up from 9 have block objects.
5159 block->serializeObjects(o, version);
5162 // We just wrote it to the disk
5163 block->resetChangedFlag();
5166 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5168 DSTACK(__FUNCTION_NAME);
5172 // Block file is map/sectors/xxxxxxxx/xxxx
5173 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5174 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5175 if(is.good() == false)
5176 throw FileNotGoodException("Cannot open block file");
5178 v3s16 p3d = getBlockPos(sectordir, blockfile);
5179 v2s16 p2d(p3d.X, p3d.Z);
5181 assert(sector->getPos() == p2d);
5183 u8 version = SER_FMT_VER_INVALID;
5184 is.read((char*)&version, 1);
5186 /*u32 block_size = MapBlock::serializedLength(version);
5187 SharedBuffer<u8> data(block_size);
5188 is.read((char*)*data, block_size);*/
5190 // This will always return a sector because we're the server
5191 //MapSector *sector = emergeSector(p2d);
5193 MapBlock *block = NULL;
5194 bool created_new = false;
5196 block = sector->getBlockNoCreate(p3d.Y);
5198 catch(InvalidPositionException &e)
5200 block = sector->createBlankBlockNoInsert(p3d.Y);
5204 // deserialize block data
5205 block->deSerialize(is, version);
5208 Versions up from 9 have block objects.
5212 block->updateObjects(is, version, NULL, 0);
5216 sector->insertBlock(block);
5219 Convert old formats to new and save
5222 // Save old format blocks in new format
5223 if(version < SER_FMT_VER_HIGHEST)
5228 // We just loaded it from the disk, so it's up-to-date.
5229 block->resetChangedFlag();
5232 catch(SerializationError &e)
5234 dstream<<"WARNING: Invalid block data on disk "
5235 "(SerializationError). Ignoring."
5240 // Gets from master heightmap
5241 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5243 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5244 //assert(m_heightmap != NULL);
5252 /*corners[0] = m_heightmap->getGroundHeight
5253 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5254 corners[1] = m_heightmap->getGroundHeight
5255 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5256 corners[2] = m_heightmap->getGroundHeight
5257 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5258 corners[3] = m_heightmap->getGroundHeight
5259 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
5262 void ServerMap::PrintInfo(std::ostream &out)
5273 ClientMap::ClientMap(
5275 MapDrawControl &control,
5276 scene::ISceneNode* parent,
5277 scene::ISceneManager* mgr,
5281 scene::ISceneNode(parent, mgr, id),
5285 //mesh_mutex.Init();
5287 /*m_box = core::aabbox3d<f32>(0,0,0,
5288 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5289 /*m_box = core::aabbox3d<f32>(0,0,0,
5290 map->getSizeNodes().X * BS,
5291 map->getSizeNodes().Y * BS,
5292 map->getSizeNodes().Z * BS);*/
5293 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5294 BS*1000000,BS*1000000,BS*1000000);
5296 //setPosition(v3f(BS,BS,BS));
5299 ClientMap::~ClientMap()
5301 /*JMutexAutoLock lock(mesh_mutex);
5310 MapSector * ClientMap::emergeSector(v2s16 p2d)
5312 DSTACK(__FUNCTION_NAME);
5313 // Check that it doesn't exist already
5315 return getSectorNoGenerate(p2d);
5317 catch(InvalidPositionException &e)
5322 ClientMapSector *sector = new ClientMapSector(this, p2d);
5325 JMutexAutoLock lock(m_sector_mutex);
5326 m_sectors.insert(p2d, sector);
5332 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5334 DSTACK(__FUNCTION_NAME);
5335 ClientMapSector *sector = NULL;
5337 JMutexAutoLock lock(m_sector_mutex);
5339 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5343 sector = (ClientMapSector*)n->getValue();
5344 assert(sector->getId() == MAPSECTOR_CLIENT);
5348 sector = new ClientMapSector(this, p2d);
5350 JMutexAutoLock lock(m_sector_mutex);
5351 m_sectors.insert(p2d, sector);
5355 sector->deSerialize(is);
5358 void ClientMap::OnRegisterSceneNode()
5362 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5363 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5366 ISceneNode::OnRegisterSceneNode();
5369 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5371 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5372 DSTACK(__FUNCTION_NAME);
5374 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5377 Get time for measuring timeout.
5379 Measuring time is very useful for long delays when the
5380 machine is swapping a lot.
5382 int time1 = time(0);
5384 u32 daynight_ratio = m_client->getDayNightRatio();
5386 m_camera_mutex.Lock();
5387 v3f camera_position = m_camera_position;
5388 v3f camera_direction = m_camera_direction;
5389 m_camera_mutex.Unlock();
5392 Get all blocks and draw all visible ones
5395 v3s16 cam_pos_nodes(
5396 camera_position.X / BS,
5397 camera_position.Y / BS,
5398 camera_position.Z / BS);
5400 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5402 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5403 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5405 // Take a fair amount as we will be dropping more out later
5407 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5408 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5409 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5411 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5412 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5413 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5415 u32 vertex_count = 0;
5417 // For limiting number of mesh updates per frame
5418 u32 mesh_update_count = 0;
5420 u32 blocks_would_have_drawn = 0;
5421 u32 blocks_drawn = 0;
5423 //NOTE: The sectors map should be locked but we're not doing it
5424 // because it'd cause too much delays
5426 int timecheck_counter = 0;
5427 core::map<v2s16, MapSector*>::Iterator si;
5428 si = m_sectors.getIterator();
5429 for(; si.atEnd() == false; si++)
5432 timecheck_counter++;
5433 if(timecheck_counter > 50)
5435 timecheck_counter = 0;
5436 int time2 = time(0);
5437 if(time2 > time1 + 4)
5439 dstream<<"ClientMap::renderMap(): "
5440 "Rendering takes ages, returning."
5447 MapSector *sector = si.getNode()->getValue();
5448 v2s16 sp = sector->getPos();
5450 if(m_control.range_all == false)
5452 if(sp.X < p_blocks_min.X
5453 || sp.X > p_blocks_max.X
5454 || sp.Y < p_blocks_min.Z
5455 || sp.Y > p_blocks_max.Z)
5459 core::list< MapBlock * > sectorblocks;
5460 sector->getBlocks(sectorblocks);
5466 core::list< MapBlock * >::Iterator i;
5467 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5469 MapBlock *block = *i;
5472 Compare block position to camera position, skip
5473 if not seen on display
5476 float range = 100000 * BS;
5477 if(m_control.range_all == false)
5478 range = m_control.wanted_range * BS;
5481 if(isBlockInSight(block->getPos(), camera_position,
5482 camera_direction, range, &d) == false)
5488 /*if(m_control.range_all == false &&
5489 d - 0.5*BS*MAP_BLOCKSIZE > range)
5494 Update expired mesh (used for day/night change)
5497 bool mesh_expired = false;
5500 JMutexAutoLock lock(block->mesh_mutex);
5502 mesh_expired = block->getMeshExpired();
5504 // Mesh has not been expired and there is no mesh:
5505 // block has no content
5506 if(block->mesh == NULL && mesh_expired == false)
5510 f32 faraway = BS*50;
5511 //f32 faraway = m_control.wanted_range * BS;
5514 This has to be done with the mesh_mutex unlocked
5516 // Pretty random but this should work somewhat nicely
5517 if(mesh_expired && (
5518 (mesh_update_count < 3
5519 && (d < faraway || mesh_update_count < 2)
5522 (m_control.range_all && mesh_update_count < 20)
5525 /*if(mesh_expired && mesh_update_count < 6
5526 && (d < faraway || mesh_update_count < 3))*/
5528 mesh_update_count++;
5530 // Mesh has been expired: generate new mesh
5531 //block->updateMeshes(daynight_i);
5532 block->updateMesh(daynight_ratio);
5534 mesh_expired = false;
5538 Don't draw an expired mesh that is far away
5540 /*if(mesh_expired && d >= faraway)
5543 // Instead, delete it
5544 JMutexAutoLock lock(block->mesh_mutex);
5547 block->mesh->drop();
5550 // And continue to next block
5555 Draw the faces of the block
5558 JMutexAutoLock lock(block->mesh_mutex);
5560 scene::SMesh *mesh = block->mesh;
5565 blocks_would_have_drawn++;
5566 if(blocks_drawn >= m_control.wanted_max_blocks
5567 && m_control.range_all == false
5568 && d > m_control.wanted_min_range * BS)
5572 u32 c = mesh->getMeshBufferCount();
5574 for(u32 i=0; i<c; i++)
5576 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5577 const video::SMaterial& material = buf->getMaterial();
5578 video::IMaterialRenderer* rnd =
5579 driver->getMaterialRenderer(material.MaterialType);
5580 bool transparent = (rnd && rnd->isTransparent());
5581 // Render transparent on transparent pass and likewise.
5582 if(transparent == is_transparent_pass)
5585 This *shouldn't* hurt too much because Irrlicht
5586 doesn't change opengl textures if the old
5587 material is set again.
5589 driver->setMaterial(buf->getMaterial());
5590 driver->drawMeshBuffer(buf);
5591 vertex_count += buf->getVertexCount();
5595 } // foreach sectorblocks
5598 m_control.blocks_drawn = blocks_drawn;
5599 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5601 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5602 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5605 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5606 core::map<v3s16, MapBlock*> *affected_blocks)
5608 bool changed = false;
5610 Add it to all blocks touching it
5613 v3s16(0,0,0), // this
5614 v3s16(0,0,1), // back
5615 v3s16(0,1,0), // top
5616 v3s16(1,0,0), // right
5617 v3s16(0,0,-1), // front
5618 v3s16(0,-1,0), // bottom
5619 v3s16(-1,0,0), // left
5621 for(u16 i=0; i<7; i++)
5623 v3s16 p2 = p + dirs[i];
5624 // Block position of neighbor (or requested) node
5625 v3s16 blockpos = getNodeBlockPos(p2);
5626 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5627 if(blockref == NULL)
5629 // Relative position of requested node
5630 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5631 if(blockref->setTempMod(relpos, mod))
5636 if(changed && affected_blocks!=NULL)
5638 for(u16 i=0; i<7; i++)
5640 v3s16 p2 = p + dirs[i];
5641 // Block position of neighbor (or requested) node
5642 v3s16 blockpos = getNodeBlockPos(p2);
5643 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5644 if(blockref == NULL)
5646 affected_blocks->insert(blockpos, blockref);
5652 bool ClientMap::clearTempMod(v3s16 p,
5653 core::map<v3s16, MapBlock*> *affected_blocks)
5655 bool changed = false;
5657 v3s16(0,0,0), // this
5658 v3s16(0,0,1), // back
5659 v3s16(0,1,0), // top
5660 v3s16(1,0,0), // right
5661 v3s16(0,0,-1), // front
5662 v3s16(0,-1,0), // bottom
5663 v3s16(-1,0,0), // left
5665 for(u16 i=0; i<7; i++)
5667 v3s16 p2 = p + dirs[i];
5668 // Block position of neighbor (or requested) node
5669 v3s16 blockpos = getNodeBlockPos(p2);
5670 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5671 if(blockref == NULL)
5673 // Relative position of requested node
5674 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5675 if(blockref->clearTempMod(relpos))
5680 if(changed && affected_blocks!=NULL)
5682 for(u16 i=0; i<7; i++)
5684 v3s16 p2 = p + dirs[i];
5685 // Block position of neighbor (or requested) node
5686 v3s16 blockpos = getNodeBlockPos(p2);
5687 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5688 if(blockref == NULL)
5690 affected_blocks->insert(blockpos, blockref);
5696 void ClientMap::PrintInfo(std::ostream &out)
5707 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5712 MapVoxelManipulator::~MapVoxelManipulator()
5714 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5718 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5720 TimeTaker timer1("emerge", &emerge_time);
5722 // Units of these are MapBlocks
5723 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5724 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5726 VoxelArea block_area_nodes
5727 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5729 addArea(block_area_nodes);
5731 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5732 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5733 for(s32 x=p_min.X; x<=p_max.X; x++)
5736 core::map<v3s16, bool>::Node *n;
5737 n = m_loaded_blocks.find(p);
5741 bool block_data_inexistent = false;
5744 TimeTaker timer1("emerge load", &emerge_load_time);
5746 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5747 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5750 dstream<<std::endl;*/
5752 MapBlock *block = m_map->getBlockNoCreate(p);
5753 if(block->isDummy())
5754 block_data_inexistent = true;
5756 block->copyTo(*this);
5758 catch(InvalidPositionException &e)
5760 block_data_inexistent = true;
5763 if(block_data_inexistent)
5765 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5766 // Fill with VOXELFLAG_INEXISTENT
5767 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5768 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5770 s32 i = m_area.index(a.MinEdge.X,y,z);
5771 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5775 m_loaded_blocks.insert(p, !block_data_inexistent);
5778 //dstream<<"emerge done"<<std::endl;
5782 SUGG: Add an option to only update eg. water and air nodes.
5783 This will make it interfere less with important stuff if
5786 void MapVoxelManipulator::blitBack
5787 (core::map<v3s16, MapBlock*> & modified_blocks)
5789 if(m_area.getExtent() == v3s16(0,0,0))
5792 //TimeTaker timer1("blitBack");
5794 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5795 <<m_loaded_blocks.size()<<std::endl;*/
5798 Initialize block cache
5800 v3s16 blockpos_last;
5801 MapBlock *block = NULL;
5802 bool block_checked_in_modified = false;
5804 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5805 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5806 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5810 u8 f = m_flags[m_area.index(p)];
5811 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5814 MapNode &n = m_data[m_area.index(p)];
5816 v3s16 blockpos = getNodeBlockPos(p);
5821 if(block == NULL || blockpos != blockpos_last){
5822 block = m_map->getBlockNoCreate(blockpos);
5823 blockpos_last = blockpos;
5824 block_checked_in_modified = false;
5827 // Calculate relative position in block
5828 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5830 // Don't continue if nothing has changed here
5831 if(block->getNode(relpos) == n)
5834 //m_map->setNode(m_area.MinEdge + p, n);
5835 block->setNode(relpos, n);
5838 Make sure block is in modified_blocks
5840 if(block_checked_in_modified == false)
5842 modified_blocks[blockpos] = block;
5843 block_checked_in_modified = true;
5846 catch(InvalidPositionException &e)
5852 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5853 MapVoxelManipulator(map)
5857 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5861 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5863 // Just create the area so that it can be pointed to
5864 VoxelManipulator::emerge(a, caller_id);
5867 void ManualMapVoxelManipulator::initialEmerge(
5868 v3s16 blockpos_min, v3s16 blockpos_max)
5870 TimeTaker timer1("initialEmerge", &emerge_time);
5872 // Units of these are MapBlocks
5873 v3s16 p_min = blockpos_min;
5874 v3s16 p_max = blockpos_max;
5876 VoxelArea block_area_nodes
5877 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5879 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5882 dstream<<"initialEmerge: area: ";
5883 block_area_nodes.print(dstream);
5884 dstream<<" ("<<size_MB<<"MB)";
5888 addArea(block_area_nodes);
5890 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5891 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5892 for(s32 x=p_min.X; x<=p_max.X; x++)
5895 core::map<v3s16, bool>::Node *n;
5896 n = m_loaded_blocks.find(p);
5900 bool block_data_inexistent = false;
5903 TimeTaker timer1("emerge load", &emerge_load_time);
5905 MapBlock *block = m_map->getBlockNoCreate(p);
5906 if(block->isDummy())
5907 block_data_inexistent = true;
5909 block->copyTo(*this);
5911 catch(InvalidPositionException &e)
5913 block_data_inexistent = true;
5916 if(block_data_inexistent)
5919 Mark area inexistent
5921 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5922 // Fill with VOXELFLAG_INEXISTENT
5923 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5924 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5926 s32 i = m_area.index(a.MinEdge.X,y,z);
5927 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5931 m_loaded_blocks.insert(p, !block_data_inexistent);
5935 void ManualMapVoxelManipulator::blitBackAll(
5936 core::map<v3s16, MapBlock*> * modified_blocks)
5938 if(m_area.getExtent() == v3s16(0,0,0))
5942 Copy data of all blocks
5944 for(core::map<v3s16, bool>::Iterator
5945 i = m_loaded_blocks.getIterator();
5946 i.atEnd() == false; i++)
5948 bool existed = i.getNode()->getValue();
5949 if(existed == false)
5951 v3s16 p = i.getNode()->getKey();
5952 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5955 dstream<<"WARNING: "<<__FUNCTION_NAME
5956 <<": got NULL block "
5957 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5962 block->copyFrom(*this);
5965 modified_blocks->insert(p, block);