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 #define AVERAGE_MUD_AMOUNT 4
2009 double base_rock_level_2d(u64 seed, v2s16 p)
2011 // The base ground level
2012 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2013 + 25. * noise2d_perlin(
2014 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2015 (seed>>32)+654879876, 6, 0.6);
2017 /*// A bit hillier one
2018 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2019 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2020 (seed>>27)+90340, 6, 0.69);
2024 // Higher ground level
2025 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2026 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2027 seed+85039, 5, 0.69);
2028 //higher = 30; // For debugging
2030 // Limit higher to at least base
2034 // Steepness factor of cliffs
2035 double b = 1.0 + 1.0 * noise2d_perlin(
2036 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2038 b = rangelim(b, 0.0, 1000.0);
2041 b = rangelim(b, 3.0, 1000.0);
2042 //dstream<<"b="<<b<<std::endl;
2045 // Offset to more low
2046 double a_off = -0.2;
2047 // High/low selector
2048 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2049 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2050 seed-359, 6, 0.7));*/
2051 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2052 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2053 seed-359, 5, 0.60));
2055 a = rangelim(a, 0.0, 1.0);
2057 //dstream<<"a="<<a<<std::endl;
2059 double h = base*(1.0-a) + higher*a;
2066 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2069 This is the main map generation method
2072 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2073 core::map<v3s16, MapBlock*> &changed_blocks,
2076 DSTACK(__FUNCTION_NAME);
2079 Don't generate if already fully generated
2083 MapChunk *chunk = getChunk(chunkpos);
2084 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2086 dstream<<"generateChunkRaw(): Chunk "
2087 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2088 <<" already generated"<<std::endl;
2093 dstream<<"generateChunkRaw(): Generating chunk "
2094 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2097 TimeTaker timer("generateChunkRaw()");
2099 // The distance how far into the neighbors the generator is allowed to go.
2100 s16 max_spread_amount_sectors = 2;
2101 assert(max_spread_amount_sectors <= m_chunksize);
2102 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2104 // Minimum amount of space left on sides for mud to fall in
2105 //s16 min_mud_fall_space = 2;
2107 // Maximum diameter of stone obstacles in X and Z
2108 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2109 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2111 s16 y_blocks_min = -4;
2112 s16 y_blocks_max = 3;
2113 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2114 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2115 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2117 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2118 s16 sectorpos_base_size = m_chunksize;
2120 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2121 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2122 v2s16 sectorpos_bigbase =
2123 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2124 s16 sectorpos_bigbase_size =
2125 sectorpos_base_size + 2 * max_spread_amount_sectors;
2127 v3s16 bigarea_blocks_min(
2128 sectorpos_bigbase.X,
2133 v3s16 bigarea_blocks_max(
2134 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2136 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2139 // Relative values to control amount of stuff in one chunk
2140 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2141 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2142 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2143 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2144 *(u32)h_blocks*MAP_BLOCKSIZE;
2147 The limiting edges of the lighting update, inclusive.
2149 s16 lighting_min_d = 0-max_spread_amount;
2150 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2153 Create the whole area of this and the neighboring chunks
2156 TimeTaker timer("generateChunkRaw() create area");
2158 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2159 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2161 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2162 ServerMapSector *sector = createSector(sectorpos);
2165 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2167 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2168 MapBlock *block = createBlock(blockpos);
2170 // Lighting won't be calculated
2171 //block->setLightingExpired(true);
2172 // Lighting will be calculated
2173 block->setLightingExpired(false);
2176 Block gets sunlight if this is true.
2178 This should be set to true when the top side of a block
2179 is completely exposed to the sky.
2181 Actually this doesn't matter now because the
2182 initial lighting is done here.
2184 block->setIsUnderground(y != y_blocks_max);
2190 Now we have a big empty area.
2192 Make a ManualMapVoxelManipulator that contains this and the
2196 ManualMapVoxelManipulator vmanip(this);
2197 // Add the area we just generated
2199 TimeTaker timer("generateChunkRaw() initialEmerge");
2200 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2204 vmanip.clearFlag(0xff);
2206 TimeTaker timer_generate("generateChunkRaw() generate");
2208 // Maximum height of the stone surface and obstacles.
2209 // This is used to disable dungeon generation from going too high.
2210 s16 stone_surface_max_y = 0;
2213 Generate general ground level to full area
2218 //TimeTaker timer1("ground level");
2220 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2221 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2224 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2227 Skip of already generated
2230 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2231 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2235 // Ground height at this point
2236 float surface_y_f = 0.0;
2238 // Use perlin noise for ground height
2239 surface_y_f = base_rock_level_2d(m_seed, p2d);
2241 /*// Experimental stuff
2243 float a = highlands_level_2d(m_seed, p2d);
2248 // Convert to integer
2249 s16 surface_y = (s16)surface_y_f;
2252 if(surface_y > stone_surface_max_y)
2253 stone_surface_max_y = surface_y;
2256 Fill ground with stone
2259 // Use fast index incrementing
2260 v3s16 em = vmanip.m_area.getExtent();
2261 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2262 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2264 vmanip.m_data[i].d = CONTENT_STONE;
2266 vmanip.m_area.add_y(em, i, 1);
2274 Randomize some parameters
2277 s32 stone_obstacle_count = 0;
2278 /*s32 stone_obstacle_count =
2279 rangelim((1.0+noise2d(m_seed+897,
2280 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2282 s16 stone_obstacle_max_height = 0;
2283 /*s16 stone_obstacle_max_height =
2284 rangelim((1.0+noise2d(m_seed+5902,
2285 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2288 Loop this part, it will make stuff look older and newer nicely
2290 //for(u32 i_age=0; i_age<1; i_age++)
2291 for(u32 i_age=0; i_age<2; i_age++)
2296 //TimeTaker timer1("stone obstacles");
2299 Add some random stone obstacles
2302 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2304 // Randomize max height so usually stuff will be quite low
2305 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2307 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2308 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2311 myrand_range(5, stone_obstacle_max_size),
2312 myrand_range(0, maxheight_randomized),
2313 myrand_range(5, stone_obstacle_max_size)
2316 // Don't make stupid small rectangle bumps
2321 myrand_range(1+ob_size.X/2+2,
2322 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2323 myrand_range(1+ob_size.Z/2+2,
2324 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2327 // Minimum space left on top of the obstacle
2328 s16 min_head_space = 12;
2330 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2331 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2333 // Node position in 2d
2334 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2336 // Find stone ground level
2337 // (ignore everything else than mud in already generated chunks)
2338 // and mud amount over the stone level
2342 v3s16 em = vmanip.m_area.getExtent();
2343 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2345 // Go to ground level
2346 for(y=y_nodes_max; y>=y_nodes_min; y--)
2348 MapNode *n = &vmanip.m_data[i];
2349 /*if(content_walkable(n.d)
2350 && n.d != CONTENT_MUD
2351 && n.d != CONTENT_GRASS)
2353 if(n->d == CONTENT_STONE)
2356 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2360 Change to mud because otherwise we might
2361 be throwing mud on grass at the next
2367 vmanip.m_area.add_y(em, i, -1);
2369 if(y >= y_nodes_min)
2372 surface_y = y_nodes_min;
2380 v3s16 em = vmanip.m_area.getExtent();
2381 s16 y_start = surface_y+1;
2382 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2386 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2388 MapNode &n = vmanip.m_data[i];
2389 n.d = CONTENT_STONE;
2391 if(y > stone_surface_max_y)
2392 stone_surface_max_y = y;
2395 if(count >= ob_size.Y)
2398 vmanip.m_area.add_y(em, i, 1);
2402 for(; y<=y_nodes_max - min_head_space; y++)
2404 MapNode &n = vmanip.m_data[i];
2407 if(count >= mud_amount)
2410 vmanip.m_area.add_y(em, i, 1);
2420 //TimeTaker timer1("dungeons");
2425 u32 dungeons_count = relative_volume / 600000;
2426 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2427 if(stone_surface_max_y < WATER_LEVEL)
2429 /*u32 dungeons_count = 0;
2430 u32 bruises_count = 0;*/
2431 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2433 s16 min_tunnel_diameter = 2;
2434 s16 max_tunnel_diameter = 6;
2435 u16 tunnel_routepoints = 25;
2437 bool bruise_surface = (jj < bruises_count);
2441 min_tunnel_diameter = 5;
2442 max_tunnel_diameter = myrand_range(10, 20);
2443 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2444 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2446 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2447 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2449 tunnel_routepoints = 5;
2452 // Allowed route area size in nodes
2454 sectorpos_base_size*MAP_BLOCKSIZE,
2455 h_blocks*MAP_BLOCKSIZE,
2456 sectorpos_base_size*MAP_BLOCKSIZE
2459 // Area starting point in nodes
2461 sectorpos_base.X*MAP_BLOCKSIZE,
2462 y_blocks_min*MAP_BLOCKSIZE,
2463 sectorpos_base.Y*MAP_BLOCKSIZE
2467 //(this should be more than the maximum radius of the tunnel)
2468 //s16 insure = 5; // Didn't work with max_d = 20
2470 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2471 ar += v3s16(1,0,1) * more * 2;
2472 of -= v3s16(1,0,1) * more;
2474 s16 route_y_min = 0;
2475 // Allow half a diameter + 7 over stone surface
2476 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2478 /*// If dungeons, don't go through surface too often
2479 if(bruise_surface == false)
2480 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2482 // Limit maximum to area
2483 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2487 /*// Minimum is at y=0
2488 route_y_min = -of.Y - 0;*/
2489 // Minimum is at y=max_tunnel_diameter/4
2490 //route_y_min = -of.Y + max_tunnel_diameter/4;
2491 //s16 min = -of.Y + max_tunnel_diameter/4;
2492 s16 min = -of.Y + 0;
2493 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2494 route_y_min = rangelim(route_y_min, 0, route_y_max);
2497 /*dstream<<"route_y_min = "<<route_y_min
2498 <<", route_y_max = "<<route_y_max<<std::endl;*/
2500 s16 route_start_y_min = route_y_min;
2501 s16 route_start_y_max = route_y_max;
2503 // Start every 2nd dungeon from surface
2504 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2506 if(coming_from_surface)
2508 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2511 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2512 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2514 // Randomize starting position
2516 (float)(myrand()%ar.X)+0.5,
2517 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2518 (float)(myrand()%ar.Z)+0.5
2521 MapNode airnode(CONTENT_AIR);
2524 Generate some tunnel starting from orp
2527 for(u16 j=0; j<tunnel_routepoints; j++)
2530 s16 min_d = min_tunnel_diameter;
2531 s16 max_d = max_tunnel_diameter;
2532 s16 rs = myrand_range(min_d, max_d);
2537 maxlen = v3s16(rs*7,rs*7,rs*7);
2541 maxlen = v3s16(15, myrand_range(1, 20), 15);
2546 if(coming_from_surface && j < 3)
2549 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2550 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2551 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2557 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2558 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2559 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2566 else if(rp.X >= ar.X)
2568 if(rp.Y < route_y_min)
2570 else if(rp.Y >= route_y_max)
2571 rp.Y = route_y_max-1;
2574 else if(rp.Z >= ar.Z)
2578 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2580 v3f fp = orp + vec * f;
2581 v3s16 cp(fp.X, fp.Y, fp.Z);
2584 s16 d1 = d0 + rs - 1;
2585 for(s16 z0=d0; z0<=d1; z0++)
2587 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2588 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2589 for(s16 x0=-si; x0<=si-1; x0++)
2591 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2592 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2593 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2594 //s16 si2 = rs - abs(x0);
2595 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2601 /*if(isInArea(p, ar) == false)
2603 // Check only height
2604 if(y < 0 || y >= ar.Y)
2608 //assert(vmanip.m_area.contains(p));
2609 if(vmanip.m_area.contains(p) == false)
2611 dstream<<"WARNING: "<<__FUNCTION_NAME
2612 <<":"<<__LINE__<<": "
2613 <<"point not in area"
2618 // Just set it to air, it will be changed to
2620 u32 i = vmanip.m_area.index(p);
2621 vmanip.m_data[i] = airnode;
2623 if(bruise_surface == false)
2626 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2641 //TimeTaker timer1("ore veins");
2646 for(u32 jj=0; jj<relative_volume/1000; jj++)
2648 s16 max_vein_diameter = 3;
2650 // Allowed route area size in nodes
2652 sectorpos_base_size*MAP_BLOCKSIZE,
2653 h_blocks*MAP_BLOCKSIZE,
2654 sectorpos_base_size*MAP_BLOCKSIZE
2657 // Area starting point in nodes
2659 sectorpos_base.X*MAP_BLOCKSIZE,
2660 y_blocks_min*MAP_BLOCKSIZE,
2661 sectorpos_base.Y*MAP_BLOCKSIZE
2665 //(this should be more than the maximum radius of the tunnel)
2667 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2668 ar += v3s16(1,0,1) * more * 2;
2669 of -= v3s16(1,0,1) * more;
2671 // Randomize starting position
2673 (float)(myrand()%ar.X)+0.5,
2674 (float)(myrand()%ar.Y)+0.5,
2675 (float)(myrand()%ar.Z)+0.5
2678 // Randomize mineral
2681 mineral = MINERAL_COAL;
2683 mineral = MINERAL_IRON;
2686 Generate some vein starting from orp
2689 for(u16 j=0; j<2; j++)
2692 (float)(myrand()%ar.X)+0.5,
2693 (float)(myrand()%ar.Y)+0.5,
2694 (float)(myrand()%ar.Z)+0.5
2696 v3f vec = rp - orp;*/
2698 v3s16 maxlen(5, 5, 5);
2700 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2701 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2702 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2707 else if(rp.X >= ar.X)
2711 else if(rp.Y >= ar.Y)
2715 else if(rp.Z >= ar.Z)
2721 s16 max_d = max_vein_diameter;
2722 s16 rs = myrand_range(min_d, max_d);
2724 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2726 v3f fp = orp + vec * f;
2727 v3s16 cp(fp.X, fp.Y, fp.Z);
2729 s16 d1 = d0 + rs - 1;
2730 for(s16 z0=d0; z0<=d1; z0++)
2732 s16 si = rs - abs(z0);
2733 for(s16 x0=-si; x0<=si-1; x0++)
2735 s16 si2 = rs - abs(x0);
2736 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2738 // Don't put mineral to every place
2746 /*if(isInArea(p, ar) == false)
2748 // Check only height
2749 if(y < 0 || y >= ar.Y)
2753 assert(vmanip.m_area.contains(p));
2755 // Just set it to air, it will be changed to
2757 u32 i = vmanip.m_area.index(p);
2758 MapNode *n = &vmanip.m_data[i];
2759 if(n->d == CONTENT_STONE)
2774 //TimeTaker timer1("add mud");
2777 Add mud to the central chunk
2780 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2781 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2783 // Node position in 2d
2784 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2786 // Randomize mud amount
2787 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2788 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2789 m_seed+1, 3, 0.55));
2791 // Find ground level
2792 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2795 If topmost node is grass, change it to mud.
2796 It might be if it was flown to there from a neighboring
2797 chunk and then converted.
2800 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2801 MapNode *n = &vmanip.m_data[i];
2802 if(n->d == CONTENT_GRASS)
2811 v3s16 em = vmanip.m_area.getExtent();
2812 s16 y_start = surface_y+1;
2813 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2814 for(s16 y=y_start; y<=y_nodes_max; y++)
2816 if(mudcount >= mud_add_amount)
2819 MapNode &n = vmanip.m_data[i];
2823 vmanip.m_area.add_y(em, i, 1);
2832 TimeTaker timer1("flow mud");
2835 Flow mud away from steep edges
2838 // Limit area by 1 because mud is flown into neighbors.
2839 s16 mudflow_minpos = 0-max_spread_amount+1;
2840 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2842 // Iterate a few times
2843 for(s16 k=0; k<3; k++)
2846 for(s16 x=mudflow_minpos;
2849 for(s16 z=mudflow_minpos;
2853 // Invert coordinates every 2nd iteration
2856 x = mudflow_maxpos - (x-mudflow_minpos);
2857 z = mudflow_maxpos - (z-mudflow_minpos);
2860 // Node position in 2d
2861 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2863 v3s16 em = vmanip.m_area.getExtent();
2864 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2871 for(; y>=y_nodes_min; y--)
2873 n = &vmanip.m_data[i];
2874 //if(content_walkable(n->d))
2876 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2879 vmanip.m_area.add_y(em, i, -1);
2882 // Stop if out of area
2883 //if(vmanip.m_area.contains(i) == false)
2887 /*// If not mud, do nothing to it
2888 MapNode *n = &vmanip.m_data[i];
2889 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2893 Don't flow it if the stuff under it is not mud
2897 vmanip.m_area.add_y(em, i2, -1);
2898 // Cancel if out of area
2899 if(vmanip.m_area.contains(i2) == false)
2901 MapNode *n2 = &vmanip.m_data[i2];
2902 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2906 // Make it exactly mud
2909 /*s16 recurse_count = 0;
2913 v3s16(0,0,1), // back
2914 v3s16(1,0,0), // right
2915 v3s16(0,0,-1), // front
2916 v3s16(-1,0,0), // left
2919 // Theck that upper is air or doesn't exist.
2920 // Cancel dropping if upper keeps it in place
2922 vmanip.m_area.add_y(em, i3, 1);
2923 if(vmanip.m_area.contains(i3) == true
2924 && content_walkable(vmanip.m_data[i3].d) == true)
2931 for(u32 di=0; di<4; di++)
2933 v3s16 dirp = dirs4[di];
2936 vmanip.m_area.add_p(em, i2, dirp);
2937 // Fail if out of area
2938 if(vmanip.m_area.contains(i2) == false)
2940 // Check that side is air
2941 MapNode *n2 = &vmanip.m_data[i2];
2942 if(content_walkable(n2->d))
2944 // Check that under side is air
2945 vmanip.m_area.add_y(em, i2, -1);
2946 if(vmanip.m_area.contains(i2) == false)
2948 n2 = &vmanip.m_data[i2];
2949 if(content_walkable(n2->d))
2951 /*// Check that under that is air (need a drop of 2)
2952 vmanip.m_area.add_y(em, i2, -1);
2953 if(vmanip.m_area.contains(i2) == false)
2955 n2 = &vmanip.m_data[i2];
2956 if(content_walkable(n2->d))
2958 // Loop further down until not air
2960 vmanip.m_area.add_y(em, i2, -1);
2961 // Fail if out of area
2962 if(vmanip.m_area.contains(i2) == false)
2964 n2 = &vmanip.m_data[i2];
2965 }while(content_walkable(n2->d) == false);
2966 // Loop one up so that we're in air
2967 vmanip.m_area.add_y(em, i2, 1);
2968 n2 = &vmanip.m_data[i2];
2970 // Move mud to new place
2972 // Set old place to be air
2973 *n = MapNode(CONTENT_AIR);
2986 //TimeTaker timer1("add water");
2989 Add water to the central chunk (and a bit more)
2992 for(s16 x=0-max_spread_amount;
2993 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2995 for(s16 z=0-max_spread_amount;
2996 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2999 // Node position in 2d
3000 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3002 // Find ground level
3003 //s16 surface_y = find_ground_level(vmanip, p2d);
3006 If ground level is over water level, skip.
3007 NOTE: This leaves caves near water without water,
3008 which looks especially crappy when the nearby water
3009 won't start flowing either for some reason
3011 /*if(surface_y > WATER_LEVEL)
3018 v3s16 em = vmanip.m_area.getExtent();
3019 u8 light = LIGHT_MAX;
3020 // Start at global water surface level
3021 s16 y_start = WATER_LEVEL;
3022 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3023 MapNode *n = &vmanip.m_data[i];
3025 /*// Add first one to transforming liquid queue, if water
3026 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3028 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3029 m_transforming_liquid.push_back(p);
3032 for(s16 y=y_start; y>=y_nodes_min; y--)
3034 n = &vmanip.m_data[i];
3036 // Stop when there is no water and no air
3037 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3038 && n->d != CONTENT_WATER)
3040 /*// Add bottom one to transforming liquid queue
3041 vmanip.m_area.add_y(em, i, 1);
3042 n = &vmanip.m_data[i];
3043 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3045 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3046 m_transforming_liquid.push_back(p);
3052 // Make water only not in dungeons
3053 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3055 n->d = CONTENT_WATERSOURCE;
3056 //n->setLight(LIGHTBANK_DAY, light);
3058 // Add to transforming liquid queue (in case it'd
3060 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3061 m_transforming_liquid.push_back(p);
3065 vmanip.m_area.add_y(em, i, -1);
3078 //TimeTaker timer1("convert mud to sand");
3084 //s16 mud_add_amount = myrand_range(2, 4);
3085 //s16 mud_add_amount = 0;
3087 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3088 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3089 for(s16 x=0-max_spread_amount+1;
3090 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3092 for(s16 z=0-max_spread_amount+1;
3093 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3096 // Node position in 2d
3097 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3099 // Determine whether to have sand here
3100 double sandnoise = noise2d_perlin(
3101 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3102 m_seed+59420, 3, 0.50);
3104 bool have_sand = (sandnoise > -0.15);
3106 if(have_sand == false)
3109 // Find ground level
3110 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3112 if(surface_y > WATER_LEVEL + 2)
3116 v3s16 em = vmanip.m_area.getExtent();
3117 s16 y_start = surface_y;
3118 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3119 u32 not_sand_counter = 0;
3120 for(s16 y=y_start; y>=y_nodes_min; y--)
3122 MapNode *n = &vmanip.m_data[i];
3123 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3125 n->d = CONTENT_SAND;
3130 if(not_sand_counter > 3)
3134 vmanip.m_area.add_y(em, i, -1);
3143 //TimeTaker timer1("generate trees");
3149 // Divide area into parts
3151 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3152 double area = sidelen * sidelen;
3153 for(s16 x0=0; x0<div; x0++)
3154 for(s16 z0=0; z0<div; z0++)
3156 // Center position of part of division
3158 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3159 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3161 // Minimum edge of part of division
3163 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3164 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3166 // Maximum edge of part of division
3168 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3169 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3172 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3173 // Put trees in random places on part of division
3174 for(u32 i=0; i<tree_count; i++)
3176 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3177 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3178 s16 y = find_ground_level(vmanip, v2s16(x,z));
3179 // Don't make a tree under water level
3184 Trees grow only on mud and grass
3187 u32 i = vmanip.m_area.index(v3s16(p));
3188 MapNode *n = &vmanip.m_data[i];
3189 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3194 make_tree(vmanip, p);
3197 /*u32 tree_max = relative_area / 60;
3198 //u32 count = myrand_range(0, tree_max);
3199 for(u32 i=0; i<count; i++)
3201 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3202 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3203 x += sectorpos_base.X*MAP_BLOCKSIZE;
3204 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3205 s16 y = find_ground_level(vmanip, v2s16(x,z));
3206 // Don't make a tree under water level
3211 make_tree(vmanip, p);
3219 //TimeTaker timer1("grow grass");
3225 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3226 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3227 for(s16 x=0-max_spread_amount;
3228 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3230 for(s16 z=0-max_spread_amount;
3231 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3234 // Node position in 2d
3235 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3238 Find the lowest surface to which enough light ends up
3241 Basically just wait until not air and not leaves.
3245 v3s16 em = vmanip.m_area.getExtent();
3246 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3248 // Go to ground level
3249 for(y=y_nodes_max; y>=y_nodes_min; y--)
3251 MapNode &n = vmanip.m_data[i];
3252 if(n.d != CONTENT_AIR
3253 && n.d != CONTENT_LEAVES)
3255 vmanip.m_area.add_y(em, i, -1);
3257 if(y >= y_nodes_min)
3260 surface_y = y_nodes_min;
3263 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3264 MapNode *n = &vmanip.m_data[i];
3265 if(n->d == CONTENT_MUD)
3266 n->d = CONTENT_GRASS;
3272 Initial lighting (sunlight)
3275 core::map<v3s16, bool> light_sources;
3278 // 750ms @cs=8, can't optimize more
3279 TimeTaker timer1("initial lighting");
3283 Go through the edges and add all nodes that have light to light_sources
3287 for(s16 i=0; i<4; i++)
3289 for(s16 j=lighting_min_d;
3296 if(i == 0 || i == 1)
3298 x = (i==0) ? lighting_min_d : lighting_max_d;
3307 z = (i==0) ? lighting_min_d : lighting_max_d;
3314 // Node position in 2d
3315 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3318 v3s16 em = vmanip.m_area.getExtent();
3319 s16 y_start = y_nodes_max;
3320 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3321 for(s16 y=y_start; y>=y_nodes_min; y--)
3323 MapNode *n = &vmanip.m_data[i];
3324 if(n->getLight(LIGHTBANK_DAY) != 0)
3326 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3328 //NOTE: This is broken, at least the index has to
3337 Go through the edges and apply sunlight to them, not caring
3342 for(s16 i=0; i<4; i++)
3344 for(s16 j=lighting_min_d;
3351 if(i == 0 || i == 1)
3353 x = (i==0) ? lighting_min_d : lighting_max_d;
3362 z = (i==0) ? lighting_min_d : lighting_max_d;
3369 // Node position in 2d
3370 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3372 // Loop from top to down
3374 u8 light = LIGHT_SUN;
3375 v3s16 em = vmanip.m_area.getExtent();
3376 s16 y_start = y_nodes_max;
3377 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3378 for(s16 y=y_start; y>=y_nodes_min; y--)
3380 MapNode *n = &vmanip.m_data[i];
3381 if(light_propagates_content(n->d) == false)
3385 else if(light != LIGHT_SUN
3386 || sunlight_propagates_content(n->d) == false)
3392 n->setLight(LIGHTBANK_DAY, light);
3393 n->setLight(LIGHTBANK_NIGHT, 0);
3397 // Insert light source
3398 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3401 // Increment index by y
3402 vmanip.m_area.add_y(em, i, -1);
3408 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3409 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3410 /*for(s16 x=0-max_spread_amount+1;
3411 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3413 for(s16 z=0-max_spread_amount+1;
3414 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3418 This has to be 1 smaller than the actual area, because
3419 neighboring nodes are checked.
3421 for(s16 x=lighting_min_d+1;
3422 x<=lighting_max_d-1;
3424 for(s16 z=lighting_min_d+1;
3425 z<=lighting_max_d-1;
3428 // Node position in 2d
3429 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3432 Apply initial sunlight
3435 u8 light = LIGHT_SUN;
3436 bool add_to_sources = false;
3437 v3s16 em = vmanip.m_area.getExtent();
3438 s16 y_start = y_nodes_max;
3439 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3440 for(s16 y=y_start; y>=y_nodes_min; y--)
3442 MapNode *n = &vmanip.m_data[i];
3444 if(light_propagates_content(n->d) == false)
3448 else if(light != LIGHT_SUN
3449 || sunlight_propagates_content(n->d) == false)
3455 // This doesn't take much time
3456 if(add_to_sources == false)
3459 Check sides. If side is not air or water, start
3460 adding to light_sources.
3463 v3s16(0,0,1), // back
3464 v3s16(1,0,0), // right
3465 v3s16(0,0,-1), // front
3466 v3s16(-1,0,0), // left
3468 for(u32 di=0; di<4; di++)
3470 v3s16 dirp = dirs4[di];
3472 vmanip.m_area.add_p(em, i2, dirp);
3473 MapNode *n2 = &vmanip.m_data[i2];
3475 n2->d != CONTENT_AIR
3476 && n2->d != CONTENT_WATERSOURCE
3477 && n2->d != CONTENT_WATER
3479 add_to_sources = true;
3485 n->setLight(LIGHTBANK_DAY, light);
3486 n->setLight(LIGHTBANK_NIGHT, 0);
3488 // This doesn't take much time
3489 if(light != 0 && add_to_sources)
3491 // Insert light source
3492 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3495 // Increment index by y
3496 vmanip.m_area.add_y(em, i, -1);
3503 for(s16 x=lighting_min_d+1;
3504 x<=lighting_max_d-1;
3506 for(s16 z=lighting_min_d+1;
3507 z<=lighting_max_d-1;
3510 // Node position in 2d
3511 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3514 Apply initial sunlight
3517 u8 light = LIGHT_SUN;
3518 v3s16 em = vmanip.m_area.getExtent();
3519 s16 y_start = y_nodes_max;
3520 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3521 for(s16 y=y_start; y>=y_nodes_min; y--)
3523 MapNode *n = &vmanip.m_data[i];
3525 if(light_propagates_content(n->d) == false)
3529 else if(light != LIGHT_SUN
3530 || sunlight_propagates_content(n->d) == false)
3536 n->setLight(LIGHTBANK_DAY, light);
3537 n->setLight(LIGHTBANK_NIGHT, 0);
3539 // This doesn't take much time
3542 // Insert light source
3543 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3546 // Increment index by y
3547 vmanip.m_area.add_y(em, i, -1);
3555 // Spread light around
3557 TimeTaker timer("generateChunkRaw() spreadLight");
3558 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3565 timer_generate.stop();
3568 Blit generated stuff to map
3572 //TimeTaker timer("generateChunkRaw() blitBackAll");
3573 vmanip.blitBackAll(&changed_blocks);
3577 Update day/night difference cache of the MapBlocks
3580 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3581 i.atEnd() == false; i++)
3583 MapBlock *block = i.getNode()->getValue();
3584 block->updateDayNightDiff();
3590 Create chunk metadata
3593 for(s16 x=-1; x<=1; x++)
3594 for(s16 y=-1; y<=1; y++)
3596 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3597 // Add chunk meta information
3598 MapChunk *chunk = getChunk(chunkpos0);
3601 chunk = new MapChunk();
3602 m_chunks.insert(chunkpos0, chunk);
3604 //chunk->setIsVolatile(true);
3605 if(chunk->getGenLevel() > GENERATED_PARTLY)
3606 chunk->setGenLevel(GENERATED_PARTLY);
3610 Set central chunk non-volatile
3612 MapChunk *chunk = getChunk(chunkpos);
3615 //chunk->setIsVolatile(false);
3616 chunk->setGenLevel(GENERATED_FULLY);
3619 Save changed parts of map
3624 Return central chunk (which was requested)
3629 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3630 core::map<v3s16, MapBlock*> &changed_blocks)
3632 dstream<<"generateChunk(): Generating chunk "
3633 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3636 /*for(s16 x=-1; x<=1; x++)
3637 for(s16 y=-1; y<=1; y++)*/
3638 for(s16 x=-0; x<=0; x++)
3639 for(s16 y=-0; y<=0; y++)
3641 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3642 MapChunk *chunk = getChunk(chunkpos0);
3643 // Skip if already generated
3644 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3646 generateChunkRaw(chunkpos0, changed_blocks);
3649 assert(chunkNonVolatile(chunkpos1));
3651 MapChunk *chunk = getChunk(chunkpos1);
3655 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3657 DSTACK("%s: p2d=(%d,%d)",
3662 Check if it exists already in memory
3664 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3669 Try to load it from disk (with blocks)
3671 if(loadSectorFull(p2d) == true)
3673 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3676 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3677 throw InvalidPositionException("");
3683 Do not create over-limit
3685 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3686 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3687 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3688 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3689 throw InvalidPositionException("createSector(): pos. over limit");
3692 Generate blank sector
3695 sector = new ServerMapSector(this, p2d);
3697 // Sector position on map in nodes
3698 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3703 m_sectors.insert(p2d, sector);
3708 MapSector * ServerMap::emergeSector(v2s16 p2d,
3709 core::map<v3s16, MapBlock*> &changed_blocks)
3711 DSTACK("%s: p2d=(%d,%d)",
3718 v2s16 chunkpos = sector_to_chunk(p2d);
3719 /*bool chunk_nonvolatile = false;
3720 MapChunk *chunk = getChunk(chunkpos);
3721 if(chunk && chunk->getIsVolatile() == false)
3722 chunk_nonvolatile = true;*/
3723 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3726 If chunk is not fully generated, generate chunk
3728 if(chunk_nonvolatile == false)
3730 // Generate chunk and neighbors
3731 generateChunk(chunkpos, changed_blocks);
3735 Return sector if it exists now
3737 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3742 Try to load it from disk
3744 if(loadSectorFull(p2d) == true)
3746 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3749 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3750 throw InvalidPositionException("");
3756 generateChunk should have generated the sector
3760 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3761 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3765 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3767 return createSector(p2d);
3772 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3775 generateChunkRaw(chunkpos, changed_blocks, true);
3778 Return sector if it exists now
3780 sector = getSectorNoGenerateNoEx(p2d);
3784 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3792 //return generateSector();
3796 NOTE: This is not used for main map generation, only for blocks
3797 that are very high or low
3799 MapBlock * ServerMap::generateBlock(
3801 MapBlock *original_dummy,
3802 ServerMapSector *sector,
3803 core::map<v3s16, MapBlock*> &changed_blocks,
3804 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3807 DSTACK("%s: p=(%d,%d,%d)",
3811 /*dstream<<"generateBlock(): "
3812 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3815 MapBlock *block = original_dummy;
3817 v2s16 p2d(p.X, p.Z);
3819 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3822 Do not generate over-limit
3824 if(blockpos_over_limit(p))
3826 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3827 throw InvalidPositionException("generateBlock(): pos. over limit");
3831 If block doesn't exist, create one.
3832 If it exists, it is a dummy. In that case unDummify() it.
3834 NOTE: This already sets the map as the parent of the block
3838 block = sector->createBlankBlockNoInsert(block_y);
3842 // Remove the block so that nobody can get a half-generated one.
3843 sector->removeBlock(block);
3844 // Allocate the block to contain the generated data
3848 u8 water_material = CONTENT_WATERSOURCE;
3850 s32 lowest_ground_y = 32767;
3851 s32 highest_ground_y = -32768;
3853 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3854 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3856 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3858 //s16 surface_y = 0;
3860 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3861 + AVERAGE_MUD_AMOUNT;
3863 if(surface_y < lowest_ground_y)
3864 lowest_ground_y = surface_y;
3865 if(surface_y > highest_ground_y)
3866 highest_ground_y = surface_y;
3868 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3870 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3872 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3877 NOTE: If there are some man-made structures above the
3878 newly created block, they won't be taken into account.
3880 if(real_y > surface_y)
3881 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3887 // If node is over heightmap y, it's air or water
3888 if(real_y > surface_y)
3890 // If under water level, it's water
3891 if(real_y < WATER_LEVEL)
3893 n.d = water_material;
3894 n.setLight(LIGHTBANK_DAY,
3895 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3897 Add to transforming liquid queue (in case it'd
3900 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3901 m_transforming_liquid.push_back(real_pos);
3907 // Else it's ground or dungeons (air)
3910 // If it's surface_depth under ground, it's stone
3911 if(real_y <= surface_y - surface_depth)
3913 n.d = CONTENT_STONE;
3917 // It is mud if it is under the first ground
3918 // level or under water
3919 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3925 n.d = CONTENT_GRASS;
3928 //n.d = CONTENT_MUD;
3930 /*// If under water level, it's mud
3931 if(real_y < WATER_LEVEL)
3933 // Only the topmost node is grass
3934 else if(real_y <= surface_y - 1)
3937 n.d = CONTENT_GRASS;*/
3941 block->setNode(v3s16(x0,y0,z0), n);
3946 Calculate some helper variables
3949 // Completely underground if the highest part of block is under lowest
3951 // This has to be very sure; it's probably one too strict now but
3952 // that's just better.
3953 bool completely_underground =
3954 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3956 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3958 bool mostly_underwater_surface = false;
3959 if(highest_ground_y < WATER_LEVEL
3960 && some_part_underground && !completely_underground)
3961 mostly_underwater_surface = true;
3964 Get local attributes
3967 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3969 float caves_amount = 0.5;
3974 NOTE: BEWARE: Too big amount of attribute points slows verything
3976 1 interpolation from 5000 points takes 2-3ms.
3978 //TimeTaker timer("generateBlock() local attribute retrieval");
3979 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3980 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3981 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3985 //dstream<<"generateBlock(): Done"<<std::endl;
3991 // Initialize temporary table
3992 const s32 ued = MAP_BLOCKSIZE;
3993 bool underground_emptiness[ued*ued*ued];
3994 for(s32 i=0; i<ued*ued*ued; i++)
3996 underground_emptiness[i] = 0;
4003 Initialize orp and ors. Try to find if some neighboring
4004 MapBlock has a tunnel ended in its side
4008 (float)(myrand()%ued)+0.5,
4009 (float)(myrand()%ued)+0.5,
4010 (float)(myrand()%ued)+0.5
4013 bool found_existing = false;
4019 for(s16 y=0; y<ued; y++)
4020 for(s16 x=0; x<ued; x++)
4022 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4023 if(getNode(ap).d == CONTENT_AIR)
4025 orp = v3f(x+1,y+1,0);
4026 found_existing = true;
4027 goto continue_generating;
4031 catch(InvalidPositionException &e){}
4037 for(s16 y=0; y<ued; y++)
4038 for(s16 x=0; x<ued; x++)
4040 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4041 if(getNode(ap).d == CONTENT_AIR)
4043 orp = v3f(x+1,y+1,ued-1);
4044 found_existing = true;
4045 goto continue_generating;
4049 catch(InvalidPositionException &e){}
4055 for(s16 y=0; y<ued; y++)
4056 for(s16 z=0; z<ued; z++)
4058 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4059 if(getNode(ap).d == CONTENT_AIR)
4061 orp = v3f(0,y+1,z+1);
4062 found_existing = true;
4063 goto continue_generating;
4067 catch(InvalidPositionException &e){}
4073 for(s16 y=0; y<ued; y++)
4074 for(s16 z=0; z<ued; z++)
4076 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4077 if(getNode(ap).d == CONTENT_AIR)
4079 orp = v3f(ued-1,y+1,z+1);
4080 found_existing = true;
4081 goto continue_generating;
4085 catch(InvalidPositionException &e){}
4091 for(s16 x=0; x<ued; x++)
4092 for(s16 z=0; z<ued; z++)
4094 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4095 if(getNode(ap).d == CONTENT_AIR)
4097 orp = v3f(x+1,0,z+1);
4098 found_existing = true;
4099 goto continue_generating;
4103 catch(InvalidPositionException &e){}
4109 for(s16 x=0; x<ued; x++)
4110 for(s16 z=0; z<ued; z++)
4112 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4113 if(getNode(ap).d == CONTENT_AIR)
4115 orp = v3f(x+1,ued-1,z+1);
4116 found_existing = true;
4117 goto continue_generating;
4121 catch(InvalidPositionException &e){}
4123 continue_generating:
4126 Choose whether to actually generate dungeon
4128 bool do_generate_dungeons = true;
4129 // Don't generate if no part is underground
4130 if(!some_part_underground)
4132 do_generate_dungeons = false;
4134 // Don't generate if mostly underwater surface
4135 /*else if(mostly_underwater_surface)
4137 do_generate_dungeons = false;
4139 // Partly underground = cave
4140 else if(!completely_underground)
4142 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4144 // Found existing dungeon underground
4145 else if(found_existing && completely_underground)
4147 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4149 // Underground and no dungeons found
4152 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4155 if(do_generate_dungeons)
4158 Generate some tunnel starting from orp and ors
4160 for(u16 i=0; i<3; i++)
4163 (float)(myrand()%ued)+0.5,
4164 (float)(myrand()%ued)+0.5,
4165 (float)(myrand()%ued)+0.5
4169 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4173 for(float f=0; f<1.0; f+=0.04)
4175 v3f fp = orp + vec * f;
4176 v3s16 cp(fp.X, fp.Y, fp.Z);
4178 s16 d1 = d0 + rs - 1;
4179 for(s16 z0=d0; z0<=d1; z0++)
4181 s16 si = rs - abs(z0);
4182 for(s16 x0=-si; x0<=si-1; x0++)
4184 s16 si2 = rs - abs(x0);
4185 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4191 if(isInArea(p, ued) == false)
4193 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4205 // Set to true if has caves.
4206 // Set when some non-air is changed to air when making caves.
4207 bool has_dungeons = false;
4210 Apply temporary cave data to block
4213 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4214 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4216 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4218 MapNode n = block->getNode(v3s16(x0,y0,z0));
4221 if(underground_emptiness[
4222 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4223 +ued*(y0*ued/MAP_BLOCKSIZE)
4224 +(x0*ued/MAP_BLOCKSIZE)])
4226 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4229 has_dungeons = true;
4235 block->setNode(v3s16(x0,y0,z0), n);
4240 This is used for guessing whether or not the block should
4241 receive sunlight from the top if the block above doesn't exist
4243 block->setIsUnderground(completely_underground);
4246 Force lighting update if some part of block is partly
4247 underground and has caves.
4249 /*if(some_part_underground && !completely_underground && has_dungeons)
4251 //dstream<<"Half-ground caves"<<std::endl;
4252 lighting_invalidated_blocks[block->getPos()] = block;
4255 // DEBUG: Always update lighting
4256 //lighting_invalidated_blocks[block->getPos()] = block;
4262 if(some_part_underground)
4264 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4269 for(s16 i=0; i<underground_level/4 + 1; i++)
4271 if(myrand()%50 == 0)
4274 (myrand()%(MAP_BLOCKSIZE-2))+1,
4275 (myrand()%(MAP_BLOCKSIZE-2))+1,
4276 (myrand()%(MAP_BLOCKSIZE-2))+1
4282 for(u16 i=0; i<27; i++)
4284 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4286 block->setNode(cp+g_27dirs[i], n);
4294 u16 coal_amount = 30;
4295 u16 coal_rareness = 60 / coal_amount;
4296 if(coal_rareness == 0)
4298 if(myrand()%coal_rareness == 0)
4300 u16 a = myrand() % 16;
4301 u16 amount = coal_amount * a*a*a / 1000;
4302 for(s16 i=0; i<amount; i++)
4305 (myrand()%(MAP_BLOCKSIZE-2))+1,
4306 (myrand()%(MAP_BLOCKSIZE-2))+1,
4307 (myrand()%(MAP_BLOCKSIZE-2))+1
4311 n.d = CONTENT_STONE;
4312 n.param = MINERAL_COAL;
4314 for(u16 i=0; i<27; i++)
4316 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4318 block->setNode(cp+g_27dirs[i], n);
4326 //TODO: change to iron_amount or whatever
4327 u16 iron_amount = 15;
4328 u16 iron_rareness = 60 / iron_amount;
4329 if(iron_rareness == 0)
4331 if(myrand()%iron_rareness == 0)
4333 u16 a = myrand() % 16;
4334 u16 amount = iron_amount * a*a*a / 1000;
4335 for(s16 i=0; i<amount; i++)
4338 (myrand()%(MAP_BLOCKSIZE-2))+1,
4339 (myrand()%(MAP_BLOCKSIZE-2))+1,
4340 (myrand()%(MAP_BLOCKSIZE-2))+1
4344 n.d = CONTENT_STONE;
4345 n.param = MINERAL_IRON;
4347 for(u16 i=0; i<27; i++)
4349 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4351 block->setNode(cp+g_27dirs[i], n);
4358 Create a few rats in empty blocks underground
4360 if(completely_underground)
4362 //for(u16 i=0; i<2; i++)
4365 (myrand()%(MAP_BLOCKSIZE-2))+1,
4366 (myrand()%(MAP_BLOCKSIZE-2))+1,
4367 (myrand()%(MAP_BLOCKSIZE-2))+1
4370 // Check that the place is empty
4371 //if(!is_ground_content(block->getNode(cp).d))
4374 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4375 block->addObject(obj);
4381 Add block to sector.
4383 sector->insertBlock(block);
4385 // Lighting is invalid after generation.
4386 block->setLightingExpired(true);
4393 <<"lighting_invalidated_blocks.size()"
4397 <<" "<<lighting_invalidated_blocks.size()
4398 <<", "<<has_dungeons
4399 <<", "<<completely_underground
4400 <<", "<<some_part_underground
4407 MapBlock * ServerMap::createBlock(v3s16 p)
4409 DSTACK("%s: p=(%d,%d,%d)",
4410 __FUNCTION_NAME, p.X, p.Y, p.Z);
4413 Do not create over-limit
4415 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4416 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4417 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4418 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4419 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4420 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4421 throw InvalidPositionException("createBlock(): pos. over limit");
4423 v2s16 p2d(p.X, p.Z);
4426 This will create or load a sector if not found in memory.
4427 If block exists on disk, it will be loaded.
4429 NOTE: On old save formats, this will be slow, as it generates
4430 lighting on blocks for them.
4432 ServerMapSector *sector;
4434 sector = (ServerMapSector*)createSector(p2d);
4435 assert(sector->getId() == MAPSECTOR_SERVER);
4437 catch(InvalidPositionException &e)
4439 dstream<<"createBlock: createSector() failed"<<std::endl;
4443 NOTE: This should not be done, or at least the exception
4444 should not be passed on as std::exception, because it
4445 won't be catched at all.
4447 /*catch(std::exception &e)
4449 dstream<<"createBlock: createSector() failed: "
4450 <<e.what()<<std::endl;
4455 Try to get a block from the sector
4458 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4462 block = sector->createBlankBlock(block_y);
4466 MapBlock * ServerMap::emergeBlock(
4468 bool only_from_disk,
4469 core::map<v3s16, MapBlock*> &changed_blocks,
4470 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4473 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4475 p.X, p.Y, p.Z, only_from_disk);
4478 Do not generate over-limit
4480 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4481 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4482 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4483 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4484 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4485 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4486 throw InvalidPositionException("emergeBlock(): pos. over limit");
4488 v2s16 p2d(p.X, p.Z);
4491 This will create or load a sector if not found in memory.
4492 If block exists on disk, it will be loaded.
4494 ServerMapSector *sector;
4496 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4497 assert(sector->getId() == MAPSECTOR_SERVER);
4499 catch(InvalidPositionException &e)
4501 dstream<<"emergeBlock: emergeSector() failed: "
4502 <<e.what()<<std::endl;
4503 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4505 <<"You could try to delete it."<<std::endl;
4508 catch(VersionMismatchException &e)
4510 dstream<<"emergeBlock: emergeSector() failed: "
4511 <<e.what()<<std::endl;
4512 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4514 <<"You could try to delete it."<<std::endl;
4518 NOTE: This should not be done, or at least the exception
4519 should not be passed on as std::exception, because it
4520 won't be catched at all.
4522 /*catch(std::exception &e)
4524 dstream<<"emergeBlock: emergeSector() failed: "
4525 <<e.what()<<std::endl;
4526 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4528 <<"You could try to delete it."<<std::endl;
4533 Try to get a block from the sector
4536 bool does_not_exist = false;
4537 bool lighting_expired = false;
4538 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4542 does_not_exist = true;
4544 else if(block->isDummy() == true)
4546 does_not_exist = true;
4548 else if(block->getLightingExpired())
4550 lighting_expired = true;
4555 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4560 If block was not found on disk and not going to generate a
4561 new one, make sure there is a dummy block in place.
4563 if(only_from_disk && (does_not_exist || lighting_expired))
4565 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4569 // Create dummy block
4570 block = new MapBlock(this, p, true);
4572 // Add block to sector
4573 sector->insertBlock(block);
4579 //dstream<<"Not found on disk, generating."<<std::endl;
4581 //TimeTaker("emergeBlock() generate");
4583 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4586 If the block doesn't exist, generate the block.
4590 block = generateBlock(p, block, sector, changed_blocks,
4591 lighting_invalidated_blocks);
4594 if(lighting_expired)
4596 lighting_invalidated_blocks.insert(p, block);
4600 Initially update sunlight
4604 core::map<v3s16, bool> light_sources;
4605 bool black_air_left = false;
4606 bool bottom_invalid =
4607 block->propagateSunlight(light_sources, true,
4608 &black_air_left, true);
4610 // If sunlight didn't reach everywhere and part of block is
4611 // above ground, lighting has to be properly updated
4612 //if(black_air_left && some_part_underground)
4615 lighting_invalidated_blocks[block->getPos()] = block;
4620 lighting_invalidated_blocks[block->getPos()] = block;
4627 void ServerMap::createDir(std::string path)
4629 if(fs::CreateDir(path) == false)
4631 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4632 <<"\""<<path<<"\""<<std::endl;
4633 throw BaseException("ServerMap failed to create directory");
4637 std::string ServerMap::getSectorSubDir(v2s16 pos)
4640 snprintf(cc, 9, "%.4x%.4x",
4641 (unsigned int)pos.X&0xffff,
4642 (unsigned int)pos.Y&0xffff);
4644 return std::string(cc);
4647 std::string ServerMap::getSectorDir(v2s16 pos)
4649 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4652 v2s16 ServerMap::getSectorPos(std::string dirname)
4654 if(dirname.size() != 8)
4655 throw InvalidFilenameException("Invalid sector directory name");
4657 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4659 throw InvalidFilenameException("Invalid sector directory name");
4660 v2s16 pos((s16)x, (s16)y);
4664 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4666 v2s16 p2d = getSectorPos(sectordir);
4668 if(blockfile.size() != 4){
4669 throw InvalidFilenameException("Invalid block filename");
4672 int r = sscanf(blockfile.c_str(), "%4x", &y);
4674 throw InvalidFilenameException("Invalid block filename");
4675 return v3s16(p2d.X, y, p2d.Y);
4678 void ServerMap::save(bool only_changed)
4680 DSTACK(__FUNCTION_NAME);
4681 if(m_map_saving_enabled == false)
4683 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4687 if(only_changed == false)
4688 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4694 u32 sector_meta_count = 0;
4695 u32 block_count = 0;
4698 JMutexAutoLock lock(m_sector_mutex);
4700 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4701 for(; i.atEnd() == false; i++)
4703 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4704 assert(sector->getId() == MAPSECTOR_SERVER);
4706 if(sector->differs_from_disk || only_changed == false)
4708 saveSectorMeta(sector);
4709 sector_meta_count++;
4711 core::list<MapBlock*> blocks;
4712 sector->getBlocks(blocks);
4713 core::list<MapBlock*>::Iterator j;
4714 for(j=blocks.begin(); j!=blocks.end(); j++)
4716 MapBlock *block = *j;
4717 if(block->getChangedFlag() || only_changed == false)
4722 /*dstream<<"ServerMap: Written block ("
4723 <<block->getPos().X<<","
4724 <<block->getPos().Y<<","
4725 <<block->getPos().Z<<")"
4734 Only print if something happened or saved whole map
4736 if(only_changed == false || sector_meta_count != 0
4737 || block_count != 0)
4739 dstream<<DTIME<<"ServerMap: Written: "
4740 <<sector_meta_count<<" sector metadata files, "
4741 <<block_count<<" block files"
4746 void ServerMap::loadAll()
4748 DSTACK(__FUNCTION_NAME);
4749 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4754 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4756 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4758 JMutexAutoLock lock(m_sector_mutex);
4761 s32 printed_counter = -100000;
4762 s32 count = list.size();
4764 std::vector<fs::DirListNode>::iterator i;
4765 for(i=list.begin(); i!=list.end(); i++)
4767 if(counter > printed_counter + 10)
4769 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4770 printed_counter = counter;
4774 MapSector *sector = NULL;
4776 // We want directories
4780 sector = loadSectorMeta(i->name);
4782 catch(InvalidFilenameException &e)
4784 // This catches unknown crap in directory
4787 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4788 (m_savedir+"/sectors/"+i->name);
4789 std::vector<fs::DirListNode>::iterator i2;
4790 for(i2=list2.begin(); i2!=list2.end(); i2++)
4796 loadBlock(i->name, i2->name, sector);
4798 catch(InvalidFilenameException &e)
4800 // This catches unknown crap in directory
4804 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4808 void ServerMap::saveMasterHeightmap()
4810 DSTACK(__FUNCTION_NAME);
4812 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4814 createDir(m_savedir);
4816 /*std::string fullpath = m_savedir + "/master_heightmap";
4817 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4818 if(o.good() == false)
4819 throw FileNotGoodException("Cannot open master heightmap");*/
4821 // Format used for writing
4822 //u8 version = SER_FMT_VER_HIGHEST;
4825 void ServerMap::loadMasterHeightmap()
4827 DSTACK(__FUNCTION_NAME);
4829 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4831 /*std::string fullpath = m_savedir + "/master_heightmap";
4832 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4833 if(is.good() == false)
4834 throw FileNotGoodException("Cannot open master heightmap");*/
4838 void ServerMap::saveMapMeta()
4840 DSTACK(__FUNCTION_NAME);
4842 dstream<<"INFO: ServerMap::saveMapMeta(): "
4843 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4846 createDir(m_savedir);
4848 std::string fullpath = m_savedir + "/map_meta.txt";
4849 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4850 if(os.good() == false)
4852 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4853 <<"could not open"<<fullpath<<std::endl;
4854 throw FileNotGoodException("Cannot open chunk metadata");
4858 params.setU64("seed", m_seed);
4859 params.setS32("chunksize", m_chunksize);
4861 params.writeLines(os);
4863 os<<"[end_of_params]\n";
4867 void ServerMap::loadMapMeta()
4869 DSTACK(__FUNCTION_NAME);
4871 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4874 std::string fullpath = m_savedir + "/map_meta.txt";
4875 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4876 if(is.good() == false)
4878 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4879 <<"could not open"<<fullpath<<std::endl;
4880 throw FileNotGoodException("Cannot open chunk metadata");
4888 throw SerializationError
4889 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4891 std::getline(is, line);
4892 std::string trimmedline = trim(line);
4893 if(trimmedline == "[end_of_params]")
4895 params.parseConfigLine(line);
4898 m_seed = params.getU64("seed");
4899 m_chunksize = params.getS32("chunksize");
4901 dstream<<"INFO: ServerMap::loadMapMeta(): "
4902 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4906 void ServerMap::saveChunkMeta()
4908 DSTACK(__FUNCTION_NAME);
4910 u32 count = m_chunks.size();
4912 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4913 <<count<<" chunks"<<std::endl;
4915 createDir(m_savedir);
4917 std::string fullpath = m_savedir + "/chunk_meta";
4918 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4919 if(os.good() == false)
4921 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4922 <<"could not open"<<fullpath<<std::endl;
4923 throw FileNotGoodException("Cannot open chunk metadata");
4929 os.write((char*)&version, 1);
4934 writeU32(buf, count);
4935 os.write((char*)buf, 4);
4937 for(core::map<v2s16, MapChunk*>::Iterator
4938 i = m_chunks.getIterator();
4939 i.atEnd()==false; i++)
4941 v2s16 p = i.getNode()->getKey();
4942 MapChunk *chunk = i.getNode()->getValue();
4945 os.write((char*)buf, 4);
4947 chunk->serialize(os, version);
4951 void ServerMap::loadChunkMeta()
4953 DSTACK(__FUNCTION_NAME);
4955 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
4958 std::string fullpath = m_savedir + "/chunk_meta";
4959 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4960 if(is.good() == false)
4962 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
4963 <<"could not open"<<fullpath<<std::endl;
4964 throw FileNotGoodException("Cannot open chunk metadata");
4970 is.read((char*)&version, 1);
4975 is.read((char*)buf, 4);
4976 u32 count = readU32(buf);
4978 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
4979 <<count<<" chunks"<<std::endl;
4981 for(u32 i=0; i<count; i++)
4984 MapChunk *chunk = new MapChunk();
4986 is.read((char*)buf, 4);
4989 chunk->deSerialize(is, version);
4990 m_chunks.insert(p, chunk);
4994 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4996 DSTACK(__FUNCTION_NAME);
4997 // Format used for writing
4998 u8 version = SER_FMT_VER_HIGHEST;
5000 v2s16 pos = sector->getPos();
5001 createDir(m_savedir);
5002 createDir(m_savedir+"/sectors");
5003 std::string dir = getSectorDir(pos);
5006 std::string fullpath = dir + "/meta";
5007 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5008 if(o.good() == false)
5009 throw FileNotGoodException("Cannot open sector metafile");
5011 sector->serialize(o, version);
5013 sector->differs_from_disk = false;
5016 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5018 DSTACK(__FUNCTION_NAME);
5020 v2s16 p2d = getSectorPos(dirname);
5021 std::string dir = m_savedir + "/sectors/" + dirname;
5023 std::string fullpath = dir + "/meta";
5024 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5025 if(is.good() == false)
5026 throw FileNotGoodException("Cannot open sector metafile");
5028 ServerMapSector *sector = ServerMapSector::deSerialize
5029 (is, this, p2d, m_sectors);
5031 sector->differs_from_disk = false;
5036 bool ServerMap::loadSectorFull(v2s16 p2d)
5038 DSTACK(__FUNCTION_NAME);
5039 std::string sectorsubdir = getSectorSubDir(p2d);
5041 MapSector *sector = NULL;
5043 JMutexAutoLock lock(m_sector_mutex);
5046 sector = loadSectorMeta(sectorsubdir);
5048 catch(InvalidFilenameException &e)
5052 catch(FileNotGoodException &e)
5056 catch(std::exception &e)
5064 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5065 (m_savedir+"/sectors/"+sectorsubdir);
5066 std::vector<fs::DirListNode>::iterator i2;
5067 for(i2=list2.begin(); i2!=list2.end(); i2++)
5073 loadBlock(sectorsubdir, i2->name, sector);
5075 catch(InvalidFilenameException &e)
5077 // This catches unknown crap in directory
5083 void ServerMap::saveBlock(MapBlock *block)
5085 DSTACK(__FUNCTION_NAME);
5087 Dummy blocks are not written
5089 if(block->isDummy())
5091 /*v3s16 p = block->getPos();
5092 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5093 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5097 // Format used for writing
5098 u8 version = SER_FMT_VER_HIGHEST;
5100 v3s16 p3d = block->getPos();
5101 v2s16 p2d(p3d.X, p3d.Z);
5102 createDir(m_savedir);
5103 createDir(m_savedir+"/sectors");
5104 std::string dir = getSectorDir(p2d);
5107 // Block file is map/sectors/xxxxxxxx/xxxx
5109 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5110 std::string fullpath = dir + "/" + cc;
5111 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5112 if(o.good() == false)
5113 throw FileNotGoodException("Cannot open block data");
5116 [0] u8 serialization version
5119 o.write((char*)&version, 1);
5121 block->serialize(o, version);
5124 Versions up from 9 have block objects.
5128 block->serializeObjects(o, version);
5131 // We just wrote it to the disk
5132 block->resetChangedFlag();
5135 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5137 DSTACK(__FUNCTION_NAME);
5141 // Block file is map/sectors/xxxxxxxx/xxxx
5142 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5143 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5144 if(is.good() == false)
5145 throw FileNotGoodException("Cannot open block file");
5147 v3s16 p3d = getBlockPos(sectordir, blockfile);
5148 v2s16 p2d(p3d.X, p3d.Z);
5150 assert(sector->getPos() == p2d);
5152 u8 version = SER_FMT_VER_INVALID;
5153 is.read((char*)&version, 1);
5156 throw SerializationError("ServerMap::loadBlock(): Failed"
5157 " to read MapBlock version");
5159 /*u32 block_size = MapBlock::serializedLength(version);
5160 SharedBuffer<u8> data(block_size);
5161 is.read((char*)*data, block_size);*/
5163 // This will always return a sector because we're the server
5164 //MapSector *sector = emergeSector(p2d);
5166 MapBlock *block = NULL;
5167 bool created_new = false;
5169 block = sector->getBlockNoCreate(p3d.Y);
5171 catch(InvalidPositionException &e)
5173 block = sector->createBlankBlockNoInsert(p3d.Y);
5177 // deserialize block data
5178 block->deSerialize(is, version);
5181 Versions up from 9 have block objects.
5185 block->updateObjects(is, version, NULL, 0);
5189 sector->insertBlock(block);
5192 Convert old formats to new and save
5195 // Save old format blocks in new format
5196 if(version < SER_FMT_VER_HIGHEST)
5201 // We just loaded it from the disk, so it's up-to-date.
5202 block->resetChangedFlag();
5205 catch(SerializationError &e)
5207 dstream<<"WARNING: Invalid block data on disk "
5208 "(SerializationError). Ignoring. "
5209 "A new one will be generated."
5214 // Gets from master heightmap
5215 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5217 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5218 //assert(m_heightmap != NULL);
5226 /*corners[0] = m_heightmap->getGroundHeight
5227 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5228 corners[1] = m_heightmap->getGroundHeight
5229 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5230 corners[2] = m_heightmap->getGroundHeight
5231 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5232 corners[3] = m_heightmap->getGroundHeight
5233 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
5236 void ServerMap::PrintInfo(std::ostream &out)
5247 ClientMap::ClientMap(
5249 MapDrawControl &control,
5250 scene::ISceneNode* parent,
5251 scene::ISceneManager* mgr,
5255 scene::ISceneNode(parent, mgr, id),
5259 //mesh_mutex.Init();
5261 /*m_box = core::aabbox3d<f32>(0,0,0,
5262 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5263 /*m_box = core::aabbox3d<f32>(0,0,0,
5264 map->getSizeNodes().X * BS,
5265 map->getSizeNodes().Y * BS,
5266 map->getSizeNodes().Z * BS);*/
5267 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5268 BS*1000000,BS*1000000,BS*1000000);
5270 //setPosition(v3f(BS,BS,BS));
5273 ClientMap::~ClientMap()
5275 /*JMutexAutoLock lock(mesh_mutex);
5284 MapSector * ClientMap::emergeSector(v2s16 p2d)
5286 DSTACK(__FUNCTION_NAME);
5287 // Check that it doesn't exist already
5289 return getSectorNoGenerate(p2d);
5291 catch(InvalidPositionException &e)
5296 ClientMapSector *sector = new ClientMapSector(this, p2d);
5299 JMutexAutoLock lock(m_sector_mutex);
5300 m_sectors.insert(p2d, sector);
5306 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5308 DSTACK(__FUNCTION_NAME);
5309 ClientMapSector *sector = NULL;
5311 JMutexAutoLock lock(m_sector_mutex);
5313 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5317 sector = (ClientMapSector*)n->getValue();
5318 assert(sector->getId() == MAPSECTOR_CLIENT);
5322 sector = new ClientMapSector(this, p2d);
5324 JMutexAutoLock lock(m_sector_mutex);
5325 m_sectors.insert(p2d, sector);
5329 sector->deSerialize(is);
5332 void ClientMap::OnRegisterSceneNode()
5336 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5337 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5340 ISceneNode::OnRegisterSceneNode();
5343 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5345 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5346 DSTACK(__FUNCTION_NAME);
5348 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5351 Get time for measuring timeout.
5353 Measuring time is very useful for long delays when the
5354 machine is swapping a lot.
5356 int time1 = time(0);
5358 u32 daynight_ratio = m_client->getDayNightRatio();
5360 m_camera_mutex.Lock();
5361 v3f camera_position = m_camera_position;
5362 v3f camera_direction = m_camera_direction;
5363 m_camera_mutex.Unlock();
5366 Get all blocks and draw all visible ones
5369 v3s16 cam_pos_nodes(
5370 camera_position.X / BS,
5371 camera_position.Y / BS,
5372 camera_position.Z / BS);
5374 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5376 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5377 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5379 // Take a fair amount as we will be dropping more out later
5381 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5382 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5383 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5385 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5386 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5387 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5389 u32 vertex_count = 0;
5391 // For limiting number of mesh updates per frame
5392 u32 mesh_update_count = 0;
5394 u32 blocks_would_have_drawn = 0;
5395 u32 blocks_drawn = 0;
5397 //NOTE: The sectors map should be locked but we're not doing it
5398 // because it'd cause too much delays
5400 int timecheck_counter = 0;
5401 core::map<v2s16, MapSector*>::Iterator si;
5402 si = m_sectors.getIterator();
5403 for(; si.atEnd() == false; si++)
5406 timecheck_counter++;
5407 if(timecheck_counter > 50)
5409 timecheck_counter = 0;
5410 int time2 = time(0);
5411 if(time2 > time1 + 4)
5413 dstream<<"ClientMap::renderMap(): "
5414 "Rendering takes ages, returning."
5421 MapSector *sector = si.getNode()->getValue();
5422 v2s16 sp = sector->getPos();
5424 if(m_control.range_all == false)
5426 if(sp.X < p_blocks_min.X
5427 || sp.X > p_blocks_max.X
5428 || sp.Y < p_blocks_min.Z
5429 || sp.Y > p_blocks_max.Z)
5433 core::list< MapBlock * > sectorblocks;
5434 sector->getBlocks(sectorblocks);
5440 core::list< MapBlock * >::Iterator i;
5441 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5443 MapBlock *block = *i;
5446 Compare block position to camera position, skip
5447 if not seen on display
5450 float range = 100000 * BS;
5451 if(m_control.range_all == false)
5452 range = m_control.wanted_range * BS;
5455 if(isBlockInSight(block->getPos(), camera_position,
5456 camera_direction, range, &d) == false)
5462 /*if(m_control.range_all == false &&
5463 d - 0.5*BS*MAP_BLOCKSIZE > range)
5468 Update expired mesh (used for day/night change)
5471 bool mesh_expired = false;
5474 JMutexAutoLock lock(block->mesh_mutex);
5476 mesh_expired = block->getMeshExpired();
5478 // Mesh has not been expired and there is no mesh:
5479 // block has no content
5480 if(block->mesh == NULL && mesh_expired == false)
5484 f32 faraway = BS*50;
5485 //f32 faraway = m_control.wanted_range * BS;
5488 This has to be done with the mesh_mutex unlocked
5490 // Pretty random but this should work somewhat nicely
5491 if(mesh_expired && (
5492 (mesh_update_count < 3
5493 && (d < faraway || mesh_update_count < 2)
5496 (m_control.range_all && mesh_update_count < 20)
5499 /*if(mesh_expired && mesh_update_count < 6
5500 && (d < faraway || mesh_update_count < 3))*/
5502 mesh_update_count++;
5504 // Mesh has been expired: generate new mesh
5505 //block->updateMeshes(daynight_i);
5506 block->updateMesh(daynight_ratio);
5508 mesh_expired = false;
5512 Don't draw an expired mesh that is far away
5514 /*if(mesh_expired && d >= faraway)
5517 // Instead, delete it
5518 JMutexAutoLock lock(block->mesh_mutex);
5521 block->mesh->drop();
5524 // And continue to next block
5529 Draw the faces of the block
5532 JMutexAutoLock lock(block->mesh_mutex);
5534 scene::SMesh *mesh = block->mesh;
5539 blocks_would_have_drawn++;
5540 if(blocks_drawn >= m_control.wanted_max_blocks
5541 && m_control.range_all == false
5542 && d > m_control.wanted_min_range * BS)
5546 u32 c = mesh->getMeshBufferCount();
5548 for(u32 i=0; i<c; i++)
5550 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5551 const video::SMaterial& material = buf->getMaterial();
5552 video::IMaterialRenderer* rnd =
5553 driver->getMaterialRenderer(material.MaterialType);
5554 bool transparent = (rnd && rnd->isTransparent());
5555 // Render transparent on transparent pass and likewise.
5556 if(transparent == is_transparent_pass)
5559 This *shouldn't* hurt too much because Irrlicht
5560 doesn't change opengl textures if the old
5561 material is set again.
5563 driver->setMaterial(buf->getMaterial());
5564 driver->drawMeshBuffer(buf);
5565 vertex_count += buf->getVertexCount();
5569 } // foreach sectorblocks
5572 m_control.blocks_drawn = blocks_drawn;
5573 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5575 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5576 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5579 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5580 core::map<v3s16, MapBlock*> *affected_blocks)
5582 bool changed = false;
5584 Add it to all blocks touching it
5587 v3s16(0,0,0), // this
5588 v3s16(0,0,1), // back
5589 v3s16(0,1,0), // top
5590 v3s16(1,0,0), // right
5591 v3s16(0,0,-1), // front
5592 v3s16(0,-1,0), // bottom
5593 v3s16(-1,0,0), // left
5595 for(u16 i=0; i<7; i++)
5597 v3s16 p2 = p + dirs[i];
5598 // Block position of neighbor (or requested) node
5599 v3s16 blockpos = getNodeBlockPos(p2);
5600 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5601 if(blockref == NULL)
5603 // Relative position of requested node
5604 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5605 if(blockref->setTempMod(relpos, mod))
5610 if(changed && affected_blocks!=NULL)
5612 for(u16 i=0; i<7; i++)
5614 v3s16 p2 = p + dirs[i];
5615 // Block position of neighbor (or requested) node
5616 v3s16 blockpos = getNodeBlockPos(p2);
5617 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5618 if(blockref == NULL)
5620 affected_blocks->insert(blockpos, blockref);
5626 bool ClientMap::clearTempMod(v3s16 p,
5627 core::map<v3s16, MapBlock*> *affected_blocks)
5629 bool changed = false;
5631 v3s16(0,0,0), // this
5632 v3s16(0,0,1), // back
5633 v3s16(0,1,0), // top
5634 v3s16(1,0,0), // right
5635 v3s16(0,0,-1), // front
5636 v3s16(0,-1,0), // bottom
5637 v3s16(-1,0,0), // left
5639 for(u16 i=0; i<7; i++)
5641 v3s16 p2 = p + dirs[i];
5642 // Block position of neighbor (or requested) node
5643 v3s16 blockpos = getNodeBlockPos(p2);
5644 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5645 if(blockref == NULL)
5647 // Relative position of requested node
5648 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5649 if(blockref->clearTempMod(relpos))
5654 if(changed && affected_blocks!=NULL)
5656 for(u16 i=0; i<7; i++)
5658 v3s16 p2 = p + dirs[i];
5659 // Block position of neighbor (or requested) node
5660 v3s16 blockpos = getNodeBlockPos(p2);
5661 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5662 if(blockref == NULL)
5664 affected_blocks->insert(blockpos, blockref);
5670 void ClientMap::PrintInfo(std::ostream &out)
5681 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5686 MapVoxelManipulator::~MapVoxelManipulator()
5688 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5692 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5694 TimeTaker timer1("emerge", &emerge_time);
5696 // Units of these are MapBlocks
5697 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5698 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5700 VoxelArea block_area_nodes
5701 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5703 addArea(block_area_nodes);
5705 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5706 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5707 for(s32 x=p_min.X; x<=p_max.X; x++)
5710 core::map<v3s16, bool>::Node *n;
5711 n = m_loaded_blocks.find(p);
5715 bool block_data_inexistent = false;
5718 TimeTaker timer1("emerge load", &emerge_load_time);
5720 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5721 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5724 dstream<<std::endl;*/
5726 MapBlock *block = m_map->getBlockNoCreate(p);
5727 if(block->isDummy())
5728 block_data_inexistent = true;
5730 block->copyTo(*this);
5732 catch(InvalidPositionException &e)
5734 block_data_inexistent = true;
5737 if(block_data_inexistent)
5739 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5740 // Fill with VOXELFLAG_INEXISTENT
5741 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5742 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5744 s32 i = m_area.index(a.MinEdge.X,y,z);
5745 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5749 m_loaded_blocks.insert(p, !block_data_inexistent);
5752 //dstream<<"emerge done"<<std::endl;
5756 SUGG: Add an option to only update eg. water and air nodes.
5757 This will make it interfere less with important stuff if
5760 void MapVoxelManipulator::blitBack
5761 (core::map<v3s16, MapBlock*> & modified_blocks)
5763 if(m_area.getExtent() == v3s16(0,0,0))
5766 //TimeTaker timer1("blitBack");
5768 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5769 <<m_loaded_blocks.size()<<std::endl;*/
5772 Initialize block cache
5774 v3s16 blockpos_last;
5775 MapBlock *block = NULL;
5776 bool block_checked_in_modified = false;
5778 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5779 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5780 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5784 u8 f = m_flags[m_area.index(p)];
5785 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5788 MapNode &n = m_data[m_area.index(p)];
5790 v3s16 blockpos = getNodeBlockPos(p);
5795 if(block == NULL || blockpos != blockpos_last){
5796 block = m_map->getBlockNoCreate(blockpos);
5797 blockpos_last = blockpos;
5798 block_checked_in_modified = false;
5801 // Calculate relative position in block
5802 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5804 // Don't continue if nothing has changed here
5805 if(block->getNode(relpos) == n)
5808 //m_map->setNode(m_area.MinEdge + p, n);
5809 block->setNode(relpos, n);
5812 Make sure block is in modified_blocks
5814 if(block_checked_in_modified == false)
5816 modified_blocks[blockpos] = block;
5817 block_checked_in_modified = true;
5820 catch(InvalidPositionException &e)
5826 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5827 MapVoxelManipulator(map)
5831 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5835 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5837 // Just create the area so that it can be pointed to
5838 VoxelManipulator::emerge(a, caller_id);
5841 void ManualMapVoxelManipulator::initialEmerge(
5842 v3s16 blockpos_min, v3s16 blockpos_max)
5844 TimeTaker timer1("initialEmerge", &emerge_time);
5846 // Units of these are MapBlocks
5847 v3s16 p_min = blockpos_min;
5848 v3s16 p_max = blockpos_max;
5850 VoxelArea block_area_nodes
5851 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5853 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5856 dstream<<"initialEmerge: area: ";
5857 block_area_nodes.print(dstream);
5858 dstream<<" ("<<size_MB<<"MB)";
5862 addArea(block_area_nodes);
5864 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5865 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5866 for(s32 x=p_min.X; x<=p_max.X; x++)
5869 core::map<v3s16, bool>::Node *n;
5870 n = m_loaded_blocks.find(p);
5874 bool block_data_inexistent = false;
5877 TimeTaker timer1("emerge load", &emerge_load_time);
5879 MapBlock *block = m_map->getBlockNoCreate(p);
5880 if(block->isDummy())
5881 block_data_inexistent = true;
5883 block->copyTo(*this);
5885 catch(InvalidPositionException &e)
5887 block_data_inexistent = true;
5890 if(block_data_inexistent)
5893 Mark area inexistent
5895 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5896 // Fill with VOXELFLAG_INEXISTENT
5897 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5898 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5900 s32 i = m_area.index(a.MinEdge.X,y,z);
5901 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5905 m_loaded_blocks.insert(p, !block_data_inexistent);
5909 void ManualMapVoxelManipulator::blitBackAll(
5910 core::map<v3s16, MapBlock*> * modified_blocks)
5912 if(m_area.getExtent() == v3s16(0,0,0))
5916 Copy data of all blocks
5918 for(core::map<v3s16, bool>::Iterator
5919 i = m_loaded_blocks.getIterator();
5920 i.atEnd() == false; i++)
5922 bool existed = i.getNode()->getValue();
5923 if(existed == false)
5925 v3s16 p = i.getNode()->getKey();
5926 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5929 dstream<<"WARNING: "<<__FUNCTION_NAME
5930 <<": got NULL block "
5931 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5936 block->copyFrom(*this);
5939 modified_blocks->insert(p, block);