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):
39 m_sector_mutex.Init();
40 assert(m_sector_mutex.IsInitialized());
48 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
49 for(; i.atEnd() == false; i++)
51 MapSector *sector = i.getNode()->getValue();
56 void Map::addEventReceiver(MapEventReceiver *event_receiver)
58 m_event_receivers.insert(event_receiver, false);
61 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
63 if(m_event_receivers.find(event_receiver) == NULL)
65 m_event_receivers.remove(event_receiver);
68 void Map::dispatchEvent(MapEditEvent *event)
70 for(core::map<MapEventReceiver*, bool>::Iterator
71 i = m_event_receivers.getIterator();
72 i.atEnd()==false; i++)
74 MapEventReceiver* event_receiver = i.getNode()->getKey();
75 event_receiver->onMapEditEvent(event);
79 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
81 if(m_sector_cache != NULL && p == m_sector_cache_p){
82 MapSector * sector = m_sector_cache;
83 // Reset inactivity timer
84 sector->usage_timer = 0.0;
88 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
93 MapSector *sector = n->getValue();
95 // Cache the last result
97 m_sector_cache = sector;
99 // Reset inactivity timer
100 sector->usage_timer = 0.0;
104 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
106 JMutexAutoLock lock(m_sector_mutex);
108 return getSectorNoGenerateNoExNoLock(p);
111 MapSector * Map::getSectorNoGenerate(v2s16 p)
113 MapSector *sector = getSectorNoGenerateNoEx(p);
115 throw InvalidPositionException();
120 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
122 v2s16 p2d(p3d.X, p3d.Z);
123 MapSector * sector = getSectorNoGenerate(p2d);
125 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
134 v2s16 p2d(p3d.X, p3d.Z);
135 MapSector * sector = getSectorNoGenerate(p2d);
136 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
139 catch(InvalidPositionException &e)
145 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorCreate(p2d);
150 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
153 block = sector->createBlankBlock(p3d.Y);
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
171 Goes recursively through the neighbours of the node.
173 Alters only transparent nodes.
175 If the lighting of the neighbour is lower than the lighting of
176 the node was (before changing it to 0 at the step before), the
177 lighting of the neighbour is set to 0 and then the same stuff
178 repeats for the neighbour.
180 The ending nodes of the routine are stored in light_sources.
181 This is useful when a light is removed. In such case, this
182 routine can be called for the light node and then again for
183 light_sources to re-light the area without the removed light.
185 values of from_nodes are lighting values.
187 There is a duplicate implementation of this in VoxelManipulator,
188 which is faster for large volumes
190 void Map::unspreadLight(enum LightBank bank,
191 core::map<v3s16, u8> & from_nodes,
192 core::map<v3s16, bool> & light_sources,
193 core::map<v3s16, MapBlock*> & modified_blocks)
196 v3s16(0,0,1), // back
198 v3s16(1,0,0), // right
199 v3s16(0,0,-1), // front
200 v3s16(0,-1,0), // bottom
201 v3s16(-1,0,0), // left
204 if(from_nodes.size() == 0)
207 u32 blockchangecount = 0;
209 core::map<v3s16, u8> unlighted_nodes;
210 core::map<v3s16, u8>::Iterator j;
211 j = from_nodes.getIterator();
214 Initialize block cache
217 MapBlock *block = NULL;
218 // Cache this a bit, too
219 bool block_checked_in_modified = false;
221 for(; j.atEnd() == false; j++)
223 v3s16 pos = j.getNode()->getKey();
224 v3s16 blockpos = getNodeBlockPos(pos);
226 // Only fetch a new block if the block position has changed
228 if(block == NULL || blockpos != blockpos_last){
229 block = getBlockNoCreate(blockpos);
230 blockpos_last = blockpos;
232 block_checked_in_modified = false;
236 catch(InvalidPositionException &e)
244 // Calculate relative position in block
245 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
247 // Get node straight from the block
248 MapNode n = block->getNode(relpos);
250 u8 oldlight = j.getNode()->getValue();
252 // Loop through 6 neighbors
253 for(u16 i=0; i<6; i++)
255 // Get the position of the neighbor node
256 v3s16 n2pos = pos + dirs[i];
258 // Get the block where the node is located
259 v3s16 blockpos = getNodeBlockPos(n2pos);
263 // Only fetch a new block if the block position has changed
265 if(block == NULL || blockpos != blockpos_last){
266 block = getBlockNoCreate(blockpos);
267 blockpos_last = blockpos;
269 block_checked_in_modified = false;
273 catch(InvalidPositionException &e)
278 // Calculate relative position in block
279 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
280 // Get node straight from the block
281 MapNode n2 = block->getNode(relpos);
283 bool changed = false;
285 //TODO: Optimize output by optimizing light_sources?
288 If the neighbor is dimmer than what was specified
289 as oldlight (the light of the previous node)
291 if(n2.getLight(bank) < oldlight)
294 And the neighbor is transparent and it has some light
296 if(n2.light_propagates() && n2.getLight(bank) != 0)
299 Set light to 0 and add to queue
302 u8 current_light = n2.getLight(bank);
303 n2.setLight(bank, 0);
304 block->setNode(relpos, n2);
306 unlighted_nodes.insert(n2pos, current_light);
310 Remove from light_sources if it is there
311 NOTE: This doesn't happen nearly at all
313 /*if(light_sources.find(n2pos))
315 std::cout<<"Removed from light_sources"<<std::endl;
316 light_sources.remove(n2pos);
321 if(light_sources.find(n2pos) != NULL)
322 light_sources.remove(n2pos);*/
325 light_sources.insert(n2pos, true);
328 // Add to modified_blocks
329 if(changed == true && block_checked_in_modified == false)
331 // If the block is not found in modified_blocks, add.
332 if(modified_blocks.find(blockpos) == NULL)
334 modified_blocks.insert(blockpos, block);
336 block_checked_in_modified = true;
339 catch(InvalidPositionException &e)
346 /*dstream<<"unspreadLight(): Changed block "
347 <<blockchangecount<<" times"
348 <<" for "<<from_nodes.size()<<" nodes"
351 if(unlighted_nodes.size() > 0)
352 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
356 A single-node wrapper of the above
358 void Map::unLightNeighbors(enum LightBank bank,
359 v3s16 pos, u8 lightwas,
360 core::map<v3s16, bool> & light_sources,
361 core::map<v3s16, MapBlock*> & modified_blocks)
363 core::map<v3s16, u8> from_nodes;
364 from_nodes.insert(pos, lightwas);
366 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
370 Lights neighbors of from_nodes, collects all them and then
373 There is a duplicate implementation of this in VoxelManipulator,
374 which is faster for large volumes
376 void Map::spreadLight(enum LightBank bank,
377 core::map<v3s16, bool> & from_nodes,
378 core::map<v3s16, MapBlock*> & modified_blocks)
380 const v3s16 dirs[6] = {
381 v3s16(0,0,1), // back
383 v3s16(1,0,0), // right
384 v3s16(0,0,-1), // front
385 v3s16(0,-1,0), // bottom
386 v3s16(-1,0,0), // left
389 if(from_nodes.size() == 0)
392 u32 blockchangecount = 0;
394 core::map<v3s16, bool> lighted_nodes;
395 core::map<v3s16, bool>::Iterator j;
396 j = from_nodes.getIterator();
399 Initialize block cache
402 MapBlock *block = NULL;
403 // Cache this a bit, too
404 bool block_checked_in_modified = false;
406 for(; j.atEnd() == false; j++)
407 //for(; j != from_nodes.end(); j++)
409 v3s16 pos = j.getNode()->getKey();
411 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
412 v3s16 blockpos = getNodeBlockPos(pos);
414 // Only fetch a new block if the block position has changed
416 if(block == NULL || blockpos != blockpos_last){
417 block = getBlockNoCreate(blockpos);
418 blockpos_last = blockpos;
420 block_checked_in_modified = false;
424 catch(InvalidPositionException &e)
432 // Calculate relative position in block
433 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
435 // Get node straight from the block
436 MapNode n = block->getNode(relpos);
438 u8 oldlight = n.getLight(bank);
439 u8 newlight = diminish_light(oldlight);
441 // Loop through 6 neighbors
442 for(u16 i=0; i<6; i++){
443 // Get the position of the neighbor node
444 v3s16 n2pos = pos + dirs[i];
446 // Get the block where the node is located
447 v3s16 blockpos = getNodeBlockPos(n2pos);
451 // Only fetch a new block if the block position has changed
453 if(block == NULL || blockpos != blockpos_last){
454 block = getBlockNoCreate(blockpos);
455 blockpos_last = blockpos;
457 block_checked_in_modified = false;
461 catch(InvalidPositionException &e)
466 // Calculate relative position in block
467 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
468 // Get node straight from the block
469 MapNode n2 = block->getNode(relpos);
471 bool changed = false;
473 If the neighbor is brighter than the current node,
474 add to list (it will light up this node on its turn)
476 if(n2.getLight(bank) > undiminish_light(oldlight))
478 lighted_nodes.insert(n2pos, true);
479 //lighted_nodes.push_back(n2pos);
483 If the neighbor is dimmer than how much light this node
484 would spread on it, add to list
486 if(n2.getLight(bank) < newlight)
488 if(n2.light_propagates())
490 n2.setLight(bank, newlight);
491 block->setNode(relpos, n2);
492 lighted_nodes.insert(n2pos, true);
493 //lighted_nodes.push_back(n2pos);
498 // Add to modified_blocks
499 if(changed == true && block_checked_in_modified == false)
501 // If the block is not found in modified_blocks, add.
502 if(modified_blocks.find(blockpos) == NULL)
504 modified_blocks.insert(blockpos, block);
506 block_checked_in_modified = true;
509 catch(InvalidPositionException &e)
516 /*dstream<<"spreadLight(): Changed block "
517 <<blockchangecount<<" times"
518 <<" for "<<from_nodes.size()<<" nodes"
521 if(lighted_nodes.size() > 0)
522 spreadLight(bank, lighted_nodes, modified_blocks);
526 A single-node source variation of the above.
528 void Map::lightNeighbors(enum LightBank bank,
530 core::map<v3s16, MapBlock*> & modified_blocks)
532 core::map<v3s16, bool> from_nodes;
533 from_nodes.insert(pos, true);
534 spreadLight(bank, from_nodes, modified_blocks);
537 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
540 v3s16(0,0,1), // back
542 v3s16(1,0,0), // right
543 v3s16(0,0,-1), // front
544 v3s16(0,-1,0), // bottom
545 v3s16(-1,0,0), // left
548 u8 brightest_light = 0;
549 v3s16 brightest_pos(0,0,0);
550 bool found_something = false;
552 // Loop through 6 neighbors
553 for(u16 i=0; i<6; i++){
554 // Get the position of the neighbor node
555 v3s16 n2pos = p + dirs[i];
560 catch(InvalidPositionException &e)
564 if(n2.getLight(bank) > brightest_light || found_something == false){
565 brightest_light = n2.getLight(bank);
566 brightest_pos = n2pos;
567 found_something = true;
571 if(found_something == false)
572 throw InvalidPositionException();
574 return brightest_pos;
578 Propagates sunlight down from a node.
579 Starting point gets sunlight.
581 Returns the lowest y value of where the sunlight went.
583 Mud is turned into grass in where the sunlight stops.
585 s16 Map::propagateSunlight(v3s16 start,
586 core::map<v3s16, MapBlock*> & modified_blocks)
591 v3s16 pos(start.X, y, start.Z);
593 v3s16 blockpos = getNodeBlockPos(pos);
596 block = getBlockNoCreate(blockpos);
598 catch(InvalidPositionException &e)
603 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
604 MapNode n = block->getNode(relpos);
606 if(n.sunlight_propagates())
608 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
609 block->setNode(relpos, n);
611 modified_blocks.insert(blockpos, block);
615 // Turn mud into grass
616 if(n.d == CONTENT_MUD)
619 block->setNode(relpos, n);
620 modified_blocks.insert(blockpos, block);
623 // Sunlight goes no further
630 void Map::updateLighting(enum LightBank bank,
631 core::map<v3s16, MapBlock*> & a_blocks,
632 core::map<v3s16, MapBlock*> & modified_blocks)
634 /*m_dout<<DTIME<<"Map::updateLighting(): "
635 <<a_blocks.size()<<" blocks."<<std::endl;*/
637 //TimeTaker timer("updateLighting");
641 //u32 count_was = modified_blocks.size();
643 core::map<v3s16, MapBlock*> blocks_to_update;
645 core::map<v3s16, bool> light_sources;
647 core::map<v3s16, u8> unlight_from;
649 core::map<v3s16, MapBlock*>::Iterator i;
650 i = a_blocks.getIterator();
651 for(; i.atEnd() == false; i++)
653 MapBlock *block = i.getNode()->getValue();
657 // Don't bother with dummy blocks.
661 v3s16 pos = block->getPos();
662 modified_blocks.insert(pos, block);
664 blocks_to_update.insert(pos, block);
667 Clear all light from block
669 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
670 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
671 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
676 MapNode n = block->getNode(v3s16(x,y,z));
677 u8 oldlight = n.getLight(bank);
679 block->setNode(v3s16(x,y,z), n);
681 // Collect borders for unlighting
682 if(x==0 || x == MAP_BLOCKSIZE-1
683 || y==0 || y == MAP_BLOCKSIZE-1
684 || z==0 || z == MAP_BLOCKSIZE-1)
686 v3s16 p_map = p + v3s16(
689 MAP_BLOCKSIZE*pos.Z);
690 unlight_from.insert(p_map, oldlight);
693 catch(InvalidPositionException &e)
696 This would happen when dealing with a
700 dstream<<"updateLighting(): InvalidPositionException"
705 if(bank == LIGHTBANK_DAY)
707 bool bottom_valid = block->propagateSunlight(light_sources);
709 // If bottom is valid, we're done.
713 else if(bank == LIGHTBANK_NIGHT)
715 // For night lighting, sunlight is not propagated
720 // Invalid lighting bank
724 /*dstream<<"Bottom for sunlight-propagated block ("
725 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
728 // Bottom sunlight is not valid; get the block and loop to it
732 block = getBlockNoCreate(pos);
734 catch(InvalidPositionException &e)
744 TimeTaker timer("unspreadLight");
745 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
750 u32 diff = modified_blocks.size() - count_was;
751 count_was = modified_blocks.size();
752 dstream<<"unspreadLight modified "<<diff<<std::endl;
756 TimeTaker timer("spreadLight");
757 spreadLight(bank, light_sources, modified_blocks);
762 u32 diff = modified_blocks.size() - count_was;
763 count_was = modified_blocks.size();
764 dstream<<"spreadLight modified "<<diff<<std::endl;
769 //MapVoxelManipulator vmanip(this);
771 // Make a manual voxel manipulator and load all the blocks
772 // that touch the requested blocks
773 ManualMapVoxelManipulator vmanip(this);
774 core::map<v3s16, MapBlock*>::Iterator i;
775 i = blocks_to_update.getIterator();
776 for(; i.atEnd() == false; i++)
778 MapBlock *block = i.getNode()->getValue();
779 v3s16 p = block->getPos();
781 // Add all surrounding blocks
782 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
785 Add all surrounding blocks that have up-to-date lighting
786 NOTE: This doesn't quite do the job (not everything
787 appropriate is lighted)
789 /*for(s16 z=-1; z<=1; z++)
790 for(s16 y=-1; y<=1; y++)
791 for(s16 x=-1; x<=1; x++)
794 MapBlock *block = getBlockNoCreateNoEx(p);
799 if(block->getLightingExpired())
801 vmanip.initialEmerge(p, p);
804 // Lighting of block will be updated completely
805 block->setLightingExpired(false);
809 //TimeTaker timer("unSpreadLight");
810 vmanip.unspreadLight(bank, unlight_from, light_sources);
813 //TimeTaker timer("spreadLight");
814 vmanip.spreadLight(bank, light_sources);
817 //TimeTaker timer("blitBack");
818 vmanip.blitBack(modified_blocks);
820 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
824 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
827 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
828 core::map<v3s16, MapBlock*> & modified_blocks)
830 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
831 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
834 Update information about whether day and night light differ
836 for(core::map<v3s16, MapBlock*>::Iterator
837 i = modified_blocks.getIterator();
838 i.atEnd() == false; i++)
840 MapBlock *block = i.getNode()->getValue();
841 block->updateDayNightDiff();
846 This is called after changing a node from transparent to opaque.
847 The lighting value of the node should be left as-is after changing
848 other values. This sets the lighting value to 0.
850 NOTE: This takes almost no time, the slow one is updateMeshes.
852 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
853 core::map<v3s16, MapBlock*> &modified_blocks)
856 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
857 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
860 From this node to nodes underneath:
861 If lighting is sunlight (1.0), unlight neighbours and
866 v3s16 toppos = p + v3s16(0,1,0);
867 v3s16 bottompos = p + v3s16(0,-1,0);
869 bool node_under_sunlight = true;
870 core::map<v3s16, bool> light_sources;
873 If there is a node at top and it doesn't have sunlight,
874 there has not been any sunlight going down.
876 Otherwise there probably is.
879 MapNode topnode = getNode(toppos);
881 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
882 node_under_sunlight = false;
884 catch(InvalidPositionException &e)
889 If the new node doesn't propagate sunlight and there is
890 grass below, change it to mud
892 if(content_features(n.d).sunlight_propagates == false)
895 MapNode bottomnode = getNode(bottompos);
897 if(bottomnode.d == CONTENT_GRASS
898 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
900 bottomnode.d = CONTENT_MUD;
901 setNode(bottompos, bottomnode);
904 catch(InvalidPositionException &e)
910 If the new node is mud and it is under sunlight, change it
913 if(n.d == CONTENT_MUD && node_under_sunlight)
919 Remove all light that has come out of this node
922 enum LightBank banks[] =
927 for(s32 i=0; i<2; i++)
929 enum LightBank bank = banks[i];
931 u8 lightwas = getNode(p).getLight(bank);
933 // Add the block of the added node to modified_blocks
934 v3s16 blockpos = getNodeBlockPos(p);
935 MapBlock * block = getBlockNoCreate(blockpos);
936 assert(block != NULL);
937 modified_blocks.insert(blockpos, block);
939 assert(isValidPosition(p));
941 // Unlight neighbours of node.
942 // This means setting light of all consequent dimmer nodes
944 // This also collects the nodes at the border which will spread
945 // light again into this.
946 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
952 Set the node on the map
958 If node is under sunlight, take all sunlighted nodes under
959 it and clear light from them and from where the light has
961 TODO: This could be optimized by mass-unlighting instead
964 if(node_under_sunlight)
968 //m_dout<<DTIME<<"y="<<y<<std::endl;
969 v3s16 n2pos(p.X, y, p.Z);
975 catch(InvalidPositionException &e)
980 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
982 unLightNeighbors(LIGHTBANK_DAY,
983 n2pos, n2.getLight(LIGHTBANK_DAY),
984 light_sources, modified_blocks);
985 n2.setLight(LIGHTBANK_DAY, 0);
993 for(s32 i=0; i<2; i++)
995 enum LightBank bank = banks[i];
998 Spread light from all nodes that might be capable of doing so
1000 spreadLight(bank, light_sources, modified_blocks);
1004 Update information about whether day and night light differ
1006 for(core::map<v3s16, MapBlock*>::Iterator
1007 i = modified_blocks.getIterator();
1008 i.atEnd() == false; i++)
1010 MapBlock *block = i.getNode()->getValue();
1011 block->updateDayNightDiff();
1015 Add neighboring liquid nodes and the node itself if it is
1016 liquid (=water node was added) to transform queue.
1019 v3s16(0,0,0), // self
1020 v3s16(0,0,1), // back
1021 v3s16(0,1,0), // top
1022 v3s16(1,0,0), // right
1023 v3s16(0,0,-1), // front
1024 v3s16(0,-1,0), // bottom
1025 v3s16(-1,0,0), // left
1027 for(u16 i=0; i<7; i++)
1032 v3s16 p2 = p + dirs[i];
1034 MapNode n2 = getNode(p2);
1035 if(content_liquid(n2.d))
1037 m_transforming_liquid.push_back(p2);
1040 }catch(InvalidPositionException &e)
1047 NOTE: This takes almost no time, the slow one is updateMeshes.
1049 void Map::removeNodeAndUpdate(v3s16 p,
1050 core::map<v3s16, MapBlock*> &modified_blocks)
1052 /*PrintInfo(m_dout);
1053 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1054 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1056 bool node_under_sunlight = true;
1058 v3s16 toppos = p + v3s16(0,1,0);
1060 // Node will be replaced with this
1061 u8 replace_material = CONTENT_AIR;
1064 If there is a node at top and it doesn't have sunlight,
1065 there will be no sunlight going down.
1068 MapNode topnode = getNode(toppos);
1070 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1071 node_under_sunlight = false;
1073 catch(InvalidPositionException &e)
1077 core::map<v3s16, bool> light_sources;
1079 enum LightBank banks[] =
1084 for(s32 i=0; i<2; i++)
1086 enum LightBank bank = banks[i];
1089 Unlight neighbors (in case the node is a light source)
1091 unLightNeighbors(bank, p,
1092 getNode(p).getLight(bank),
1093 light_sources, modified_blocks);
1098 This also clears the lighting.
1102 n.d = replace_material;
1105 for(s32 i=0; i<2; i++)
1107 enum LightBank bank = banks[i];
1110 Recalculate lighting
1112 spreadLight(bank, light_sources, modified_blocks);
1115 // Add the block of the removed node to modified_blocks
1116 v3s16 blockpos = getNodeBlockPos(p);
1117 MapBlock * block = getBlockNoCreate(blockpos);
1118 assert(block != NULL);
1119 modified_blocks.insert(blockpos, block);
1122 If the removed node was under sunlight, propagate the
1123 sunlight down from it and then light all neighbors
1124 of the propagated blocks.
1126 if(node_under_sunlight)
1128 s16 ybottom = propagateSunlight(p, modified_blocks);
1129 /*m_dout<<DTIME<<"Node was under sunlight. "
1130 "Propagating sunlight";
1131 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1133 for(; y >= ybottom; y--)
1135 v3s16 p2(p.X, y, p.Z);
1136 /*m_dout<<DTIME<<"lighting neighbors of node ("
1137 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1139 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1144 // Set the lighting of this node to 0
1145 // TODO: Is this needed? Lighting is cleared up there already.
1147 MapNode n = getNode(p);
1148 n.setLight(LIGHTBANK_DAY, 0);
1151 catch(InvalidPositionException &e)
1157 for(s32 i=0; i<2; i++)
1159 enum LightBank bank = banks[i];
1161 // Get the brightest neighbour node and propagate light from it
1162 v3s16 n2p = getBrightestNeighbour(bank, p);
1164 MapNode n2 = getNode(n2p);
1165 lightNeighbors(bank, n2p, modified_blocks);
1167 catch(InvalidPositionException &e)
1173 Update information about whether day and night light differ
1175 for(core::map<v3s16, MapBlock*>::Iterator
1176 i = modified_blocks.getIterator();
1177 i.atEnd() == false; i++)
1179 MapBlock *block = i.getNode()->getValue();
1180 block->updateDayNightDiff();
1184 Add neighboring liquid nodes to transform queue.
1187 v3s16(0,0,1), // back
1188 v3s16(0,1,0), // top
1189 v3s16(1,0,0), // right
1190 v3s16(0,0,-1), // front
1191 v3s16(0,-1,0), // bottom
1192 v3s16(-1,0,0), // left
1194 for(u16 i=0; i<6; i++)
1199 v3s16 p2 = p + dirs[i];
1201 MapNode n2 = getNode(p2);
1202 if(content_liquid(n2.d))
1204 m_transforming_liquid.push_back(p2);
1207 }catch(InvalidPositionException &e)
1213 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1216 event.type = MEET_ADDNODE;
1220 bool succeeded = true;
1222 core::map<v3s16, MapBlock*> modified_blocks;
1223 addNodeAndUpdate(p, n, modified_blocks);
1225 // Copy modified_blocks to event
1226 for(core::map<v3s16, MapBlock*>::Iterator
1227 i = modified_blocks.getIterator();
1228 i.atEnd()==false; i++)
1230 event.modified_blocks.insert(i.getNode()->getKey(), false);
1233 catch(InvalidPositionException &e){
1237 dispatchEvent(&event);
1242 bool Map::removeNodeWithEvent(v3s16 p)
1245 event.type = MEET_REMOVENODE;
1248 bool succeeded = true;
1250 core::map<v3s16, MapBlock*> modified_blocks;
1251 removeNodeAndUpdate(p, modified_blocks);
1253 // Copy modified_blocks to event
1254 for(core::map<v3s16, MapBlock*>::Iterator
1255 i = modified_blocks.getIterator();
1256 i.atEnd()==false; i++)
1258 event.modified_blocks.insert(i.getNode()->getKey(), false);
1261 catch(InvalidPositionException &e){
1265 dispatchEvent(&event);
1270 bool Map::dayNightDiffed(v3s16 blockpos)
1273 v3s16 p = blockpos + v3s16(0,0,0);
1274 MapBlock *b = getBlockNoCreate(p);
1275 if(b->dayNightDiffed())
1278 catch(InvalidPositionException &e){}
1281 v3s16 p = blockpos + v3s16(-1,0,0);
1282 MapBlock *b = getBlockNoCreate(p);
1283 if(b->dayNightDiffed())
1286 catch(InvalidPositionException &e){}
1288 v3s16 p = blockpos + v3s16(0,-1,0);
1289 MapBlock *b = getBlockNoCreate(p);
1290 if(b->dayNightDiffed())
1293 catch(InvalidPositionException &e){}
1295 v3s16 p = blockpos + v3s16(0,0,-1);
1296 MapBlock *b = getBlockNoCreate(p);
1297 if(b->dayNightDiffed())
1300 catch(InvalidPositionException &e){}
1303 v3s16 p = blockpos + v3s16(1,0,0);
1304 MapBlock *b = getBlockNoCreate(p);
1305 if(b->dayNightDiffed())
1308 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(0,1,0);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1317 v3s16 p = blockpos + v3s16(0,0,1);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1328 Updates usage timers
1330 void Map::timerUpdate(float dtime)
1332 JMutexAutoLock lock(m_sector_mutex);
1334 core::map<v2s16, MapSector*>::Iterator si;
1336 si = m_sectors.getIterator();
1337 for(; si.atEnd() == false; si++)
1339 MapSector *sector = si.getNode()->getValue();
1340 sector->usage_timer += dtime;
1344 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1347 Wait for caches to be removed before continuing.
1349 This disables the existence of caches while locked
1351 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1353 core::list<v2s16>::Iterator j;
1354 for(j=list.begin(); j!=list.end(); j++)
1356 MapSector *sector = m_sectors[*j];
1359 sector->deleteBlocks();
1364 If sector is in sector cache, remove it from there
1366 if(m_sector_cache == sector)
1368 m_sector_cache = NULL;
1371 Remove from map and delete
1373 m_sectors.remove(*j);
1379 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1380 core::list<v3s16> *deleted_blocks)
1382 JMutexAutoLock lock(m_sector_mutex);
1384 core::list<v2s16> sector_deletion_queue;
1385 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1386 for(; i.atEnd() == false; i++)
1388 MapSector *sector = i.getNode()->getValue();
1390 Delete sector from memory if it hasn't been used in a long time
1392 if(sector->usage_timer > timeout)
1394 sector_deletion_queue.push_back(i.getNode()->getKey());
1396 if(deleted_blocks != NULL)
1398 // Collect positions of blocks of sector
1399 MapSector *sector = i.getNode()->getValue();
1400 core::list<MapBlock*> blocks;
1401 sector->getBlocks(blocks);
1402 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1403 i != blocks.end(); i++)
1405 deleted_blocks->push_back((*i)->getPos());
1410 deleteSectors(sector_deletion_queue, only_blocks);
1411 return sector_deletion_queue.getSize();
1414 void Map::PrintInfo(std::ostream &out)
1419 #define WATER_DROP_BOOST 4
1421 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1423 DSTACK(__FUNCTION_NAME);
1424 //TimeTaker timer("transformLiquids()");
1427 u32 initial_size = m_transforming_liquid.size();
1429 /*if(initial_size != 0)
1430 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1432 while(m_transforming_liquid.size() != 0)
1438 Get a queued transforming liquid node
1440 v3s16 p0 = m_transforming_liquid.pop_front();
1442 MapNode n0 = getNode(p0);
1444 // Don't deal with non-liquids
1445 if(content_liquid(n0.d) == false)
1448 bool is_source = !content_flowing_liquid(n0.d);
1450 u8 liquid_level = 8;
1451 if(is_source == false)
1452 liquid_level = n0.param2 & 0x0f;
1454 // Turn possible source into non-source
1455 u8 nonsource_c = make_liquid_flowing(n0.d);
1458 If not source, check that some node flows into this one
1459 and what is the level of liquid in this one
1461 if(is_source == false)
1463 s8 new_liquid_level_max = -1;
1465 v3s16 dirs_from[5] = {
1466 v3s16(0,1,0), // top
1467 v3s16(0,0,1), // back
1468 v3s16(1,0,0), // right
1469 v3s16(0,0,-1), // front
1470 v3s16(-1,0,0), // left
1472 for(u16 i=0; i<5; i++)
1477 bool from_top = (i==0);
1479 v3s16 p2 = p0 + dirs_from[i];
1480 MapNode n2 = getNode(p2);
1482 if(content_liquid(n2.d))
1484 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1485 // Check that the liquids are the same type
1486 if(n2_nonsource_c != nonsource_c)
1488 dstream<<"WARNING: Not handling: different liquids"
1489 " collide"<<std::endl;
1492 bool n2_is_source = !content_flowing_liquid(n2.d);
1493 s8 n2_liquid_level = 8;
1494 if(n2_is_source == false)
1495 n2_liquid_level = n2.param2 & 0x07;
1497 s8 new_liquid_level = -1;
1500 //new_liquid_level = 7;
1501 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1502 new_liquid_level = 7;
1504 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1506 else if(n2_liquid_level > 0)
1508 new_liquid_level = n2_liquid_level - 1;
1511 if(new_liquid_level > new_liquid_level_max)
1512 new_liquid_level_max = new_liquid_level;
1515 }catch(InvalidPositionException &e)
1521 If liquid level should be something else, update it and
1522 add all the neighboring water nodes to the transform queue.
1524 if(new_liquid_level_max != liquid_level)
1526 if(new_liquid_level_max == -1)
1528 // Remove water alltoghether
1535 n0.param2 = new_liquid_level_max;
1539 // Block has been modified
1541 v3s16 blockpos = getNodeBlockPos(p0);
1542 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1544 modified_blocks.insert(blockpos, block);
1548 Add neighboring non-source liquid nodes to transform queue.
1551 v3s16(0,0,1), // back
1552 v3s16(0,1,0), // top
1553 v3s16(1,0,0), // right
1554 v3s16(0,0,-1), // front
1555 v3s16(0,-1,0), // bottom
1556 v3s16(-1,0,0), // left
1558 for(u16 i=0; i<6; i++)
1563 v3s16 p2 = p0 + dirs[i];
1565 MapNode n2 = getNode(p2);
1566 if(content_flowing_liquid(n2.d))
1568 m_transforming_liquid.push_back(p2);
1571 }catch(InvalidPositionException &e)
1578 // Get a new one from queue if the node has turned into non-water
1579 if(content_liquid(n0.d) == false)
1583 Flow water from this node
1585 v3s16 dirs_to[5] = {
1586 v3s16(0,-1,0), // bottom
1587 v3s16(0,0,1), // back
1588 v3s16(1,0,0), // right
1589 v3s16(0,0,-1), // front
1590 v3s16(-1,0,0), // left
1592 for(u16 i=0; i<5; i++)
1597 bool to_bottom = (i == 0);
1599 // If liquid is at lowest possible height, it's not going
1600 // anywhere except down
1601 if(liquid_level == 0 && to_bottom == false)
1604 u8 liquid_next_level = 0;
1605 // If going to bottom
1608 //liquid_next_level = 7;
1609 if(liquid_level >= 7 - WATER_DROP_BOOST)
1610 liquid_next_level = 7;
1612 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1615 liquid_next_level = liquid_level - 1;
1617 bool n2_changed = false;
1618 bool flowed = false;
1620 v3s16 p2 = p0 + dirs_to[i];
1622 MapNode n2 = getNode(p2);
1623 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1625 if(content_liquid(n2.d))
1627 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1628 // Check that the liquids are the same type
1629 if(n2_nonsource_c != nonsource_c)
1631 dstream<<"WARNING: Not handling: different liquids"
1632 " collide"<<std::endl;
1635 bool n2_is_source = !content_flowing_liquid(n2.d);
1636 u8 n2_liquid_level = 8;
1637 if(n2_is_source == false)
1638 n2_liquid_level = n2.param2 & 0x07;
1647 // Just flow into the source, nothing changes.
1648 // n2_changed is not set because destination didn't change
1653 if(liquid_next_level > liquid_level)
1655 n2.param2 = liquid_next_level;
1663 else if(n2.d == CONTENT_AIR)
1666 n2.param2 = liquid_next_level;
1673 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1677 m_transforming_liquid.push_back(p2);
1679 v3s16 blockpos = getNodeBlockPos(p2);
1680 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1682 modified_blocks.insert(blockpos, block);
1685 // If n2_changed to bottom, don't flow anywhere else
1686 if(to_bottom && flowed && !is_source)
1689 }catch(InvalidPositionException &e)
1695 if(loopcount >= initial_size * 1 || loopcount >= 1000)
1698 }catch(InvalidPositionException &e)
1702 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1709 ServerMap::ServerMap(std::string savedir):
1713 // TODO: Save to and load from a file
1714 m_seed = (((u64)(myrand()%0xffff)<<0)
1715 + ((u64)(myrand()%0xffff)<<16)
1716 + ((u64)(myrand()%0xffff)<<32)
1717 + ((u64)(myrand()%0xffff)<<48));
1720 Experimental and debug stuff
1727 Try to load map; if not found, create a new one.
1730 m_savedir = savedir;
1731 m_map_saving_enabled = false;
1735 // If directory exists, check contents and load if possible
1736 if(fs::PathExists(m_savedir))
1738 // If directory is empty, it is safe to save into it.
1739 if(fs::GetDirListing(m_savedir).size() == 0)
1741 dstream<<DTIME<<"Server: Empty save directory is valid."
1743 m_map_saving_enabled = true;
1747 // Load map metadata (seed)
1750 /*// Load sector (0,0) and throw and exception on fail
1751 if(loadSectorFull(v2s16(0,0)) == false)
1752 throw LoadError("Failed to load sector (0,0)");*/
1754 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1755 "metadata and sector (0,0) from "<<savedir<<
1756 ", assuming valid save directory."
1759 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1760 <<"and chunk metadata from "<<savedir
1761 <<", assuming valid save directory."
1764 m_map_saving_enabled = true;
1765 // Map loaded, not creating new one
1769 // If directory doesn't exist, it is safe to save to it
1771 m_map_saving_enabled = true;
1774 catch(std::exception &e)
1776 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1777 <<", exception: "<<e.what()<<std::endl;
1778 dstream<<"Please remove the map or fix it."<<std::endl;
1779 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1782 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1784 // Create zero sector
1785 emergeSector(v2s16(0,0));
1787 // Initially write whole map
1791 ServerMap::~ServerMap()
1795 if(m_map_saving_enabled)
1798 // Save only changed parts
1800 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1804 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1807 catch(std::exception &e)
1809 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1810 <<", exception: "<<e.what()<<std::endl;
1815 Some helper functions for the map generator
1818 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1820 v3s16 em = vmanip.m_area.getExtent();
1821 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1822 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1823 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1825 for(y=y_nodes_max; y>=y_nodes_min; y--)
1827 MapNode &n = vmanip.m_data[i];
1828 if(content_walkable(n.d))
1831 vmanip.m_area.add_y(em, i, -1);
1833 if(y >= y_nodes_min)
1839 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1841 v3s16 em = vmanip.m_area.getExtent();
1842 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1843 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1844 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1846 for(y=y_nodes_max; y>=y_nodes_min; y--)
1848 MapNode &n = vmanip.m_data[i];
1849 if(content_walkable(n.d)
1850 && n.d != CONTENT_TREE
1851 && n.d != CONTENT_LEAVES)
1854 vmanip.m_area.add_y(em, i, -1);
1856 if(y >= y_nodes_min)
1862 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1864 MapNode treenode(CONTENT_TREE);
1865 MapNode leavesnode(CONTENT_LEAVES);
1866 leavesnode.setLight(LIGHTBANK_DAY, LIGHT_MAX-1);
1868 vmanip.emerge(VoxelArea(p0-v3s16(2,0,2),p0+v3s16(2,7+2,2)));
1870 s16 trunk_h = myrand_range(4, 7);
1872 for(s16 ii=0; ii<trunk_h; ii++)
1874 if(vmanip.m_area.contains(p1))
1875 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1879 // p1 is now the last piece of the trunk
1882 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1883 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1884 Buffer<u8> leaves_d(leaves_a.getVolume());
1885 for(s32 i=0; i<leaves_a.getVolume(); i++)
1888 // Force leaves at near the end of the trunk
1891 for(s16 z=-d; z<=d; z++)
1892 for(s16 y=-d; y<=d; y++)
1893 for(s16 x=-d; x<=d; x++)
1895 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1899 // Add leaves randomly
1900 for(u32 iii=0; iii<7; iii++)
1905 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1906 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1907 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1910 for(s16 z=0; z<=d; z++)
1911 for(s16 y=0; y<=d; y++)
1912 for(s16 x=0; x<=d; x++)
1914 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1918 // Blit leaves to vmanip
1919 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1920 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1921 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1925 if(vmanip.m_area.contains(p) == false)
1927 u32 vi = vmanip.m_area.index(p);
1928 if(vmanip.m_data[vi].d != CONTENT_AIR)
1930 u32 i = leaves_a.index(x,y,z);
1931 if(leaves_d[i] == 1)
1932 vmanip.m_data[vi] = leavesnode;
1937 Noise functions. Make sure seed is mangled differently in each one.
1940 // Amount of trees per area in nodes
1941 double tree_amount_2d(u64 seed, v2s16 p)
1943 double noise = noise2d_perlin(
1944 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1946 double zeroval = -0.5;
1950 return 0.03 * (noise-zeroval) / (1.0-zeroval);
1953 #define AVERAGE_MUD_AMOUNT 4.0
1955 double get_mud_amount(u64 seed, v2f p)
1957 return ((float)AVERAGE_MUD_AMOUNT + 2.5 * noise2d_perlin(
1958 0.5+p.X/200, 0.5+p.Y/200,
1962 bool get_have_sand_coast(u64 seed, v2f p)
1964 double sandnoise = noise2d_perlin(
1965 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1966 seed+59420, 3, 0.50);
1967 return (sandnoise > -0.25);
1970 bool get_have_sand_ground(u64 seed, v2f p)
1972 double sandnoise = noise2d_perlin(
1973 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
1974 seed+54290232, 6, 0.65);
1975 return (sandnoise > 1.0);
1978 // -1->0, 0->1, 1->0
1979 double contour(double v)
1987 // -1->0, -r->1, 0->1, r->1, 1->0
1988 double contour_flat_top(double v, double r)
1993 double rmax = 0.999;
1999 return ((1.0-r)-v) / (1.0-r);
2000 //return easeCurve(((1.0-r)-v) / (1.0-r));
2003 double base_rock_level_2d(u64 seed, v2f p)
2005 // The ground level (return value)
2006 double h = WATER_LEVEL-1.5;
2008 // Raises from 0 when parameter is -1...1
2009 /*double m2 = contour_flat_top(-0.8 + 2.0 * noise2d_perlin(
2010 0.0+(float)p.X/1500., 0.0+(float)p.Y/1500.,
2011 (seed>>32)+34758, 5, 0.55), 0.10);*/
2016 double m1 = 200.0 + 300.0 * noise2d_perlin(
2017 0.0+(float)p.X/1000., 0.0+(float)p.Y/1000.,
2018 (seed>>32)+98525, 8, 0.5);
2023 /*double tm2 = contour_flat_top(-1.0 + 3.0 * noise2d_perlin(
2024 0.0+(float)p.X/300., 0.0+(float)p.Y/300.,
2025 (seed>>32)+78593, 5, 0.55), 0.15);
2031 double m3 = 100.0 - 600.0 * noise2d_perlin_abs(
2032 0.324+(float)p.X/2000., 0.423+(float)p.Y/2000.,
2033 (seed>>32)+985251, 9, 0.55);
2041 // More mountain ranges
2043 double a1 = d*2.0 - d*7 * noise2d_perlin_abs(
2044 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2045 seed+850342, 7, 0.55);
2047 a1 = d + sqrt(a1-d);*/
2048 a1 = (1.0 - exp(-a1/d))*d;
2058 // More mountain ranges
2060 double a1 = d*2.0 - d*7 * noise2d_perlin_abs(
2061 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2062 seed+850342, 7, 0.55);
2064 a1 = d + sqrt(a1-d);*/
2065 a1 = (1.0 - exp(-a1/d))*d;
2075 // Very steep mountain ranges
2077 double a1 = d*2 - d*6.5 * noise2d_perlin_abs(
2078 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2079 seed+850342, 6, 0.6);
2081 a1 = d + sqrt(a1-d);*/
2082 a1 = (1.0 - exp(-a1/d))*d;
2087 /*double a = noise2d_perlin_abs(
2088 0.94+(float)p.X/2000., 0.26+(float)p.Y/2000.,
2089 (seed>>32)+65012102, 8, 0.50);
2090 double m4 = 100.0 - 400.0 * a;
2097 The stuff before this comment is usually not used.
2098 The stuff after this comment is usually used.
2104 double m4 = 1.0 - 3.0 * noise2d_perlin_abs(
2105 0.324+(float)p.X/1000., 0.423+(float)p.Y/1000.,
2106 (seed>>32)+65012102, 8, 0.57);
2114 // Some kind of hill chains or something
2116 double a1 = 1.0 - 2.5 * noise2d_perlin_abs(
2117 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2118 seed+850342, 5, 0.6);
2122 a1 = d + sqrt(a1-d);
2131 double base = -2. + 25. * noise2d_perlin(
2132 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2133 (seed>>32)+653876, 7, 0.65);
2140 Combined with turbulence, this thing here is able to make very
2141 awesome terrain, albeit rarely.
2143 This is also responsible for small islands.
2146 double higher = 40. * noise2d_perlin(
2147 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2148 seed+39292, 6, 0.50);
2149 /*double higher = 50. * noise2d_perlin_abs(
2150 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2151 seed+85039, 5, 0.63);*/
2156 // Steepness factor of cliffs
2157 double b = 1.0 + 1.0 * noise2d_perlin(
2158 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2160 b = rangelim(b, 0.0, 1000.0);
2164 b = rangelim(b, 3.0, 1000.0);
2165 //dstream<<"b="<<b<<std::endl;
2167 // Offset to more low
2168 //double a_off = -0.30;
2169 double a_off = -0.20;
2170 // High/low selector
2171 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2172 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2173 seed-359, 7, 0.70));
2178 b = rangelim(b, 3.0, 20.0);*/
2180 double a = -1.5 + 5.0 * (noise2d_perlin_abs(
2181 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2184 /*double a = 5.0 * (noise2d_perlin(
2185 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2186 seed-359, 5, 0.6));*/
2187 //a = contour_flat_top(a, 0.2);
2190 a = rangelim(a, 0.0, 1.0);
2193 //dstream<<"a="<<a<<std::endl;
2195 /*double h2 = higher * a;
2199 h += base*(1.0-a) + higher*a;
2212 double base_rock_level_2d(u64 seed, v2s16 p)
2214 return base_rock_level_2d(seed, v2f((float)p.X, (float)p.Y));
2217 double get_turbulence_factor_2d(u64 seed, v2f p)
2219 double vv = -0.50 + 2.0 * noise2d_perlin(
2222 seed+85498783983, 4, 0.5);
2223 vv = rangelim(vv, 0.0, 1.0);
2227 #define TURBULENCE_BOTTOM_CUTOFF_Y (WATER_LEVEL-7)
2229 double get_turbulence_factor_y(u64 seed, f32 y)
2232 double min = TURBULENCE_BOTTOM_CUTOFF_Y;
2235 else if(y < min + d)
2240 v2f get_raw_turbulence(u64 seed, v3f p)
2244 double v1 = f * noise3d_perlin(
2248 seed+4045, 5, 0.65);
2250 double v2 = f * noise3d_perlin(
2254 seed+9495, 5, 0.65);
2259 // Shouldn't be used, provided for compatibility.
2260 v2f base_ground_turbulence(u64 seed, v3f p)
2262 double tfxz = get_turbulence_factor_2d(seed, v2f(p.X,p.Z));
2263 double tfy = get_turbulence_factor_y(seed, p.Y);
2264 v2f t = get_raw_turbulence(seed, p);
2269 v2f base_ground_turbulence(u64 seed, v3f p)
2275 // Cut off at a minimum height
2278 double min = WATER_LEVEL-5;
2281 else if(p.Y < min + d)
2287 double vv = 0.50 + 1.0 * noise3d_perlin(
2291 seed+1324381, 4, 0.5);
2292 double vve = rangelim(vv, 0.0, 1.0);
2293 /*double vv = 1.0 - 2.0 * noise3d_perlin_abs(
2297 seed+1324031, 4, 0.5);
2298 double vve = 1.0 - exp(-MYMAX(0, vv*2.0));*/
2299 //double vve = rangelim(vv, 0, 1.0);
2300 //dstream<<"vve="<<vve<<std::endl;
2302 /*// Limit turbulence near water level
2303 double a = contour((p.Y-WATER_LEVEL)/10.0);
2304 vve = (1.-a) * vve;*/
2306 // Increase turbulence in elevated heights
2307 double ah = WATER_LEVEL + 30;
2316 double v1 = f * noise3d_perlin(
2320 seed+4045, 5, 0.65);
2322 double v2 = f * noise3d_perlin(
2326 seed+9495, 5, 0.65);
2328 return v2f(v1*vve, v2*vve);
2335 bool is_carved(u64 seed, v3f p)
2338 double v1 = noise3d_perlin_abs(
2342 seed+657890854, 5, 0.7);
2351 double v4 = contour(f*noise3d_perlin(
2355 seed+87592, 5, 0.7));
2356 // Tilted 90 degrees
2357 double v5 = contour(f*noise3d_perlin(
2361 seed+98594, 5, 0.7));
2370 bool is_underground_mud(u64 seed, v3f p)
2372 double v1 = noise3d_perlin_abs(
2376 seed+83401, 5, 0.75);
2381 if depth_guess!=NULL, it is set to a guessed value of how deep
2382 underground the position is.
2384 bool is_base_ground(u64 seed, v3f p, double *depth_guess=NULL)
2387 // This is used for testing the output of the cave function
2393 return is_carved(seed, p);
2397 // This is used for testing the output of the underground mud function
2403 return is_underground_mud(seed, p);
2407 bool is_ground = true;
2410 if(is_carved(seed, p))
2414 if(depth_guess || is_ground == true)
2416 v2f t = base_ground_turbulence(seed, p);
2418 double surface_y_f = base_rock_level_2d(seed, v2f(p.X+t.X, p.Z+t.Y));
2423 // Find highest surface near current
2430 double s2 = surface_y_f;
2431 for(u32 i=0; i<4; i++)
2434 // Get turbulence at around there
2435 v2f t2 = base_ground_turbulence(seed, p+dir);
2436 // Get ground height
2437 v2f l = v2f(p.X+t2.X+dir.X, p.Z+t2.Y+dir.Z);
2438 double s = base_rock_level_2d(seed, l);
2442 *depth_guess = s2 - p.Y;
2448 // Check a bit lower also, take highest surface
2449 v2f t2 = base_ground_turbulence(seed, p + v3f(0,-2,0));
2450 double s2 = base_rock_level_2d(seed, v2f(p.X+t2.X, p.Z+t2.Y));
2451 if(s2 > surface_y_f)
2452 *depth_guess = s2 - p.Y;
2454 *depth_guess = surface_y_f - p.Y;
2459 *depth_guess = surface_y_f - p.Y;
2462 if(p.Y > surface_y_f)
2468 // Guess surface point
2469 v3f p2(p.X, surface_y_f, p.Z);
2470 v2f t2 = base_ground_turbulence
2472 double s1 = base_rock_level_2d(seed, v2f(p.X+v1,p.Z+v2));
2478 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2481 This is the main map generation method
2485 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2486 core::map<v3s16, MapBlock*> &changed_blocks,
2489 DSTACK(__FUNCTION_NAME);
2491 // Shall be not used now
2497 Don't generate if already fully generated
2501 MapChunk *chunk = getChunk(chunkpos);
2502 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2504 dstream<<"generateChunkRaw(): Chunk "
2505 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2506 <<" already generated"<<std::endl;
2511 dstream<<"generateChunkRaw(): Generating chunk "
2512 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2515 TimeTaker timer("generateChunkRaw()");
2517 // The distance how far into the neighbors the generator is allowed to go.
2518 s16 max_spread_amount_sectors = 2;
2519 assert(max_spread_amount_sectors <= m_chunksize);
2520 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2522 // Minimum amount of space left on sides for mud to fall in
2523 //s16 min_mud_fall_space = 2;
2525 // Maximum diameter of stone obstacles in X and Z
2526 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2527 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2529 s16 y_blocks_min = -4;
2530 s16 y_blocks_max = 3;
2531 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2532 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2533 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2535 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2536 s16 sectorpos_base_size = m_chunksize;
2538 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2539 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2540 v2s16 sectorpos_bigbase =
2541 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2542 s16 sectorpos_bigbase_size =
2543 sectorpos_base_size + 2 * max_spread_amount_sectors;
2545 v3s16 bigarea_blocks_min(
2546 sectorpos_bigbase.X,
2551 v3s16 bigarea_blocks_max(
2552 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2554 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2557 // Relative values to control amount of stuff in one chunk
2558 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2559 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2560 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2561 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2562 *(u32)h_blocks*MAP_BLOCKSIZE;
2565 The limiting edges of the lighting update, inclusive.
2567 s16 lighting_min_d = 0-max_spread_amount;
2568 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2571 Create the whole area of this and the neighboring chunks
2574 TimeTaker timer("generateChunkRaw() create area");
2576 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2577 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2579 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2580 ServerMapSector *sector = createSector(sectorpos);
2583 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2585 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2586 MapBlock *block = createBlock(blockpos);
2588 // Lighting won't be calculated
2589 //block->setLightingExpired(true);
2590 // Lighting will be calculated
2591 block->setLightingExpired(false);
2594 Block gets sunlight if this is true.
2596 This should be set to true when the top side of a block
2597 is completely exposed to the sky.
2599 Actually this doesn't matter now because the
2600 initial lighting is done here.
2602 block->setIsUnderground(y != y_blocks_max);
2608 Now we have a big empty area.
2610 Make a ManualMapVoxelManipulator that contains this and the
2614 ManualMapVoxelManipulator vmanip(this);
2615 // Add the area we just generated
2617 TimeTaker timer("generateChunkRaw() initialEmerge");
2618 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2622 vmanip.clearFlag(0xff);
2624 TimeTaker timer_generate("generateChunkRaw() generate");
2626 // Maximum height of the stone surface and obstacles.
2627 // This is used to disable dungeon generation from going too high.
2628 s16 stone_surface_max_y = 0;
2631 Generate general ground level to full area
2636 TimeTaker timer1("ground level");
2637 dstream<<"Generating base ground..."<<std::endl;
2639 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2640 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2643 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2646 Skip if already generated
2649 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2650 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2654 v2f p2df(p2d.X, p2d.Y);
2657 // Use fast index incrementing
2658 v3s16 em = vmanip.m_area.getExtent();
2659 s16 min = y_nodes_min;
2660 s16 max = y_nodes_max;
2663 //float surface_y_f = base_rock_level_2d(m_seed, p2df);
2664 u32 i = vmanip.m_area.index(v3s16(p2d.X, min, p2d.Y));
2665 for(s16 y=min; y<=max; y++)
2668 bool is = is_base_ground(m_seed, v3f(p2df.X,y,p2df.Y));
2670 vmanip.m_data[i].d = CONTENT_STONE;
2672 vmanip.m_data[i].d = CONTENT_AIR;
2675 double v = noise3d_perlin(
2676 0.5+(float)p2d.X/200,
2678 0.5+(float)p2d.Y/200,
2679 m_seed+293, 6, 0.55);
2681 vmanip.m_data[i].d = CONTENT_STONE;
2683 vmanip.m_data[i].d = CONTENT_AIR;
2686 /*double v1 = 5 * noise3d_perlin(
2687 0.5+(float)p2df.X/200,
2689 0.5+(float)p2df.Y/200,
2690 m_seed+293, 6, 0.55);
2692 double v2 = 5 * noise3d_perlin(
2693 0.5+(float)p2df.X/200,
2695 0.5+(float)p2df.Y/200,
2696 m_seed+293, 6, 0.55);*/
2701 float surface_y_f = base_rock_level_2d(m_seed, p2df+v2f(v1,v2));
2703 if(y <= surface_y_f)
2704 vmanip.m_data[i].d = CONTENT_STONE;
2706 vmanip.m_data[i].d = CONTENT_AIR;
2709 vmanip.m_area.add_y(em, i, 1);
2715 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2718 Skip if already generated
2721 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2722 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2726 // Ground height at this point
2727 float surface_y_f = 0.0;
2729 // Use perlin noise for ground height
2730 surface_y_f = base_rock_level_2d(m_seed, p2d);
2732 /*// Experimental stuff
2734 float a = highlands_level_2d(m_seed, p2d);
2739 // Convert to integer
2740 s16 surface_y = (s16)surface_y_f;
2743 if(surface_y > stone_surface_max_y)
2744 stone_surface_max_y = surface_y;
2747 Fill ground with stone
2750 // Use fast index incrementing
2751 v3s16 em = vmanip.m_area.getExtent();
2752 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2753 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2755 vmanip.m_data[i].d = CONTENT_STONE;
2757 vmanip.m_area.add_y(em, i, 1);
2766 Randomize some parameters
2769 s32 stone_obstacle_count = 0;
2770 /*s32 stone_obstacle_count =
2771 rangelim((1.0+noise2d(m_seed+897,
2772 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2774 s16 stone_obstacle_max_height = 0;
2775 /*s16 stone_obstacle_max_height =
2776 rangelim((1.0+noise2d(m_seed+5902,
2777 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2780 Loop this part, it will make stuff look older and newer nicely
2783 for(u32 i_age=0; i_age<age_count; i_age++)
2788 //TimeTaker timer1("stone obstacles");
2791 Add some random stone obstacles
2794 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2796 // Randomize max height so usually stuff will be quite low
2797 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2799 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2800 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2803 myrand_range(5, stone_obstacle_max_size),
2804 myrand_range(0, maxheight_randomized),
2805 myrand_range(5, stone_obstacle_max_size)
2808 // Don't make stupid small rectangle bumps
2813 myrand_range(1+ob_size.X/2+2,
2814 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2815 myrand_range(1+ob_size.Z/2+2,
2816 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2819 // Minimum space left on top of the obstacle
2820 s16 min_head_space = 12;
2822 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2823 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2825 // Node position in 2d
2826 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2828 // Find stone ground level
2829 // (ignore everything else than mud in already generated chunks)
2830 // and mud amount over the stone level
2834 v3s16 em = vmanip.m_area.getExtent();
2835 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2837 // Go to ground level
2838 for(y=y_nodes_max; y>=y_nodes_min; y--)
2840 MapNode *n = &vmanip.m_data[i];
2841 /*if(content_walkable(n.d)
2842 && n.d != CONTENT_MUD
2843 && n.d != CONTENT_GRASS)
2845 if(n->d == CONTENT_STONE)
2848 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2852 Change to mud because otherwise we might
2853 be throwing mud on grass at the next
2859 vmanip.m_area.add_y(em, i, -1);
2861 if(y >= y_nodes_min)
2864 surface_y = y_nodes_min;
2872 v3s16 em = vmanip.m_area.getExtent();
2873 s16 y_start = surface_y+1;
2874 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2878 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2880 MapNode &n = vmanip.m_data[i];
2881 n.d = CONTENT_STONE;
2883 if(y > stone_surface_max_y)
2884 stone_surface_max_y = y;
2887 if(count >= ob_size.Y)
2890 vmanip.m_area.add_y(em, i, 1);
2894 for(; y<=y_nodes_max - min_head_space; y++)
2896 MapNode &n = vmanip.m_data[i];
2899 if(count >= mud_amount)
2902 vmanip.m_area.add_y(em, i, 1);
2912 //TimeTaker timer1("dungeons");
2917 u32 dungeons_count = relative_volume / 600000;
2918 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2919 if(stone_surface_max_y < WATER_LEVEL)
2921 /*u32 dungeons_count = 0;
2922 u32 bruises_count = 0;*/
2923 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2925 s16 min_tunnel_diameter = 2;
2926 s16 max_tunnel_diameter = 6;
2927 u16 tunnel_routepoints = 25;
2929 bool bruise_surface = (jj < bruises_count);
2933 min_tunnel_diameter = 5;
2934 max_tunnel_diameter = myrand_range(10, 20);
2935 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2936 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2938 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2939 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2941 tunnel_routepoints = 5;
2944 // Allowed route area size in nodes
2946 sectorpos_base_size*MAP_BLOCKSIZE,
2947 h_blocks*MAP_BLOCKSIZE,
2948 sectorpos_base_size*MAP_BLOCKSIZE
2951 // Area starting point in nodes
2953 sectorpos_base.X*MAP_BLOCKSIZE,
2954 y_blocks_min*MAP_BLOCKSIZE,
2955 sectorpos_base.Y*MAP_BLOCKSIZE
2959 //(this should be more than the maximum radius of the tunnel)
2960 //s16 insure = 5; // Didn't work with max_d = 20
2962 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2963 ar += v3s16(1,0,1) * more * 2;
2964 of -= v3s16(1,0,1) * more;
2966 s16 route_y_min = 0;
2967 // Allow half a diameter + 7 over stone surface
2968 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2970 /*// If dungeons, don't go through surface too often
2971 if(bruise_surface == false)
2972 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2974 // Limit maximum to area
2975 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2979 /*// Minimum is at y=0
2980 route_y_min = -of.Y - 0;*/
2981 // Minimum is at y=max_tunnel_diameter/4
2982 //route_y_min = -of.Y + max_tunnel_diameter/4;
2983 //s16 min = -of.Y + max_tunnel_diameter/4;
2984 s16 min = -of.Y + 0;
2985 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2986 route_y_min = rangelim(route_y_min, 0, route_y_max);
2989 /*dstream<<"route_y_min = "<<route_y_min
2990 <<", route_y_max = "<<route_y_max<<std::endl;*/
2992 s16 route_start_y_min = route_y_min;
2993 s16 route_start_y_max = route_y_max;
2995 // Start every 2nd dungeon from surface
2996 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2998 if(coming_from_surface)
3000 route_start_y_min = -of.Y + stone_surface_max_y + 5;
3003 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
3004 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
3006 // Randomize starting position
3008 (float)(myrand()%ar.X)+0.5,
3009 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
3010 (float)(myrand()%ar.Z)+0.5
3013 MapNode airnode(CONTENT_AIR);
3016 Generate some tunnel starting from orp
3019 for(u16 j=0; j<tunnel_routepoints; j++)
3022 s16 min_d = min_tunnel_diameter;
3023 s16 max_d = max_tunnel_diameter;
3024 s16 rs = myrand_range(min_d, max_d);
3029 maxlen = v3s16(rs*7,rs*7,rs*7);
3033 maxlen = v3s16(15, myrand_range(1, 20), 15);
3038 if(coming_from_surface && j < 3)
3041 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
3042 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
3043 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3049 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
3050 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
3051 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3058 else if(rp.X >= ar.X)
3060 if(rp.Y < route_y_min)
3062 else if(rp.Y >= route_y_max)
3063 rp.Y = route_y_max-1;
3066 else if(rp.Z >= ar.Z)
3070 for(float f=0; f<1.0; f+=1.0/vec.getLength())
3072 v3f fp = orp + vec * f;
3073 v3s16 cp(fp.X, fp.Y, fp.Z);
3076 s16 d1 = d0 + rs - 1;
3077 for(s16 z0=d0; z0<=d1; z0++)
3079 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
3080 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
3081 for(s16 x0=-si; x0<=si-1; x0++)
3083 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
3084 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
3085 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
3086 //s16 si2 = rs - abs(x0);
3087 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
3093 /*if(isInArea(p, ar) == false)
3095 // Check only height
3096 if(y < 0 || y >= ar.Y)
3100 //assert(vmanip.m_area.contains(p));
3101 if(vmanip.m_area.contains(p) == false)
3103 dstream<<"WARNING: "<<__FUNCTION_NAME
3104 <<":"<<__LINE__<<": "
3105 <<"point not in area"
3110 // Just set it to air, it will be changed to
3112 u32 i = vmanip.m_area.index(p);
3113 vmanip.m_data[i] = airnode;
3115 if(bruise_surface == false)
3118 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
3133 //TimeTaker timer1("ore veins");
3138 for(u32 jj=0; jj<relative_volume/1000; jj++)
3140 s16 max_vein_diameter = 3;
3142 // Allowed route area size in nodes
3144 sectorpos_base_size*MAP_BLOCKSIZE,
3145 h_blocks*MAP_BLOCKSIZE,
3146 sectorpos_base_size*MAP_BLOCKSIZE
3149 // Area starting point in nodes
3151 sectorpos_base.X*MAP_BLOCKSIZE,
3152 y_blocks_min*MAP_BLOCKSIZE,
3153 sectorpos_base.Y*MAP_BLOCKSIZE
3157 //(this should be more than the maximum radius of the tunnel)
3159 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
3160 ar += v3s16(1,0,1) * more * 2;
3161 of -= v3s16(1,0,1) * more;
3163 // Randomize starting position
3165 (float)(myrand()%ar.X)+0.5,
3166 (float)(myrand()%ar.Y)+0.5,
3167 (float)(myrand()%ar.Z)+0.5
3170 // Randomize mineral
3173 mineral = MINERAL_COAL;
3175 mineral = MINERAL_IRON;
3178 Generate some vein starting from orp
3181 for(u16 j=0; j<2; j++)
3184 (float)(myrand()%ar.X)+0.5,
3185 (float)(myrand()%ar.Y)+0.5,
3186 (float)(myrand()%ar.Z)+0.5
3188 v3f vec = rp - orp;*/
3190 v3s16 maxlen(5, 5, 5);
3192 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
3193 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
3194 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3199 else if(rp.X >= ar.X)
3203 else if(rp.Y >= ar.Y)
3207 else if(rp.Z >= ar.Z)
3213 s16 max_d = max_vein_diameter;
3214 s16 rs = myrand_range(min_d, max_d);
3216 for(float f=0; f<1.0; f+=1.0/vec.getLength())
3218 v3f fp = orp + vec * f;
3219 v3s16 cp(fp.X, fp.Y, fp.Z);
3221 s16 d1 = d0 + rs - 1;
3222 for(s16 z0=d0; z0<=d1; z0++)
3224 s16 si = rs - abs(z0);
3225 for(s16 x0=-si; x0<=si-1; x0++)
3227 s16 si2 = rs - abs(x0);
3228 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3230 // Don't put mineral to every place
3238 /*if(isInArea(p, ar) == false)
3240 // Check only height
3241 if(y < 0 || y >= ar.Y)
3245 assert(vmanip.m_area.contains(p));
3247 // Just set it to air, it will be changed to
3249 u32 i = vmanip.m_area.index(p);
3250 MapNode *n = &vmanip.m_data[i];
3251 if(n->d == CONTENT_STONE)
3266 //TimeTaker timer1("add mud");
3269 Add mud to the central chunk
3272 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3273 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
3275 // Node position in 2d
3276 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3278 // Randomize mud amount
3279 s16 mud_add_amount = get_mud_amount(m_seed, v2f(p2d.X,p2d.Y))/age_count;
3281 // Find ground level
3282 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3285 If topmost node is grass, change it to mud.
3286 It might be if it was flown to there from a neighboring
3287 chunk and then converted.
3290 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
3291 MapNode *n = &vmanip.m_data[i];
3292 if(n->d == CONTENT_GRASS)
3301 v3s16 em = vmanip.m_area.getExtent();
3302 s16 y_start = surface_y+1;
3303 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3304 for(s16 y=y_start; y<=y_nodes_max; y++)
3306 if(mudcount >= mud_add_amount)
3309 MapNode &n = vmanip.m_data[i];
3313 vmanip.m_area.add_y(em, i, 1);
3322 //TimeTaker timer1("flow mud");
3325 Flow mud away from steep edges
3328 // Limit area by 1 because mud is flown into neighbors.
3329 s16 mudflow_minpos = 0-max_spread_amount+1;
3330 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
3332 // Iterate a few times
3333 for(s16 k=0; k<3; k++)
3336 for(s16 x=mudflow_minpos;
3339 for(s16 z=mudflow_minpos;
3343 // Invert coordinates every 2nd iteration
3346 x = mudflow_maxpos - (x-mudflow_minpos);
3347 z = mudflow_maxpos - (z-mudflow_minpos);
3350 // Node position in 2d
3351 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3353 v3s16 em = vmanip.m_area.getExtent();
3354 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3361 for(; y>=y_nodes_min; y--)
3363 n = &vmanip.m_data[i];
3364 //if(content_walkable(n->d))
3366 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3369 vmanip.m_area.add_y(em, i, -1);
3372 // Stop if out of area
3373 //if(vmanip.m_area.contains(i) == false)
3377 /*// If not mud, do nothing to it
3378 MapNode *n = &vmanip.m_data[i];
3379 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3383 Don't flow it if the stuff under it is not mud
3387 vmanip.m_area.add_y(em, i2, -1);
3388 // Cancel if out of area
3389 if(vmanip.m_area.contains(i2) == false)
3391 MapNode *n2 = &vmanip.m_data[i2];
3392 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
3396 // Make it exactly mud
3399 /*s16 recurse_count = 0;
3403 v3s16(0,0,1), // back
3404 v3s16(1,0,0), // right
3405 v3s16(0,0,-1), // front
3406 v3s16(-1,0,0), // left
3409 // Theck that upper is air or doesn't exist.
3410 // Cancel dropping if upper keeps it in place
3412 vmanip.m_area.add_y(em, i3, 1);
3413 if(vmanip.m_area.contains(i3) == true
3414 && content_walkable(vmanip.m_data[i3].d) == true)
3421 for(u32 di=0; di<4; di++)
3423 v3s16 dirp = dirs4[di];
3426 vmanip.m_area.add_p(em, i2, dirp);
3427 // Fail if out of area
3428 if(vmanip.m_area.contains(i2) == false)
3430 // Check that side is air
3431 MapNode *n2 = &vmanip.m_data[i2];
3432 if(content_walkable(n2->d))
3434 // Check that under side is air
3435 vmanip.m_area.add_y(em, i2, -1);
3436 if(vmanip.m_area.contains(i2) == false)
3438 n2 = &vmanip.m_data[i2];
3439 if(content_walkable(n2->d))
3441 /*// Check that under that is air (need a drop of 2)
3442 vmanip.m_area.add_y(em, i2, -1);
3443 if(vmanip.m_area.contains(i2) == false)
3445 n2 = &vmanip.m_data[i2];
3446 if(content_walkable(n2->d))
3448 // Loop further down until not air
3450 vmanip.m_area.add_y(em, i2, -1);
3451 // Fail if out of area
3452 if(vmanip.m_area.contains(i2) == false)
3454 n2 = &vmanip.m_data[i2];
3455 }while(content_walkable(n2->d) == false);
3456 // Loop one up so that we're in air
3457 vmanip.m_area.add_y(em, i2, 1);
3458 n2 = &vmanip.m_data[i2];
3460 // Move mud to new place
3462 // Set old place to be air
3463 *n = MapNode(CONTENT_AIR);
3476 //TimeTaker timer1("add water");
3479 Add water to the central chunk (and a bit more)
3482 for(s16 x=0-max_spread_amount;
3483 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3485 for(s16 z=0-max_spread_amount;
3486 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3489 // Node position in 2d
3490 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3492 // Find ground level
3493 //s16 surface_y = find_ground_level(vmanip, p2d);
3496 If ground level is over water level, skip.
3497 NOTE: This leaves caves near water without water,
3498 which looks especially crappy when the nearby water
3499 won't start flowing either for some reason
3501 /*if(surface_y > WATER_LEVEL)
3508 v3s16 em = vmanip.m_area.getExtent();
3509 u8 light = LIGHT_MAX;
3510 // Start at global water surface level
3511 s16 y_start = WATER_LEVEL;
3512 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3513 MapNode *n = &vmanip.m_data[i];
3515 /*// Add first one to transforming liquid queue, if water
3516 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3518 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3519 m_transforming_liquid.push_back(p);
3522 for(s16 y=y_start; y>=y_nodes_min; y--)
3524 n = &vmanip.m_data[i];
3526 // Stop when there is no water and no air
3527 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3528 && n->d != CONTENT_WATER)
3530 /*// Add bottom one to transforming liquid queue
3531 vmanip.m_area.add_y(em, i, 1);
3532 n = &vmanip.m_data[i];
3533 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3535 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3536 m_transforming_liquid.push_back(p);
3542 // Make water only not in dungeons
3543 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3545 n->d = CONTENT_WATERSOURCE;
3546 //n->setLight(LIGHTBANK_DAY, light);
3548 // Add to transforming liquid queue (in case it'd
3550 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3551 m_transforming_liquid.push_back(p);
3555 vmanip.m_area.add_y(em, i, -1);
3568 //TimeTaker timer1("convert mud to sand");
3574 //s16 mud_add_amount = myrand_range(2, 4);
3575 //s16 mud_add_amount = 0;
3577 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3578 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3579 for(s16 x=0-max_spread_amount+1;
3580 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3582 for(s16 z=0-max_spread_amount+1;
3583 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3586 // Node position in 2d
3587 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3589 // Determine whether to have sand here
3590 bool have_sand = get_have_sand_coast(p2d);
3592 if(have_sand == false)
3595 // Find ground level
3596 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3598 if(surface_y > WATER_LEVEL + 2)
3602 v3s16 em = vmanip.m_area.getExtent();
3603 s16 y_start = surface_y;
3604 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3605 u32 not_sand_counter = 0;
3606 for(s16 y=y_start; y>=y_nodes_min; y--)
3608 MapNode *n = &vmanip.m_data[i];
3609 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3611 n->d = CONTENT_SAND;
3616 if(not_sand_counter > 3)
3620 vmanip.m_area.add_y(em, i, -1);
3629 //TimeTaker timer1("generate trees");
3635 // Divide area into parts
3637 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3638 double area = sidelen * sidelen;
3639 for(s16 x0=0; x0<div; x0++)
3640 for(s16 z0=0; z0<div; z0++)
3642 // Center position of part of division
3644 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3645 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3647 // Minimum edge of part of division
3649 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3650 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3652 // Maximum edge of part of division
3654 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3655 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3658 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3659 // Put trees in random places on part of division
3660 for(u32 i=0; i<tree_count; i++)
3662 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3663 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3664 s16 y = find_ground_level(vmanip, v2s16(x,z));
3665 // Don't make a tree under water level
3670 Trees grow only on mud and grass
3673 u32 i = vmanip.m_area.index(v3s16(p));
3674 MapNode *n = &vmanip.m_data[i];
3675 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3680 make_tree(vmanip, p);
3683 /*u32 tree_max = relative_area / 60;
3684 //u32 count = myrand_range(0, tree_max);
3685 for(u32 i=0; i<count; i++)
3687 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3688 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3689 x += sectorpos_base.X*MAP_BLOCKSIZE;
3690 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3691 s16 y = find_ground_level(vmanip, v2s16(x,z));
3692 // Don't make a tree under water level
3697 make_tree(vmanip, p);
3705 //TimeTaker timer1("grow grass");
3711 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3712 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3713 for(s16 x=0-max_spread_amount;
3714 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3716 for(s16 z=0-max_spread_amount;
3717 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3720 // Node position in 2d
3721 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3724 Find the lowest surface to which enough light ends up
3727 Basically just wait until not air and not leaves.
3731 v3s16 em = vmanip.m_area.getExtent();
3732 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3734 // Go to ground level
3735 for(y=y_nodes_max; y>=y_nodes_min; y--)
3737 MapNode &n = vmanip.m_data[i];
3738 if(n.d != CONTENT_AIR
3739 && n.d != CONTENT_LEAVES)
3741 vmanip.m_area.add_y(em, i, -1);
3743 if(y >= y_nodes_min)
3746 surface_y = y_nodes_min;
3749 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3750 MapNode *n = &vmanip.m_data[i];
3751 if(n->d == CONTENT_MUD)
3752 n->d = CONTENT_GRASS;
3758 Initial lighting (sunlight)
3761 core::map<v3s16, bool> light_sources;
3764 // 750ms @cs=8, can't optimize more
3765 TimeTaker timer1("initial lighting");
3769 Go through the edges and add all nodes that have light to light_sources
3773 for(s16 i=0; i<4; i++)
3775 for(s16 j=lighting_min_d;
3782 if(i == 0 || i == 1)
3784 x = (i==0) ? lighting_min_d : lighting_max_d;
3793 z = (i==0) ? lighting_min_d : lighting_max_d;
3800 // Node position in 2d
3801 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3804 v3s16 em = vmanip.m_area.getExtent();
3805 s16 y_start = y_nodes_max;
3806 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3807 for(s16 y=y_start; y>=y_nodes_min; y--)
3809 MapNode *n = &vmanip.m_data[i];
3810 if(n->getLight(LIGHTBANK_DAY) != 0)
3812 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3814 //NOTE: This is broken, at least the index has to
3823 Go through the edges and apply sunlight to them, not caring
3828 for(s16 i=0; i<4; i++)
3830 for(s16 j=lighting_min_d;
3837 if(i == 0 || i == 1)
3839 x = (i==0) ? lighting_min_d : lighting_max_d;
3848 z = (i==0) ? lighting_min_d : lighting_max_d;
3855 // Node position in 2d
3856 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3858 // Loop from top to down
3860 u8 light = LIGHT_SUN;
3861 v3s16 em = vmanip.m_area.getExtent();
3862 s16 y_start = y_nodes_max;
3863 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3864 for(s16 y=y_start; y>=y_nodes_min; y--)
3866 MapNode *n = &vmanip.m_data[i];
3867 if(light_propagates_content(n->d) == false)
3871 else if(light != LIGHT_SUN
3872 || sunlight_propagates_content(n->d) == false)
3878 n->setLight(LIGHTBANK_DAY, light);
3879 n->setLight(LIGHTBANK_NIGHT, 0);
3883 // Insert light source
3884 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3887 // Increment index by y
3888 vmanip.m_area.add_y(em, i, -1);
3894 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3895 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3896 /*for(s16 x=0-max_spread_amount+1;
3897 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3899 for(s16 z=0-max_spread_amount+1;
3900 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3904 This has to be 1 smaller than the actual area, because
3905 neighboring nodes are checked.
3907 for(s16 x=lighting_min_d+1;
3908 x<=lighting_max_d-1;
3910 for(s16 z=lighting_min_d+1;
3911 z<=lighting_max_d-1;
3914 // Node position in 2d
3915 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3918 Apply initial sunlight
3921 u8 light = LIGHT_SUN;
3922 bool add_to_sources = false;
3923 v3s16 em = vmanip.m_area.getExtent();
3924 s16 y_start = y_nodes_max;
3925 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3926 for(s16 y=y_start; y>=y_nodes_min; y--)
3928 MapNode *n = &vmanip.m_data[i];
3930 if(light_propagates_content(n->d) == false)
3934 else if(light != LIGHT_SUN
3935 || sunlight_propagates_content(n->d) == false)
3941 // This doesn't take much time
3942 if(add_to_sources == false)
3945 Check sides. If side is not air or water, start
3946 adding to light_sources.
3949 v3s16(0,0,1), // back
3950 v3s16(1,0,0), // right
3951 v3s16(0,0,-1), // front
3952 v3s16(-1,0,0), // left
3954 for(u32 di=0; di<4; di++)
3956 v3s16 dirp = dirs4[di];
3958 vmanip.m_area.add_p(em, i2, dirp);
3959 MapNode *n2 = &vmanip.m_data[i2];
3961 n2->d != CONTENT_AIR
3962 && n2->d != CONTENT_WATERSOURCE
3963 && n2->d != CONTENT_WATER
3965 add_to_sources = true;
3971 n->setLight(LIGHTBANK_DAY, light);
3972 n->setLight(LIGHTBANK_NIGHT, 0);
3974 // This doesn't take much time
3975 if(light != 0 && add_to_sources)
3977 // Insert light source
3978 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3981 // Increment index by y
3982 vmanip.m_area.add_y(em, i, -1);
3990 // Spread light around
3992 TimeTaker timer("generateChunkRaw() spreadLight");
3993 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
4000 timer_generate.stop();
4003 Blit generated stuff to map
4007 //TimeTaker timer("generateChunkRaw() blitBackAll");
4008 vmanip.blitBackAll(&changed_blocks);
4012 Update day/night difference cache of the MapBlocks
4015 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
4016 i.atEnd() == false; i++)
4018 MapBlock *block = i.getNode()->getValue();
4019 block->updateDayNightDiff();
4026 Create chunk metadata
4029 for(s16 x=-1; x<=1; x++)
4030 for(s16 y=-1; y<=1; y++)
4032 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
4033 // Add chunk meta information
4034 MapChunk *chunk = getChunk(chunkpos0);
4037 chunk = new MapChunk();
4038 m_chunks.insert(chunkpos0, chunk);
4040 //chunk->setIsVolatile(true);
4041 if(chunk->getGenLevel() > GENERATED_PARTLY)
4042 chunk->setGenLevel(GENERATED_PARTLY);
4046 Set central chunk non-volatile
4048 MapChunk *chunk = getChunk(chunkpos);
4051 //chunk->setIsVolatile(false);
4052 chunk->setGenLevel(GENERATED_FULLY);
4055 Save changed parts of map
4060 Return central chunk (which was requested)
4067 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
4068 core::map<v3s16, MapBlock*> &changed_blocks,
4071 DSTACK(__FUNCTION_NAME);
4074 Don't generate if already fully generated
4078 MapChunk *chunk = getChunk(chunkpos);
4079 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
4081 dstream<<"generateChunkRaw(): Chunk "
4082 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
4083 <<" already generated"<<std::endl;
4089 dstream<<"generateChunkRaw(): Generating chunk "
4090 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
4093 TimeTaker timer("generateChunkRaw()");
4095 // The distance how far into the neighbors the generator is allowed to go.
4096 s16 max_spread_amount_sectors = 1;
4097 assert(max_spread_amount_sectors <= m_chunksize);
4098 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
4100 // Minimum amount of space left on sides for mud to fall in
4101 //s16 min_mud_fall_space = 2;
4103 // Maximum diameter of stone obstacles in X and Z
4104 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
4105 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
4107 s16 y_blocks_min = -2;
4108 s16 y_blocks_max = 3;
4109 //s16 h_blocks = y_blocks_max - y_blocks_min + 1;
4110 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
4111 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
4113 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
4114 s16 sectorpos_base_size = m_chunksize;
4116 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
4117 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
4118 v2s16 sectorpos_bigbase =
4119 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
4120 s16 sectorpos_bigbase_size =
4121 sectorpos_base_size + 2 * max_spread_amount_sectors;
4123 v3s16 bigarea_blocks_min(
4124 sectorpos_bigbase.X,
4129 v3s16 bigarea_blocks_max(
4130 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
4132 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
4135 // Relative values to control amount of stuff in one chunk
4136 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
4137 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
4138 /*u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
4139 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
4140 *(u32)h_blocks*MAP_BLOCKSIZE;*/
4143 The limiting edges of the lighting update, inclusive.
4145 s16 lighting_min_d = 0-max_spread_amount;
4146 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
4149 Create the whole area of this and the neighboring chunks
4152 TimeTaker timer("generateChunkRaw() create area");
4154 for(s16 x=0; x<sectorpos_bigbase_size; x++)
4155 for(s16 z=0; z<sectorpos_bigbase_size; z++)
4157 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
4158 ServerMapSector *sector = createSector(sectorpos);
4161 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
4163 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
4164 MapBlock *block = createBlock(blockpos);
4166 // Lighting won't be calculated
4167 //block->setLightingExpired(true);
4168 // Lighting will be calculated
4169 block->setLightingExpired(false);
4172 Block gets sunlight if this is true.
4174 This should be set to true when the top side of a block
4175 is completely exposed to the sky.
4177 Actually this doesn't matter now because the
4178 initial lighting is done here.
4180 block->setIsUnderground(y != y_blocks_max);
4186 Now we have a big empty area.
4188 Make a ManualMapVoxelManipulator that contains this and the
4192 ManualMapVoxelManipulator vmanip(this);
4193 // Add the area we just generated
4195 TimeTaker timer("generateChunkRaw() initialEmerge");
4196 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
4200 vmanip.clearFlag(0xff);
4202 TimeTaker timer_generate("generateChunkRaw() generate");
4205 Generate general ground level to full area
4210 TimeTaker timer1("ground level");
4211 dstream<<"Generating base ground..."<<std::endl;
4213 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
4214 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
4217 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
4220 Skip if already generated
4223 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
4224 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
4228 v2f p2df(p2d.X, p2d.Y);
4230 s16 mud_amount = get_mud_amount(m_seed, p2df);
4232 double tfxz = get_turbulence_factor_2d(m_seed, p2df);
4233 bool turbulence_is_used = (tfxz > 0.001);
4237 float noturb_surface_y_f = base_rock_level_2d(m_seed, p2df);
4238 s16 noturb_surface_y = noturb_surface_y_f;
4241 s16 depth_counter = 0;
4242 s16 min = y_nodes_min;
4243 s16 max = y_nodes_max;
4244 // Use fast index incrementing
4245 v3s16 em = vmanip.m_area.getExtent();
4246 u32 i = vmanip.m_area.index(v3s16(p2d.X, max, p2d.Y));
4247 for(s16 y=max; y>=min; y--)
4249 v3f p3df(p2df.X, y, p2df.Y);
4251 bool is_ground = false;
4253 bool turb_for_node = (turbulence_is_used
4254 && y >= TURBULENCE_BOTTOM_CUTOFF_Y);
4256 if(is_carved(m_seed, p3df))
4265 is_ground = is_base_ground(m_seed,
4266 p3df, &depth_guess);
4268 // Estimate the surface height
4269 surface_y = y + depth_guess;
4273 surface_y = noturb_surface_y;
4276 is_ground = (y <= surface_y);
4281 //vmanip.m_data[i].d = CONTENT_STONE;
4282 /*if(y > surface_y - mud_amount)
4283 vmanip.m_data[i].d = CONTENT_MUD;
4285 vmanip.m_data[i].d = CONTENT_STONE;*/
4286 if(depth_counter < mud_amount)
4287 vmanip.m_data[i].d = CONTENT_MUD;
4289 vmanip.m_data[i].d = CONTENT_STONE;
4292 vmanip.m_data[i].d = CONTENT_AIR;
4294 if(is_ground || depth_counter != 0)
4299 bool is = is_base_ground(m_seed, v3f(p2df.X,y,p2df.Y));
4301 vmanip.m_data[i].d = CONTENT_STONE;
4303 vmanip.m_data[i].d = CONTENT_AIR;
4307 vmanip.m_area.add_y(em, i, -1);
4316 //TimeTaker timer1("add water");
4319 Add water to the central chunk (and a bit more)
4322 for(s16 x=0-max_spread_amount;
4323 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
4325 for(s16 z=0-max_spread_amount;
4326 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
4329 // Node position in 2d
4330 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
4332 // Find ground level
4333 //s16 surface_y = find_ground_level(vmanip, p2d);
4336 If ground level is over water level, skip.
4337 NOTE: This leaves caves near water without water,
4338 which looks especially crappy when the nearby water
4339 won't start flowing either for some reason
4341 /*if(surface_y > WATER_LEVEL)
4348 v3s16 em = vmanip.m_area.getExtent();
4349 u8 light = LIGHT_MAX;
4350 // Start at global water surface level
4351 s16 y_start = WATER_LEVEL;
4352 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
4353 MapNode *n = &vmanip.m_data[i];
4355 /*// Add first one to transforming liquid queue, if water
4356 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
4358 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
4359 m_transforming_liquid.push_back(p);
4362 for(s16 y=y_start; y>=y_nodes_min; y--)
4364 n = &vmanip.m_data[i];
4366 // Stop when there is no water and no air
4367 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
4368 && n->d != CONTENT_WATER)
4370 /*// Add bottom one to transforming liquid queue
4371 vmanip.m_area.add_y(em, i, 1);
4372 n = &vmanip.m_data[i];
4373 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
4375 v3s16 p = v3s16(p2d.X, y, p2d.Y);
4376 m_transforming_liquid.push_back(p);
4382 // Make water only not in dungeons
4383 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
4385 n->d = CONTENT_WATERSOURCE;
4386 //n->setLight(LIGHTBANK_DAY, light);
4388 // Add to transforming liquid queue (in case it'd
4390 v3s16 p = v3s16(p2d.X, y, p2d.Y);
4391 m_transforming_liquid.push_back(p);
4395 vmanip.m_area.add_y(em, i, -1);
4406 //TimeTaker timer1("convert mud to sand");
4412 //s16 mud_add_amount = myrand_range(2, 4);
4413 //s16 mud_add_amount = 0;
4415 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
4416 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
4417 for(s16 x=0-max_spread_amount+1;
4418 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
4420 for(s16 z=0-max_spread_amount+1;
4421 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
4424 // Node position in 2d
4425 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
4427 // Determine whether to have sand here
4428 bool have_sand = get_have_sand_coast(m_seed, v2f(p2d.X,p2d.Y));
4430 if(have_sand == false)
4433 // Find ground level
4434 s16 surface_y = find_ground_level_clever(vmanip, p2d);
4436 if(surface_y > WATER_LEVEL + 2)
4440 v3s16 em = vmanip.m_area.getExtent();
4441 s16 y_start = surface_y;
4442 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
4443 u32 not_sand_counter = 0;
4444 for(s16 y=y_start; y>=y_nodes_min; y--)
4446 MapNode *n = &vmanip.m_data[i];
4447 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
4449 n->d = CONTENT_SAND;
4454 if(not_sand_counter > 3)
4458 vmanip.m_area.add_y(em, i, -1);
4468 //TimeTaker timer1("grow grass");
4474 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
4475 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
4476 for(s16 x=0-max_spread_amount;
4477 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
4479 for(s16 z=0-max_spread_amount;
4480 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
4483 // Node position in 2d
4484 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
4487 Find the lowest surface to which enough light ends up
4490 Basically just wait until not air and not leaves.
4494 v3s16 em = vmanip.m_area.getExtent();
4495 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
4497 // Go to ground level
4498 for(y=y_nodes_max; y>=y_nodes_min; y--)
4500 MapNode &n = vmanip.m_data[i];
4501 if(n.d != CONTENT_AIR
4502 && n.d != CONTENT_LEAVES)
4504 vmanip.m_area.add_y(em, i, -1);
4506 if(y >= y_nodes_min)
4509 surface_y = y_nodes_min;
4512 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
4513 MapNode *n = &vmanip.m_data[i];
4514 if(n->d == CONTENT_MUD)
4515 n->d = CONTENT_GRASS;
4522 //TimeTaker timer1("generate trees");
4528 // Divide area into parts
4530 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
4531 double area = sidelen * sidelen;
4532 for(s16 x0=0; x0<div; x0++)
4533 for(s16 z0=0; z0<div; z0++)
4535 // Center position of part of division
4537 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
4538 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
4540 // Minimum edge of part of division
4542 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
4543 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
4545 // Maximum edge of part of division
4547 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
4548 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
4551 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
4552 // Put trees in random places on part of division
4553 for(u32 i=0; i<tree_count; i++)
4555 s16 x = myrand_range(p2d_min.X, p2d_max.X);
4556 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
4557 s16 y = find_ground_level(vmanip, v2s16(x,z));
4558 // Don't make a tree under water level
4563 Trees grow only on mud and grass
4566 u32 i = vmanip.m_area.index(v3s16(p));
4567 MapNode *n = &vmanip.m_data[i];
4568 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
4573 make_tree(vmanip, p);
4576 /*u32 tree_max = relative_area / 60;
4577 //u32 count = myrand_range(0, tree_max);
4578 for(u32 i=0; i<count; i++)
4580 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
4581 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
4582 x += sectorpos_base.X*MAP_BLOCKSIZE;
4583 z += sectorpos_base.Y*MAP_BLOCKSIZE;
4584 s16 y = find_ground_level(vmanip, v2s16(x,z));
4585 // Don't make a tree under water level
4590 make_tree(vmanip, p);
4598 Initial lighting (sunlight)
4601 core::map<v3s16, bool> light_sources;
4604 // 750ms @cs=8, can't optimize more
4605 TimeTaker timer1("initial lighting");
4609 This has to be 1 smaller than the actual area, because
4610 neighboring nodes are checked.
4612 for(s16 x=lighting_min_d+1;
4613 x<=lighting_max_d-1;
4615 for(s16 z=lighting_min_d+1;
4616 z<=lighting_max_d-1;
4619 // Node position in 2d
4620 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
4623 Apply initial sunlight
4626 u8 light = LIGHT_SUN;
4627 bool add_to_sources = false;
4628 v3s16 em = vmanip.m_area.getExtent();
4629 s16 y_start = y_nodes_max;
4630 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
4631 for(s16 y=y_start; y>=y_nodes_min; y--)
4633 MapNode *n = &vmanip.m_data[i];
4635 if(light_propagates_content(n->d) == false)
4639 else if(light != LIGHT_SUN
4640 || sunlight_propagates_content(n->d) == false)
4646 // This doesn't take much time
4647 if(add_to_sources == false)
4650 Check sides. If side is not air or water, start
4651 adding to light_sources.
4654 v3s16(0,0,1), // back
4655 v3s16(1,0,0), // right
4656 v3s16(0,0,-1), // front
4657 v3s16(-1,0,0), // left
4659 for(u32 di=0; di<4; di++)
4661 v3s16 dirp = dirs4[di];
4663 vmanip.m_area.add_p(em, i2, dirp);
4664 MapNode *n2 = &vmanip.m_data[i2];
4666 n2->d != CONTENT_AIR
4667 && n2->d != CONTENT_WATERSOURCE
4668 && n2->d != CONTENT_WATER
4670 add_to_sources = true;
4676 n->setLight(LIGHTBANK_DAY, light);
4677 n->setLight(LIGHTBANK_NIGHT, 0);
4679 // This doesn't take much time
4680 if(light != 0 && add_to_sources)
4682 // Insert light source
4683 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
4686 // Increment index by y
4687 vmanip.m_area.add_y(em, i, -1);
4695 // Spread light around
4697 TimeTaker timer("generateChunkRaw() spreadLight");
4698 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
4705 timer_generate.stop();
4708 Blit generated stuff to map
4712 //TimeTaker timer("generateChunkRaw() blitBackAll");
4713 vmanip.blitBackAll(&changed_blocks);
4717 Update day/night difference cache of the MapBlocks
4720 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
4721 i.atEnd() == false; i++)
4723 MapBlock *block = i.getNode()->getValue();
4724 block->updateDayNightDiff();
4730 Create chunk metadata
4733 for(s16 x=-1; x<=1; x++)
4734 for(s16 y=-1; y<=1; y++)
4736 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
4737 // Add chunk meta information
4738 MapChunk *chunk = getChunk(chunkpos0);
4741 chunk = new MapChunk();
4742 m_chunks.insert(chunkpos0, chunk);
4744 //chunk->setIsVolatile(true);
4745 if(chunk->getGenLevel() > GENERATED_PARTLY)
4746 chunk->setGenLevel(GENERATED_PARTLY);
4750 Set central chunk non-volatile
4752 MapChunk *chunk = getChunk(chunkpos);
4755 //chunk->setIsVolatile(false);
4756 chunk->setGenLevel(GENERATED_FULLY);
4759 Save changed parts of map
4764 Return central chunk (which was requested)
4770 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
4771 core::map<v3s16, MapBlock*> &changed_blocks)
4773 dstream<<"generateChunk(): Generating chunk "
4774 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
4777 // Shall be not used now
4780 /*for(s16 x=-1; x<=1; x++)
4781 for(s16 y=-1; y<=1; y++)*/
4782 for(s16 x=-0; x<=0; x++)
4783 for(s16 y=-0; y<=0; y++)
4785 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
4786 MapChunk *chunk = getChunk(chunkpos0);
4787 // Skip if already generated
4788 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
4790 generateChunkRaw(chunkpos0, changed_blocks);
4793 assert(chunkNonVolatile(chunkpos1));
4795 MapChunk *chunk = getChunk(chunkpos1);
4800 ServerMapSector * ServerMap::createSector(v2s16 p2d)
4802 DSTACK("%s: p2d=(%d,%d)",
4807 Check if it exists already in memory
4809 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
4814 Try to load it from disk (with blocks)
4816 if(loadSectorFull(p2d) == true)
4818 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
4821 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
4822 throw InvalidPositionException("");
4828 Do not create over-limit
4830 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4831 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4832 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4833 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4834 throw InvalidPositionException("createSector(): pos. over limit");
4837 Generate blank sector
4840 sector = new ServerMapSector(this, p2d);
4842 // Sector position on map in nodes
4843 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4848 m_sectors.insert(p2d, sector);
4853 MapSector * ServerMap::emergeSector(v2s16 p2d,
4854 core::map<v3s16, MapBlock*> &changed_blocks)
4856 DSTACK("%s: p2d=(%d,%d)",
4864 v2s16 chunkpos = sector_to_chunk(p2d);
4865 /*bool chunk_nonvolatile = false;
4866 MapChunk *chunk = getChunk(chunkpos);
4867 if(chunk && chunk->getIsVolatile() == false)
4868 chunk_nonvolatile = true;*/
4869 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
4872 If chunk is not fully generated, generate chunk
4874 if(chunk_nonvolatile == false)
4876 // Generate chunk and neighbors
4877 generateChunk(chunkpos, changed_blocks);
4882 Return sector if it exists now
4884 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4889 Try to load it from disk
4891 if(loadSectorFull(p2d) == true)
4893 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4896 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
4897 throw InvalidPositionException("");
4903 generateChunk should have generated the sector
4907 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
4908 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
4912 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
4915 generateChunkRaw(chunkpos, changed_blocks, true);
4918 Return sector if it exists now
4920 sector = getSectorNoGenerateNoEx(p2d);
4924 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
4930 dstream<<"WARNING: Creating an empty sector."<<std::endl;
4932 return createSector(p2d);
4939 //return generateSector();
4948 MapBlock* ServerMap::generateBlockRaw(v3s16 blockpos0,
4949 core::map<v3s16, MapBlock*> &changed_blocks,
4952 DSTACK(__FUNCTION_NAME);
4955 Don't generate if already fully generated
4959 MapBlock *block = getBlockNoCreateNoEx(blockpos0);
4960 if(block != NULL && block->isFullyGenerated())
4962 dstream<<"generateBlockRaw(): Block "
4963 <<"("<<blockpos0.X<<","<<blockpos0.Y
4964 <<","<<blockpos0.Z<<")"
4965 <<" already generated (not forced)"<<std::endl;
4970 /*dstream<<"generateBlockRaw(): Generating block "
4971 <<"("<<blockpos0.X<<","<<blockpos0.Y
4972 <<","<<blockpos0.Z<<")"
4975 //TimeTaker timer("generateBlockRaw()");
4978 Calculate some simple values
4981 v2s16 sectorpos0(blockpos0.X, blockpos0.Z);
4984 Fill in some variables for the code that was copied from
4987 s16 y_blocks_min = blockpos0.Y-1;
4988 s16 y_blocks_max = blockpos0.Y+1;
4989 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
4990 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
4991 v2s16 sectorpos_bigbase = sectorpos0 - v2s16(1,1);
4992 s16 sectorpos_bigbase_size = 3;
4993 v2s16 sectorpos_base = sectorpos0;
4994 s16 sectorpos_base_size = 1;
4995 s16 max_spread_amount = MAP_BLOCKSIZE;
4996 s16 lighting_min_d = 0-max_spread_amount;
4997 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
5000 Create the whole area of this and the neighboring blocks
5003 core::list<v3s16> blocks_created;
5006 //TimeTaker timer("generateBlockRaw() create area");
5008 for(s16 x=-1; x<=1; x++)
5009 for(s16 z=-1; z<=1; z++)
5011 v2s16 sectorpos = sectorpos0 + v2s16(x,z);
5012 ServerMapSector *sector = createSector(sectorpos);
5015 for(s16 y=blockpos0.Y-1; y<=blockpos0.Y+1; y++)
5017 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
5019 MapBlock *block = getBlockNoCreateNoEx(blockpos);
5020 if(block && block->isDummy() == false)
5023 block = createBlock(blockpos);
5024 block->setFullyGenerated(false);
5026 blocks_created.push_back(blockpos);
5028 // Lighting won't be calculated
5029 block->setLightingExpired(true);
5030 // Lighting will be calculated
5031 //block->setLightingExpired(false);
5034 Block gets sunlight if this is true.
5036 This should be set to true when the top side of a block
5037 is completely exposed to the sky.
5039 This doesn't matter if the initial lighting is done
5042 //block->setIsUnderground(y != y_blocks_max);
5043 block->setIsUnderground(false);
5049 Now we have a big empty area of (16x16x16)x27.
5051 Make a ManualMapVoxelManipulator that contains the whole area.
5054 ManualMapVoxelManipulator vmanip(this);
5055 // Add the area we just generated
5057 //TimeTaker timer("generateBlockRaw() initialEmerge");
5058 vmanip.initialEmerge(blockpos0-v3s16(1,1,1), blockpos0+v3s16(1,1,1));
5062 vmanip.clearFlag(0xff);
5064 // Block type of blockpos0
5065 BlockType center_block_type = BT_SURFACE;
5068 Generate general ground level to newly created blocks.
5069 Only stone is used and it is converted to other stuff later on.
5073 //dstream<<"Generating base ground..."<<std::endl;
5074 //TimeTaker timer1("ground level");
5076 // Loop through created blocks
5077 for(core::list<v3s16>::Iterator i = blocks_created.begin();
5078 i != blocks_created.end(); i++)
5080 v3s16 blockpos = *i;
5081 v2s16 sectorpos(blockpos.X, blockpos.Z);
5084 Approximate whether this block is a surface block, an air
5085 block or a ground block.
5087 This shall never mark a surface block as non-surface.
5090 BlockType block_type = BT_SURFACE;
5091 v3s16 p_nodes = blockpos * MAP_BLOCKSIZE;
5092 s32 lowest_ground_y = 32767;
5093 s32 highest_ground_y = -32768;
5094 u8 water_material = CONTENT_WATERSOURCE;
5098 Estimate surface at different positions of the block, to
5099 try to accomodate the effect of turbulence.
5112 v3f p_nodes_f = intToFloat(p_nodes, 1);
5113 float surface_y_max = -1000000;
5114 float surface_y_min = 1000000;
5115 for(u32 i=0; i<sizeof(checklist)/sizeof(checklist[0]); i++)
5117 v3f p_map_f = p_nodes_f + checklist[i]*MAP_BLOCKSIZE;
5120 /*bool is_ground =*/ is_base_ground(m_seed, p_map_f, &depth_guess);
5122 // Estimate the surface height
5123 float surface_y_f = p_map_f.Y + depth_guess;
5125 if(surface_y_f > surface_y_max)
5126 surface_y_max = surface_y_f;
5127 if(surface_y_f < surface_y_min)
5128 surface_y_min = surface_y_f;
5131 float block_low_y_f = p_nodes_f.Y;
5132 float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE;
5134 /*dstream<<"surface_y_max="<<surface_y_max
5135 <<", surface_y_min="<<surface_y_min
5136 <<", block_low_y_f="<<block_low_y_f
5137 <<", block_high_y_f="<<block_high_y_f
5140 // A fuzzyness value
5141 // Must accomodate mud and turbulence holes
5143 // Must accomodate a bit less
5146 if(block_high_y_f < surface_y_min - d_down)
5148 //dstream<<"BT_GROUND"<<std::endl;
5150 block_type = BT_GROUND;
5152 else if(block_low_y_f >= surface_y_max + d_up
5153 && block_low_y_f > WATER_LEVEL + d_up)
5155 //dstream<<"BT_SKY"<<std::endl;
5157 block_type = BT_SKY;
5161 //dstream<<"BT_SURFACE"<<std::endl;
5163 block_type = BT_SURFACE;
5166 if(/*block_type == BT_GROUND ||*/ block_type == BT_SKY)
5168 lowest_ground_y = surface_y_min;
5169 highest_ground_y = surface_y_max;
5173 if(blockpos == blockpos0)
5174 center_block_type = block_type;
5176 if(block_type == BT_GROUND)
5178 MapBlock *block = getBlockNoCreateNoEx(blockpos);
5180 block->setIsUnderground(true);
5184 If the block has ground, generate ground precisely.
5187 if(block_type == BT_SURFACE || block_type == BT_GROUND)
5189 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
5190 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
5192 v2s16 real_p2d = v2s16(x0,z0) + sectorpos*MAP_BLOCKSIZE;
5194 v2f real_p2d_f(real_p2d.X,real_p2d.Y);
5196 double tfxz = get_turbulence_factor_2d(m_seed, real_p2d_f);
5197 bool turbulence_is_used = (tfxz > 0.001);
5199 float surface_y_f = 0;
5202 float noturb_surface_y_f = base_rock_level_2d(m_seed, real_p2d_f);
5203 s16 noturb_surface_y = noturb_surface_y_f;
5205 // Get some statistics of surface height
5206 if(noturb_surface_y < lowest_ground_y)
5207 lowest_ground_y = noturb_surface_y;
5208 if(noturb_surface_y > highest_ground_y)
5209 highest_ground_y = noturb_surface_y;
5211 // Use fast index incrementing
5212 v3s16 em = vmanip.m_area.getExtent();
5213 u32 i = vmanip.m_area.index(v3s16(
5215 blockpos.Y*MAP_BLOCKSIZE,
5217 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
5219 s16 real_y = blockpos.Y * MAP_BLOCKSIZE + y0;
5220 v3s16 real_pos = v3s16(x0,y0,z0) + p_nodes;
5227 bool is_ground = false;
5228 v3f real_pos_f = intToFloat(real_pos, 1);
5230 bool turb_for_node = (turbulence_is_used
5231 && real_y >= TURBULENCE_BOTTOM_CUTOFF_Y);
5233 bool is_cavern = false;
5235 if(is_carved(m_seed, real_pos_f))
5238 if(real_y < noturb_surface_y)
5246 is_ground = is_base_ground(m_seed,
5247 real_pos_f, &depth_guess);
5249 // Estimate the surface height
5250 surface_y_f = (float)real_y + depth_guess;
5251 surface_y = real_y + depth_guess;
5253 // Save some statistics of surface height
5254 if(surface_y < lowest_ground_y)
5255 lowest_ground_y = surface_y;
5256 if(surface_y > highest_ground_y)
5257 highest_ground_y = surface_y;
5261 surface_y = noturb_surface_y;
5264 is_ground = (real_y <= surface_y);
5267 // If node is not ground, it's air or water
5268 if(is_ground == false)
5270 // If under water level, it's water
5271 if(real_y < WATER_LEVEL && !is_cavern)
5273 n.d = water_material;
5275 if(real_y >= surface_y)
5276 dist = WATER_LEVEL-real_y+1;
5277 n.setLight(LIGHTBANK_DAY,
5278 diminish_light(LIGHT_SUN, dist));
5280 Add to transforming liquid queue (in case it'd
5283 m_transforming_liquid.push_back(real_pos);
5293 if(real_y > surface_y + 4)
5294 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
5300 if(is_underground_mud(m_seed, real_pos_f))
5303 n.d = CONTENT_STONE;
5306 vmanip.m_data[i] = n;
5307 vmanip.m_area.add_y(em, i, 1);
5311 else // BT_SKY or anything else
5314 if(block_type == BT_GROUND)
5316 //n_fill.d = CONTENT_STONE;
5318 else if(block_type == BT_SKY)
5320 n_fill.d = CONTENT_AIR;
5321 n_fill.setLight(LIGHTBANK_DAY, LIGHT_SUN);
5325 n_fill.d = CONTENT_MESE;
5328 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
5329 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
5332 v2s16 p2d = sectorpos*MAP_BLOCKSIZE + v2s16(x,z);
5335 // Use fast index incrementing
5336 v3s16 em = vmanip.m_area.getExtent();
5337 s16 min = blockpos.Y*MAP_BLOCKSIZE;
5338 s16 max = min + MAP_BLOCKSIZE-1;
5339 u32 i = vmanip.m_area.index(v3s16(p2d.X, min, p2d.Y));
5340 for(s16 y=min; y<=max; y++)
5342 vmanip.m_data[i] = n_fill;
5343 vmanip.m_area.add_y(em, i, 1);
5353 Convert surface ground to mud
5356 if(center_block_type == BT_SURFACE)
5359 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
5360 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
5363 v2s16 p2d = sectorpos0*MAP_BLOCKSIZE + v2s16(x,z);
5364 v2f real_p2d_f(p2d.X,p2d.Y);
5367 // Use fast index incrementing
5368 v3s16 em = vmanip.m_area.getExtent();
5369 s16 min = blockpos0.Y*MAP_BLOCKSIZE;
5370 // Start from one above the central block
5371 s16 max = min + MAP_BLOCKSIZE-1+1;
5372 u32 i = vmanip.m_area.index(v3s16(p2d.X, max, p2d.Y));
5373 // If stone, there won't be mud
5374 if(vmanip.m_data[i].d == CONTENT_STONE)
5376 // Find top of ground
5379 for(y=max; y>=min; y--)
5381 if(vmanip.m_data[i].d == CONTENT_STONE)
5386 vmanip.m_area.add_y(em, i, -1);
5391 s16 mud_amount = get_mud_amount(m_seed, real_p2d_f);
5392 for(s16 j=0; j<mud_amount; j++)
5394 if(vmanip.m_data[i].d != CONTENT_STONE)
5398 if(j==0 && y >= WATER_LEVEL)
5399 vmanip.m_data[i].d = CONTENT_GRASS;
5401 vmanip.m_data[i].d = CONTENT_MUD;
5402 vmanip.m_area.add_y(em, i, -1);
5412 if(center_block_type == BT_SURFACE)
5414 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
5415 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
5417 // Node position in 2d
5418 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
5420 // Determine whether to have sand here
5421 bool have_sand = get_have_sand_coast(m_seed, v2f(p2d.X,p2d.Y));
5423 if(have_sand == false)
5426 // Find ground level
5427 s16 surface_y = find_ground_level_clever(vmanip, p2d);
5429 if(surface_y >= WATER_LEVEL + 2)
5433 v3s16 em = vmanip.m_area.getExtent();
5434 s16 y_start = surface_y;
5435 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
5436 u32 not_sand_counter = 0;
5437 for(s16 y=y_start; y>=y_nodes_min; y--)
5439 MapNode *n = &vmanip.m_data[i];
5440 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
5442 n->d = CONTENT_SAND;
5447 if(not_sand_counter > 3)
5451 vmanip.m_area.add_y(em, i, -1);
5461 if(center_block_type == BT_SURFACE || center_block_type == BT_GROUND)
5463 s16 underground_level = 1 - blockpos0.Y;
5468 for(s16 i=0; i<underground_level/4 + 1; i++)
5470 if(myrand()%25 == 0)
5473 (myrand()%(MAP_BLOCKSIZE-2))+1,
5474 (myrand()%(MAP_BLOCKSIZE-2))+1,
5475 (myrand()%(MAP_BLOCKSIZE-2))+1
5477 cp += blockpos0*MAP_BLOCKSIZE;
5482 for(u16 i=0; i<27; i++)
5484 if(vmanip.getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
5486 vmanip.setNode(cp+g_27dirs[i], n);
5494 u16 coal_amount = 60;
5495 u16 coal_rareness = 120 / coal_amount;
5496 if(coal_rareness == 0)
5498 if(myrand()%coal_rareness == 0)
5500 u16 a = myrand() % 16;
5501 u16 amount = coal_amount * a*a*a / 1000;
5502 for(s16 i=0; i<amount; i++)
5505 (myrand()%(MAP_BLOCKSIZE-2))+1,
5506 (myrand()%(MAP_BLOCKSIZE-2))+1,
5507 (myrand()%(MAP_BLOCKSIZE-2))+1
5509 cp += blockpos0*MAP_BLOCKSIZE;
5512 n.d = CONTENT_STONE;
5513 n.param = MINERAL_COAL;
5515 for(u16 i=0; i<27; i++)
5517 if(vmanip.getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
5519 vmanip.setNode(cp+g_27dirs[i], n);
5527 u16 iron_amount = 40;
5528 u16 iron_rareness = 80 / iron_amount;
5529 if(iron_rareness == 0)
5531 if(myrand()%iron_rareness == 0)
5533 u16 a = myrand() % 16;
5534 u16 amount = iron_amount * a*a*a / 1000;
5535 for(s16 i=0; i<amount; i++)
5538 (myrand()%(MAP_BLOCKSIZE-2))+1,
5539 (myrand()%(MAP_BLOCKSIZE-2))+1,
5540 (myrand()%(MAP_BLOCKSIZE-2))+1
5542 cp += blockpos0*MAP_BLOCKSIZE;
5545 n.d = CONTENT_STONE;
5546 n.param = MINERAL_IRON;
5548 for(u16 i=0; i<27; i++)
5550 if(vmanip.getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
5552 vmanip.setNode(cp+g_27dirs[i], n);
5562 if(center_block_type == BT_SURFACE)
5564 // Divide area into this amount of parts
5566 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
5567 double area = sidelen * sidelen;
5568 for(s16 x0=0; x0<div; x0++)
5569 for(s16 z0=0; z0<div; z0++)
5571 // Center position of part of division
5573 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
5574 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
5576 // Minimum edge of part of division
5578 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
5579 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
5581 // Maximum edge of part of division
5583 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
5584 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
5587 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
5588 // Put trees in random places on part of division
5589 for(u32 i=0; i<tree_count; i++)
5591 s16 x = myrand_range(p2d_min.X, p2d_max.X);
5592 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
5593 s16 y = find_ground_level(vmanip, v2s16(x,z));
5594 // Don't make a tree under water level
5597 // Don't make a tree in other blocks
5598 if(y < blockpos0.Y*MAP_BLOCKSIZE
5599 || y >= (blockpos0.Y+1)*MAP_BLOCKSIZE)
5603 Trees grow only on mud and grass
5606 u32 i = vmanip.m_area.index(v3s16(p));
5607 MapNode *n = &vmanip.m_data[i];
5608 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
5613 make_tree(vmanip, p);
5620 Initial lighting (sunlight)
5621 TODO: Do the lighting this way, with the VoxelManipulator
5624 core::map<v3s16, bool> light_sources;
5627 // 750ms @cs=8, can't optimize more
5628 TimeTaker timer1("initial lighting");
5631 Go through the edges and apply sunlight to them, not caring
5636 for(s16 i=0; i<4; i++)
5638 for(s16 j=lighting_min_d;
5645 if(i == 0 || i == 1)
5647 x = (i==0) ? lighting_min_d : lighting_max_d;
5656 z = (i==0) ? lighting_min_d : lighting_max_d;
5663 // Node position in 2d
5664 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
5666 // Loop from top to down
5668 u8 light = LIGHT_SUN;
5669 v3s16 em = vmanip.m_area.getExtent();
5670 s16 y_start = y_nodes_max;
5671 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
5672 for(s16 y=y_start; y>=y_nodes_min; y--)
5674 MapNode *n = &vmanip.m_data[i];
5675 if(light_propagates_content(n->d) == false)
5679 else if(light != LIGHT_SUN
5680 || sunlight_propagates_content(n->d) == false)
5686 n->setLight(LIGHTBANK_DAY, light);
5687 n->setLight(LIGHTBANK_NIGHT, 0);
5691 // Insert light source
5692 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
5695 // Increment index by y
5696 vmanip.m_area.add_y(em, i, -1);
5702 This has to be 1 smaller than the actual area, because
5703 neighboring nodes are checked.
5705 for(s16 x=lighting_min_d+1;
5706 x<=lighting_max_d-1;
5708 for(s16 z=lighting_min_d+1;
5709 z<=lighting_max_d-1;
5712 // Node position in 2d
5713 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
5716 Apply initial sunlight
5719 u8 light = LIGHT_SUN;
5720 bool add_to_sources = false;
5721 v3s16 em = vmanip.m_area.getExtent();
5722 s16 y_start = y_nodes_max;
5723 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
5724 for(s16 y=y_start; y>=y_nodes_min; y--)
5726 MapNode *n = &vmanip.m_data[i];
5728 if(light_propagates_content(n->d) == false)
5732 else if(light != LIGHT_SUN
5733 || sunlight_propagates_content(n->d) == false)
5739 // This doesn't take much time
5740 if(add_to_sources == false)
5743 Check sides. If side is not air or water, start
5744 adding to light_sources.
5747 v3s16(0,0,1), // back
5748 v3s16(1,0,0), // right
5749 v3s16(0,0,-1), // front
5750 v3s16(-1,0,0), // left
5752 for(u32 di=0; di<4; di++)
5754 v3s16 dirp = dirs4[di];
5756 vmanip.m_area.add_p(em, i2, dirp);
5757 MapNode *n2 = &vmanip.m_data[i2];
5759 n2->d != CONTENT_AIR
5760 && n2->d != CONTENT_WATERSOURCE
5761 && n2->d != CONTENT_WATER
5763 add_to_sources = true;
5769 n->setLight(LIGHTBANK_DAY, light);
5770 n->setLight(LIGHTBANK_NIGHT, 0);
5772 // This doesn't take much time
5773 if(light != 0 && add_to_sources)
5775 // Insert light source
5776 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
5779 // Increment index by y
5780 vmanip.m_area.add_y(em, i, -1);
5787 // Spread light around
5789 TimeTaker timer("generateBlockRaw() spreadLight");
5790 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
5795 Blit generated stuff to map
5798 vmanip.blitBackAll(&changed_blocks);
5803 Update day/night difference cache of the MapBlocks
5806 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
5807 i.atEnd() == false; i++)
5809 MapBlock *block = i.getNode()->getValue();
5810 block->updateDayNightDiff();
5815 /*for(core::map<v3s16, MapBlock*>::Iterator
5816 i = changed_blocks*/
5819 MapBlock *block = getBlockNoCreate(blockpos0);
5820 block->setFullyGenerated(true);
5823 TODO: Calculate lighting with the VoxelManipulator, not this way
5825 // emergeBlock reads this
5826 block->setLightingExpired(true);
5827 // Also set the above one, trees often will be there
5829 MapBlock *block = getBlockNoCreate(blockpos0+v3s16(0,1,0));
5831 block->setLightingExpired(true);
5838 /*MapBlock* ServerMap::generateBlock(v3s16 blockpos1,
5839 core::map<v3s16, MapBlock*> &changed_blocks)*/
5840 MapBlock * ServerMap::generateBlock(
5842 MapBlock *original_dummy,
5843 ServerMapSector *sector,
5844 core::map<v3s16, MapBlock*> &changed_blocks,
5845 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
5848 dstream<<"generateBlock(): Generating block "
5849 <<"("<<blockpos1.X<<","<<blockpos1.Y<<","<<blockpos1.Z<<")"
5853 The block at blockpos1 should be generated fully, so that it won't
5854 change in the future anymore when more stuff is generated.
5856 Now, a block can be generated with generateBlockRaw().
5857 generateBlockRaw() accesses the block and all its neighbors.
5858 Here is what generateBlockRaw() does:
5859 - If the asked block has been marked fully generated, do nothing.
5860 - Create the asked block and its neighbors and generate the base
5861 ground in them (if they don't exist)
5862 - In places where the ground level is in the asked block, convert
5863 top layer of ground to mud or sand. Conversion is extended to
5864 the block below as needed.
5865 - Add trees and other objects to the asked block. Parts of them
5866 can be located in the neighboring blocks.
5867 - Mark the asked block as fully generated.
5869 This means the block and all its neighbors have to be generated to
5870 obtain a block that won't change in the future.
5872 for(s16 x=-1; x<=1; x++)
5873 for(s16 y=-1; y<=1; y++)
5874 for(s16 z=-1; z<=1; z++)
5876 v3s16 blockpos0 = blockpos1 + v3s16(x,y,z);
5877 MapBlock *block = getBlockNoCreateNoEx(blockpos0);
5878 // Skip if already generated
5879 if(block != NULL && block->isFullyGenerated())
5881 generateBlockRaw(blockpos0, changed_blocks);
5884 assert(blockNonVolatile(blockpos1));
5886 MapBlock *block = getBlockNoCreate(blockpos1);
5893 NOTE: This is not used for main map generation, only for blocks
5894 that are very high or low.
5895 NOTE: Now it is used mainly. Might change in the future.
5897 MapBlock * ServerMap::generateBlock(
5899 MapBlock *original_dummy,
5900 ServerMapSector *sector,
5901 core::map<v3s16, MapBlock*> &changed_blocks,
5902 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
5905 DSTACK("%s: p=(%d,%d,%d)",
5909 /*dstream<<"generateBlock(): "
5910 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5913 MapBlock *block = original_dummy;
5915 v2s16 p2d(p.X, p.Z);
5917 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
5918 v3s16 p_nodes = p * MAP_BLOCKSIZE;
5921 Do not generate over-limit
5923 if(blockpos_over_limit(p))
5925 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
5926 throw InvalidPositionException("generateBlock(): pos. over limit");
5930 If block doesn't exist, create one.
5931 If it exists, it is a dummy. In that case unDummify() it.
5933 NOTE: This already sets the map as the parent of the block
5937 block = sector->createBlankBlockNoInsert(block_y);
5941 // Remove the block so that nobody can get a half-generated one.
5942 sector->removeBlock(block);
5943 // Allocate the block to contain the generated data
5947 u8 water_material = CONTENT_WATERSOURCE;
5949 s32 lowest_ground_y = 32767;
5950 s32 highest_ground_y = -32768;
5956 } block_type = BT_SURFACE;
5958 {// ground_timer (0ms or ~100ms)
5959 TimeTaker ground_timer("Ground generation");
5962 Approximate whether this block is a surface block, an air
5963 block or a ground block.
5965 This shall never mark a surface block as non-surface.
5970 Estimate surface at different positions of the block, to
5971 try to accomodate the effect of turbulence.
5984 v3f p_nodes_f = intToFloat(p_nodes, 1);
5985 float surface_y_max = -1000000;
5986 float surface_y_min = 1000000;
5987 for(u32 i=0; i<sizeof(checklist)/sizeof(checklist[0]); i++)
5989 v3f p_map_f = p_nodes_f + checklist[i]*MAP_BLOCKSIZE;
5992 /*bool is_ground =*/ is_base_ground(m_seed, p_map_f, &depth_guess);
5994 // Estimate the surface height
5995 float surface_y_f = p_map_f.Y + depth_guess;
5997 if(surface_y_f > surface_y_max)
5998 surface_y_max = surface_y_f;
5999 if(surface_y_f < surface_y_min)
6000 surface_y_min = surface_y_f;
6003 float block_low_y_f = p_nodes_f.Y;
6004 float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE;
6006 /*dstream<<"surface_y_max="<<surface_y_max
6007 <<", surface_y_min="<<surface_y_min
6008 <<", block_low_y_f="<<block_low_y_f
6009 <<", block_high_y_f="<<block_high_y_f
6012 // A fuzzyness value
6013 // Must accomodate mud and turbulence holes
6015 // Must accomodate a bit less
6018 if(block_high_y_f < surface_y_min - d_down)
6020 //dstream<<"BT_GROUND"<<std::endl;
6022 block_type = BT_GROUND;
6024 else if(block_low_y_f >= surface_y_max + d_up
6025 && block_low_y_f > WATER_LEVEL + d_up)
6027 //dstream<<"BT_SKY"<<std::endl;
6029 block_type = BT_SKY;
6033 //dstream<<"BT_SURFACE"<<std::endl;
6035 block_type = BT_SURFACE;
6038 if(/*block_type == BT_GROUND ||*/ block_type == BT_SKY)
6040 lowest_ground_y = surface_y_min;
6041 highest_ground_y = surface_y_max;
6045 if(block_type == BT_SURFACE || block_type == BT_GROUND)
6048 Generate ground precisely
6051 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
6052 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
6054 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
6056 //s16 surface_y = 0;
6058 /*s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
6059 + AVERAGE_MUD_AMOUNT;
6061 if(surface_y < lowest_ground_y)
6062 lowest_ground_y = surface_y;
6063 if(surface_y > highest_ground_y)
6064 highest_ground_y = surface_y;*/
6066 v2s16 real_p2d = v2s16(x0,z0) + p2d*MAP_BLOCKSIZE;
6068 v2f real_p2d_f(real_p2d.X,real_p2d.Y);
6070 s16 surface_depth = get_mud_amount(m_seed, real_p2d_f);
6072 double tfxz = get_turbulence_factor_2d(m_seed, real_p2d_f);
6073 bool turbulence_is_used = (tfxz > 0.001);
6075 float surface_y_f = 0;
6078 float noturb_surface_y_f = base_rock_level_2d(m_seed, real_p2d_f);
6079 s16 noturb_surface_y = noturb_surface_y_f;
6081 // Get some statistics of surface height
6082 if(noturb_surface_y < lowest_ground_y)
6083 lowest_ground_y = noturb_surface_y;
6084 if(noturb_surface_y > highest_ground_y)
6085 highest_ground_y = noturb_surface_y;
6087 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
6090 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
6091 v3s16 real_pos = v3s16(x0,y0,z0) + p_nodes;
6096 NOTE: If there are some man-made structures above the
6097 newly created block, they won't be taken into account.
6099 /*if(real_y > surface_y)
6100 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);*/
6106 bool is_ground = false;
6107 v3f real_pos_f = intToFloat(real_pos, 1);
6109 bool turb_for_node = (turbulence_is_used
6110 && real_y >= TURBULENCE_BOTTOM_CUTOFF_Y);
6112 bool is_cavern = false;
6114 if(is_carved(m_seed, real_pos_f))
6117 if(real_y < noturb_surface_y)
6125 is_ground = is_base_ground(m_seed,
6126 real_pos_f, &depth_guess);
6128 // Estimate the surface height
6129 surface_y_f = (float)real_y + depth_guess;
6130 surface_y = real_y + depth_guess;
6132 // Get some statistics of surface height
6133 if(surface_y < lowest_ground_y)
6134 lowest_ground_y = surface_y;
6135 if(surface_y > highest_ground_y)
6136 highest_ground_y = surface_y;
6140 surface_y = noturb_surface_y;
6143 is_ground = (real_y <= surface_y);
6146 // If node is not ground, it's air or water
6147 if(is_ground == false)
6149 // If under water level, it's water
6150 if(real_y < WATER_LEVEL && !is_cavern)
6152 n.d = water_material;
6154 if(real_y >= surface_y)
6155 dist = WATER_LEVEL-real_y+1;
6156 n.setLight(LIGHTBANK_DAY,
6157 diminish_light(LIGHT_SUN, dist));
6159 Add to transforming liquid queue (in case it'd
6162 m_transforming_liquid.push_back(real_pos);
6168 // Else it's ground or dungeons (air)
6171 // If it's surface_depth under ground, it's stone
6172 if((float)real_y <= surface_y_f - surface_depth - 0.75)
6174 if(is_underground_mud(m_seed, real_pos_f))
6177 n.d = CONTENT_STONE;
6179 else if(surface_y_f <= WATER_LEVEL + 2.1
6180 && get_have_sand_coast(m_seed, real_p2d_f))
6186 /*// It is mud if it is under the first ground
6187 // level or under water
6188 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
6194 n.d = CONTENT_GRASS;
6197 if(get_have_sand_ground(m_seed, real_p2d_f))
6202 /*// If under water level, it's mud
6203 if(real_y < WATER_LEVEL)
6205 // Only the topmost node is grass
6206 else if(real_y <= surface_y - 1)
6209 n.d = CONTENT_GRASS;*/
6213 block->setNode(v3s16(x0,y0,z0), n);
6216 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
6221 NOTE: If there are some man-made structures above the
6222 newly created block, they won't be taken into account.
6224 if(real_y > surface_y)
6225 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
6231 // If node is over heightmap y, it's air or water
6232 if(real_y > surface_y)
6234 // If under water level, it's water
6235 if(real_y < WATER_LEVEL)
6237 n.d = water_material;
6238 n.setLight(LIGHTBANK_DAY,
6239 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
6241 Add to transforming liquid queue (in case it'd
6244 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
6245 m_transforming_liquid.push_back(real_pos);
6251 // Else it's ground or dungeons (air)
6254 // If it's surface_depth under ground, it's stone
6255 if(real_y <= surface_y - surface_depth)
6257 n.d = CONTENT_STONE;
6261 // It is mud if it is under the first ground
6262 // level or under water
6263 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
6269 n.d = CONTENT_GRASS;
6272 //n.d = CONTENT_MUD;
6274 /*// If under water level, it's mud
6275 if(real_y < WATER_LEVEL)
6277 // Only the topmost node is grass
6278 else if(real_y <= surface_y - 1)
6281 n.d = CONTENT_GRASS;*/
6285 block->setNode(v3s16(x0,y0,z0), n);
6290 else // BT_GROUND, BT_SKY or anything else
6293 if(block_type == BT_GROUND)
6295 //n_fill.d = CONTENT_STONE;
6297 else if(block_type == BT_SKY)
6299 n_fill.d = CONTENT_AIR;
6300 n_fill.setLight(LIGHTBANK_DAY, LIGHT_SUN);
6304 n_fill.d = CONTENT_MESE;
6308 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
6309 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
6310 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
6312 //MapNode n = block->getNode(v3s16(x0,y0,z0));
6313 block->setNode(v3s16(x0,y0,z0), n_fill);
6320 Calculate some helper variables
6323 // Completely underground if the highest part of block is under lowest
6325 // This has to be very sure; it's probably one too strict now but
6326 // that's just better.
6327 bool completely_underground =
6328 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
6330 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
6332 bool mostly_underwater_surface = false;
6333 if(highest_ground_y < WATER_LEVEL
6334 && some_part_underground && !completely_underground)
6335 mostly_underwater_surface = true;
6338 Get local attributes
6341 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
6343 //float caves_amount = 0.5;
6348 NOTE: BEWARE: Too big amount of attribute points slows verything
6350 1 interpolation from 5000 points takes 2-3ms.
6352 //TimeTaker timer("generateBlock() local attribute retrieval");
6353 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
6354 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
6355 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
6359 //dstream<<"generateBlock(): Done"<<std::endl;
6362 // Set to true if has caves.
6363 // Set when some non-air is changed to air when making caves.
6364 bool has_dungeons = false;
6370 // Initialize temporary table
6371 const s32 ued = MAP_BLOCKSIZE;
6372 bool underground_emptiness[ued*ued*ued];
6373 for(s32 i=0; i<ued*ued*ued; i++)
6375 underground_emptiness[i] = 0;
6382 Initialize orp and ors. Try to find if some neighboring
6383 MapBlock has a tunnel ended in its side
6387 (float)(myrand()%ued)+0.5,
6388 (float)(myrand()%ued)+0.5,
6389 (float)(myrand()%ued)+0.5
6392 bool found_existing = false;
6398 for(s16 y=0; y<ued; y++)
6399 for(s16 x=0; x<ued; x++)
6401 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
6402 if(getNode(ap).d == CONTENT_AIR)
6404 orp = v3f(x+1,y+1,0);
6405 found_existing = true;
6406 goto continue_generating;
6410 catch(InvalidPositionException &e){}
6416 for(s16 y=0; y<ued; y++)
6417 for(s16 x=0; x<ued; x++)
6419 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
6420 if(getNode(ap).d == CONTENT_AIR)
6422 orp = v3f(x+1,y+1,ued-1);
6423 found_existing = true;
6424 goto continue_generating;
6428 catch(InvalidPositionException &e){}
6434 for(s16 y=0; y<ued; y++)
6435 for(s16 z=0; z<ued; z++)
6437 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
6438 if(getNode(ap).d == CONTENT_AIR)
6440 orp = v3f(0,y+1,z+1);
6441 found_existing = true;
6442 goto continue_generating;
6446 catch(InvalidPositionException &e){}
6452 for(s16 y=0; y<ued; y++)
6453 for(s16 z=0; z<ued; z++)
6455 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
6456 if(getNode(ap).d == CONTENT_AIR)
6458 orp = v3f(ued-1,y+1,z+1);
6459 found_existing = true;
6460 goto continue_generating;
6464 catch(InvalidPositionException &e){}
6470 for(s16 x=0; x<ued; x++)
6471 for(s16 z=0; z<ued; z++)
6473 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
6474 if(getNode(ap).d == CONTENT_AIR)
6476 orp = v3f(x+1,0,z+1);
6477 found_existing = true;
6478 goto continue_generating;
6482 catch(InvalidPositionException &e){}
6488 for(s16 x=0; x<ued; x++)
6489 for(s16 z=0; z<ued; z++)
6491 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
6492 if(getNode(ap).d == CONTENT_AIR)
6494 orp = v3f(x+1,ued-1,z+1);
6495 found_existing = true;
6496 goto continue_generating;
6500 catch(InvalidPositionException &e){}
6502 continue_generating:
6505 Choose whether to actually generate dungeon
6507 bool do_generate_dungeons = true;
6508 // Don't generate if no part is underground
6509 if(!some_part_underground)
6511 do_generate_dungeons = false;
6513 // Don't generate if mostly underwater surface
6514 /*else if(mostly_underwater_surface)
6516 do_generate_dungeons = false;
6518 // Partly underground = cave
6519 else if(!completely_underground)
6521 //do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
6522 do_generate_dungeons = false;
6524 // Found existing dungeon underground
6525 else if(found_existing && completely_underground)
6527 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
6529 // Underground and no dungeons found
6532 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
6535 if(do_generate_dungeons)
6538 Generate some tunnel starting from orp and ors
6540 for(u16 i=0; i<3; i++)
6543 (float)(myrand()%ued)+0.5,
6544 (float)(myrand()%ued)+0.5,
6545 (float)(myrand()%ued)+0.5
6549 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
6553 for(float f=0; f<1.0; f+=0.04)
6555 v3f fp = orp + vec * f;
6556 v3s16 cp(fp.X, fp.Y, fp.Z);
6558 s16 d1 = d0 + rs - 1;
6559 for(s16 z0=d0; z0<=d1; z0++)
6561 s16 si = rs - abs(z0);
6562 for(s16 x0=-si; x0<=si-1; x0++)
6564 s16 si2 = rs - abs(x0);
6565 for(s16 y0=-si2+1; y0<=si2-1; y0++)
6571 if(isInArea(p, ued) == false)
6573 underground_emptiness[ued*ued*z + ued*y + x] = 1;
6586 Apply temporary cave data to block
6589 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
6590 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
6592 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
6594 MapNode n = block->getNode(v3s16(x0,y0,z0));
6597 if(underground_emptiness[
6598 ued*ued*(z0*ued/MAP_BLOCKSIZE)
6599 +ued*(y0*ued/MAP_BLOCKSIZE)
6600 +(x0*ued/MAP_BLOCKSIZE)])
6602 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
6605 has_dungeons = true;
6611 block->setNode(v3s16(x0,y0,z0), n);
6617 This is used for guessing whether or not the block should
6618 receive sunlight from the top if the block above doesn't exist
6620 block->setIsUnderground(completely_underground);
6623 Force lighting update if some part of block is partly
6624 underground and has caves.
6626 /*if(some_part_underground && !completely_underground && has_dungeons)
6628 //dstream<<"Half-ground caves"<<std::endl;
6629 lighting_invalidated_blocks[block->getPos()] = block;
6632 // DEBUG: Always update lighting
6633 //lighting_invalidated_blocks[block->getPos()] = block;
6639 if(some_part_underground)
6641 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
6646 for(s16 i=0; i<underground_level/4 + 1; i++)
6648 if(myrand()%50 == 0)
6651 (myrand()%(MAP_BLOCKSIZE-2))+1,
6652 (myrand()%(MAP_BLOCKSIZE-2))+1,
6653 (myrand()%(MAP_BLOCKSIZE-2))+1
6659 for(u16 i=0; i<27; i++)
6661 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
6663 block->setNode(cp+g_27dirs[i], n);
6671 u16 coal_amount = 60;
6672 u16 coal_rareness = 120 / coal_amount;
6673 if(coal_rareness == 0)
6675 if(myrand()%coal_rareness == 0)
6677 u16 a = myrand() % 16;
6678 u16 amount = coal_amount * a*a*a / 1000;
6679 for(s16 i=0; i<amount; i++)
6682 (myrand()%(MAP_BLOCKSIZE-2))+1,
6683 (myrand()%(MAP_BLOCKSIZE-2))+1,
6684 (myrand()%(MAP_BLOCKSIZE-2))+1
6688 n.d = CONTENT_STONE;
6689 n.param = MINERAL_COAL;
6691 for(u16 i=0; i<27; i++)
6693 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
6695 block->setNode(cp+g_27dirs[i], n);
6703 u16 iron_amount = 40;
6704 u16 iron_rareness = 80 / iron_amount;
6705 if(iron_rareness == 0)
6707 if(myrand()%iron_rareness == 0)
6709 u16 a = myrand() % 16;
6710 u16 amount = iron_amount * a*a*a / 1000;
6711 for(s16 i=0; i<amount; i++)
6714 (myrand()%(MAP_BLOCKSIZE-2))+1,
6715 (myrand()%(MAP_BLOCKSIZE-2))+1,
6716 (myrand()%(MAP_BLOCKSIZE-2))+1
6720 n.d = CONTENT_STONE;
6721 n.param = MINERAL_IRON;
6723 for(u16 i=0; i<27; i++)
6725 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
6727 block->setNode(cp+g_27dirs[i], n);
6734 Create a few rats in empty blocks underground
6736 if(completely_underground)
6738 //for(u16 i=0; i<2; i++)
6741 (myrand()%(MAP_BLOCKSIZE-2))+1,
6742 (myrand()%(MAP_BLOCKSIZE-2))+1,
6743 (myrand()%(MAP_BLOCKSIZE-2))+1
6746 // Check that the place is empty
6747 //if(!is_ground_content(block->getNode(cp).d))
6750 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
6751 block->addObject(obj);
6759 sector->insertBlock(block);
6761 // Lighting is invalid after generation for surface blocks
6762 if(block_type == BT_SURFACE)
6765 block->setLightingExpired(true);
6766 lighting_invalidated_blocks.insert(p, block);
6768 block->setLightingExpired(false);
6771 // Lighting is not invalid for other blocks
6774 block->setLightingExpired(false);
6781 if(some_part_underground && !completely_underground)
6783 MapVoxelManipulator vm(this);
6785 double a = tree_amount_2d(m_seed, v2s16(p_nodes.X+8, p_nodes.Z+8));
6786 u16 tree_count = (u16)(a*MAP_BLOCKSIZE*MAP_BLOCKSIZE);
6787 for(u16 i=0; i<tree_count/2; i++)
6789 v3s16 tree_p = p_nodes + v3s16(
6790 myrand_range(0,MAP_BLOCKSIZE-1),
6792 myrand_range(0,MAP_BLOCKSIZE-1)
6795 /*bool is_ground =*/ is_base_ground(m_seed,
6796 intToFloat(tree_p, 1), &depth_guess);
6797 tree_p.Y += (depth_guess - 0.5);
6798 if(tree_p.Y <= WATER_LEVEL)
6800 make_tree(vm, tree_p);
6803 vm.blitBack(changed_blocks);
6812 <<"lighting_invalidated_blocks.size()"
6816 <<" "<<lighting_invalidated_blocks.size()
6817 <<", "<<has_dungeons
6818 <<", "<<completely_underground
6819 <<", "<<some_part_underground
6827 MapBlock * ServerMap::createBlock(v3s16 p)
6829 DSTACK("%s: p=(%d,%d,%d)",
6830 __FUNCTION_NAME, p.X, p.Y, p.Z);
6833 Do not create over-limit
6835 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6836 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6837 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6838 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6839 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6840 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
6841 throw InvalidPositionException("createBlock(): pos. over limit");
6843 v2s16 p2d(p.X, p.Z);
6846 This will create or load a sector if not found in memory.
6847 If block exists on disk, it will be loaded.
6849 NOTE: On old save formats, this will be slow, as it generates
6850 lighting on blocks for them.
6852 ServerMapSector *sector;
6854 sector = (ServerMapSector*)createSector(p2d);
6855 assert(sector->getId() == MAPSECTOR_SERVER);
6857 catch(InvalidPositionException &e)
6859 dstream<<"createBlock: createSector() failed"<<std::endl;
6863 NOTE: This should not be done, or at least the exception
6864 should not be passed on as std::exception, because it
6865 won't be catched at all.
6867 /*catch(std::exception &e)
6869 dstream<<"createBlock: createSector() failed: "
6870 <<e.what()<<std::endl;
6875 Try to get a block from the sector
6878 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
6882 block = sector->createBlankBlock(block_y);
6886 MapBlock * ServerMap::emergeBlock(
6888 bool only_from_disk,
6889 core::map<v3s16, MapBlock*> &changed_blocks,
6890 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
6893 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
6895 p.X, p.Y, p.Z, only_from_disk);
6898 Do not generate over-limit
6900 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6901 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6902 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6903 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6904 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
6905 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
6906 throw InvalidPositionException("emergeBlock(): pos. over limit");
6908 v2s16 p2d(p.X, p.Z);
6911 This will create or load a sector if not found in memory.
6912 If block exists on disk, it will be loaded.
6914 ServerMapSector *sector;
6916 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
6917 assert(sector->getId() == MAPSECTOR_SERVER);
6919 catch(InvalidPositionException &e)
6921 dstream<<"emergeBlock: emergeSector() failed: "
6922 <<e.what()<<std::endl;
6923 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
6925 <<"You could try to delete it."<<std::endl;
6928 catch(VersionMismatchException &e)
6930 dstream<<"emergeBlock: emergeSector() failed: "
6931 <<e.what()<<std::endl;
6932 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
6934 <<"You could try to delete it."<<std::endl;
6938 NOTE: This should not be done, or at least the exception
6939 should not be passed on as std::exception, because it
6940 won't be catched at all.
6942 /*catch(std::exception &e)
6944 dstream<<"emergeBlock: emergeSector() failed: "
6945 <<e.what()<<std::endl;
6946 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
6948 <<"You could try to delete it."<<std::endl;
6953 Try to get a block from the sector
6956 bool does_not_exist = false;
6957 bool lighting_expired = false;
6958 bool half_generated = false;
6959 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
6963 does_not_exist = true;
6965 else if(block->isDummy() == true)
6967 does_not_exist = true;
6969 else if(block->getLightingExpired())
6971 lighting_expired = true;
6973 else if(block->isFullyGenerated() == false)
6975 half_generated = true;
6980 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
6984 if(half_generated == false)
6987 If block was not found on disk and not going to generate a
6988 new one, make sure there is a dummy block in place.
6990 if(only_from_disk && (does_not_exist || lighting_expired))
6992 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
6996 // Create dummy block
6997 block = new MapBlock(this, p, true);
6999 // Add block to sector
7000 sector->insertBlock(block);
7007 //dstream<<"Not found on disk, generating."<<std::endl;
7009 //TimeTaker("emergeBlock() generate");
7011 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
7014 If the block doesn't exist, generate the block.
7016 if(does_not_exist || half_generated)
7018 block = generateBlock(p, block, sector, changed_blocks,
7019 lighting_invalidated_blocks);
7021 lighting_expired = block->getLightingExpired();
7024 if(lighting_expired)
7026 lighting_invalidated_blocks.insert(p, block);
7030 Initially update sunlight
7033 if(lighting_expired)
7035 core::map<v3s16, bool> light_sources;
7036 bool black_air_left = false;
7037 bool bottom_invalid =
7038 block->propagateSunlight(light_sources, true,
7039 &black_air_left, true);
7041 // If sunlight didn't reach everywhere and part of block is
7042 // above ground, lighting has to be properly updated
7043 //if(black_air_left && some_part_underground)
7046 lighting_invalidated_blocks[block->getPos()] = block;
7051 lighting_invalidated_blocks[block->getPos()] = block;
7058 s16 ServerMap::findGroundLevel(v2s16 p2d)
7061 Uh, just do something random...
7063 // Find existing map from top to down
7066 v3s16 p(p2d.X, max, p2d.Y);
7067 for(; p.Y>min; p.Y--)
7069 MapNode n = getNodeNoEx(p);
7070 if(n.d != CONTENT_IGNORE)
7075 // If this node is not air, go to plan b
7076 if(getNodeNoEx(p).d != CONTENT_AIR)
7078 // Search existing walkable and return it
7079 for(; p.Y>min; p.Y--)
7081 MapNode n = getNodeNoEx(p);
7082 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
7088 Plan B: Get from map generator perlin noise function
7090 double level = base_rock_level_2d(m_seed, p2d);
7094 void ServerMap::createDir(std::string path)
7096 if(fs::CreateDir(path) == false)
7098 m_dout<<DTIME<<"ServerMap: Failed to create directory "
7099 <<"\""<<path<<"\""<<std::endl;
7100 throw BaseException("ServerMap failed to create directory");
7104 std::string ServerMap::getSectorSubDir(v2s16 pos)
7107 snprintf(cc, 9, "%.4x%.4x",
7108 (unsigned int)pos.X&0xffff,
7109 (unsigned int)pos.Y&0xffff);
7111 return std::string(cc);
7114 std::string ServerMap::getSectorDir(v2s16 pos)
7116 return m_savedir + "/sectors/" + getSectorSubDir(pos);
7119 v2s16 ServerMap::getSectorPos(std::string dirname)
7121 if(dirname.size() != 8)
7122 throw InvalidFilenameException("Invalid sector directory name");
7124 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
7126 throw InvalidFilenameException("Invalid sector directory name");
7127 v2s16 pos((s16)x, (s16)y);
7131 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
7133 v2s16 p2d = getSectorPos(sectordir);
7135 if(blockfile.size() != 4){
7136 throw InvalidFilenameException("Invalid block filename");
7139 int r = sscanf(blockfile.c_str(), "%4x", &y);
7141 throw InvalidFilenameException("Invalid block filename");
7142 return v3s16(p2d.X, y, p2d.Y);
7145 void ServerMap::save(bool only_changed)
7147 DSTACK(__FUNCTION_NAME);
7148 if(m_map_saving_enabled == false)
7150 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
7154 if(only_changed == false)
7155 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
7161 u32 sector_meta_count = 0;
7162 u32 block_count = 0;
7165 JMutexAutoLock lock(m_sector_mutex);
7167 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
7168 for(; i.atEnd() == false; i++)
7170 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
7171 assert(sector->getId() == MAPSECTOR_SERVER);
7173 if(sector->differs_from_disk || only_changed == false)
7175 saveSectorMeta(sector);
7176 sector_meta_count++;
7178 core::list<MapBlock*> blocks;
7179 sector->getBlocks(blocks);
7180 core::list<MapBlock*>::Iterator j;
7181 for(j=blocks.begin(); j!=blocks.end(); j++)
7183 MapBlock *block = *j;
7184 if(block->getChangedFlag() || only_changed == false)
7189 /*dstream<<"ServerMap: Written block ("
7190 <<block->getPos().X<<","
7191 <<block->getPos().Y<<","
7192 <<block->getPos().Z<<")"
7201 Only print if something happened or saved whole map
7203 if(only_changed == false || sector_meta_count != 0
7204 || block_count != 0)
7206 dstream<<DTIME<<"ServerMap: Written: "
7207 <<sector_meta_count<<" sector metadata files, "
7208 <<block_count<<" block files"
7213 void ServerMap::loadAll()
7215 DSTACK(__FUNCTION_NAME);
7216 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
7221 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
7223 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
7225 JMutexAutoLock lock(m_sector_mutex);
7228 s32 printed_counter = -100000;
7229 s32 count = list.size();
7231 std::vector<fs::DirListNode>::iterator i;
7232 for(i=list.begin(); i!=list.end(); i++)
7234 if(counter > printed_counter + 10)
7236 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
7237 printed_counter = counter;
7241 MapSector *sector = NULL;
7243 // We want directories
7247 sector = loadSectorMeta(i->name);
7249 catch(InvalidFilenameException &e)
7251 // This catches unknown crap in directory
7254 std::vector<fs::DirListNode> list2 = fs::GetDirListing
7255 (m_savedir+"/sectors/"+i->name);
7256 std::vector<fs::DirListNode>::iterator i2;
7257 for(i2=list2.begin(); i2!=list2.end(); i2++)
7263 loadBlock(i->name, i2->name, sector);
7265 catch(InvalidFilenameException &e)
7267 // This catches unknown crap in directory
7271 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
7275 void ServerMap::saveMasterHeightmap()
7277 DSTACK(__FUNCTION_NAME);
7279 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
7281 createDir(m_savedir);
7283 /*std::string fullpath = m_savedir + "/master_heightmap";
7284 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
7285 if(o.good() == false)
7286 throw FileNotGoodException("Cannot open master heightmap");*/
7288 // Format used for writing
7289 //u8 version = SER_FMT_VER_HIGHEST;
7292 void ServerMap::loadMasterHeightmap()
7294 DSTACK(__FUNCTION_NAME);
7296 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
7298 /*std::string fullpath = m_savedir + "/master_heightmap";
7299 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
7300 if(is.good() == false)
7301 throw FileNotGoodException("Cannot open master heightmap");*/
7305 void ServerMap::saveMapMeta()
7307 DSTACK(__FUNCTION_NAME);
7309 dstream<<"INFO: ServerMap::saveMapMeta(): "
7311 /*<<", chunksize="<<m_chunksize*/
7314 createDir(m_savedir);
7316 std::string fullpath = m_savedir + "/map_meta.txt";
7317 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
7318 if(os.good() == false)
7320 dstream<<"ERROR: ServerMap::saveMapMeta(): "
7321 <<"could not open"<<fullpath<<std::endl;
7322 throw FileNotGoodException("Cannot open chunk metadata");
7326 params.setU64("seed", m_seed);
7327 //params.setS32("chunksize", m_chunksize);
7329 params.writeLines(os);
7331 os<<"[end_of_params]\n";
7335 void ServerMap::loadMapMeta()
7337 DSTACK(__FUNCTION_NAME);
7339 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
7342 std::string fullpath = m_savedir + "/map_meta.txt";
7343 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
7344 if(is.good() == false)
7346 dstream<<"ERROR: ServerMap::loadMapMeta(): "
7347 <<"could not open"<<fullpath<<std::endl;
7348 throw FileNotGoodException("Cannot open chunk metadata");
7356 throw SerializationError
7357 ("ServerMap::loadMapMeta(): [end_of_params] not found");
7359 std::getline(is, line);
7360 std::string trimmedline = trim(line);
7361 if(trimmedline == "[end_of_params]")
7363 params.parseConfigLine(line);
7366 m_seed = params.getU64("seed");
7367 //m_chunksize = params.getS32("chunksize");
7369 dstream<<"INFO: ServerMap::loadMapMeta(): "
7371 /*<<", chunksize="<<m_chunksize*/
7376 void ServerMap::saveChunkMeta()
7378 DSTACK(__FUNCTION_NAME);
7380 u32 count = m_chunks.size();
7382 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
7383 <<count<<" chunks"<<std::endl;
7385 createDir(m_savedir);
7387 std::string fullpath = m_savedir + "/chunk_meta";
7388 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
7389 if(os.good() == false)
7391 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
7392 <<"could not open"<<fullpath<<std::endl;
7393 throw FileNotGoodException("Cannot open chunk metadata");
7399 os.write((char*)&version, 1);
7404 writeU32(buf, count);
7405 os.write((char*)buf, 4);
7407 for(core::map<v2s16, MapChunk*>::Iterator
7408 i = m_chunks.getIterator();
7409 i.atEnd()==false; i++)
7411 v2s16 p = i.getNode()->getKey();
7412 MapChunk *chunk = i.getNode()->getValue();
7415 os.write((char*)buf, 4);
7417 chunk->serialize(os, version);
7421 void ServerMap::loadChunkMeta()
7423 DSTACK(__FUNCTION_NAME);
7425 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
7428 std::string fullpath = m_savedir + "/chunk_meta";
7429 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
7430 if(is.good() == false)
7432 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
7433 <<"could not open"<<fullpath<<std::endl;
7434 throw FileNotGoodException("Cannot open chunk metadata");
7440 is.read((char*)&version, 1);
7445 is.read((char*)buf, 4);
7446 u32 count = readU32(buf);
7448 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
7449 <<count<<" chunks"<<std::endl;
7451 for(u32 i=0; i<count; i++)
7454 MapChunk *chunk = new MapChunk();
7456 is.read((char*)buf, 4);
7459 chunk->deSerialize(is, version);
7460 m_chunks.insert(p, chunk);
7465 void ServerMap::saveSectorMeta(ServerMapSector *sector)
7467 DSTACK(__FUNCTION_NAME);
7468 // Format used for writing
7469 u8 version = SER_FMT_VER_HIGHEST;
7471 v2s16 pos = sector->getPos();
7472 createDir(m_savedir);
7473 createDir(m_savedir+"/sectors");
7474 std::string dir = getSectorDir(pos);
7477 std::string fullpath = dir + "/meta";
7478 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
7479 if(o.good() == false)
7480 throw FileNotGoodException("Cannot open sector metafile");
7482 sector->serialize(o, version);
7484 sector->differs_from_disk = false;
7487 MapSector* ServerMap::loadSectorMeta(std::string dirname)
7489 DSTACK(__FUNCTION_NAME);
7491 v2s16 p2d = getSectorPos(dirname);
7492 std::string dir = m_savedir + "/sectors/" + dirname;
7494 std::string fullpath = dir + "/meta";
7495 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
7496 if(is.good() == false)
7497 throw FileNotGoodException("Cannot open sector metafile");
7499 ServerMapSector *sector = ServerMapSector::deSerialize
7500 (is, this, p2d, m_sectors);
7502 sector->differs_from_disk = false;
7507 bool ServerMap::loadSectorFull(v2s16 p2d)
7509 DSTACK(__FUNCTION_NAME);
7510 std::string sectorsubdir = getSectorSubDir(p2d);
7512 MapSector *sector = NULL;
7514 JMutexAutoLock lock(m_sector_mutex);
7517 sector = loadSectorMeta(sectorsubdir);
7519 catch(InvalidFilenameException &e)
7523 catch(FileNotGoodException &e)
7527 catch(std::exception &e)
7535 std::vector<fs::DirListNode> list2 = fs::GetDirListing
7536 (m_savedir+"/sectors/"+sectorsubdir);
7537 std::vector<fs::DirListNode>::iterator i2;
7538 for(i2=list2.begin(); i2!=list2.end(); i2++)
7544 loadBlock(sectorsubdir, i2->name, sector);
7546 catch(InvalidFilenameException &e)
7548 // This catches unknown crap in directory
7554 void ServerMap::saveBlock(MapBlock *block)
7556 DSTACK(__FUNCTION_NAME);
7558 Dummy blocks are not written
7560 if(block->isDummy())
7562 /*v3s16 p = block->getPos();
7563 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
7564 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
7568 // Format used for writing
7569 u8 version = SER_FMT_VER_HIGHEST;
7571 v3s16 p3d = block->getPos();
7572 v2s16 p2d(p3d.X, p3d.Z);
7573 createDir(m_savedir);
7574 createDir(m_savedir+"/sectors");
7575 std::string dir = getSectorDir(p2d);
7578 // Block file is map/sectors/xxxxxxxx/xxxx
7580 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
7581 std::string fullpath = dir + "/" + cc;
7582 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
7583 if(o.good() == false)
7584 throw FileNotGoodException("Cannot open block data");
7587 [0] u8 serialization version
7590 o.write((char*)&version, 1);
7592 block->serialize(o, version);
7595 Versions up from 9 have block objects.
7599 block->serializeObjects(o, version);
7602 // We just wrote it to the disk
7603 block->resetChangedFlag();
7606 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
7608 DSTACK(__FUNCTION_NAME);
7612 // Block file is map/sectors/xxxxxxxx/xxxx
7613 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
7614 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
7615 if(is.good() == false)
7616 throw FileNotGoodException("Cannot open block file");
7618 v3s16 p3d = getBlockPos(sectordir, blockfile);
7619 v2s16 p2d(p3d.X, p3d.Z);
7621 assert(sector->getPos() == p2d);
7623 u8 version = SER_FMT_VER_INVALID;
7624 is.read((char*)&version, 1);
7627 throw SerializationError("ServerMap::loadBlock(): Failed"
7628 " to read MapBlock version");
7630 /*u32 block_size = MapBlock::serializedLength(version);
7631 SharedBuffer<u8> data(block_size);
7632 is.read((char*)*data, block_size);*/
7634 // This will always return a sector because we're the server
7635 //MapSector *sector = emergeSector(p2d);
7637 MapBlock *block = NULL;
7638 bool created_new = false;
7640 block = sector->getBlockNoCreate(p3d.Y);
7642 catch(InvalidPositionException &e)
7644 block = sector->createBlankBlockNoInsert(p3d.Y);
7648 // deserialize block data
7649 block->deSerialize(is, version);
7652 Versions up from 9 have block objects.
7656 block->updateObjects(is, version, NULL, 0);
7660 sector->insertBlock(block);
7663 Convert old formats to new and save
7666 // Save old format blocks in new format
7667 if(version < SER_FMT_VER_HIGHEST)
7672 // We just loaded it from the disk, so it's up-to-date.
7673 block->resetChangedFlag();
7676 catch(SerializationError &e)
7678 dstream<<"WARNING: Invalid block data on disk "
7679 "(SerializationError). Ignoring. "
7680 "A new one will be generated."
7685 void ServerMap::PrintInfo(std::ostream &out)
7696 ClientMap::ClientMap(
7698 MapDrawControl &control,
7699 scene::ISceneNode* parent,
7700 scene::ISceneManager* mgr,
7704 scene::ISceneNode(parent, mgr, id),
7707 m_camera_position(0,0,0),
7708 m_camera_direction(0,0,1)
7710 m_camera_mutex.Init();
7711 assert(m_camera_mutex.IsInitialized());
7713 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
7714 BS*1000000,BS*1000000,BS*1000000);
7717 ClientMap::~ClientMap()
7719 /*JMutexAutoLock lock(mesh_mutex);
7728 MapSector * ClientMap::emergeSector(v2s16 p2d)
7730 DSTACK(__FUNCTION_NAME);
7731 // Check that it doesn't exist already
7733 return getSectorNoGenerate(p2d);
7735 catch(InvalidPositionException &e)
7740 ClientMapSector *sector = new ClientMapSector(this, p2d);
7743 JMutexAutoLock lock(m_sector_mutex);
7744 m_sectors.insert(p2d, sector);
7750 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
7752 DSTACK(__FUNCTION_NAME);
7753 ClientMapSector *sector = NULL;
7755 JMutexAutoLock lock(m_sector_mutex);
7757 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
7761 sector = (ClientMapSector*)n->getValue();
7762 assert(sector->getId() == MAPSECTOR_CLIENT);
7766 sector = new ClientMapSector(this, p2d);
7768 JMutexAutoLock lock(m_sector_mutex);
7769 m_sectors.insert(p2d, sector);
7773 sector->deSerialize(is);
7776 void ClientMap::OnRegisterSceneNode()
7780 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
7781 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
7784 ISceneNode::OnRegisterSceneNode();
7787 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
7789 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
7790 DSTACK(__FUNCTION_NAME);
7792 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
7795 Get time for measuring timeout.
7797 Measuring time is very useful for long delays when the
7798 machine is swapping a lot.
7800 int time1 = time(0);
7802 u32 daynight_ratio = m_client->getDayNightRatio();
7804 m_camera_mutex.Lock();
7805 v3f camera_position = m_camera_position;
7806 v3f camera_direction = m_camera_direction;
7807 m_camera_mutex.Unlock();
7810 Get all blocks and draw all visible ones
7813 v3s16 cam_pos_nodes(
7814 camera_position.X / BS,
7815 camera_position.Y / BS,
7816 camera_position.Z / BS);
7818 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
7820 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
7821 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
7823 // Take a fair amount as we will be dropping more out later
7825 p_nodes_min.X / MAP_BLOCKSIZE - 1,
7826 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
7827 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
7829 p_nodes_max.X / MAP_BLOCKSIZE + 1,
7830 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
7831 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
7833 u32 vertex_count = 0;
7835 // For limiting number of mesh updates per frame
7836 u32 mesh_update_count = 0;
7838 u32 blocks_would_have_drawn = 0;
7839 u32 blocks_drawn = 0;
7841 //NOTE: The sectors map should be locked but we're not doing it
7842 // because it'd cause too much delays
7844 int timecheck_counter = 0;
7845 core::map<v2s16, MapSector*>::Iterator si;
7846 si = m_sectors.getIterator();
7847 for(; si.atEnd() == false; si++)
7850 timecheck_counter++;
7851 if(timecheck_counter > 50)
7853 timecheck_counter = 0;
7854 int time2 = time(0);
7855 if(time2 > time1 + 4)
7857 dstream<<"ClientMap::renderMap(): "
7858 "Rendering takes ages, returning."
7865 MapSector *sector = si.getNode()->getValue();
7866 v2s16 sp = sector->getPos();
7868 if(m_control.range_all == false)
7870 if(sp.X < p_blocks_min.X
7871 || sp.X > p_blocks_max.X
7872 || sp.Y < p_blocks_min.Z
7873 || sp.Y > p_blocks_max.Z)
7877 core::list< MapBlock * > sectorblocks;
7878 sector->getBlocks(sectorblocks);
7884 core::list< MapBlock * >::Iterator i;
7885 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
7887 MapBlock *block = *i;
7890 Compare block position to camera position, skip
7891 if not seen on display
7894 float range = 100000 * BS;
7895 if(m_control.range_all == false)
7896 range = m_control.wanted_range * BS;
7899 if(isBlockInSight(block->getPos(), camera_position,
7900 camera_direction, range, &d) == false)
7906 /*if(m_control.range_all == false &&
7907 d - 0.5*BS*MAP_BLOCKSIZE > range)
7912 Update expired mesh (used for day/night change)
7915 bool mesh_expired = false;
7918 JMutexAutoLock lock(block->mesh_mutex);
7920 mesh_expired = block->getMeshExpired();
7922 // Mesh has not been expired and there is no mesh:
7923 // block has no content
7924 if(block->mesh == NULL && mesh_expired == false)
7928 f32 faraway = BS*50;
7929 //f32 faraway = m_control.wanted_range * BS;
7932 This has to be done with the mesh_mutex unlocked
7934 // Pretty random but this should work somewhat nicely
7935 if(mesh_expired && (
7936 (mesh_update_count < 3
7937 && (d < faraway || mesh_update_count < 2)
7940 (m_control.range_all && mesh_update_count < 20)
7943 /*if(mesh_expired && mesh_update_count < 6
7944 && (d < faraway || mesh_update_count < 3))*/
7946 mesh_update_count++;
7948 // Mesh has been expired: generate new mesh
7949 //block->updateMeshes(daynight_i);
7950 block->updateMesh(daynight_ratio);
7952 mesh_expired = false;
7956 Don't draw an expired mesh that is far away
7958 /*if(mesh_expired && d >= faraway)
7961 // Instead, delete it
7962 JMutexAutoLock lock(block->mesh_mutex);
7965 block->mesh->drop();
7968 // And continue to next block
7973 Draw the faces of the block
7976 JMutexAutoLock lock(block->mesh_mutex);
7978 scene::SMesh *mesh = block->mesh;
7983 blocks_would_have_drawn++;
7984 if(blocks_drawn >= m_control.wanted_max_blocks
7985 && m_control.range_all == false
7986 && d > m_control.wanted_min_range * BS)
7990 u32 c = mesh->getMeshBufferCount();
7992 for(u32 i=0; i<c; i++)
7994 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
7995 const video::SMaterial& material = buf->getMaterial();
7996 video::IMaterialRenderer* rnd =
7997 driver->getMaterialRenderer(material.MaterialType);
7998 bool transparent = (rnd && rnd->isTransparent());
7999 // Render transparent on transparent pass and likewise.
8000 if(transparent == is_transparent_pass)
8003 This *shouldn't* hurt too much because Irrlicht
8004 doesn't change opengl textures if the old
8005 material is set again.
8007 driver->setMaterial(buf->getMaterial());
8008 driver->drawMeshBuffer(buf);
8009 vertex_count += buf->getVertexCount();
8013 } // foreach sectorblocks
8016 m_control.blocks_drawn = blocks_drawn;
8017 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
8019 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
8020 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
8023 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
8024 core::map<v3s16, MapBlock*> *affected_blocks)
8026 bool changed = false;
8028 Add it to all blocks touching it
8031 v3s16(0,0,0), // this
8032 v3s16(0,0,1), // back
8033 v3s16(0,1,0), // top
8034 v3s16(1,0,0), // right
8035 v3s16(0,0,-1), // front
8036 v3s16(0,-1,0), // bottom
8037 v3s16(-1,0,0), // left
8039 for(u16 i=0; i<7; i++)
8041 v3s16 p2 = p + dirs[i];
8042 // Block position of neighbor (or requested) node
8043 v3s16 blockpos = getNodeBlockPos(p2);
8044 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
8045 if(blockref == NULL)
8047 // Relative position of requested node
8048 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
8049 if(blockref->setTempMod(relpos, mod))
8054 if(changed && affected_blocks!=NULL)
8056 for(u16 i=0; i<7; i++)
8058 v3s16 p2 = p + dirs[i];
8059 // Block position of neighbor (or requested) node
8060 v3s16 blockpos = getNodeBlockPos(p2);
8061 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
8062 if(blockref == NULL)
8064 affected_blocks->insert(blockpos, blockref);
8070 bool ClientMap::clearTempMod(v3s16 p,
8071 core::map<v3s16, MapBlock*> *affected_blocks)
8073 bool changed = false;
8075 v3s16(0,0,0), // this
8076 v3s16(0,0,1), // back
8077 v3s16(0,1,0), // top
8078 v3s16(1,0,0), // right
8079 v3s16(0,0,-1), // front
8080 v3s16(0,-1,0), // bottom
8081 v3s16(-1,0,0), // left
8083 for(u16 i=0; i<7; i++)
8085 v3s16 p2 = p + dirs[i];
8086 // Block position of neighbor (or requested) node
8087 v3s16 blockpos = getNodeBlockPos(p2);
8088 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
8089 if(blockref == NULL)
8091 // Relative position of requested node
8092 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
8093 if(blockref->clearTempMod(relpos))
8098 if(changed && affected_blocks!=NULL)
8100 for(u16 i=0; i<7; i++)
8102 v3s16 p2 = p + dirs[i];
8103 // Block position of neighbor (or requested) node
8104 v3s16 blockpos = getNodeBlockPos(p2);
8105 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
8106 if(blockref == NULL)
8108 affected_blocks->insert(blockpos, blockref);
8114 void ClientMap::expireMeshes(bool only_daynight_diffed)
8116 TimeTaker timer("expireMeshes()");
8118 core::map<v2s16, MapSector*>::Iterator si;
8119 si = m_sectors.getIterator();
8120 for(; si.atEnd() == false; si++)
8122 MapSector *sector = si.getNode()->getValue();
8124 core::list< MapBlock * > sectorblocks;
8125 sector->getBlocks(sectorblocks);
8127 core::list< MapBlock * >::Iterator i;
8128 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
8130 MapBlock *block = *i;
8132 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
8138 JMutexAutoLock lock(block->mesh_mutex);
8139 if(block->mesh != NULL)
8141 /*block->mesh->drop();
8142 block->mesh = NULL;*/
8143 block->setMeshExpired(true);
8150 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
8153 v3s16 p = blockpos + v3s16(0,0,0);
8154 MapBlock *b = getBlockNoCreate(p);
8155 b->updateMesh(daynight_ratio);
8157 catch(InvalidPositionException &e){}
8160 v3s16 p = blockpos + v3s16(-1,0,0);
8161 MapBlock *b = getBlockNoCreate(p);
8162 b->updateMesh(daynight_ratio);
8164 catch(InvalidPositionException &e){}
8166 v3s16 p = blockpos + v3s16(0,-1,0);
8167 MapBlock *b = getBlockNoCreate(p);
8168 b->updateMesh(daynight_ratio);
8170 catch(InvalidPositionException &e){}
8172 v3s16 p = blockpos + v3s16(0,0,-1);
8173 MapBlock *b = getBlockNoCreate(p);
8174 b->updateMesh(daynight_ratio);
8176 catch(InvalidPositionException &e){}
8180 Update mesh of block in which the node is, and if the node is at the
8181 leading edge, update the appropriate leading blocks too.
8183 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
8191 v3s16 blockposes[4];
8192 for(u32 i=0; i<4; i++)
8194 v3s16 np = nodepos + dirs[i];
8195 blockposes[i] = getNodeBlockPos(np);
8196 // Don't update mesh of block if it has been done already
8197 bool already_updated = false;
8198 for(u32 j=0; j<i; j++)
8200 if(blockposes[j] == blockposes[i])
8202 already_updated = true;
8209 MapBlock *b = getBlockNoCreate(blockposes[i]);
8210 b->updateMesh(daynight_ratio);
8214 void ClientMap::PrintInfo(std::ostream &out)
8225 MapVoxelManipulator::MapVoxelManipulator(Map *map)
8230 MapVoxelManipulator::~MapVoxelManipulator()
8232 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
8236 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
8238 TimeTaker timer1("emerge", &emerge_time);
8240 // Units of these are MapBlocks
8241 v3s16 p_min = getNodeBlockPos(a.MinEdge);
8242 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
8244 VoxelArea block_area_nodes
8245 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
8247 addArea(block_area_nodes);
8249 for(s32 z=p_min.Z; z<=p_max.Z; z++)
8250 for(s32 y=p_min.Y; y<=p_max.Y; y++)
8251 for(s32 x=p_min.X; x<=p_max.X; x++)
8254 core::map<v3s16, bool>::Node *n;
8255 n = m_loaded_blocks.find(p);
8259 bool block_data_inexistent = false;
8262 TimeTaker timer1("emerge load", &emerge_load_time);
8264 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
8265 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
8268 dstream<<std::endl;*/
8270 MapBlock *block = m_map->getBlockNoCreate(p);
8271 if(block->isDummy())
8272 block_data_inexistent = true;
8274 block->copyTo(*this);
8276 catch(InvalidPositionException &e)
8278 block_data_inexistent = true;
8281 if(block_data_inexistent)
8283 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
8284 // Fill with VOXELFLAG_INEXISTENT
8285 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
8286 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
8288 s32 i = m_area.index(a.MinEdge.X,y,z);
8289 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
8293 m_loaded_blocks.insert(p, !block_data_inexistent);
8296 //dstream<<"emerge done"<<std::endl;
8300 SUGG: Add an option to only update eg. water and air nodes.
8301 This will make it interfere less with important stuff if
8304 void MapVoxelManipulator::blitBack
8305 (core::map<v3s16, MapBlock*> & modified_blocks)
8307 if(m_area.getExtent() == v3s16(0,0,0))
8310 //TimeTaker timer1("blitBack");
8312 /*dstream<<"blitBack(): m_loaded_blocks.size()="
8313 <<m_loaded_blocks.size()<<std::endl;*/
8316 Initialize block cache
8318 v3s16 blockpos_last;
8319 MapBlock *block = NULL;
8320 bool block_checked_in_modified = false;
8322 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
8323 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
8324 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
8328 u8 f = m_flags[m_area.index(p)];
8329 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
8332 MapNode &n = m_data[m_area.index(p)];
8334 v3s16 blockpos = getNodeBlockPos(p);
8339 if(block == NULL || blockpos != blockpos_last){
8340 block = m_map->getBlockNoCreate(blockpos);
8341 blockpos_last = blockpos;
8342 block_checked_in_modified = false;
8345 // Calculate relative position in block
8346 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
8348 // Don't continue if nothing has changed here
8349 if(block->getNode(relpos) == n)
8352 //m_map->setNode(m_area.MinEdge + p, n);
8353 block->setNode(relpos, n);
8356 Make sure block is in modified_blocks
8358 if(block_checked_in_modified == false)
8360 modified_blocks[blockpos] = block;
8361 block_checked_in_modified = true;
8364 catch(InvalidPositionException &e)
8370 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
8371 MapVoxelManipulator(map)
8375 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
8379 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
8381 // Just create the area so that it can be pointed to
8382 VoxelManipulator::emerge(a, caller_id);
8385 void ManualMapVoxelManipulator::initialEmerge(
8386 v3s16 blockpos_min, v3s16 blockpos_max)
8388 TimeTaker timer1("initialEmerge", &emerge_time);
8390 // Units of these are MapBlocks
8391 v3s16 p_min = blockpos_min;
8392 v3s16 p_max = blockpos_max;
8394 VoxelArea block_area_nodes
8395 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
8397 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
8400 dstream<<"initialEmerge: area: ";
8401 block_area_nodes.print(dstream);
8402 dstream<<" ("<<size_MB<<"MB)";
8406 addArea(block_area_nodes);
8408 for(s32 z=p_min.Z; z<=p_max.Z; z++)
8409 for(s32 y=p_min.Y; y<=p_max.Y; y++)
8410 for(s32 x=p_min.X; x<=p_max.X; x++)
8413 core::map<v3s16, bool>::Node *n;
8414 n = m_loaded_blocks.find(p);
8418 bool block_data_inexistent = false;
8421 TimeTaker timer1("emerge load", &emerge_load_time);
8423 MapBlock *block = m_map->getBlockNoCreate(p);
8424 if(block->isDummy())
8425 block_data_inexistent = true;
8427 block->copyTo(*this);
8429 catch(InvalidPositionException &e)
8431 block_data_inexistent = true;
8434 if(block_data_inexistent)
8437 Mark area inexistent
8439 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
8440 // Fill with VOXELFLAG_INEXISTENT
8441 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
8442 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
8444 s32 i = m_area.index(a.MinEdge.X,y,z);
8445 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
8449 m_loaded_blocks.insert(p, !block_data_inexistent);
8453 void ManualMapVoxelManipulator::blitBackAll(
8454 core::map<v3s16, MapBlock*> * modified_blocks)
8456 if(m_area.getExtent() == v3s16(0,0,0))
8460 Copy data of all blocks
8462 for(core::map<v3s16, bool>::Iterator
8463 i = m_loaded_blocks.getIterator();
8464 i.atEnd() == false; i++)
8466 bool existed = i.getNode()->getValue();
8467 if(existed == false)
8469 v3s16 p = i.getNode()->getKey();
8470 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
8473 dstream<<"WARNING: "<<__FUNCTION_NAME
8474 <<": got NULL block "
8475 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
8480 block->copyFrom(*this);
8483 modified_blocks->insert(p, block);