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):
1715 //m_chunksize = 16; // Too slow
1716 //m_chunksize = 8; // Takes a few seconds
1717 m_chunksize = 4; // Too small?
1720 // TODO: Save to and load from a file
1721 m_seed = (((u64)(myrand()%0xffff)<<0)
1722 + ((u64)(myrand()%0xffff)<<16)
1723 + ((u64)(myrand()%0xffff)<<32)
1724 + ((u64)(myrand()%0xffff)<<48));
1727 Experimental and debug stuff
1734 Try to load map; if not found, create a new one.
1737 m_savedir = savedir;
1738 m_map_saving_enabled = false;
1742 // If directory exists, check contents and load if possible
1743 if(fs::PathExists(m_savedir))
1745 // If directory is empty, it is safe to save into it.
1746 if(fs::GetDirListing(m_savedir).size() == 0)
1748 dstream<<DTIME<<"Server: Empty save directory is valid."
1750 m_map_saving_enabled = true;
1754 // Load map metadata (seed, chunksize)
1757 // Load chunk metadata
1760 /*// Load sector (0,0) and throw and exception on fail
1761 if(loadSectorFull(v2s16(0,0)) == false)
1762 throw LoadError("Failed to load sector (0,0)");*/
1764 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1765 "metadata and sector (0,0) from "<<savedir<<
1766 ", assuming valid save directory."
1769 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1770 <<"and chunk metadata from "<<savedir
1771 <<", assuming valid save directory."
1774 m_map_saving_enabled = true;
1775 // Map loaded, not creating new one
1779 // If directory doesn't exist, it is safe to save to it
1781 m_map_saving_enabled = true;
1784 catch(std::exception &e)
1786 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1787 <<", exception: "<<e.what()<<std::endl;
1788 dstream<<"Please remove the map or fix it."<<std::endl;
1789 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1792 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1794 // Create zero sector
1795 emergeSector(v2s16(0,0));
1797 // Initially write whole map
1801 ServerMap::~ServerMap()
1805 if(m_map_saving_enabled)
1808 // Save only changed parts
1810 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1814 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1817 catch(std::exception &e)
1819 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1820 <<", exception: "<<e.what()<<std::endl;
1826 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1827 for(; i.atEnd() == false; i++)
1829 MapChunk *chunk = i.getNode()->getValue();
1835 Some helper functions for the map generator
1838 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1840 v3s16 em = vmanip.m_area.getExtent();
1841 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1842 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1843 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1845 for(y=y_nodes_max; y>=y_nodes_min; y--)
1847 MapNode &n = vmanip.m_data[i];
1848 if(content_walkable(n.d))
1851 vmanip.m_area.add_y(em, i, -1);
1853 if(y >= y_nodes_min)
1859 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1861 v3s16 em = vmanip.m_area.getExtent();
1862 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1863 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1864 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1866 for(y=y_nodes_max; y>=y_nodes_min; y--)
1868 MapNode &n = vmanip.m_data[i];
1869 if(content_walkable(n.d)
1870 && n.d != CONTENT_TREE
1871 && n.d != CONTENT_LEAVES)
1874 vmanip.m_area.add_y(em, i, -1);
1876 if(y >= y_nodes_min)
1882 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1884 MapNode treenode(CONTENT_TREE);
1885 MapNode leavesnode(CONTENT_LEAVES);
1887 vmanip.emerge(VoxelArea(p0-v3s16(2,0,2),p0+v3s16(2,7+2,2)));
1889 s16 trunk_h = myrand_range(4, 7);
1891 for(s16 ii=0; ii<trunk_h; ii++)
1893 if(vmanip.m_area.contains(p1))
1894 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1898 // p1 is now the last piece of the trunk
1901 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1902 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1903 Buffer<u8> leaves_d(leaves_a.getVolume());
1904 for(s32 i=0; i<leaves_a.getVolume(); i++)
1907 // Force leaves at near the end of the trunk
1910 for(s16 z=-d; z<=d; z++)
1911 for(s16 y=-d; y<=d; y++)
1912 for(s16 x=-d; x<=d; x++)
1914 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1918 // Add leaves randomly
1919 for(u32 iii=0; iii<7; iii++)
1924 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1925 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1926 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1929 for(s16 z=0; z<=d; z++)
1930 for(s16 y=0; y<=d; y++)
1931 for(s16 x=0; x<=d; x++)
1933 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1937 // Blit leaves to vmanip
1938 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1939 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1940 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1944 if(vmanip.m_area.contains(p) == false)
1946 u32 vi = vmanip.m_area.index(p);
1947 if(vmanip.m_data[vi].d != CONTENT_AIR)
1949 u32 i = leaves_a.index(x,y,z);
1950 if(leaves_d[i] == 1)
1951 vmanip.m_data[vi] = leavesnode;
1956 Noise functions. Make sure seed is mangled differently in each one.
1959 // Amount of trees per area in nodes
1960 double tree_amount_2d(u64 seed, v2s16 p)
1962 double noise = noise2d_perlin(
1963 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1965 double zeroval = -0.4;
1969 return 0.025 * (noise-zeroval) / (1.0-zeroval);
1972 #define AVERAGE_MUD_AMOUNT 4.0
1974 double get_mud_amount(u64 seed, v2f p)
1976 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
1977 0.5+p.X/200, 0.5+p.Y/200,
1981 bool get_have_sand_coast(u64 seed, v2f p)
1983 double sandnoise = noise2d_perlin(
1984 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1985 seed+59420, 3, 0.50);
1986 return (sandnoise > -0.25);
1989 bool get_have_sand_ground(u64 seed, v2f p)
1991 double sandnoise = noise2d_perlin(
1992 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
1993 seed+54290232, 6, 0.65);
1994 return (sandnoise > 1.0);
1997 // -1->0, 0->1, 1->0
1998 double contour(double v)
2006 // -1->0, -r->1, 0->1, r->1, 1->0
2007 double contour_flat_top(double v, double r)
2012 double rmax = 0.999;
2018 return ((1.0-r)-v) / (1.0-r);
2019 //return easeCurve(((1.0-r)-v) / (1.0-r));
2022 double base_rock_level_2d(u64 seed, v2f p)
2024 // The ground level (return value)
2025 double h = WATER_LEVEL-1.5;
2027 // Raises from 0 when parameter is -1...1
2028 /*double m2 = contour_flat_top(-0.8 + 2.0 * noise2d_perlin(
2029 0.0+(float)p.X/1500., 0.0+(float)p.Y/1500.,
2030 (seed>>32)+34758, 5, 0.55), 0.10);*/
2035 double m1 = 200.0 + 300.0 * noise2d_perlin(
2036 0.0+(float)p.X/1000., 0.0+(float)p.Y/1000.,
2037 (seed>>32)+98525, 8, 0.5);
2042 /*double tm2 = contour_flat_top(-1.0 + 3.0 * noise2d_perlin(
2043 0.0+(float)p.X/300., 0.0+(float)p.Y/300.,
2044 (seed>>32)+78593, 5, 0.55), 0.15);
2050 double m3 = 100.0 - 600.0 * noise2d_perlin_abs(
2051 0.324+(float)p.X/2000., 0.423+(float)p.Y/2000.,
2052 (seed>>32)+985251, 9, 0.55);
2060 // More mountain ranges
2062 double a1 = d*2.0 - d*7 * noise2d_perlin_abs(
2063 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2064 seed+850342, 7, 0.55);
2066 a1 = d + sqrt(a1-d);*/
2067 a1 = (1.0 - exp(-a1/d))*d;
2077 // More mountain ranges
2079 double a1 = d*2.0 - d*7 * noise2d_perlin_abs(
2080 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2081 seed+850342, 7, 0.55);
2083 a1 = d + sqrt(a1-d);*/
2084 a1 = (1.0 - exp(-a1/d))*d;
2094 // Very steep mountain ranges
2096 double a1 = d*2 - d*6.5 * noise2d_perlin_abs(
2097 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2098 seed+850342, 6, 0.6);
2100 a1 = d + sqrt(a1-d);*/
2101 a1 = (1.0 - exp(-a1/d))*d;
2106 /*double a = noise2d_perlin_abs(
2107 0.94+(float)p.X/2000., 0.26+(float)p.Y/2000.,
2108 (seed>>32)+65012102, 8, 0.50);
2109 double m4 = 100.0 - 400.0 * a;
2116 The stuff before this comment is usually not used.
2117 The stuff after this comment is usually used.
2123 double m4 = 1.0 - 3.0 * noise2d_perlin_abs(
2124 0.324+(float)p.X/2000., 0.423+(float)p.Y/2000.,
2125 (seed>>32)+65012102, 9, 0.57);
2133 // Some kind of hill chains or something
2135 double a1 = 1.0 - 2.5 * noise2d_perlin_abs(
2136 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2137 seed+850342, 5, 0.6);
2141 a1 = d + sqrt(a1-d);
2150 double base = -2. + 25. * noise2d_perlin(
2151 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2152 (seed>>32)+653876, 7, 0.65);
2159 Combined with turbulence, this thing here is able to make very
2160 awesome terrain, albeit rarely.
2162 This is also responsible for small islands.
2165 double higher = 40. * noise2d_perlin(
2166 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2167 seed+39292, 6, 0.50);
2168 /*double higher = 50. * noise2d_perlin_abs(
2169 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2170 seed+85039, 5, 0.63);*/
2175 // Steepness factor of cliffs
2176 double b = 1.0 + 1.0 * noise2d_perlin(
2177 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2179 b = rangelim(b, 0.0, 1000.0);
2183 b = rangelim(b, 3.0, 1000.0);
2184 //dstream<<"b="<<b<<std::endl;
2186 // Offset to more low
2187 //double a_off = -0.30;
2188 double a_off = -0.20;
2189 // High/low selector
2190 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2191 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2192 seed-359, 7, 0.70));
2197 b = rangelim(b, 3.0, 20.0);*/
2199 double a = -1.5 + 5.0 * (noise2d_perlin_abs(
2200 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2203 /*double a = 5.0 * (noise2d_perlin(
2204 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2205 seed-359, 5, 0.6));*/
2206 //a = contour_flat_top(a, 0.2);
2209 a = rangelim(a, 0.0, 1.0);
2212 //dstream<<"a="<<a<<std::endl;
2214 /*double h2 = higher * a;
2218 h += base*(1.0-a) + higher*a;
2231 double base_rock_level_2d(u64 seed, v2s16 p)
2233 return base_rock_level_2d(seed, v2f((float)p.X, (float)p.Y));
2236 double get_turbulence_factor_2d(u64 seed, v2f p)
2238 double vv = -0.50 + 2.0 * noise2d_perlin(
2241 seed+85498783983, 4, 0.5);
2242 vv = rangelim(vv, 0.0, 1.0);
2246 #define TURBULENCE_BOTTOM_CUTOFF_Y (WATER_LEVEL-7)
2248 double get_turbulence_factor_y(u64 seed, f32 y)
2251 double min = TURBULENCE_BOTTOM_CUTOFF_Y;
2254 else if(y < min + d)
2259 v2f get_raw_turbulence(u64 seed, v3f p)
2263 double v1 = f * noise3d_perlin(
2267 seed+4045, 5, 0.65);
2269 double v2 = f * noise3d_perlin(
2273 seed+9495, 5, 0.65);
2278 // Shouldn't be used, provided for compatibility.
2279 v2f base_ground_turbulence(u64 seed, v3f p)
2281 double tfxz = get_turbulence_factor_2d(seed, v2f(p.X,p.Z));
2282 double tfy = get_turbulence_factor_y(seed, p.Y);
2283 v2f t = get_raw_turbulence(seed, p);
2288 v2f base_ground_turbulence(u64 seed, v3f p)
2294 // Cut off at a minimum height
2297 double min = WATER_LEVEL-5;
2300 else if(p.Y < min + d)
2306 double vv = 0.50 + 1.0 * noise3d_perlin(
2310 seed+1324381, 4, 0.5);
2311 double vve = rangelim(vv, 0.0, 1.0);
2312 /*double vv = 1.0 - 2.0 * noise3d_perlin_abs(
2316 seed+1324031, 4, 0.5);
2317 double vve = 1.0 - exp(-MYMAX(0, vv*2.0));*/
2318 //double vve = rangelim(vv, 0, 1.0);
2319 //dstream<<"vve="<<vve<<std::endl;
2321 /*// Limit turbulence near water level
2322 double a = contour((p.Y-WATER_LEVEL)/10.0);
2323 vve = (1.-a) * vve;*/
2325 // Increase turbulence in elevated heights
2326 double ah = WATER_LEVEL + 30;
2335 double v1 = f * noise3d_perlin(
2339 seed+4045, 5, 0.65);
2341 double v2 = f * noise3d_perlin(
2345 seed+9495, 5, 0.65);
2347 return v2f(v1*vve, v2*vve);
2354 bool is_carved(u64 seed, v3f p)
2357 double v1 = noise3d_perlin_abs(
2361 seed+657890854, 5, 0.7);
2370 double v4 = contour(f*noise3d_perlin(
2374 seed+87592, 5, 0.7));
2375 // Tilted 90 degrees
2376 double v5 = contour(f*noise3d_perlin(
2380 seed+98594, 5, 0.7));
2389 bool is_underground_mud(u64 seed, v3f p)
2391 double v1 = noise3d_perlin_abs(
2395 seed+83401, 5, 0.75);
2400 if depth_guess!=NULL, it is set to a guessed value of how deep
2401 underground the position is.
2403 bool is_base_ground(u64 seed, v3f p, double *depth_guess=NULL)
2406 // This is used for testing the output of the cave function
2412 return is_carved(seed, p);
2416 // This is used for testing the output of the underground mud function
2422 return is_underground_mud(seed, p);
2426 bool is_ground = true;
2429 if(is_carved(seed, p))
2433 if(depth_guess || is_ground == true)
2435 v2f t = base_ground_turbulence(seed, p);
2437 double surface_y_f = base_rock_level_2d(seed, v2f(p.X+t.X, p.Z+t.Y));
2442 // Find highest surface near current
2449 double s2 = surface_y_f;
2450 for(u32 i=0; i<4; i++)
2453 // Get turbulence at around there
2454 v2f t2 = base_ground_turbulence(seed, p+dir);
2455 // Get ground height
2456 v2f l = v2f(p.X+t2.X+dir.X, p.Z+t2.Y+dir.Z);
2457 double s = base_rock_level_2d(seed, l);
2461 *depth_guess = s2 - p.Y;
2467 // Check a bit lower also, take highest surface
2468 v2f t2 = base_ground_turbulence(seed, p + v3f(0,-2,0));
2469 double s2 = base_rock_level_2d(seed, v2f(p.X+t2.X, p.Z+t2.Y));
2470 if(s2 > surface_y_f)
2471 *depth_guess = s2 - p.Y;
2473 *depth_guess = surface_y_f - p.Y;
2478 *depth_guess = surface_y_f - p.Y;
2481 if(p.Y > surface_y_f)
2487 // Guess surface point
2488 v3f p2(p.X, surface_y_f, p.Z);
2489 v2f t2 = base_ground_turbulence
2491 double s1 = base_rock_level_2d(seed, v2f(p.X+v1,p.Z+v2));
2497 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2500 This is the main map generation method
2504 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2505 core::map<v3s16, MapBlock*> &changed_blocks,
2508 DSTACK(__FUNCTION_NAME);
2510 // Shall be not used now
2516 Don't generate if already fully generated
2520 MapChunk *chunk = getChunk(chunkpos);
2521 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2523 dstream<<"generateChunkRaw(): Chunk "
2524 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2525 <<" already generated"<<std::endl;
2530 dstream<<"generateChunkRaw(): Generating chunk "
2531 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2534 TimeTaker timer("generateChunkRaw()");
2536 // The distance how far into the neighbors the generator is allowed to go.
2537 s16 max_spread_amount_sectors = 2;
2538 assert(max_spread_amount_sectors <= m_chunksize);
2539 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2541 // Minimum amount of space left on sides for mud to fall in
2542 //s16 min_mud_fall_space = 2;
2544 // Maximum diameter of stone obstacles in X and Z
2545 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2546 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2548 s16 y_blocks_min = -4;
2549 s16 y_blocks_max = 3;
2550 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2551 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2552 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2554 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2555 s16 sectorpos_base_size = m_chunksize;
2557 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2558 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2559 v2s16 sectorpos_bigbase =
2560 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2561 s16 sectorpos_bigbase_size =
2562 sectorpos_base_size + 2 * max_spread_amount_sectors;
2564 v3s16 bigarea_blocks_min(
2565 sectorpos_bigbase.X,
2570 v3s16 bigarea_blocks_max(
2571 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2573 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2576 // Relative values to control amount of stuff in one chunk
2577 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2578 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2579 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2580 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2581 *(u32)h_blocks*MAP_BLOCKSIZE;
2584 The limiting edges of the lighting update, inclusive.
2586 s16 lighting_min_d = 0-max_spread_amount;
2587 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2590 Create the whole area of this and the neighboring chunks
2593 TimeTaker timer("generateChunkRaw() create area");
2595 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2596 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2598 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2599 ServerMapSector *sector = createSector(sectorpos);
2602 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2604 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2605 MapBlock *block = createBlock(blockpos);
2607 // Lighting won't be calculated
2608 //block->setLightingExpired(true);
2609 // Lighting will be calculated
2610 block->setLightingExpired(false);
2613 Block gets sunlight if this is true.
2615 This should be set to true when the top side of a block
2616 is completely exposed to the sky.
2618 Actually this doesn't matter now because the
2619 initial lighting is done here.
2621 block->setIsUnderground(y != y_blocks_max);
2627 Now we have a big empty area.
2629 Make a ManualMapVoxelManipulator that contains this and the
2633 ManualMapVoxelManipulator vmanip(this);
2634 // Add the area we just generated
2636 TimeTaker timer("generateChunkRaw() initialEmerge");
2637 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2641 vmanip.clearFlag(0xff);
2643 TimeTaker timer_generate("generateChunkRaw() generate");
2645 // Maximum height of the stone surface and obstacles.
2646 // This is used to disable dungeon generation from going too high.
2647 s16 stone_surface_max_y = 0;
2650 Generate general ground level to full area
2655 TimeTaker timer1("ground level");
2656 dstream<<"Generating base ground..."<<std::endl;
2658 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2659 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2662 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2665 Skip if already generated
2668 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2669 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2673 v2f p2df(p2d.X, p2d.Y);
2676 // Use fast index incrementing
2677 v3s16 em = vmanip.m_area.getExtent();
2678 s16 min = y_nodes_min;
2679 s16 max = y_nodes_max;
2682 //float surface_y_f = base_rock_level_2d(m_seed, p2df);
2683 u32 i = vmanip.m_area.index(v3s16(p2d.X, min, p2d.Y));
2684 for(s16 y=min; y<=max; y++)
2687 bool is = is_base_ground(m_seed, v3f(p2df.X,y,p2df.Y));
2689 vmanip.m_data[i].d = CONTENT_STONE;
2691 vmanip.m_data[i].d = CONTENT_AIR;
2694 double v = noise3d_perlin(
2695 0.5+(float)p2d.X/200,
2697 0.5+(float)p2d.Y/200,
2698 m_seed+293, 6, 0.55);
2700 vmanip.m_data[i].d = CONTENT_STONE;
2702 vmanip.m_data[i].d = CONTENT_AIR;
2705 /*double v1 = 5 * noise3d_perlin(
2706 0.5+(float)p2df.X/200,
2708 0.5+(float)p2df.Y/200,
2709 m_seed+293, 6, 0.55);
2711 double v2 = 5 * noise3d_perlin(
2712 0.5+(float)p2df.X/200,
2714 0.5+(float)p2df.Y/200,
2715 m_seed+293, 6, 0.55);*/
2720 float surface_y_f = base_rock_level_2d(m_seed, p2df+v2f(v1,v2));
2722 if(y <= surface_y_f)
2723 vmanip.m_data[i].d = CONTENT_STONE;
2725 vmanip.m_data[i].d = CONTENT_AIR;
2728 vmanip.m_area.add_y(em, i, 1);
2734 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2737 Skip if already generated
2740 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2741 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2745 // Ground height at this point
2746 float surface_y_f = 0.0;
2748 // Use perlin noise for ground height
2749 surface_y_f = base_rock_level_2d(m_seed, p2d);
2751 /*// Experimental stuff
2753 float a = highlands_level_2d(m_seed, p2d);
2758 // Convert to integer
2759 s16 surface_y = (s16)surface_y_f;
2762 if(surface_y > stone_surface_max_y)
2763 stone_surface_max_y = surface_y;
2766 Fill ground with stone
2769 // Use fast index incrementing
2770 v3s16 em = vmanip.m_area.getExtent();
2771 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2772 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2774 vmanip.m_data[i].d = CONTENT_STONE;
2776 vmanip.m_area.add_y(em, i, 1);
2785 Randomize some parameters
2788 s32 stone_obstacle_count = 0;
2789 /*s32 stone_obstacle_count =
2790 rangelim((1.0+noise2d(m_seed+897,
2791 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2793 s16 stone_obstacle_max_height = 0;
2794 /*s16 stone_obstacle_max_height =
2795 rangelim((1.0+noise2d(m_seed+5902,
2796 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2799 Loop this part, it will make stuff look older and newer nicely
2802 for(u32 i_age=0; i_age<age_count; i_age++)
2807 //TimeTaker timer1("stone obstacles");
2810 Add some random stone obstacles
2813 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2815 // Randomize max height so usually stuff will be quite low
2816 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2818 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2819 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2822 myrand_range(5, stone_obstacle_max_size),
2823 myrand_range(0, maxheight_randomized),
2824 myrand_range(5, stone_obstacle_max_size)
2827 // Don't make stupid small rectangle bumps
2832 myrand_range(1+ob_size.X/2+2,
2833 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2834 myrand_range(1+ob_size.Z/2+2,
2835 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2838 // Minimum space left on top of the obstacle
2839 s16 min_head_space = 12;
2841 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2842 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2844 // Node position in 2d
2845 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2847 // Find stone ground level
2848 // (ignore everything else than mud in already generated chunks)
2849 // and mud amount over the stone level
2853 v3s16 em = vmanip.m_area.getExtent();
2854 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2856 // Go to ground level
2857 for(y=y_nodes_max; y>=y_nodes_min; y--)
2859 MapNode *n = &vmanip.m_data[i];
2860 /*if(content_walkable(n.d)
2861 && n.d != CONTENT_MUD
2862 && n.d != CONTENT_GRASS)
2864 if(n->d == CONTENT_STONE)
2867 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2871 Change to mud because otherwise we might
2872 be throwing mud on grass at the next
2878 vmanip.m_area.add_y(em, i, -1);
2880 if(y >= y_nodes_min)
2883 surface_y = y_nodes_min;
2891 v3s16 em = vmanip.m_area.getExtent();
2892 s16 y_start = surface_y+1;
2893 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2897 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2899 MapNode &n = vmanip.m_data[i];
2900 n.d = CONTENT_STONE;
2902 if(y > stone_surface_max_y)
2903 stone_surface_max_y = y;
2906 if(count >= ob_size.Y)
2909 vmanip.m_area.add_y(em, i, 1);
2913 for(; y<=y_nodes_max - min_head_space; y++)
2915 MapNode &n = vmanip.m_data[i];
2918 if(count >= mud_amount)
2921 vmanip.m_area.add_y(em, i, 1);
2931 //TimeTaker timer1("dungeons");
2936 u32 dungeons_count = relative_volume / 600000;
2937 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2938 if(stone_surface_max_y < WATER_LEVEL)
2940 /*u32 dungeons_count = 0;
2941 u32 bruises_count = 0;*/
2942 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2944 s16 min_tunnel_diameter = 2;
2945 s16 max_tunnel_diameter = 6;
2946 u16 tunnel_routepoints = 25;
2948 bool bruise_surface = (jj < bruises_count);
2952 min_tunnel_diameter = 5;
2953 max_tunnel_diameter = myrand_range(10, 20);
2954 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2955 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2957 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2958 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2960 tunnel_routepoints = 5;
2963 // Allowed route area size in nodes
2965 sectorpos_base_size*MAP_BLOCKSIZE,
2966 h_blocks*MAP_BLOCKSIZE,
2967 sectorpos_base_size*MAP_BLOCKSIZE
2970 // Area starting point in nodes
2972 sectorpos_base.X*MAP_BLOCKSIZE,
2973 y_blocks_min*MAP_BLOCKSIZE,
2974 sectorpos_base.Y*MAP_BLOCKSIZE
2978 //(this should be more than the maximum radius of the tunnel)
2979 //s16 insure = 5; // Didn't work with max_d = 20
2981 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2982 ar += v3s16(1,0,1) * more * 2;
2983 of -= v3s16(1,0,1) * more;
2985 s16 route_y_min = 0;
2986 // Allow half a diameter + 7 over stone surface
2987 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2989 /*// If dungeons, don't go through surface too often
2990 if(bruise_surface == false)
2991 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2993 // Limit maximum to area
2994 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2998 /*// Minimum is at y=0
2999 route_y_min = -of.Y - 0;*/
3000 // Minimum is at y=max_tunnel_diameter/4
3001 //route_y_min = -of.Y + max_tunnel_diameter/4;
3002 //s16 min = -of.Y + max_tunnel_diameter/4;
3003 s16 min = -of.Y + 0;
3004 route_y_min = myrand_range(min, min + max_tunnel_diameter);
3005 route_y_min = rangelim(route_y_min, 0, route_y_max);
3008 /*dstream<<"route_y_min = "<<route_y_min
3009 <<", route_y_max = "<<route_y_max<<std::endl;*/
3011 s16 route_start_y_min = route_y_min;
3012 s16 route_start_y_max = route_y_max;
3014 // Start every 2nd dungeon from surface
3015 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
3017 if(coming_from_surface)
3019 route_start_y_min = -of.Y + stone_surface_max_y + 5;
3022 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
3023 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
3025 // Randomize starting position
3027 (float)(myrand()%ar.X)+0.5,
3028 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
3029 (float)(myrand()%ar.Z)+0.5
3032 MapNode airnode(CONTENT_AIR);
3035 Generate some tunnel starting from orp
3038 for(u16 j=0; j<tunnel_routepoints; j++)
3041 s16 min_d = min_tunnel_diameter;
3042 s16 max_d = max_tunnel_diameter;
3043 s16 rs = myrand_range(min_d, max_d);
3048 maxlen = v3s16(rs*7,rs*7,rs*7);
3052 maxlen = v3s16(15, myrand_range(1, 20), 15);
3057 if(coming_from_surface && j < 3)
3060 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
3061 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
3062 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3068 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
3069 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
3070 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3077 else if(rp.X >= ar.X)
3079 if(rp.Y < route_y_min)
3081 else if(rp.Y >= route_y_max)
3082 rp.Y = route_y_max-1;
3085 else if(rp.Z >= ar.Z)
3089 for(float f=0; f<1.0; f+=1.0/vec.getLength())
3091 v3f fp = orp + vec * f;
3092 v3s16 cp(fp.X, fp.Y, fp.Z);
3095 s16 d1 = d0 + rs - 1;
3096 for(s16 z0=d0; z0<=d1; z0++)
3098 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
3099 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
3100 for(s16 x0=-si; x0<=si-1; x0++)
3102 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
3103 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
3104 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
3105 //s16 si2 = rs - abs(x0);
3106 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
3112 /*if(isInArea(p, ar) == false)
3114 // Check only height
3115 if(y < 0 || y >= ar.Y)
3119 //assert(vmanip.m_area.contains(p));
3120 if(vmanip.m_area.contains(p) == false)
3122 dstream<<"WARNING: "<<__FUNCTION_NAME
3123 <<":"<<__LINE__<<": "
3124 <<"point not in area"
3129 // Just set it to air, it will be changed to
3131 u32 i = vmanip.m_area.index(p);
3132 vmanip.m_data[i] = airnode;
3134 if(bruise_surface == false)
3137 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
3152 //TimeTaker timer1("ore veins");
3157 for(u32 jj=0; jj<relative_volume/1000; jj++)
3159 s16 max_vein_diameter = 3;
3161 // Allowed route area size in nodes
3163 sectorpos_base_size*MAP_BLOCKSIZE,
3164 h_blocks*MAP_BLOCKSIZE,
3165 sectorpos_base_size*MAP_BLOCKSIZE
3168 // Area starting point in nodes
3170 sectorpos_base.X*MAP_BLOCKSIZE,
3171 y_blocks_min*MAP_BLOCKSIZE,
3172 sectorpos_base.Y*MAP_BLOCKSIZE
3176 //(this should be more than the maximum radius of the tunnel)
3178 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
3179 ar += v3s16(1,0,1) * more * 2;
3180 of -= v3s16(1,0,1) * more;
3182 // Randomize starting position
3184 (float)(myrand()%ar.X)+0.5,
3185 (float)(myrand()%ar.Y)+0.5,
3186 (float)(myrand()%ar.Z)+0.5
3189 // Randomize mineral
3192 mineral = MINERAL_COAL;
3194 mineral = MINERAL_IRON;
3197 Generate some vein starting from orp
3200 for(u16 j=0; j<2; j++)
3203 (float)(myrand()%ar.X)+0.5,
3204 (float)(myrand()%ar.Y)+0.5,
3205 (float)(myrand()%ar.Z)+0.5
3207 v3f vec = rp - orp;*/
3209 v3s16 maxlen(5, 5, 5);
3211 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
3212 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
3213 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3218 else if(rp.X >= ar.X)
3222 else if(rp.Y >= ar.Y)
3226 else if(rp.Z >= ar.Z)
3232 s16 max_d = max_vein_diameter;
3233 s16 rs = myrand_range(min_d, max_d);
3235 for(float f=0; f<1.0; f+=1.0/vec.getLength())
3237 v3f fp = orp + vec * f;
3238 v3s16 cp(fp.X, fp.Y, fp.Z);
3240 s16 d1 = d0 + rs - 1;
3241 for(s16 z0=d0; z0<=d1; z0++)
3243 s16 si = rs - abs(z0);
3244 for(s16 x0=-si; x0<=si-1; x0++)
3246 s16 si2 = rs - abs(x0);
3247 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3249 // Don't put mineral to every place
3257 /*if(isInArea(p, ar) == false)
3259 // Check only height
3260 if(y < 0 || y >= ar.Y)
3264 assert(vmanip.m_area.contains(p));
3266 // Just set it to air, it will be changed to
3268 u32 i = vmanip.m_area.index(p);
3269 MapNode *n = &vmanip.m_data[i];
3270 if(n->d == CONTENT_STONE)
3285 //TimeTaker timer1("add mud");
3288 Add mud to the central chunk
3291 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3292 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
3294 // Node position in 2d
3295 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3297 // Randomize mud amount
3298 s16 mud_add_amount = get_mud_amount(m_seed, v2f(p2d.X,p2d.Y))/age_count;
3300 // Find ground level
3301 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3304 If topmost node is grass, change it to mud.
3305 It might be if it was flown to there from a neighboring
3306 chunk and then converted.
3309 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
3310 MapNode *n = &vmanip.m_data[i];
3311 if(n->d == CONTENT_GRASS)
3320 v3s16 em = vmanip.m_area.getExtent();
3321 s16 y_start = surface_y+1;
3322 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3323 for(s16 y=y_start; y<=y_nodes_max; y++)
3325 if(mudcount >= mud_add_amount)
3328 MapNode &n = vmanip.m_data[i];
3332 vmanip.m_area.add_y(em, i, 1);
3341 //TimeTaker timer1("flow mud");
3344 Flow mud away from steep edges
3347 // Limit area by 1 because mud is flown into neighbors.
3348 s16 mudflow_minpos = 0-max_spread_amount+1;
3349 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
3351 // Iterate a few times
3352 for(s16 k=0; k<3; k++)
3355 for(s16 x=mudflow_minpos;
3358 for(s16 z=mudflow_minpos;
3362 // Invert coordinates every 2nd iteration
3365 x = mudflow_maxpos - (x-mudflow_minpos);
3366 z = mudflow_maxpos - (z-mudflow_minpos);
3369 // Node position in 2d
3370 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3372 v3s16 em = vmanip.m_area.getExtent();
3373 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3380 for(; y>=y_nodes_min; y--)
3382 n = &vmanip.m_data[i];
3383 //if(content_walkable(n->d))
3385 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3388 vmanip.m_area.add_y(em, i, -1);
3391 // Stop if out of area
3392 //if(vmanip.m_area.contains(i) == false)
3396 /*// If not mud, do nothing to it
3397 MapNode *n = &vmanip.m_data[i];
3398 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3402 Don't flow it if the stuff under it is not mud
3406 vmanip.m_area.add_y(em, i2, -1);
3407 // Cancel if out of area
3408 if(vmanip.m_area.contains(i2) == false)
3410 MapNode *n2 = &vmanip.m_data[i2];
3411 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
3415 // Make it exactly mud
3418 /*s16 recurse_count = 0;
3422 v3s16(0,0,1), // back
3423 v3s16(1,0,0), // right
3424 v3s16(0,0,-1), // front
3425 v3s16(-1,0,0), // left
3428 // Theck that upper is air or doesn't exist.
3429 // Cancel dropping if upper keeps it in place
3431 vmanip.m_area.add_y(em, i3, 1);
3432 if(vmanip.m_area.contains(i3) == true
3433 && content_walkable(vmanip.m_data[i3].d) == true)
3440 for(u32 di=0; di<4; di++)
3442 v3s16 dirp = dirs4[di];
3445 vmanip.m_area.add_p(em, i2, dirp);
3446 // Fail if out of area
3447 if(vmanip.m_area.contains(i2) == false)
3449 // Check that side is air
3450 MapNode *n2 = &vmanip.m_data[i2];
3451 if(content_walkable(n2->d))
3453 // Check that under side is air
3454 vmanip.m_area.add_y(em, i2, -1);
3455 if(vmanip.m_area.contains(i2) == false)
3457 n2 = &vmanip.m_data[i2];
3458 if(content_walkable(n2->d))
3460 /*// Check that under that is air (need a drop of 2)
3461 vmanip.m_area.add_y(em, i2, -1);
3462 if(vmanip.m_area.contains(i2) == false)
3464 n2 = &vmanip.m_data[i2];
3465 if(content_walkable(n2->d))
3467 // Loop further down until not air
3469 vmanip.m_area.add_y(em, i2, -1);
3470 // Fail if out of area
3471 if(vmanip.m_area.contains(i2) == false)
3473 n2 = &vmanip.m_data[i2];
3474 }while(content_walkable(n2->d) == false);
3475 // Loop one up so that we're in air
3476 vmanip.m_area.add_y(em, i2, 1);
3477 n2 = &vmanip.m_data[i2];
3479 // Move mud to new place
3481 // Set old place to be air
3482 *n = MapNode(CONTENT_AIR);
3495 //TimeTaker timer1("add water");
3498 Add water to the central chunk (and a bit more)
3501 for(s16 x=0-max_spread_amount;
3502 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3504 for(s16 z=0-max_spread_amount;
3505 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3508 // Node position in 2d
3509 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3511 // Find ground level
3512 //s16 surface_y = find_ground_level(vmanip, p2d);
3515 If ground level is over water level, skip.
3516 NOTE: This leaves caves near water without water,
3517 which looks especially crappy when the nearby water
3518 won't start flowing either for some reason
3520 /*if(surface_y > WATER_LEVEL)
3527 v3s16 em = vmanip.m_area.getExtent();
3528 u8 light = LIGHT_MAX;
3529 // Start at global water surface level
3530 s16 y_start = WATER_LEVEL;
3531 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3532 MapNode *n = &vmanip.m_data[i];
3534 /*// Add first one to transforming liquid queue, if water
3535 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3537 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3538 m_transforming_liquid.push_back(p);
3541 for(s16 y=y_start; y>=y_nodes_min; y--)
3543 n = &vmanip.m_data[i];
3545 // Stop when there is no water and no air
3546 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3547 && n->d != CONTENT_WATER)
3549 /*// Add bottom one to transforming liquid queue
3550 vmanip.m_area.add_y(em, i, 1);
3551 n = &vmanip.m_data[i];
3552 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3554 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3555 m_transforming_liquid.push_back(p);
3561 // Make water only not in dungeons
3562 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3564 n->d = CONTENT_WATERSOURCE;
3565 //n->setLight(LIGHTBANK_DAY, light);
3567 // Add to transforming liquid queue (in case it'd
3569 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3570 m_transforming_liquid.push_back(p);
3574 vmanip.m_area.add_y(em, i, -1);
3587 //TimeTaker timer1("convert mud to sand");
3593 //s16 mud_add_amount = myrand_range(2, 4);
3594 //s16 mud_add_amount = 0;
3596 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3597 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3598 for(s16 x=0-max_spread_amount+1;
3599 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3601 for(s16 z=0-max_spread_amount+1;
3602 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3605 // Node position in 2d
3606 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3608 // Determine whether to have sand here
3609 bool have_sand = get_have_sand_coast(p2d);
3611 if(have_sand == false)
3614 // Find ground level
3615 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3617 if(surface_y > WATER_LEVEL + 2)
3621 v3s16 em = vmanip.m_area.getExtent();
3622 s16 y_start = surface_y;
3623 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3624 u32 not_sand_counter = 0;
3625 for(s16 y=y_start; y>=y_nodes_min; y--)
3627 MapNode *n = &vmanip.m_data[i];
3628 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3630 n->d = CONTENT_SAND;
3635 if(not_sand_counter > 3)
3639 vmanip.m_area.add_y(em, i, -1);
3648 //TimeTaker timer1("generate trees");
3654 // Divide area into parts
3656 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3657 double area = sidelen * sidelen;
3658 for(s16 x0=0; x0<div; x0++)
3659 for(s16 z0=0; z0<div; z0++)
3661 // Center position of part of division
3663 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3664 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3666 // Minimum edge of part of division
3668 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3669 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3671 // Maximum edge of part of division
3673 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3674 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3677 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3678 // Put trees in random places on part of division
3679 for(u32 i=0; i<tree_count; i++)
3681 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3682 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3683 s16 y = find_ground_level(vmanip, v2s16(x,z));
3684 // Don't make a tree under water level
3689 Trees grow only on mud and grass
3692 u32 i = vmanip.m_area.index(v3s16(p));
3693 MapNode *n = &vmanip.m_data[i];
3694 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3699 make_tree(vmanip, p);
3702 /*u32 tree_max = relative_area / 60;
3703 //u32 count = myrand_range(0, tree_max);
3704 for(u32 i=0; i<count; i++)
3706 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3707 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3708 x += sectorpos_base.X*MAP_BLOCKSIZE;
3709 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3710 s16 y = find_ground_level(vmanip, v2s16(x,z));
3711 // Don't make a tree under water level
3716 make_tree(vmanip, p);
3724 //TimeTaker timer1("grow grass");
3730 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3731 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3732 for(s16 x=0-max_spread_amount;
3733 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3735 for(s16 z=0-max_spread_amount;
3736 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3739 // Node position in 2d
3740 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3743 Find the lowest surface to which enough light ends up
3746 Basically just wait until not air and not leaves.
3750 v3s16 em = vmanip.m_area.getExtent();
3751 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3753 // Go to ground level
3754 for(y=y_nodes_max; y>=y_nodes_min; y--)
3756 MapNode &n = vmanip.m_data[i];
3757 if(n.d != CONTENT_AIR
3758 && n.d != CONTENT_LEAVES)
3760 vmanip.m_area.add_y(em, i, -1);
3762 if(y >= y_nodes_min)
3765 surface_y = y_nodes_min;
3768 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3769 MapNode *n = &vmanip.m_data[i];
3770 if(n->d == CONTENT_MUD)
3771 n->d = CONTENT_GRASS;
3777 Initial lighting (sunlight)
3780 core::map<v3s16, bool> light_sources;
3783 // 750ms @cs=8, can't optimize more
3784 TimeTaker timer1("initial lighting");
3788 Go through the edges and add all nodes that have light to light_sources
3792 for(s16 i=0; i<4; i++)
3794 for(s16 j=lighting_min_d;
3801 if(i == 0 || i == 1)
3803 x = (i==0) ? lighting_min_d : lighting_max_d;
3812 z = (i==0) ? lighting_min_d : lighting_max_d;
3819 // Node position in 2d
3820 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3823 v3s16 em = vmanip.m_area.getExtent();
3824 s16 y_start = y_nodes_max;
3825 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3826 for(s16 y=y_start; y>=y_nodes_min; y--)
3828 MapNode *n = &vmanip.m_data[i];
3829 if(n->getLight(LIGHTBANK_DAY) != 0)
3831 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3833 //NOTE: This is broken, at least the index has to
3842 Go through the edges and apply sunlight to them, not caring
3847 for(s16 i=0; i<4; i++)
3849 for(s16 j=lighting_min_d;
3856 if(i == 0 || i == 1)
3858 x = (i==0) ? lighting_min_d : lighting_max_d;
3867 z = (i==0) ? lighting_min_d : lighting_max_d;
3874 // Node position in 2d
3875 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3877 // Loop from top to down
3879 u8 light = LIGHT_SUN;
3880 v3s16 em = vmanip.m_area.getExtent();
3881 s16 y_start = y_nodes_max;
3882 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3883 for(s16 y=y_start; y>=y_nodes_min; y--)
3885 MapNode *n = &vmanip.m_data[i];
3886 if(light_propagates_content(n->d) == false)
3890 else if(light != LIGHT_SUN
3891 || sunlight_propagates_content(n->d) == false)
3897 n->setLight(LIGHTBANK_DAY, light);
3898 n->setLight(LIGHTBANK_NIGHT, 0);
3902 // Insert light source
3903 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3906 // Increment index by y
3907 vmanip.m_area.add_y(em, i, -1);
3913 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3914 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3915 /*for(s16 x=0-max_spread_amount+1;
3916 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3918 for(s16 z=0-max_spread_amount+1;
3919 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3923 This has to be 1 smaller than the actual area, because
3924 neighboring nodes are checked.
3926 for(s16 x=lighting_min_d+1;
3927 x<=lighting_max_d-1;
3929 for(s16 z=lighting_min_d+1;
3930 z<=lighting_max_d-1;
3933 // Node position in 2d
3934 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3937 Apply initial sunlight
3940 u8 light = LIGHT_SUN;
3941 bool add_to_sources = false;
3942 v3s16 em = vmanip.m_area.getExtent();
3943 s16 y_start = y_nodes_max;
3944 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3945 for(s16 y=y_start; y>=y_nodes_min; y--)
3947 MapNode *n = &vmanip.m_data[i];
3949 if(light_propagates_content(n->d) == false)
3953 else if(light != LIGHT_SUN
3954 || sunlight_propagates_content(n->d) == false)
3960 // This doesn't take much time
3961 if(add_to_sources == false)
3964 Check sides. If side is not air or water, start
3965 adding to light_sources.
3968 v3s16(0,0,1), // back
3969 v3s16(1,0,0), // right
3970 v3s16(0,0,-1), // front
3971 v3s16(-1,0,0), // left
3973 for(u32 di=0; di<4; di++)
3975 v3s16 dirp = dirs4[di];
3977 vmanip.m_area.add_p(em, i2, dirp);
3978 MapNode *n2 = &vmanip.m_data[i2];
3980 n2->d != CONTENT_AIR
3981 && n2->d != CONTENT_WATERSOURCE
3982 && n2->d != CONTENT_WATER
3984 add_to_sources = true;
3990 n->setLight(LIGHTBANK_DAY, light);
3991 n->setLight(LIGHTBANK_NIGHT, 0);
3993 // This doesn't take much time
3994 if(light != 0 && add_to_sources)
3996 // Insert light source
3997 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
4000 // Increment index by y
4001 vmanip.m_area.add_y(em, i, -1);
4009 // Spread light around
4011 TimeTaker timer("generateChunkRaw() spreadLight");
4012 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
4019 timer_generate.stop();
4022 Blit generated stuff to map
4026 //TimeTaker timer("generateChunkRaw() blitBackAll");
4027 vmanip.blitBackAll(&changed_blocks);
4031 Update day/night difference cache of the MapBlocks
4034 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
4035 i.atEnd() == false; i++)
4037 MapBlock *block = i.getNode()->getValue();
4038 block->updateDayNightDiff();
4045 Create chunk metadata
4048 for(s16 x=-1; x<=1; x++)
4049 for(s16 y=-1; y<=1; y++)
4051 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
4052 // Add chunk meta information
4053 MapChunk *chunk = getChunk(chunkpos0);
4056 chunk = new MapChunk();
4057 m_chunks.insert(chunkpos0, chunk);
4059 //chunk->setIsVolatile(true);
4060 if(chunk->getGenLevel() > GENERATED_PARTLY)
4061 chunk->setGenLevel(GENERATED_PARTLY);
4065 Set central chunk non-volatile
4067 MapChunk *chunk = getChunk(chunkpos);
4070 //chunk->setIsVolatile(false);
4071 chunk->setGenLevel(GENERATED_FULLY);
4074 Save changed parts of map
4079 Return central chunk (which was requested)
4085 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
4086 core::map<v3s16, MapBlock*> &changed_blocks,
4089 DSTACK(__FUNCTION_NAME);
4092 Don't generate if already fully generated
4096 MapChunk *chunk = getChunk(chunkpos);
4097 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
4099 dstream<<"generateChunkRaw(): Chunk "
4100 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
4101 <<" already generated"<<std::endl;
4107 dstream<<"generateChunkRaw(): Generating chunk "
4108 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
4111 TimeTaker timer("generateChunkRaw()");
4113 // The distance how far into the neighbors the generator is allowed to go.
4114 s16 max_spread_amount_sectors = 1;
4115 assert(max_spread_amount_sectors <= m_chunksize);
4116 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
4118 // Minimum amount of space left on sides for mud to fall in
4119 //s16 min_mud_fall_space = 2;
4121 // Maximum diameter of stone obstacles in X and Z
4122 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
4123 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
4125 s16 y_blocks_min = -2;
4126 s16 y_blocks_max = 3;
4127 //s16 h_blocks = y_blocks_max - y_blocks_min + 1;
4128 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
4129 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
4131 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
4132 s16 sectorpos_base_size = m_chunksize;
4134 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
4135 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
4136 v2s16 sectorpos_bigbase =
4137 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
4138 s16 sectorpos_bigbase_size =
4139 sectorpos_base_size + 2 * max_spread_amount_sectors;
4141 v3s16 bigarea_blocks_min(
4142 sectorpos_bigbase.X,
4147 v3s16 bigarea_blocks_max(
4148 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
4150 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
4153 // Relative values to control amount of stuff in one chunk
4154 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
4155 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
4156 /*u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
4157 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
4158 *(u32)h_blocks*MAP_BLOCKSIZE;*/
4161 The limiting edges of the lighting update, inclusive.
4163 s16 lighting_min_d = 0-max_spread_amount;
4164 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
4167 Create the whole area of this and the neighboring chunks
4170 TimeTaker timer("generateChunkRaw() create area");
4172 for(s16 x=0; x<sectorpos_bigbase_size; x++)
4173 for(s16 z=0; z<sectorpos_bigbase_size; z++)
4175 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
4176 ServerMapSector *sector = createSector(sectorpos);
4179 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
4181 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
4182 MapBlock *block = createBlock(blockpos);
4184 // Lighting won't be calculated
4185 //block->setLightingExpired(true);
4186 // Lighting will be calculated
4187 block->setLightingExpired(false);
4190 Block gets sunlight if this is true.
4192 This should be set to true when the top side of a block
4193 is completely exposed to the sky.
4195 Actually this doesn't matter now because the
4196 initial lighting is done here.
4198 block->setIsUnderground(y != y_blocks_max);
4204 Now we have a big empty area.
4206 Make a ManualMapVoxelManipulator that contains this and the
4210 ManualMapVoxelManipulator vmanip(this);
4211 // Add the area we just generated
4213 TimeTaker timer("generateChunkRaw() initialEmerge");
4214 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
4218 vmanip.clearFlag(0xff);
4220 TimeTaker timer_generate("generateChunkRaw() generate");
4223 Generate general ground level to full area
4228 TimeTaker timer1("ground level");
4229 dstream<<"Generating base ground..."<<std::endl;
4231 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
4232 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
4235 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
4238 Skip if already generated
4241 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
4242 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
4246 v2f p2df(p2d.X, p2d.Y);
4248 s16 mud_amount = get_mud_amount(m_seed, p2df);
4250 double tfxz = get_turbulence_factor_2d(m_seed, p2df);
4251 bool turbulence_is_used = (tfxz > 0.001);
4255 float noturb_surface_y_f = base_rock_level_2d(m_seed, p2df);
4256 s16 noturb_surface_y = noturb_surface_y_f;
4259 s16 depth_counter = 0;
4260 s16 min = y_nodes_min;
4261 s16 max = y_nodes_max;
4262 // Use fast index incrementing
4263 v3s16 em = vmanip.m_area.getExtent();
4264 u32 i = vmanip.m_area.index(v3s16(p2d.X, max, p2d.Y));
4265 for(s16 y=max; y>=min; y--)
4267 v3f p3df(p2df.X, y, p2df.Y);
4269 bool is_ground = false;
4271 bool turb_for_node = (turbulence_is_used
4272 && y >= TURBULENCE_BOTTOM_CUTOFF_Y);
4274 if(is_carved(m_seed, p3df))
4283 is_ground = is_base_ground(m_seed,
4284 p3df, &depth_guess);
4286 // Estimate the surface height
4287 surface_y = y + depth_guess;
4291 surface_y = noturb_surface_y;
4294 is_ground = (y <= surface_y);
4299 //vmanip.m_data[i].d = CONTENT_STONE;
4300 /*if(y > surface_y - mud_amount)
4301 vmanip.m_data[i].d = CONTENT_MUD;
4303 vmanip.m_data[i].d = CONTENT_STONE;*/
4304 if(depth_counter < mud_amount)
4305 vmanip.m_data[i].d = CONTENT_MUD;
4307 vmanip.m_data[i].d = CONTENT_STONE;
4310 vmanip.m_data[i].d = CONTENT_AIR;
4312 if(is_ground || depth_counter != 0)
4317 bool is = is_base_ground(m_seed, v3f(p2df.X,y,p2df.Y));
4319 vmanip.m_data[i].d = CONTENT_STONE;
4321 vmanip.m_data[i].d = CONTENT_AIR;
4325 vmanip.m_area.add_y(em, i, -1);
4334 //TimeTaker timer1("add water");
4337 Add water to the central chunk (and a bit more)
4340 for(s16 x=0-max_spread_amount;
4341 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
4343 for(s16 z=0-max_spread_amount;
4344 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
4347 // Node position in 2d
4348 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
4350 // Find ground level
4351 //s16 surface_y = find_ground_level(vmanip, p2d);
4354 If ground level is over water level, skip.
4355 NOTE: This leaves caves near water without water,
4356 which looks especially crappy when the nearby water
4357 won't start flowing either for some reason
4359 /*if(surface_y > WATER_LEVEL)
4366 v3s16 em = vmanip.m_area.getExtent();
4367 u8 light = LIGHT_MAX;
4368 // Start at global water surface level
4369 s16 y_start = WATER_LEVEL;
4370 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
4371 MapNode *n = &vmanip.m_data[i];
4373 /*// Add first one to transforming liquid queue, if water
4374 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
4376 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
4377 m_transforming_liquid.push_back(p);
4380 for(s16 y=y_start; y>=y_nodes_min; y--)
4382 n = &vmanip.m_data[i];
4384 // Stop when there is no water and no air
4385 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
4386 && n->d != CONTENT_WATER)
4388 /*// Add bottom one to transforming liquid queue
4389 vmanip.m_area.add_y(em, i, 1);
4390 n = &vmanip.m_data[i];
4391 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
4393 v3s16 p = v3s16(p2d.X, y, p2d.Y);
4394 m_transforming_liquid.push_back(p);
4400 // Make water only not in dungeons
4401 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
4403 n->d = CONTENT_WATERSOURCE;
4404 //n->setLight(LIGHTBANK_DAY, light);
4406 // Add to transforming liquid queue (in case it'd
4408 v3s16 p = v3s16(p2d.X, y, p2d.Y);
4409 m_transforming_liquid.push_back(p);
4413 vmanip.m_area.add_y(em, i, -1);
4424 //TimeTaker timer1("convert mud to sand");
4430 //s16 mud_add_amount = myrand_range(2, 4);
4431 //s16 mud_add_amount = 0;
4433 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
4434 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
4435 for(s16 x=0-max_spread_amount+1;
4436 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
4438 for(s16 z=0-max_spread_amount+1;
4439 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
4442 // Node position in 2d
4443 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
4445 // Determine whether to have sand here
4446 bool have_sand = get_have_sand_coast(m_seed, v2f(p2d.X,p2d.Y));
4448 if(have_sand == false)
4451 // Find ground level
4452 s16 surface_y = find_ground_level_clever(vmanip, p2d);
4454 if(surface_y > WATER_LEVEL + 2)
4458 v3s16 em = vmanip.m_area.getExtent();
4459 s16 y_start = surface_y;
4460 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
4461 u32 not_sand_counter = 0;
4462 for(s16 y=y_start; y>=y_nodes_min; y--)
4464 MapNode *n = &vmanip.m_data[i];
4465 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
4467 n->d = CONTENT_SAND;
4472 if(not_sand_counter > 3)
4476 vmanip.m_area.add_y(em, i, -1);
4486 //TimeTaker timer1("grow grass");
4492 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
4493 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
4494 for(s16 x=0-max_spread_amount;
4495 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
4497 for(s16 z=0-max_spread_amount;
4498 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
4501 // Node position in 2d
4502 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
4505 Find the lowest surface to which enough light ends up
4508 Basically just wait until not air and not leaves.
4512 v3s16 em = vmanip.m_area.getExtent();
4513 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
4515 // Go to ground level
4516 for(y=y_nodes_max; y>=y_nodes_min; y--)
4518 MapNode &n = vmanip.m_data[i];
4519 if(n.d != CONTENT_AIR
4520 && n.d != CONTENT_LEAVES)
4522 vmanip.m_area.add_y(em, i, -1);
4524 if(y >= y_nodes_min)
4527 surface_y = y_nodes_min;
4530 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
4531 MapNode *n = &vmanip.m_data[i];
4532 if(n->d == CONTENT_MUD)
4533 n->d = CONTENT_GRASS;
4540 //TimeTaker timer1("generate trees");
4546 // Divide area into parts
4548 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
4549 double area = sidelen * sidelen;
4550 for(s16 x0=0; x0<div; x0++)
4551 for(s16 z0=0; z0<div; z0++)
4553 // Center position of part of division
4555 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
4556 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
4558 // Minimum edge of part of division
4560 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
4561 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
4563 // Maximum edge of part of division
4565 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
4566 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
4569 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
4570 // Put trees in random places on part of division
4571 for(u32 i=0; i<tree_count; i++)
4573 s16 x = myrand_range(p2d_min.X, p2d_max.X);
4574 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
4575 s16 y = find_ground_level(vmanip, v2s16(x,z));
4576 // Don't make a tree under water level
4581 Trees grow only on mud and grass
4584 u32 i = vmanip.m_area.index(v3s16(p));
4585 MapNode *n = &vmanip.m_data[i];
4586 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
4591 make_tree(vmanip, p);
4594 /*u32 tree_max = relative_area / 60;
4595 //u32 count = myrand_range(0, tree_max);
4596 for(u32 i=0; i<count; i++)
4598 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
4599 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
4600 x += sectorpos_base.X*MAP_BLOCKSIZE;
4601 z += sectorpos_base.Y*MAP_BLOCKSIZE;
4602 s16 y = find_ground_level(vmanip, v2s16(x,z));
4603 // Don't make a tree under water level
4608 make_tree(vmanip, p);
4616 Initial lighting (sunlight)
4619 core::map<v3s16, bool> light_sources;
4622 // 750ms @cs=8, can't optimize more
4623 TimeTaker timer1("initial lighting");
4627 This has to be 1 smaller than the actual area, because
4628 neighboring nodes are checked.
4630 for(s16 x=lighting_min_d+1;
4631 x<=lighting_max_d-1;
4633 for(s16 z=lighting_min_d+1;
4634 z<=lighting_max_d-1;
4637 // Node position in 2d
4638 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
4641 Apply initial sunlight
4644 u8 light = LIGHT_SUN;
4645 bool add_to_sources = false;
4646 v3s16 em = vmanip.m_area.getExtent();
4647 s16 y_start = y_nodes_max;
4648 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
4649 for(s16 y=y_start; y>=y_nodes_min; y--)
4651 MapNode *n = &vmanip.m_data[i];
4653 if(light_propagates_content(n->d) == false)
4657 else if(light != LIGHT_SUN
4658 || sunlight_propagates_content(n->d) == false)
4664 // This doesn't take much time
4665 if(add_to_sources == false)
4668 Check sides. If side is not air or water, start
4669 adding to light_sources.
4672 v3s16(0,0,1), // back
4673 v3s16(1,0,0), // right
4674 v3s16(0,0,-1), // front
4675 v3s16(-1,0,0), // left
4677 for(u32 di=0; di<4; di++)
4679 v3s16 dirp = dirs4[di];
4681 vmanip.m_area.add_p(em, i2, dirp);
4682 MapNode *n2 = &vmanip.m_data[i2];
4684 n2->d != CONTENT_AIR
4685 && n2->d != CONTENT_WATERSOURCE
4686 && n2->d != CONTENT_WATER
4688 add_to_sources = true;
4694 n->setLight(LIGHTBANK_DAY, light);
4695 n->setLight(LIGHTBANK_NIGHT, 0);
4697 // This doesn't take much time
4698 if(light != 0 && add_to_sources)
4700 // Insert light source
4701 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
4704 // Increment index by y
4705 vmanip.m_area.add_y(em, i, -1);
4713 // Spread light around
4715 TimeTaker timer("generateChunkRaw() spreadLight");
4716 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
4723 timer_generate.stop();
4726 Blit generated stuff to map
4730 //TimeTaker timer("generateChunkRaw() blitBackAll");
4731 vmanip.blitBackAll(&changed_blocks);
4735 Update day/night difference cache of the MapBlocks
4738 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
4739 i.atEnd() == false; i++)
4741 MapBlock *block = i.getNode()->getValue();
4742 block->updateDayNightDiff();
4748 Create chunk metadata
4751 for(s16 x=-1; x<=1; x++)
4752 for(s16 y=-1; y<=1; y++)
4754 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
4755 // Add chunk meta information
4756 MapChunk *chunk = getChunk(chunkpos0);
4759 chunk = new MapChunk();
4760 m_chunks.insert(chunkpos0, chunk);
4762 //chunk->setIsVolatile(true);
4763 if(chunk->getGenLevel() > GENERATED_PARTLY)
4764 chunk->setGenLevel(GENERATED_PARTLY);
4768 Set central chunk non-volatile
4770 MapChunk *chunk = getChunk(chunkpos);
4773 //chunk->setIsVolatile(false);
4774 chunk->setGenLevel(GENERATED_FULLY);
4777 Save changed parts of map
4782 Return central chunk (which was requested)
4788 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
4789 core::map<v3s16, MapBlock*> &changed_blocks)
4791 dstream<<"generateChunk(): Generating chunk "
4792 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
4795 // Shall be not used now
4798 /*for(s16 x=-1; x<=1; x++)
4799 for(s16 y=-1; y<=1; y++)*/
4800 for(s16 x=-0; x<=0; x++)
4801 for(s16 y=-0; y<=0; y++)
4803 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
4804 MapChunk *chunk = getChunk(chunkpos0);
4805 // Skip if already generated
4806 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
4808 generateChunkRaw(chunkpos0, changed_blocks);
4811 assert(chunkNonVolatile(chunkpos1));
4813 MapChunk *chunk = getChunk(chunkpos1);
4817 ServerMapSector * ServerMap::createSector(v2s16 p2d)
4819 DSTACK("%s: p2d=(%d,%d)",
4824 Check if it exists already in memory
4826 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
4831 Try to load it from disk (with blocks)
4833 if(loadSectorFull(p2d) == true)
4835 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
4838 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
4839 throw InvalidPositionException("");
4845 Do not create over-limit
4847 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4848 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4849 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4850 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4851 throw InvalidPositionException("createSector(): pos. over limit");
4854 Generate blank sector
4857 sector = new ServerMapSector(this, p2d);
4859 // Sector position on map in nodes
4860 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4865 m_sectors.insert(p2d, sector);
4870 MapSector * ServerMap::emergeSector(v2s16 p2d,
4871 core::map<v3s16, MapBlock*> &changed_blocks)
4873 DSTACK("%s: p2d=(%d,%d)",
4880 v2s16 chunkpos = sector_to_chunk(p2d);
4881 /*bool chunk_nonvolatile = false;
4882 MapChunk *chunk = getChunk(chunkpos);
4883 if(chunk && chunk->getIsVolatile() == false)
4884 chunk_nonvolatile = true;*/
4885 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
4888 If chunk is not fully generated, generate chunk
4890 if(chunk_nonvolatile == false)
4892 // Generate chunk and neighbors
4893 generateChunk(chunkpos, changed_blocks);
4897 Return sector if it exists now
4899 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4904 Try to load it from disk
4906 if(loadSectorFull(p2d) == true)
4908 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4911 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
4912 throw InvalidPositionException("");
4918 generateChunk should have generated the sector
4922 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
4923 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
4927 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
4930 generateChunkRaw(chunkpos, changed_blocks, true);
4933 Return sector if it exists now
4935 sector = getSectorNoGenerateNoEx(p2d);
4939 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
4945 dstream<<"WARNING: Creating an empty sector."<<std::endl;
4947 return createSector(p2d);
4954 //return generateSector();
4958 NOTE: This is not used for main map generation, only for blocks
4959 that are very high or low.
4960 NOTE: Now it is used mainly. Might change in the future.
4962 MapBlock * ServerMap::generateBlock(
4964 MapBlock *original_dummy,
4965 ServerMapSector *sector,
4966 core::map<v3s16, MapBlock*> &changed_blocks,
4967 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4970 DSTACK("%s: p=(%d,%d,%d)",
4974 /*dstream<<"generateBlock(): "
4975 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4978 MapBlock *block = original_dummy;
4980 v2s16 p2d(p.X, p.Z);
4982 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4983 v3s16 p_nodes = p * MAP_BLOCKSIZE;
4986 Do not generate over-limit
4988 if(blockpos_over_limit(p))
4990 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4991 throw InvalidPositionException("generateBlock(): pos. over limit");
4995 If block doesn't exist, create one.
4996 If it exists, it is a dummy. In that case unDummify() it.
4998 NOTE: This already sets the map as the parent of the block
5002 block = sector->createBlankBlockNoInsert(block_y);
5006 // Remove the block so that nobody can get a half-generated one.
5007 sector->removeBlock(block);
5008 // Allocate the block to contain the generated data
5012 u8 water_material = CONTENT_WATERSOURCE;
5014 s32 lowest_ground_y = 32767;
5015 s32 highest_ground_y = -32768;
5021 } block_type = BT_SURFACE;
5023 {// ground_timer (0ms or ~100ms)
5024 TimeTaker ground_timer("Ground generation");
5027 Approximate whether this block is a surface block, an air
5028 block or a ground block.
5030 This shall never mark a surface block as non-surface.
5035 Estimate surface at different positions of the block, to
5036 try to accomodate the effect of turbulence.
5049 v3f p_nodes_f = intToFloat(p_nodes, 1);
5050 float surface_y_max = -1000000;
5051 float surface_y_min = 1000000;
5052 for(u32 i=0; i<sizeof(checklist)/sizeof(checklist[0]); i++)
5054 v3f p_map_f = p_nodes_f + checklist[i]*MAP_BLOCKSIZE;
5057 /*bool is_ground =*/ is_base_ground(m_seed, p_map_f, &depth_guess);
5059 // Estimate the surface height
5060 float surface_y_f = p_map_f.Y + depth_guess;
5062 if(surface_y_f > surface_y_max)
5063 surface_y_max = surface_y_f;
5064 if(surface_y_f < surface_y_min)
5065 surface_y_min = surface_y_f;
5068 float block_low_y_f = p_nodes_f.Y;
5069 float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE;
5071 /*dstream<<"surface_y_max="<<surface_y_max
5072 <<", surface_y_min="<<surface_y_min
5073 <<", block_low_y_f="<<block_low_y_f
5074 <<", block_high_y_f="<<block_high_y_f
5077 // A fuzzyness value
5078 // Must accomodate mud and turbulence holes
5080 // Must accomodate a bit less
5083 if(block_high_y_f < surface_y_min - d_down)
5085 //dstream<<"BT_GROUND"<<std::endl;
5087 block_type = BT_GROUND;
5089 else if(block_low_y_f >= surface_y_max + d_up
5090 && block_low_y_f > WATER_LEVEL + d_up)
5092 //dstream<<"BT_SKY"<<std::endl;
5094 block_type = BT_SKY;
5098 //dstream<<"BT_SURFACE"<<std::endl;
5100 block_type = BT_SURFACE;
5103 if(/*block_type == BT_GROUND ||*/ block_type == BT_SKY)
5105 lowest_ground_y = surface_y_min;
5106 highest_ground_y = surface_y_max;
5110 if(block_type == BT_SURFACE || block_type == BT_GROUND)
5113 Generate ground precisely
5116 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
5117 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
5119 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
5121 //s16 surface_y = 0;
5123 /*s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
5124 + AVERAGE_MUD_AMOUNT;
5126 if(surface_y < lowest_ground_y)
5127 lowest_ground_y = surface_y;
5128 if(surface_y > highest_ground_y)
5129 highest_ground_y = surface_y;*/
5131 v2s16 real_p2d = v2s16(x0,z0) + p2d*MAP_BLOCKSIZE;
5133 v2f real_p2d_f(real_p2d.X,real_p2d.Y);
5135 s16 surface_depth = get_mud_amount(m_seed, real_p2d_f);
5137 double tfxz = get_turbulence_factor_2d(m_seed, real_p2d_f);
5138 bool turbulence_is_used = (tfxz > 0.001);
5140 float surface_y_f = 0;
5143 float noturb_surface_y_f = base_rock_level_2d(m_seed, real_p2d_f);
5144 s16 noturb_surface_y = noturb_surface_y_f;
5146 // Get some statistics of surface height
5147 if(noturb_surface_y < lowest_ground_y)
5148 lowest_ground_y = noturb_surface_y;
5149 if(noturb_surface_y > highest_ground_y)
5150 highest_ground_y = noturb_surface_y;
5152 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
5155 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
5156 v3s16 real_pos = v3s16(x0,y0,z0) + p_nodes;
5161 NOTE: If there are some man-made structures above the
5162 newly created block, they won't be taken into account.
5164 /*if(real_y > surface_y)
5165 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);*/
5171 bool is_ground = false;
5172 v3f real_pos_f = intToFloat(real_pos, 1);
5174 bool turb_for_node = (turbulence_is_used
5175 && real_y >= TURBULENCE_BOTTOM_CUTOFF_Y);
5177 bool is_cavern = false;
5179 if(is_carved(m_seed, real_pos_f))
5182 if(real_y < noturb_surface_y)
5190 is_ground = is_base_ground(m_seed,
5191 real_pos_f, &depth_guess);
5193 // Estimate the surface height
5194 surface_y_f = (float)real_y + depth_guess;
5195 surface_y = real_y + depth_guess;
5197 // Get some statistics of surface height
5198 if(surface_y < lowest_ground_y)
5199 lowest_ground_y = surface_y;
5200 if(surface_y > highest_ground_y)
5201 highest_ground_y = surface_y;
5205 surface_y = noturb_surface_y;
5208 is_ground = (real_y <= surface_y);
5211 // If node is not ground, it's air or water
5212 if(is_ground == false)
5214 // If under water level, it's water
5215 if(real_y < WATER_LEVEL && !is_cavern)
5217 n.d = water_material;
5219 if(real_y >= surface_y)
5220 dist = WATER_LEVEL-real_y+1;
5221 n.setLight(LIGHTBANK_DAY,
5222 diminish_light(LIGHT_SUN, dist));
5224 Add to transforming liquid queue (in case it'd
5227 m_transforming_liquid.push_back(real_pos);
5233 // Else it's ground or dungeons (air)
5236 // If it's surface_depth under ground, it's stone
5237 if((float)real_y <= surface_y_f - surface_depth - 0.75)
5239 if(is_underground_mud(m_seed, real_pos_f))
5242 n.d = CONTENT_STONE;
5244 else if(surface_y_f <= WATER_LEVEL + 2.1
5245 && get_have_sand_coast(m_seed, real_p2d_f))
5251 /*// It is mud if it is under the first ground
5252 // level or under water
5253 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
5259 n.d = CONTENT_GRASS;
5262 if(get_have_sand_ground(m_seed, real_p2d_f))
5267 /*// If under water level, it's mud
5268 if(real_y < WATER_LEVEL)
5270 // Only the topmost node is grass
5271 else if(real_y <= surface_y - 1)
5274 n.d = CONTENT_GRASS;*/
5278 block->setNode(v3s16(x0,y0,z0), n);
5281 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
5286 NOTE: If there are some man-made structures above the
5287 newly created block, they won't be taken into account.
5289 if(real_y > surface_y)
5290 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
5296 // If node is over heightmap y, it's air or water
5297 if(real_y > surface_y)
5299 // If under water level, it's water
5300 if(real_y < WATER_LEVEL)
5302 n.d = water_material;
5303 n.setLight(LIGHTBANK_DAY,
5304 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
5306 Add to transforming liquid queue (in case it'd
5309 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
5310 m_transforming_liquid.push_back(real_pos);
5316 // Else it's ground or dungeons (air)
5319 // If it's surface_depth under ground, it's stone
5320 if(real_y <= surface_y - surface_depth)
5322 n.d = CONTENT_STONE;
5326 // It is mud if it is under the first ground
5327 // level or under water
5328 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
5334 n.d = CONTENT_GRASS;
5337 //n.d = CONTENT_MUD;
5339 /*// If under water level, it's mud
5340 if(real_y < WATER_LEVEL)
5342 // Only the topmost node is grass
5343 else if(real_y <= surface_y - 1)
5346 n.d = CONTENT_GRASS;*/
5350 block->setNode(v3s16(x0,y0,z0), n);
5355 else // BT_GROUND, BT_SKY or anything else
5358 if(block_type == BT_GROUND)
5360 //n_fill.d = CONTENT_STONE;
5362 else if(block_type == BT_SKY)
5364 n_fill.d = CONTENT_AIR;
5365 n_fill.setLight(LIGHTBANK_DAY, LIGHT_SUN);
5369 n_fill.d = CONTENT_MESE;
5373 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
5374 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
5375 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
5377 //MapNode n = block->getNode(v3s16(x0,y0,z0));
5378 block->setNode(v3s16(x0,y0,z0), n_fill);
5385 Calculate some helper variables
5388 // Completely underground if the highest part of block is under lowest
5390 // This has to be very sure; it's probably one too strict now but
5391 // that's just better.
5392 bool completely_underground =
5393 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
5395 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
5397 bool mostly_underwater_surface = false;
5398 if(highest_ground_y < WATER_LEVEL
5399 && some_part_underground && !completely_underground)
5400 mostly_underwater_surface = true;
5403 Get local attributes
5406 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
5408 //float caves_amount = 0.5;
5413 NOTE: BEWARE: Too big amount of attribute points slows verything
5415 1 interpolation from 5000 points takes 2-3ms.
5417 //TimeTaker timer("generateBlock() local attribute retrieval");
5418 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
5419 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
5420 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
5424 //dstream<<"generateBlock(): Done"<<std::endl;
5427 // Set to true if has caves.
5428 // Set when some non-air is changed to air when making caves.
5429 bool has_dungeons = false;
5435 // Initialize temporary table
5436 const s32 ued = MAP_BLOCKSIZE;
5437 bool underground_emptiness[ued*ued*ued];
5438 for(s32 i=0; i<ued*ued*ued; i++)
5440 underground_emptiness[i] = 0;
5447 Initialize orp and ors. Try to find if some neighboring
5448 MapBlock has a tunnel ended in its side
5452 (float)(myrand()%ued)+0.5,
5453 (float)(myrand()%ued)+0.5,
5454 (float)(myrand()%ued)+0.5
5457 bool found_existing = false;
5463 for(s16 y=0; y<ued; y++)
5464 for(s16 x=0; x<ued; x++)
5466 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
5467 if(getNode(ap).d == CONTENT_AIR)
5469 orp = v3f(x+1,y+1,0);
5470 found_existing = true;
5471 goto continue_generating;
5475 catch(InvalidPositionException &e){}
5481 for(s16 y=0; y<ued; y++)
5482 for(s16 x=0; x<ued; x++)
5484 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
5485 if(getNode(ap).d == CONTENT_AIR)
5487 orp = v3f(x+1,y+1,ued-1);
5488 found_existing = true;
5489 goto continue_generating;
5493 catch(InvalidPositionException &e){}
5499 for(s16 y=0; y<ued; y++)
5500 for(s16 z=0; z<ued; z++)
5502 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
5503 if(getNode(ap).d == CONTENT_AIR)
5505 orp = v3f(0,y+1,z+1);
5506 found_existing = true;
5507 goto continue_generating;
5511 catch(InvalidPositionException &e){}
5517 for(s16 y=0; y<ued; y++)
5518 for(s16 z=0; z<ued; z++)
5520 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
5521 if(getNode(ap).d == CONTENT_AIR)
5523 orp = v3f(ued-1,y+1,z+1);
5524 found_existing = true;
5525 goto continue_generating;
5529 catch(InvalidPositionException &e){}
5535 for(s16 x=0; x<ued; x++)
5536 for(s16 z=0; z<ued; z++)
5538 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
5539 if(getNode(ap).d == CONTENT_AIR)
5541 orp = v3f(x+1,0,z+1);
5542 found_existing = true;
5543 goto continue_generating;
5547 catch(InvalidPositionException &e){}
5553 for(s16 x=0; x<ued; x++)
5554 for(s16 z=0; z<ued; z++)
5556 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
5557 if(getNode(ap).d == CONTENT_AIR)
5559 orp = v3f(x+1,ued-1,z+1);
5560 found_existing = true;
5561 goto continue_generating;
5565 catch(InvalidPositionException &e){}
5567 continue_generating:
5570 Choose whether to actually generate dungeon
5572 bool do_generate_dungeons = true;
5573 // Don't generate if no part is underground
5574 if(!some_part_underground)
5576 do_generate_dungeons = false;
5578 // Don't generate if mostly underwater surface
5579 /*else if(mostly_underwater_surface)
5581 do_generate_dungeons = false;
5583 // Partly underground = cave
5584 else if(!completely_underground)
5586 //do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
5587 do_generate_dungeons = false;
5589 // Found existing dungeon underground
5590 else if(found_existing && completely_underground)
5592 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
5594 // Underground and no dungeons found
5597 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
5600 if(do_generate_dungeons)
5603 Generate some tunnel starting from orp and ors
5605 for(u16 i=0; i<3; i++)
5608 (float)(myrand()%ued)+0.5,
5609 (float)(myrand()%ued)+0.5,
5610 (float)(myrand()%ued)+0.5
5614 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
5618 for(float f=0; f<1.0; f+=0.04)
5620 v3f fp = orp + vec * f;
5621 v3s16 cp(fp.X, fp.Y, fp.Z);
5623 s16 d1 = d0 + rs - 1;
5624 for(s16 z0=d0; z0<=d1; z0++)
5626 s16 si = rs - abs(z0);
5627 for(s16 x0=-si; x0<=si-1; x0++)
5629 s16 si2 = rs - abs(x0);
5630 for(s16 y0=-si2+1; y0<=si2-1; y0++)
5636 if(isInArea(p, ued) == false)
5638 underground_emptiness[ued*ued*z + ued*y + x] = 1;
5651 Apply temporary cave data to block
5654 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
5655 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
5657 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
5659 MapNode n = block->getNode(v3s16(x0,y0,z0));
5662 if(underground_emptiness[
5663 ued*ued*(z0*ued/MAP_BLOCKSIZE)
5664 +ued*(y0*ued/MAP_BLOCKSIZE)
5665 +(x0*ued/MAP_BLOCKSIZE)])
5667 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
5670 has_dungeons = true;
5676 block->setNode(v3s16(x0,y0,z0), n);
5682 This is used for guessing whether or not the block should
5683 receive sunlight from the top if the block above doesn't exist
5685 block->setIsUnderground(completely_underground);
5688 Force lighting update if some part of block is partly
5689 underground and has caves.
5691 /*if(some_part_underground && !completely_underground && has_dungeons)
5693 //dstream<<"Half-ground caves"<<std::endl;
5694 lighting_invalidated_blocks[block->getPos()] = block;
5697 // DEBUG: Always update lighting
5698 //lighting_invalidated_blocks[block->getPos()] = block;
5704 if(some_part_underground)
5706 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
5711 for(s16 i=0; i<underground_level/4 + 1; i++)
5713 if(myrand()%50 == 0)
5716 (myrand()%(MAP_BLOCKSIZE-2))+1,
5717 (myrand()%(MAP_BLOCKSIZE-2))+1,
5718 (myrand()%(MAP_BLOCKSIZE-2))+1
5724 for(u16 i=0; i<27; i++)
5726 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
5728 block->setNode(cp+g_27dirs[i], n);
5736 u16 coal_amount = 60;
5737 u16 coal_rareness = 120 / coal_amount;
5738 if(coal_rareness == 0)
5740 if(myrand()%coal_rareness == 0)
5742 u16 a = myrand() % 16;
5743 u16 amount = coal_amount * a*a*a / 1000;
5744 for(s16 i=0; i<amount; i++)
5747 (myrand()%(MAP_BLOCKSIZE-2))+1,
5748 (myrand()%(MAP_BLOCKSIZE-2))+1,
5749 (myrand()%(MAP_BLOCKSIZE-2))+1
5753 n.d = CONTENT_STONE;
5754 n.param = MINERAL_COAL;
5756 for(u16 i=0; i<27; i++)
5758 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
5760 block->setNode(cp+g_27dirs[i], n);
5768 u16 iron_amount = 40;
5769 u16 iron_rareness = 80 / iron_amount;
5770 if(iron_rareness == 0)
5772 if(myrand()%iron_rareness == 0)
5774 u16 a = myrand() % 16;
5775 u16 amount = iron_amount * a*a*a / 1000;
5776 for(s16 i=0; i<amount; i++)
5779 (myrand()%(MAP_BLOCKSIZE-2))+1,
5780 (myrand()%(MAP_BLOCKSIZE-2))+1,
5781 (myrand()%(MAP_BLOCKSIZE-2))+1
5785 n.d = CONTENT_STONE;
5786 n.param = MINERAL_IRON;
5788 for(u16 i=0; i<27; i++)
5790 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
5792 block->setNode(cp+g_27dirs[i], n);
5799 Create a few rats in empty blocks underground
5801 if(completely_underground)
5803 //for(u16 i=0; i<2; i++)
5806 (myrand()%(MAP_BLOCKSIZE-2))+1,
5807 (myrand()%(MAP_BLOCKSIZE-2))+1,
5808 (myrand()%(MAP_BLOCKSIZE-2))+1
5811 // Check that the place is empty
5812 //if(!is_ground_content(block->getNode(cp).d))
5815 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
5816 block->addObject(obj);
5824 sector->insertBlock(block);
5826 // Lighting is invalid after generation for surface blocks
5827 if(block_type == BT_SURFACE)
5830 block->setLightingExpired(true);
5831 lighting_invalidated_blocks.insert(p, block);
5833 block->setLightingExpired(false);
5836 // Lighting is not invalid for other blocks
5839 block->setLightingExpired(false);
5846 if(some_part_underground && !completely_underground)
5848 MapVoxelManipulator vm(this);
5850 double a = tree_amount_2d(m_seed, v2s16(p_nodes.X+8, p_nodes.Z+8));
5851 u16 tree_count = (u16)(a*MAP_BLOCKSIZE*MAP_BLOCKSIZE);
5852 for(u16 i=0; i<tree_count/2; i++)
5854 v3s16 tree_p = p_nodes + v3s16(
5855 myrand_range(0,MAP_BLOCKSIZE-1),
5857 myrand_range(0,MAP_BLOCKSIZE-1)
5860 /*bool is_ground =*/ is_base_ground(m_seed,
5861 intToFloat(tree_p, 1), &depth_guess);
5862 tree_p.Y += (depth_guess - 0.5);
5863 if(tree_p.Y <= WATER_LEVEL)
5865 make_tree(vm, tree_p);
5868 vm.blitBack(changed_blocks);
5877 <<"lighting_invalidated_blocks.size()"
5881 <<" "<<lighting_invalidated_blocks.size()
5882 <<", "<<has_dungeons
5883 <<", "<<completely_underground
5884 <<", "<<some_part_underground
5891 MapBlock * ServerMap::createBlock(v3s16 p)
5893 DSTACK("%s: p=(%d,%d,%d)",
5894 __FUNCTION_NAME, p.X, p.Y, p.Z);
5897 Do not create over-limit
5899 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5900 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5901 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5902 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5903 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5904 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
5905 throw InvalidPositionException("createBlock(): pos. over limit");
5907 v2s16 p2d(p.X, p.Z);
5910 This will create or load a sector if not found in memory.
5911 If block exists on disk, it will be loaded.
5913 NOTE: On old save formats, this will be slow, as it generates
5914 lighting on blocks for them.
5916 ServerMapSector *sector;
5918 sector = (ServerMapSector*)createSector(p2d);
5919 assert(sector->getId() == MAPSECTOR_SERVER);
5921 catch(InvalidPositionException &e)
5923 dstream<<"createBlock: createSector() failed"<<std::endl;
5927 NOTE: This should not be done, or at least the exception
5928 should not be passed on as std::exception, because it
5929 won't be catched at all.
5931 /*catch(std::exception &e)
5933 dstream<<"createBlock: createSector() failed: "
5934 <<e.what()<<std::endl;
5939 Try to get a block from the sector
5942 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
5946 block = sector->createBlankBlock(block_y);
5950 MapBlock * ServerMap::emergeBlock(
5952 bool only_from_disk,
5953 core::map<v3s16, MapBlock*> &changed_blocks,
5954 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
5957 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
5959 p.X, p.Y, p.Z, only_from_disk);
5962 Do not generate over-limit
5964 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5965 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5966 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5967 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5968 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5969 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
5970 throw InvalidPositionException("emergeBlock(): pos. over limit");
5972 v2s16 p2d(p.X, p.Z);
5975 This will create or load a sector if not found in memory.
5976 If block exists on disk, it will be loaded.
5978 ServerMapSector *sector;
5980 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
5981 assert(sector->getId() == MAPSECTOR_SERVER);
5983 catch(InvalidPositionException &e)
5985 dstream<<"emergeBlock: emergeSector() failed: "
5986 <<e.what()<<std::endl;
5987 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
5989 <<"You could try to delete it."<<std::endl;
5992 catch(VersionMismatchException &e)
5994 dstream<<"emergeBlock: emergeSector() failed: "
5995 <<e.what()<<std::endl;
5996 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
5998 <<"You could try to delete it."<<std::endl;
6002 NOTE: This should not be done, or at least the exception
6003 should not be passed on as std::exception, because it
6004 won't be catched at all.
6006 /*catch(std::exception &e)
6008 dstream<<"emergeBlock: emergeSector() failed: "
6009 <<e.what()<<std::endl;
6010 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
6012 <<"You could try to delete it."<<std::endl;
6017 Try to get a block from the sector
6020 bool does_not_exist = false;
6021 bool lighting_expired = false;
6022 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
6026 does_not_exist = true;
6028 else if(block->isDummy() == true)
6030 does_not_exist = true;
6032 else if(block->getLightingExpired())
6034 lighting_expired = true;
6039 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
6044 If block was not found on disk and not going to generate a
6045 new one, make sure there is a dummy block in place.
6047 if(only_from_disk && (does_not_exist || lighting_expired))
6049 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
6053 // Create dummy block
6054 block = new MapBlock(this, p, true);
6056 // Add block to sector
6057 sector->insertBlock(block);
6063 //dstream<<"Not found on disk, generating."<<std::endl;
6065 //TimeTaker("emergeBlock() generate");
6067 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
6070 If the block doesn't exist, generate the block.
6074 block = generateBlock(p, block, sector, changed_blocks,
6075 lighting_invalidated_blocks);
6077 lighting_expired = block->getLightingExpired();
6080 if(lighting_expired)
6082 lighting_invalidated_blocks.insert(p, block);
6086 Initially update sunlight
6089 if(lighting_expired)
6091 core::map<v3s16, bool> light_sources;
6092 bool black_air_left = false;
6093 bool bottom_invalid =
6094 block->propagateSunlight(light_sources, true,
6095 &black_air_left, true);
6097 // If sunlight didn't reach everywhere and part of block is
6098 // above ground, lighting has to be properly updated
6099 //if(black_air_left && some_part_underground)
6102 lighting_invalidated_blocks[block->getPos()] = block;
6107 lighting_invalidated_blocks[block->getPos()] = block;
6114 s16 ServerMap::findGroundLevel(v2s16 p2d)
6117 Uh, just do something random...
6119 // Find existing map from top to down
6122 v3s16 p(p2d.X, max, p2d.Y);
6123 for(; p.Y>min; p.Y--)
6125 MapNode n = getNodeNoEx(p);
6126 if(n.d != CONTENT_IGNORE)
6131 // If this node is not air, go to plan b
6132 if(getNodeNoEx(p).d != CONTENT_AIR)
6134 // Search existing walkable and return it
6135 for(; p.Y>min; p.Y--)
6137 MapNode n = getNodeNoEx(p);
6138 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
6144 Plan B: Get from map generator perlin noise function
6146 double level = base_rock_level_2d(m_seed, p2d);
6150 void ServerMap::createDir(std::string path)
6152 if(fs::CreateDir(path) == false)
6154 m_dout<<DTIME<<"ServerMap: Failed to create directory "
6155 <<"\""<<path<<"\""<<std::endl;
6156 throw BaseException("ServerMap failed to create directory");
6160 std::string ServerMap::getSectorSubDir(v2s16 pos)
6163 snprintf(cc, 9, "%.4x%.4x",
6164 (unsigned int)pos.X&0xffff,
6165 (unsigned int)pos.Y&0xffff);
6167 return std::string(cc);
6170 std::string ServerMap::getSectorDir(v2s16 pos)
6172 return m_savedir + "/sectors/" + getSectorSubDir(pos);
6175 v2s16 ServerMap::getSectorPos(std::string dirname)
6177 if(dirname.size() != 8)
6178 throw InvalidFilenameException("Invalid sector directory name");
6180 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
6182 throw InvalidFilenameException("Invalid sector directory name");
6183 v2s16 pos((s16)x, (s16)y);
6187 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
6189 v2s16 p2d = getSectorPos(sectordir);
6191 if(blockfile.size() != 4){
6192 throw InvalidFilenameException("Invalid block filename");
6195 int r = sscanf(blockfile.c_str(), "%4x", &y);
6197 throw InvalidFilenameException("Invalid block filename");
6198 return v3s16(p2d.X, y, p2d.Y);
6201 void ServerMap::save(bool only_changed)
6203 DSTACK(__FUNCTION_NAME);
6204 if(m_map_saving_enabled == false)
6206 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
6210 if(only_changed == false)
6211 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
6217 u32 sector_meta_count = 0;
6218 u32 block_count = 0;
6221 JMutexAutoLock lock(m_sector_mutex);
6223 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
6224 for(; i.atEnd() == false; i++)
6226 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
6227 assert(sector->getId() == MAPSECTOR_SERVER);
6229 if(sector->differs_from_disk || only_changed == false)
6231 saveSectorMeta(sector);
6232 sector_meta_count++;
6234 core::list<MapBlock*> blocks;
6235 sector->getBlocks(blocks);
6236 core::list<MapBlock*>::Iterator j;
6237 for(j=blocks.begin(); j!=blocks.end(); j++)
6239 MapBlock *block = *j;
6240 if(block->getChangedFlag() || only_changed == false)
6245 /*dstream<<"ServerMap: Written block ("
6246 <<block->getPos().X<<","
6247 <<block->getPos().Y<<","
6248 <<block->getPos().Z<<")"
6257 Only print if something happened or saved whole map
6259 if(only_changed == false || sector_meta_count != 0
6260 || block_count != 0)
6262 dstream<<DTIME<<"ServerMap: Written: "
6263 <<sector_meta_count<<" sector metadata files, "
6264 <<block_count<<" block files"
6269 void ServerMap::loadAll()
6271 DSTACK(__FUNCTION_NAME);
6272 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
6277 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
6279 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
6281 JMutexAutoLock lock(m_sector_mutex);
6284 s32 printed_counter = -100000;
6285 s32 count = list.size();
6287 std::vector<fs::DirListNode>::iterator i;
6288 for(i=list.begin(); i!=list.end(); i++)
6290 if(counter > printed_counter + 10)
6292 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
6293 printed_counter = counter;
6297 MapSector *sector = NULL;
6299 // We want directories
6303 sector = loadSectorMeta(i->name);
6305 catch(InvalidFilenameException &e)
6307 // This catches unknown crap in directory
6310 std::vector<fs::DirListNode> list2 = fs::GetDirListing
6311 (m_savedir+"/sectors/"+i->name);
6312 std::vector<fs::DirListNode>::iterator i2;
6313 for(i2=list2.begin(); i2!=list2.end(); i2++)
6319 loadBlock(i->name, i2->name, sector);
6321 catch(InvalidFilenameException &e)
6323 // This catches unknown crap in directory
6327 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
6331 void ServerMap::saveMasterHeightmap()
6333 DSTACK(__FUNCTION_NAME);
6335 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
6337 createDir(m_savedir);
6339 /*std::string fullpath = m_savedir + "/master_heightmap";
6340 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
6341 if(o.good() == false)
6342 throw FileNotGoodException("Cannot open master heightmap");*/
6344 // Format used for writing
6345 //u8 version = SER_FMT_VER_HIGHEST;
6348 void ServerMap::loadMasterHeightmap()
6350 DSTACK(__FUNCTION_NAME);
6352 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
6354 /*std::string fullpath = m_savedir + "/master_heightmap";
6355 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
6356 if(is.good() == false)
6357 throw FileNotGoodException("Cannot open master heightmap");*/
6361 void ServerMap::saveMapMeta()
6363 DSTACK(__FUNCTION_NAME);
6365 dstream<<"INFO: ServerMap::saveMapMeta(): "
6366 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
6369 createDir(m_savedir);
6371 std::string fullpath = m_savedir + "/map_meta.txt";
6372 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
6373 if(os.good() == false)
6375 dstream<<"ERROR: ServerMap::saveMapMeta(): "
6376 <<"could not open"<<fullpath<<std::endl;
6377 throw FileNotGoodException("Cannot open chunk metadata");
6381 params.setU64("seed", m_seed);
6382 params.setS32("chunksize", m_chunksize);
6384 params.writeLines(os);
6386 os<<"[end_of_params]\n";
6390 void ServerMap::loadMapMeta()
6392 DSTACK(__FUNCTION_NAME);
6394 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
6397 std::string fullpath = m_savedir + "/map_meta.txt";
6398 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
6399 if(is.good() == false)
6401 dstream<<"ERROR: ServerMap::loadMapMeta(): "
6402 <<"could not open"<<fullpath<<std::endl;
6403 throw FileNotGoodException("Cannot open chunk metadata");
6411 throw SerializationError
6412 ("ServerMap::loadMapMeta(): [end_of_params] not found");
6414 std::getline(is, line);
6415 std::string trimmedline = trim(line);
6416 if(trimmedline == "[end_of_params]")
6418 params.parseConfigLine(line);
6421 m_seed = params.getU64("seed");
6422 m_chunksize = params.getS32("chunksize");
6424 dstream<<"INFO: ServerMap::loadMapMeta(): "
6425 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
6429 void ServerMap::saveChunkMeta()
6431 DSTACK(__FUNCTION_NAME);
6433 u32 count = m_chunks.size();
6435 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
6436 <<count<<" chunks"<<std::endl;
6438 createDir(m_savedir);
6440 std::string fullpath = m_savedir + "/chunk_meta";
6441 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
6442 if(os.good() == false)
6444 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
6445 <<"could not open"<<fullpath<<std::endl;
6446 throw FileNotGoodException("Cannot open chunk metadata");
6452 os.write((char*)&version, 1);
6457 writeU32(buf, count);
6458 os.write((char*)buf, 4);
6460 for(core::map<v2s16, MapChunk*>::Iterator
6461 i = m_chunks.getIterator();
6462 i.atEnd()==false; i++)
6464 v2s16 p = i.getNode()->getKey();
6465 MapChunk *chunk = i.getNode()->getValue();
6468 os.write((char*)buf, 4);
6470 chunk->serialize(os, version);
6474 void ServerMap::loadChunkMeta()
6476 DSTACK(__FUNCTION_NAME);
6478 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
6481 std::string fullpath = m_savedir + "/chunk_meta";
6482 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
6483 if(is.good() == false)
6485 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
6486 <<"could not open"<<fullpath<<std::endl;
6487 throw FileNotGoodException("Cannot open chunk metadata");
6493 is.read((char*)&version, 1);
6498 is.read((char*)buf, 4);
6499 u32 count = readU32(buf);
6501 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
6502 <<count<<" chunks"<<std::endl;
6504 for(u32 i=0; i<count; i++)
6507 MapChunk *chunk = new MapChunk();
6509 is.read((char*)buf, 4);
6512 chunk->deSerialize(is, version);
6513 m_chunks.insert(p, chunk);
6517 void ServerMap::saveSectorMeta(ServerMapSector *sector)
6519 DSTACK(__FUNCTION_NAME);
6520 // Format used for writing
6521 u8 version = SER_FMT_VER_HIGHEST;
6523 v2s16 pos = sector->getPos();
6524 createDir(m_savedir);
6525 createDir(m_savedir+"/sectors");
6526 std::string dir = getSectorDir(pos);
6529 std::string fullpath = dir + "/meta";
6530 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
6531 if(o.good() == false)
6532 throw FileNotGoodException("Cannot open sector metafile");
6534 sector->serialize(o, version);
6536 sector->differs_from_disk = false;
6539 MapSector* ServerMap::loadSectorMeta(std::string dirname)
6541 DSTACK(__FUNCTION_NAME);
6543 v2s16 p2d = getSectorPos(dirname);
6544 std::string dir = m_savedir + "/sectors/" + dirname;
6546 std::string fullpath = dir + "/meta";
6547 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
6548 if(is.good() == false)
6549 throw FileNotGoodException("Cannot open sector metafile");
6551 ServerMapSector *sector = ServerMapSector::deSerialize
6552 (is, this, p2d, m_sectors);
6554 sector->differs_from_disk = false;
6559 bool ServerMap::loadSectorFull(v2s16 p2d)
6561 DSTACK(__FUNCTION_NAME);
6562 std::string sectorsubdir = getSectorSubDir(p2d);
6564 MapSector *sector = NULL;
6566 JMutexAutoLock lock(m_sector_mutex);
6569 sector = loadSectorMeta(sectorsubdir);
6571 catch(InvalidFilenameException &e)
6575 catch(FileNotGoodException &e)
6579 catch(std::exception &e)
6587 std::vector<fs::DirListNode> list2 = fs::GetDirListing
6588 (m_savedir+"/sectors/"+sectorsubdir);
6589 std::vector<fs::DirListNode>::iterator i2;
6590 for(i2=list2.begin(); i2!=list2.end(); i2++)
6596 loadBlock(sectorsubdir, i2->name, sector);
6598 catch(InvalidFilenameException &e)
6600 // This catches unknown crap in directory
6606 void ServerMap::saveBlock(MapBlock *block)
6608 DSTACK(__FUNCTION_NAME);
6610 Dummy blocks are not written
6612 if(block->isDummy())
6614 /*v3s16 p = block->getPos();
6615 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
6616 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
6620 // Format used for writing
6621 u8 version = SER_FMT_VER_HIGHEST;
6623 v3s16 p3d = block->getPos();
6624 v2s16 p2d(p3d.X, p3d.Z);
6625 createDir(m_savedir);
6626 createDir(m_savedir+"/sectors");
6627 std::string dir = getSectorDir(p2d);
6630 // Block file is map/sectors/xxxxxxxx/xxxx
6632 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
6633 std::string fullpath = dir + "/" + cc;
6634 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
6635 if(o.good() == false)
6636 throw FileNotGoodException("Cannot open block data");
6639 [0] u8 serialization version
6642 o.write((char*)&version, 1);
6644 block->serialize(o, version);
6647 Versions up from 9 have block objects.
6651 block->serializeObjects(o, version);
6654 // We just wrote it to the disk
6655 block->resetChangedFlag();
6658 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
6660 DSTACK(__FUNCTION_NAME);
6664 // Block file is map/sectors/xxxxxxxx/xxxx
6665 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
6666 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
6667 if(is.good() == false)
6668 throw FileNotGoodException("Cannot open block file");
6670 v3s16 p3d = getBlockPos(sectordir, blockfile);
6671 v2s16 p2d(p3d.X, p3d.Z);
6673 assert(sector->getPos() == p2d);
6675 u8 version = SER_FMT_VER_INVALID;
6676 is.read((char*)&version, 1);
6679 throw SerializationError("ServerMap::loadBlock(): Failed"
6680 " to read MapBlock version");
6682 /*u32 block_size = MapBlock::serializedLength(version);
6683 SharedBuffer<u8> data(block_size);
6684 is.read((char*)*data, block_size);*/
6686 // This will always return a sector because we're the server
6687 //MapSector *sector = emergeSector(p2d);
6689 MapBlock *block = NULL;
6690 bool created_new = false;
6692 block = sector->getBlockNoCreate(p3d.Y);
6694 catch(InvalidPositionException &e)
6696 block = sector->createBlankBlockNoInsert(p3d.Y);
6700 // deserialize block data
6701 block->deSerialize(is, version);
6704 Versions up from 9 have block objects.
6708 block->updateObjects(is, version, NULL, 0);
6712 sector->insertBlock(block);
6715 Convert old formats to new and save
6718 // Save old format blocks in new format
6719 if(version < SER_FMT_VER_HIGHEST)
6724 // We just loaded it from the disk, so it's up-to-date.
6725 block->resetChangedFlag();
6728 catch(SerializationError &e)
6730 dstream<<"WARNING: Invalid block data on disk "
6731 "(SerializationError). Ignoring. "
6732 "A new one will be generated."
6737 void ServerMap::PrintInfo(std::ostream &out)
6748 ClientMap::ClientMap(
6750 MapDrawControl &control,
6751 scene::ISceneNode* parent,
6752 scene::ISceneManager* mgr,
6756 scene::ISceneNode(parent, mgr, id),
6759 m_camera_position(0,0,0),
6760 m_camera_direction(0,0,1)
6762 m_camera_mutex.Init();
6763 assert(m_camera_mutex.IsInitialized());
6765 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
6766 BS*1000000,BS*1000000,BS*1000000);
6769 ClientMap::~ClientMap()
6771 /*JMutexAutoLock lock(mesh_mutex);
6780 MapSector * ClientMap::emergeSector(v2s16 p2d)
6782 DSTACK(__FUNCTION_NAME);
6783 // Check that it doesn't exist already
6785 return getSectorNoGenerate(p2d);
6787 catch(InvalidPositionException &e)
6792 ClientMapSector *sector = new ClientMapSector(this, p2d);
6795 JMutexAutoLock lock(m_sector_mutex);
6796 m_sectors.insert(p2d, sector);
6802 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
6804 DSTACK(__FUNCTION_NAME);
6805 ClientMapSector *sector = NULL;
6807 JMutexAutoLock lock(m_sector_mutex);
6809 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
6813 sector = (ClientMapSector*)n->getValue();
6814 assert(sector->getId() == MAPSECTOR_CLIENT);
6818 sector = new ClientMapSector(this, p2d);
6820 JMutexAutoLock lock(m_sector_mutex);
6821 m_sectors.insert(p2d, sector);
6825 sector->deSerialize(is);
6828 void ClientMap::OnRegisterSceneNode()
6832 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
6833 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
6836 ISceneNode::OnRegisterSceneNode();
6839 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
6841 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
6842 DSTACK(__FUNCTION_NAME);
6844 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
6847 Get time for measuring timeout.
6849 Measuring time is very useful for long delays when the
6850 machine is swapping a lot.
6852 int time1 = time(0);
6854 u32 daynight_ratio = m_client->getDayNightRatio();
6856 m_camera_mutex.Lock();
6857 v3f camera_position = m_camera_position;
6858 v3f camera_direction = m_camera_direction;
6859 m_camera_mutex.Unlock();
6862 Get all blocks and draw all visible ones
6865 v3s16 cam_pos_nodes(
6866 camera_position.X / BS,
6867 camera_position.Y / BS,
6868 camera_position.Z / BS);
6870 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
6872 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
6873 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
6875 // Take a fair amount as we will be dropping more out later
6877 p_nodes_min.X / MAP_BLOCKSIZE - 1,
6878 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
6879 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
6881 p_nodes_max.X / MAP_BLOCKSIZE + 1,
6882 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
6883 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
6885 u32 vertex_count = 0;
6887 // For limiting number of mesh updates per frame
6888 u32 mesh_update_count = 0;
6890 u32 blocks_would_have_drawn = 0;
6891 u32 blocks_drawn = 0;
6893 //NOTE: The sectors map should be locked but we're not doing it
6894 // because it'd cause too much delays
6896 int timecheck_counter = 0;
6897 core::map<v2s16, MapSector*>::Iterator si;
6898 si = m_sectors.getIterator();
6899 for(; si.atEnd() == false; si++)
6902 timecheck_counter++;
6903 if(timecheck_counter > 50)
6905 timecheck_counter = 0;
6906 int time2 = time(0);
6907 if(time2 > time1 + 4)
6909 dstream<<"ClientMap::renderMap(): "
6910 "Rendering takes ages, returning."
6917 MapSector *sector = si.getNode()->getValue();
6918 v2s16 sp = sector->getPos();
6920 if(m_control.range_all == false)
6922 if(sp.X < p_blocks_min.X
6923 || sp.X > p_blocks_max.X
6924 || sp.Y < p_blocks_min.Z
6925 || sp.Y > p_blocks_max.Z)
6929 core::list< MapBlock * > sectorblocks;
6930 sector->getBlocks(sectorblocks);
6936 core::list< MapBlock * >::Iterator i;
6937 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6939 MapBlock *block = *i;
6942 Compare block position to camera position, skip
6943 if not seen on display
6946 float range = 100000 * BS;
6947 if(m_control.range_all == false)
6948 range = m_control.wanted_range * BS;
6951 if(isBlockInSight(block->getPos(), camera_position,
6952 camera_direction, range, &d) == false)
6958 /*if(m_control.range_all == false &&
6959 d - 0.5*BS*MAP_BLOCKSIZE > range)
6964 Update expired mesh (used for day/night change)
6967 bool mesh_expired = false;
6970 JMutexAutoLock lock(block->mesh_mutex);
6972 mesh_expired = block->getMeshExpired();
6974 // Mesh has not been expired and there is no mesh:
6975 // block has no content
6976 if(block->mesh == NULL && mesh_expired == false)
6980 f32 faraway = BS*50;
6981 //f32 faraway = m_control.wanted_range * BS;
6984 This has to be done with the mesh_mutex unlocked
6986 // Pretty random but this should work somewhat nicely
6987 if(mesh_expired && (
6988 (mesh_update_count < 3
6989 && (d < faraway || mesh_update_count < 2)
6992 (m_control.range_all && mesh_update_count < 20)
6995 /*if(mesh_expired && mesh_update_count < 6
6996 && (d < faraway || mesh_update_count < 3))*/
6998 mesh_update_count++;
7000 // Mesh has been expired: generate new mesh
7001 //block->updateMeshes(daynight_i);
7002 block->updateMesh(daynight_ratio);
7004 mesh_expired = false;
7008 Don't draw an expired mesh that is far away
7010 /*if(mesh_expired && d >= faraway)
7013 // Instead, delete it
7014 JMutexAutoLock lock(block->mesh_mutex);
7017 block->mesh->drop();
7020 // And continue to next block
7025 Draw the faces of the block
7028 JMutexAutoLock lock(block->mesh_mutex);
7030 scene::SMesh *mesh = block->mesh;
7035 blocks_would_have_drawn++;
7036 if(blocks_drawn >= m_control.wanted_max_blocks
7037 && m_control.range_all == false
7038 && d > m_control.wanted_min_range * BS)
7042 u32 c = mesh->getMeshBufferCount();
7044 for(u32 i=0; i<c; i++)
7046 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
7047 const video::SMaterial& material = buf->getMaterial();
7048 video::IMaterialRenderer* rnd =
7049 driver->getMaterialRenderer(material.MaterialType);
7050 bool transparent = (rnd && rnd->isTransparent());
7051 // Render transparent on transparent pass and likewise.
7052 if(transparent == is_transparent_pass)
7055 This *shouldn't* hurt too much because Irrlicht
7056 doesn't change opengl textures if the old
7057 material is set again.
7059 driver->setMaterial(buf->getMaterial());
7060 driver->drawMeshBuffer(buf);
7061 vertex_count += buf->getVertexCount();
7065 } // foreach sectorblocks
7068 m_control.blocks_drawn = blocks_drawn;
7069 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
7071 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
7072 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
7075 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
7076 core::map<v3s16, MapBlock*> *affected_blocks)
7078 bool changed = false;
7080 Add it to all blocks touching it
7083 v3s16(0,0,0), // this
7084 v3s16(0,0,1), // back
7085 v3s16(0,1,0), // top
7086 v3s16(1,0,0), // right
7087 v3s16(0,0,-1), // front
7088 v3s16(0,-1,0), // bottom
7089 v3s16(-1,0,0), // left
7091 for(u16 i=0; i<7; i++)
7093 v3s16 p2 = p + dirs[i];
7094 // Block position of neighbor (or requested) node
7095 v3s16 blockpos = getNodeBlockPos(p2);
7096 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
7097 if(blockref == NULL)
7099 // Relative position of requested node
7100 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
7101 if(blockref->setTempMod(relpos, mod))
7106 if(changed && affected_blocks!=NULL)
7108 for(u16 i=0; i<7; i++)
7110 v3s16 p2 = p + dirs[i];
7111 // Block position of neighbor (or requested) node
7112 v3s16 blockpos = getNodeBlockPos(p2);
7113 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
7114 if(blockref == NULL)
7116 affected_blocks->insert(blockpos, blockref);
7122 bool ClientMap::clearTempMod(v3s16 p,
7123 core::map<v3s16, MapBlock*> *affected_blocks)
7125 bool changed = false;
7127 v3s16(0,0,0), // this
7128 v3s16(0,0,1), // back
7129 v3s16(0,1,0), // top
7130 v3s16(1,0,0), // right
7131 v3s16(0,0,-1), // front
7132 v3s16(0,-1,0), // bottom
7133 v3s16(-1,0,0), // left
7135 for(u16 i=0; i<7; i++)
7137 v3s16 p2 = p + dirs[i];
7138 // Block position of neighbor (or requested) node
7139 v3s16 blockpos = getNodeBlockPos(p2);
7140 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
7141 if(blockref == NULL)
7143 // Relative position of requested node
7144 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
7145 if(blockref->clearTempMod(relpos))
7150 if(changed && affected_blocks!=NULL)
7152 for(u16 i=0; i<7; i++)
7154 v3s16 p2 = p + dirs[i];
7155 // Block position of neighbor (or requested) node
7156 v3s16 blockpos = getNodeBlockPos(p2);
7157 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
7158 if(blockref == NULL)
7160 affected_blocks->insert(blockpos, blockref);
7166 void ClientMap::expireMeshes(bool only_daynight_diffed)
7168 TimeTaker timer("expireMeshes()");
7170 core::map<v2s16, MapSector*>::Iterator si;
7171 si = m_sectors.getIterator();
7172 for(; si.atEnd() == false; si++)
7174 MapSector *sector = si.getNode()->getValue();
7176 core::list< MapBlock * > sectorblocks;
7177 sector->getBlocks(sectorblocks);
7179 core::list< MapBlock * >::Iterator i;
7180 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
7182 MapBlock *block = *i;
7184 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
7190 JMutexAutoLock lock(block->mesh_mutex);
7191 if(block->mesh != NULL)
7193 /*block->mesh->drop();
7194 block->mesh = NULL;*/
7195 block->setMeshExpired(true);
7202 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
7205 v3s16 p = blockpos + v3s16(0,0,0);
7206 MapBlock *b = getBlockNoCreate(p);
7207 b->updateMesh(daynight_ratio);
7209 catch(InvalidPositionException &e){}
7212 v3s16 p = blockpos + v3s16(-1,0,0);
7213 MapBlock *b = getBlockNoCreate(p);
7214 b->updateMesh(daynight_ratio);
7216 catch(InvalidPositionException &e){}
7218 v3s16 p = blockpos + v3s16(0,-1,0);
7219 MapBlock *b = getBlockNoCreate(p);
7220 b->updateMesh(daynight_ratio);
7222 catch(InvalidPositionException &e){}
7224 v3s16 p = blockpos + v3s16(0,0,-1);
7225 MapBlock *b = getBlockNoCreate(p);
7226 b->updateMesh(daynight_ratio);
7228 catch(InvalidPositionException &e){}
7232 Update mesh of block in which the node is, and if the node is at the
7233 leading edge, update the appropriate leading blocks too.
7235 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
7243 v3s16 blockposes[4];
7244 for(u32 i=0; i<4; i++)
7246 v3s16 np = nodepos + dirs[i];
7247 blockposes[i] = getNodeBlockPos(np);
7248 // Don't update mesh of block if it has been done already
7249 bool already_updated = false;
7250 for(u32 j=0; j<i; j++)
7252 if(blockposes[j] == blockposes[i])
7254 already_updated = true;
7261 MapBlock *b = getBlockNoCreate(blockposes[i]);
7262 b->updateMesh(daynight_ratio);
7266 void ClientMap::PrintInfo(std::ostream &out)
7277 MapVoxelManipulator::MapVoxelManipulator(Map *map)
7282 MapVoxelManipulator::~MapVoxelManipulator()
7284 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
7288 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
7290 TimeTaker timer1("emerge", &emerge_time);
7292 // Units of these are MapBlocks
7293 v3s16 p_min = getNodeBlockPos(a.MinEdge);
7294 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
7296 VoxelArea block_area_nodes
7297 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
7299 addArea(block_area_nodes);
7301 for(s32 z=p_min.Z; z<=p_max.Z; z++)
7302 for(s32 y=p_min.Y; y<=p_max.Y; y++)
7303 for(s32 x=p_min.X; x<=p_max.X; x++)
7306 core::map<v3s16, bool>::Node *n;
7307 n = m_loaded_blocks.find(p);
7311 bool block_data_inexistent = false;
7314 TimeTaker timer1("emerge load", &emerge_load_time);
7316 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
7317 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
7320 dstream<<std::endl;*/
7322 MapBlock *block = m_map->getBlockNoCreate(p);
7323 if(block->isDummy())
7324 block_data_inexistent = true;
7326 block->copyTo(*this);
7328 catch(InvalidPositionException &e)
7330 block_data_inexistent = true;
7333 if(block_data_inexistent)
7335 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
7336 // Fill with VOXELFLAG_INEXISTENT
7337 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
7338 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
7340 s32 i = m_area.index(a.MinEdge.X,y,z);
7341 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
7345 m_loaded_blocks.insert(p, !block_data_inexistent);
7348 //dstream<<"emerge done"<<std::endl;
7352 SUGG: Add an option to only update eg. water and air nodes.
7353 This will make it interfere less with important stuff if
7356 void MapVoxelManipulator::blitBack
7357 (core::map<v3s16, MapBlock*> & modified_blocks)
7359 if(m_area.getExtent() == v3s16(0,0,0))
7362 //TimeTaker timer1("blitBack");
7364 /*dstream<<"blitBack(): m_loaded_blocks.size()="
7365 <<m_loaded_blocks.size()<<std::endl;*/
7368 Initialize block cache
7370 v3s16 blockpos_last;
7371 MapBlock *block = NULL;
7372 bool block_checked_in_modified = false;
7374 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
7375 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
7376 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
7380 u8 f = m_flags[m_area.index(p)];
7381 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
7384 MapNode &n = m_data[m_area.index(p)];
7386 v3s16 blockpos = getNodeBlockPos(p);
7391 if(block == NULL || blockpos != blockpos_last){
7392 block = m_map->getBlockNoCreate(blockpos);
7393 blockpos_last = blockpos;
7394 block_checked_in_modified = false;
7397 // Calculate relative position in block
7398 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
7400 // Don't continue if nothing has changed here
7401 if(block->getNode(relpos) == n)
7404 //m_map->setNode(m_area.MinEdge + p, n);
7405 block->setNode(relpos, n);
7408 Make sure block is in modified_blocks
7410 if(block_checked_in_modified == false)
7412 modified_blocks[blockpos] = block;
7413 block_checked_in_modified = true;
7416 catch(InvalidPositionException &e)
7422 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
7423 MapVoxelManipulator(map)
7427 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
7431 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
7433 // Just create the area so that it can be pointed to
7434 VoxelManipulator::emerge(a, caller_id);
7437 void ManualMapVoxelManipulator::initialEmerge(
7438 v3s16 blockpos_min, v3s16 blockpos_max)
7440 TimeTaker timer1("initialEmerge", &emerge_time);
7442 // Units of these are MapBlocks
7443 v3s16 p_min = blockpos_min;
7444 v3s16 p_max = blockpos_max;
7446 VoxelArea block_area_nodes
7447 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
7449 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
7452 dstream<<"initialEmerge: area: ";
7453 block_area_nodes.print(dstream);
7454 dstream<<" ("<<size_MB<<"MB)";
7458 addArea(block_area_nodes);
7460 for(s32 z=p_min.Z; z<=p_max.Z; z++)
7461 for(s32 y=p_min.Y; y<=p_max.Y; y++)
7462 for(s32 x=p_min.X; x<=p_max.X; x++)
7465 core::map<v3s16, bool>::Node *n;
7466 n = m_loaded_blocks.find(p);
7470 bool block_data_inexistent = false;
7473 TimeTaker timer1("emerge load", &emerge_load_time);
7475 MapBlock *block = m_map->getBlockNoCreate(p);
7476 if(block->isDummy())
7477 block_data_inexistent = true;
7479 block->copyTo(*this);
7481 catch(InvalidPositionException &e)
7483 block_data_inexistent = true;
7486 if(block_data_inexistent)
7489 Mark area inexistent
7491 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
7492 // Fill with VOXELFLAG_INEXISTENT
7493 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
7494 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
7496 s32 i = m_area.index(a.MinEdge.X,y,z);
7497 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
7501 m_loaded_blocks.insert(p, !block_data_inexistent);
7505 void ManualMapVoxelManipulator::blitBackAll(
7506 core::map<v3s16, MapBlock*> * modified_blocks)
7508 if(m_area.getExtent() == v3s16(0,0,0))
7512 Copy data of all blocks
7514 for(core::map<v3s16, bool>::Iterator
7515 i = m_loaded_blocks.getIterator();
7516 i.atEnd() == false; i++)
7518 bool existed = i.getNode()->getValue();
7519 if(existed == false)
7521 v3s16 p = i.getNode()->getKey();
7522 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
7525 dstream<<"WARNING: "<<__FUNCTION_NAME
7526 <<": got NULL block "
7527 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
7532 block->copyFrom(*this);
7535 modified_blocks->insert(p, block);