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 void Map::unspreadLight(enum LightBank bank,
188 core::map<v3s16, u8> & from_nodes,
189 core::map<v3s16, bool> & light_sources,
190 core::map<v3s16, MapBlock*> & modified_blocks)
193 v3s16(0,0,1), // back
195 v3s16(1,0,0), // right
196 v3s16(0,0,-1), // front
197 v3s16(0,-1,0), // bottom
198 v3s16(-1,0,0), // left
201 if(from_nodes.size() == 0)
204 u32 blockchangecount = 0;
206 core::map<v3s16, u8> unlighted_nodes;
207 core::map<v3s16, u8>::Iterator j;
208 j = from_nodes.getIterator();
211 Initialize block cache
214 MapBlock *block = NULL;
215 // Cache this a bit, too
216 bool block_checked_in_modified = false;
218 for(; j.atEnd() == false; j++)
220 v3s16 pos = j.getNode()->getKey();
221 v3s16 blockpos = getNodeBlockPos(pos);
223 // Only fetch a new block if the block position has changed
225 if(block == NULL || blockpos != blockpos_last){
226 block = getBlockNoCreate(blockpos);
227 blockpos_last = blockpos;
229 block_checked_in_modified = false;
233 catch(InvalidPositionException &e)
241 // Calculate relative position in block
242 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
244 // Get node straight from the block
245 MapNode n = block->getNode(relpos);
247 u8 oldlight = j.getNode()->getValue();
249 // Loop through 6 neighbors
250 for(u16 i=0; i<6; i++)
252 // Get the position of the neighbor node
253 v3s16 n2pos = pos + dirs[i];
255 // Get the block where the node is located
256 v3s16 blockpos = getNodeBlockPos(n2pos);
260 // Only fetch a new block if the block position has changed
262 if(block == NULL || blockpos != blockpos_last){
263 block = getBlockNoCreate(blockpos);
264 blockpos_last = blockpos;
266 block_checked_in_modified = false;
270 catch(InvalidPositionException &e)
275 // Calculate relative position in block
276 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
277 // Get node straight from the block
278 MapNode n2 = block->getNode(relpos);
280 bool changed = false;
282 //TODO: Optimize output by optimizing light_sources?
285 If the neighbor is dimmer than what was specified
286 as oldlight (the light of the previous node)
288 if(n2.getLight(bank) < oldlight)
291 And the neighbor is transparent and it has some light
293 if(n2.light_propagates() && n2.getLight(bank) != 0)
296 Set light to 0 and add to queue
299 u8 current_light = n2.getLight(bank);
300 n2.setLight(bank, 0);
301 block->setNode(relpos, n2);
303 unlighted_nodes.insert(n2pos, current_light);
307 Remove from light_sources if it is there
308 NOTE: This doesn't happen nearly at all
310 /*if(light_sources.find(n2pos))
312 std::cout<<"Removed from light_sources"<<std::endl;
313 light_sources.remove(n2pos);
318 if(light_sources.find(n2pos) != NULL)
319 light_sources.remove(n2pos);*/
322 light_sources.insert(n2pos, true);
325 // Add to modified_blocks
326 if(changed == true && block_checked_in_modified == false)
328 // If the block is not found in modified_blocks, add.
329 if(modified_blocks.find(blockpos) == NULL)
331 modified_blocks.insert(blockpos, block);
333 block_checked_in_modified = true;
336 catch(InvalidPositionException &e)
343 /*dstream<<"unspreadLight(): Changed block "
344 <<blockchangecount<<" times"
345 <<" for "<<from_nodes.size()<<" nodes"
348 if(unlighted_nodes.size() > 0)
349 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
353 A single-node wrapper of the above
355 void Map::unLightNeighbors(enum LightBank bank,
356 v3s16 pos, u8 lightwas,
357 core::map<v3s16, bool> & light_sources,
358 core::map<v3s16, MapBlock*> & modified_blocks)
360 core::map<v3s16, u8> from_nodes;
361 from_nodes.insert(pos, lightwas);
363 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
367 Lights neighbors of from_nodes, collects all them and then
370 void Map::spreadLight(enum LightBank bank,
371 core::map<v3s16, bool> & from_nodes,
372 core::map<v3s16, MapBlock*> & modified_blocks)
374 const v3s16 dirs[6] = {
375 v3s16(0,0,1), // back
377 v3s16(1,0,0), // right
378 v3s16(0,0,-1), // front
379 v3s16(0,-1,0), // bottom
380 v3s16(-1,0,0), // left
383 if(from_nodes.size() == 0)
386 u32 blockchangecount = 0;
388 core::map<v3s16, bool> lighted_nodes;
389 core::map<v3s16, bool>::Iterator j;
390 j = from_nodes.getIterator();
393 Initialize block cache
396 MapBlock *block = NULL;
397 // Cache this a bit, too
398 bool block_checked_in_modified = false;
400 for(; j.atEnd() == false; j++)
401 //for(; j != from_nodes.end(); j++)
403 v3s16 pos = j.getNode()->getKey();
405 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
406 v3s16 blockpos = getNodeBlockPos(pos);
408 // Only fetch a new block if the block position has changed
410 if(block == NULL || blockpos != blockpos_last){
411 block = getBlockNoCreate(blockpos);
412 blockpos_last = blockpos;
414 block_checked_in_modified = false;
418 catch(InvalidPositionException &e)
426 // Calculate relative position in block
427 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
429 // Get node straight from the block
430 MapNode n = block->getNode(relpos);
432 u8 oldlight = n.getLight(bank);
433 u8 newlight = diminish_light(oldlight);
435 // Loop through 6 neighbors
436 for(u16 i=0; i<6; i++){
437 // Get the position of the neighbor node
438 v3s16 n2pos = pos + dirs[i];
440 // Get the block where the node is located
441 v3s16 blockpos = getNodeBlockPos(n2pos);
445 // Only fetch a new block if the block position has changed
447 if(block == NULL || blockpos != blockpos_last){
448 block = getBlockNoCreate(blockpos);
449 blockpos_last = blockpos;
451 block_checked_in_modified = false;
455 catch(InvalidPositionException &e)
460 // Calculate relative position in block
461 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
462 // Get node straight from the block
463 MapNode n2 = block->getNode(relpos);
465 bool changed = false;
467 If the neighbor is brighter than the current node,
468 add to list (it will light up this node on its turn)
470 if(n2.getLight(bank) > undiminish_light(oldlight))
472 lighted_nodes.insert(n2pos, true);
473 //lighted_nodes.push_back(n2pos);
477 If the neighbor is dimmer than how much light this node
478 would spread on it, add to list
480 if(n2.getLight(bank) < newlight)
482 if(n2.light_propagates())
484 n2.setLight(bank, newlight);
485 block->setNode(relpos, n2);
486 lighted_nodes.insert(n2pos, true);
487 //lighted_nodes.push_back(n2pos);
492 // Add to modified_blocks
493 if(changed == true && block_checked_in_modified == false)
495 // If the block is not found in modified_blocks, add.
496 if(modified_blocks.find(blockpos) == NULL)
498 modified_blocks.insert(blockpos, block);
500 block_checked_in_modified = true;
503 catch(InvalidPositionException &e)
510 /*dstream<<"spreadLight(): Changed block "
511 <<blockchangecount<<" times"
512 <<" for "<<from_nodes.size()<<" nodes"
515 if(lighted_nodes.size() > 0)
516 spreadLight(bank, lighted_nodes, modified_blocks);
520 A single-node source variation of the above.
522 void Map::lightNeighbors(enum LightBank bank,
524 core::map<v3s16, MapBlock*> & modified_blocks)
526 core::map<v3s16, bool> from_nodes;
527 from_nodes.insert(pos, true);
528 spreadLight(bank, from_nodes, modified_blocks);
531 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
534 v3s16(0,0,1), // back
536 v3s16(1,0,0), // right
537 v3s16(0,0,-1), // front
538 v3s16(0,-1,0), // bottom
539 v3s16(-1,0,0), // left
542 u8 brightest_light = 0;
543 v3s16 brightest_pos(0,0,0);
544 bool found_something = false;
546 // Loop through 6 neighbors
547 for(u16 i=0; i<6; i++){
548 // Get the position of the neighbor node
549 v3s16 n2pos = p + dirs[i];
554 catch(InvalidPositionException &e)
558 if(n2.getLight(bank) > brightest_light || found_something == false){
559 brightest_light = n2.getLight(bank);
560 brightest_pos = n2pos;
561 found_something = true;
565 if(found_something == false)
566 throw InvalidPositionException();
568 return brightest_pos;
572 Propagates sunlight down from a node.
573 Starting point gets sunlight.
575 Returns the lowest y value of where the sunlight went.
577 Mud is turned into grass in where the sunlight stops.
579 s16 Map::propagateSunlight(v3s16 start,
580 core::map<v3s16, MapBlock*> & modified_blocks)
585 v3s16 pos(start.X, y, start.Z);
587 v3s16 blockpos = getNodeBlockPos(pos);
590 block = getBlockNoCreate(blockpos);
592 catch(InvalidPositionException &e)
597 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
598 MapNode n = block->getNode(relpos);
600 if(n.sunlight_propagates())
602 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
603 block->setNode(relpos, n);
605 modified_blocks.insert(blockpos, block);
609 // Turn mud into grass
610 if(n.d == CONTENT_MUD)
613 block->setNode(relpos, n);
614 modified_blocks.insert(blockpos, block);
617 // Sunlight goes no further
624 void Map::updateLighting(enum LightBank bank,
625 core::map<v3s16, MapBlock*> & a_blocks,
626 core::map<v3s16, MapBlock*> & modified_blocks)
628 /*m_dout<<DTIME<<"Map::updateLighting(): "
629 <<a_blocks.size()<<" blocks."<<std::endl;*/
631 //TimeTaker timer("updateLighting");
635 //u32 count_was = modified_blocks.size();
637 core::map<v3s16, MapBlock*> blocks_to_update;
639 core::map<v3s16, bool> light_sources;
641 core::map<v3s16, u8> unlight_from;
643 core::map<v3s16, MapBlock*>::Iterator i;
644 i = a_blocks.getIterator();
645 for(; i.atEnd() == false; i++)
647 MapBlock *block = i.getNode()->getValue();
651 // Don't bother with dummy blocks.
655 v3s16 pos = block->getPos();
656 modified_blocks.insert(pos, block);
658 blocks_to_update.insert(pos, block);
661 Clear all light from block
663 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
664 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
665 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
670 MapNode n = block->getNode(v3s16(x,y,z));
671 u8 oldlight = n.getLight(bank);
673 block->setNode(v3s16(x,y,z), n);
675 // Collect borders for unlighting
676 if(x==0 || x == MAP_BLOCKSIZE-1
677 || y==0 || y == MAP_BLOCKSIZE-1
678 || z==0 || z == MAP_BLOCKSIZE-1)
680 v3s16 p_map = p + v3s16(
683 MAP_BLOCKSIZE*pos.Z);
684 unlight_from.insert(p_map, oldlight);
687 catch(InvalidPositionException &e)
690 This would happen when dealing with a
694 dstream<<"updateLighting(): InvalidPositionException"
699 if(bank == LIGHTBANK_DAY)
701 bool bottom_valid = block->propagateSunlight(light_sources);
703 // If bottom is valid, we're done.
707 else if(bank == LIGHTBANK_NIGHT)
709 // For night lighting, sunlight is not propagated
714 // Invalid lighting bank
718 /*dstream<<"Bottom for sunlight-propagated block ("
719 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
722 // Bottom sunlight is not valid; get the block and loop to it
726 block = getBlockNoCreate(pos);
728 catch(InvalidPositionException &e)
738 TimeTaker timer("unspreadLight");
739 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
744 u32 diff = modified_blocks.size() - count_was;
745 count_was = modified_blocks.size();
746 dstream<<"unspreadLight modified "<<diff<<std::endl;
750 TimeTaker timer("spreadLight");
751 spreadLight(bank, light_sources, modified_blocks);
756 u32 diff = modified_blocks.size() - count_was;
757 count_was = modified_blocks.size();
758 dstream<<"spreadLight modified "<<diff<<std::endl;
763 //MapVoxelManipulator vmanip(this);
765 // Make a manual voxel manipulator and load all the blocks
766 // that touch the requested blocks
767 ManualMapVoxelManipulator vmanip(this);
768 core::map<v3s16, MapBlock*>::Iterator i;
769 i = blocks_to_update.getIterator();
770 for(; i.atEnd() == false; i++)
772 MapBlock *block = i.getNode()->getValue();
773 v3s16 p = block->getPos();
775 // Add all surrounding blocks
776 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
779 Add all surrounding blocks that have up-to-date lighting
780 NOTE: This doesn't quite do the job (not everything
781 appropriate is lighted)
783 /*for(s16 z=-1; z<=1; z++)
784 for(s16 y=-1; y<=1; y++)
785 for(s16 x=-1; x<=1; x++)
788 MapBlock *block = getBlockNoCreateNoEx(p);
793 if(block->getLightingExpired())
795 vmanip.initialEmerge(p, p);
798 // Lighting of block will be updated completely
799 block->setLightingExpired(false);
803 //TimeTaker timer("unSpreadLight");
804 vmanip.unspreadLight(bank, unlight_from, light_sources);
807 //TimeTaker timer("spreadLight");
808 vmanip.spreadLight(bank, light_sources);
811 //TimeTaker timer("blitBack");
812 vmanip.blitBack(modified_blocks);
814 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
818 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
821 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
822 core::map<v3s16, MapBlock*> & modified_blocks)
824 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
825 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
828 Update information about whether day and night light differ
830 for(core::map<v3s16, MapBlock*>::Iterator
831 i = modified_blocks.getIterator();
832 i.atEnd() == false; i++)
834 MapBlock *block = i.getNode()->getValue();
835 block->updateDayNightDiff();
840 This is called after changing a node from transparent to opaque.
841 The lighting value of the node should be left as-is after changing
842 other values. This sets the lighting value to 0.
844 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
845 core::map<v3s16, MapBlock*> &modified_blocks)
848 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
849 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
852 From this node to nodes underneath:
853 If lighting is sunlight (1.0), unlight neighbours and
858 v3s16 toppos = p + v3s16(0,1,0);
859 v3s16 bottompos = p + v3s16(0,-1,0);
861 bool node_under_sunlight = true;
862 core::map<v3s16, bool> light_sources;
865 If there is a node at top and it doesn't have sunlight,
866 there has not been any sunlight going down.
868 Otherwise there probably is.
871 MapNode topnode = getNode(toppos);
873 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
874 node_under_sunlight = false;
876 catch(InvalidPositionException &e)
881 If the new node doesn't propagate sunlight and there is
882 grass below, change it to mud
884 if(content_features(n.d).sunlight_propagates == false)
887 MapNode bottomnode = getNode(bottompos);
889 if(bottomnode.d == CONTENT_GRASS
890 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
892 bottomnode.d = CONTENT_MUD;
893 setNode(bottompos, bottomnode);
896 catch(InvalidPositionException &e)
902 If the new node is mud and it is under sunlight, change it
905 if(n.d == CONTENT_MUD && node_under_sunlight)
911 Remove all light that has come out of this node
914 enum LightBank banks[] =
919 for(s32 i=0; i<2; i++)
921 enum LightBank bank = banks[i];
923 u8 lightwas = getNode(p).getLight(bank);
925 // Add the block of the added node to modified_blocks
926 v3s16 blockpos = getNodeBlockPos(p);
927 MapBlock * block = getBlockNoCreate(blockpos);
928 assert(block != NULL);
929 modified_blocks.insert(blockpos, block);
931 assert(isValidPosition(p));
933 // Unlight neighbours of node.
934 // This means setting light of all consequent dimmer nodes
936 // This also collects the nodes at the border which will spread
937 // light again into this.
938 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
944 Set the node on the map
950 If node is under sunlight, take all sunlighted nodes under
951 it and clear light from them and from where the light has
953 TODO: This could be optimized by mass-unlighting instead
956 if(node_under_sunlight)
960 //m_dout<<DTIME<<"y="<<y<<std::endl;
961 v3s16 n2pos(p.X, y, p.Z);
967 catch(InvalidPositionException &e)
972 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
974 unLightNeighbors(LIGHTBANK_DAY,
975 n2pos, n2.getLight(LIGHTBANK_DAY),
976 light_sources, modified_blocks);
977 n2.setLight(LIGHTBANK_DAY, 0);
985 for(s32 i=0; i<2; i++)
987 enum LightBank bank = banks[i];
990 Spread light from all nodes that might be capable of doing so
992 spreadLight(bank, light_sources, modified_blocks);
996 Update information about whether day and night light differ
998 for(core::map<v3s16, MapBlock*>::Iterator
999 i = modified_blocks.getIterator();
1000 i.atEnd() == false; i++)
1002 MapBlock *block = i.getNode()->getValue();
1003 block->updateDayNightDiff();
1007 Add neighboring liquid nodes and the node itself if it is
1008 liquid (=water node was added) to transform queue.
1011 v3s16(0,0,0), // self
1012 v3s16(0,0,1), // back
1013 v3s16(0,1,0), // top
1014 v3s16(1,0,0), // right
1015 v3s16(0,0,-1), // front
1016 v3s16(0,-1,0), // bottom
1017 v3s16(-1,0,0), // left
1019 for(u16 i=0; i<7; i++)
1024 v3s16 p2 = p + dirs[i];
1026 MapNode n2 = getNode(p2);
1027 if(content_liquid(n2.d))
1029 m_transforming_liquid.push_back(p2);
1032 }catch(InvalidPositionException &e)
1040 void Map::removeNodeAndUpdate(v3s16 p,
1041 core::map<v3s16, MapBlock*> &modified_blocks)
1043 /*PrintInfo(m_dout);
1044 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1045 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1047 bool node_under_sunlight = true;
1049 v3s16 toppos = p + v3s16(0,1,0);
1051 // Node will be replaced with this
1052 u8 replace_material = CONTENT_AIR;
1055 If there is a node at top and it doesn't have sunlight,
1056 there will be no sunlight going down.
1059 MapNode topnode = getNode(toppos);
1061 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1062 node_under_sunlight = false;
1064 catch(InvalidPositionException &e)
1068 core::map<v3s16, bool> light_sources;
1070 enum LightBank banks[] =
1075 for(s32 i=0; i<2; i++)
1077 enum LightBank bank = banks[i];
1080 Unlight neighbors (in case the node is a light source)
1082 unLightNeighbors(bank, p,
1083 getNode(p).getLight(bank),
1084 light_sources, modified_blocks);
1089 This also clears the lighting.
1093 n.d = replace_material;
1096 for(s32 i=0; i<2; i++)
1098 enum LightBank bank = banks[i];
1101 Recalculate lighting
1103 spreadLight(bank, light_sources, modified_blocks);
1106 // Add the block of the removed node to modified_blocks
1107 v3s16 blockpos = getNodeBlockPos(p);
1108 MapBlock * block = getBlockNoCreate(blockpos);
1109 assert(block != NULL);
1110 modified_blocks.insert(blockpos, block);
1113 If the removed node was under sunlight, propagate the
1114 sunlight down from it and then light all neighbors
1115 of the propagated blocks.
1117 if(node_under_sunlight)
1119 s16 ybottom = propagateSunlight(p, modified_blocks);
1120 /*m_dout<<DTIME<<"Node was under sunlight. "
1121 "Propagating sunlight";
1122 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1124 for(; y >= ybottom; y--)
1126 v3s16 p2(p.X, y, p.Z);
1127 /*m_dout<<DTIME<<"lighting neighbors of node ("
1128 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1130 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1135 // Set the lighting of this node to 0
1136 // TODO: Is this needed? Lighting is cleared up there already.
1138 MapNode n = getNode(p);
1139 n.setLight(LIGHTBANK_DAY, 0);
1142 catch(InvalidPositionException &e)
1148 for(s32 i=0; i<2; i++)
1150 enum LightBank bank = banks[i];
1152 // Get the brightest neighbour node and propagate light from it
1153 v3s16 n2p = getBrightestNeighbour(bank, p);
1155 MapNode n2 = getNode(n2p);
1156 lightNeighbors(bank, n2p, modified_blocks);
1158 catch(InvalidPositionException &e)
1164 Update information about whether day and night light differ
1166 for(core::map<v3s16, MapBlock*>::Iterator
1167 i = modified_blocks.getIterator();
1168 i.atEnd() == false; i++)
1170 MapBlock *block = i.getNode()->getValue();
1171 block->updateDayNightDiff();
1175 Add neighboring liquid nodes to transform queue.
1178 v3s16(0,0,1), // back
1179 v3s16(0,1,0), // top
1180 v3s16(1,0,0), // right
1181 v3s16(0,0,-1), // front
1182 v3s16(0,-1,0), // bottom
1183 v3s16(-1,0,0), // left
1185 for(u16 i=0; i<6; i++)
1190 v3s16 p2 = p + dirs[i];
1192 MapNode n2 = getNode(p2);
1193 if(content_liquid(n2.d))
1195 m_transforming_liquid.push_back(p2);
1198 }catch(InvalidPositionException &e)
1204 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1207 event.type = MEET_ADDNODE;
1211 bool succeeded = true;
1213 core::map<v3s16, MapBlock*> modified_blocks;
1214 addNodeAndUpdate(p, n, modified_blocks);
1216 // Copy modified_blocks to event
1217 for(core::map<v3s16, MapBlock*>::Iterator
1218 i = modified_blocks.getIterator();
1219 i.atEnd()==false; i++)
1221 event.modified_blocks.insert(i.getNode()->getKey(), false);
1224 catch(InvalidPositionException &e){
1228 dispatchEvent(&event);
1233 bool Map::removeNodeWithEvent(v3s16 p)
1236 event.type = MEET_REMOVENODE;
1239 bool succeeded = true;
1241 core::map<v3s16, MapBlock*> modified_blocks;
1242 removeNodeAndUpdate(p, modified_blocks);
1244 // Copy modified_blocks to event
1245 for(core::map<v3s16, MapBlock*>::Iterator
1246 i = modified_blocks.getIterator();
1247 i.atEnd()==false; i++)
1249 event.modified_blocks.insert(i.getNode()->getKey(), false);
1252 catch(InvalidPositionException &e){
1256 dispatchEvent(&event);
1261 bool Map::dayNightDiffed(v3s16 blockpos)
1264 v3s16 p = blockpos + v3s16(0,0,0);
1265 MapBlock *b = getBlockNoCreate(p);
1266 if(b->dayNightDiffed())
1269 catch(InvalidPositionException &e){}
1272 v3s16 p = blockpos + v3s16(-1,0,0);
1273 MapBlock *b = getBlockNoCreate(p);
1274 if(b->dayNightDiffed())
1277 catch(InvalidPositionException &e){}
1279 v3s16 p = blockpos + v3s16(0,-1,0);
1280 MapBlock *b = getBlockNoCreate(p);
1281 if(b->dayNightDiffed())
1284 catch(InvalidPositionException &e){}
1286 v3s16 p = blockpos + v3s16(0,0,-1);
1287 MapBlock *b = getBlockNoCreate(p);
1288 if(b->dayNightDiffed())
1291 catch(InvalidPositionException &e){}
1294 v3s16 p = blockpos + v3s16(1,0,0);
1295 MapBlock *b = getBlockNoCreate(p);
1296 if(b->dayNightDiffed())
1299 catch(InvalidPositionException &e){}
1301 v3s16 p = blockpos + v3s16(0,1,0);
1302 MapBlock *b = getBlockNoCreate(p);
1303 if(b->dayNightDiffed())
1306 catch(InvalidPositionException &e){}
1308 v3s16 p = blockpos + v3s16(0,0,1);
1309 MapBlock *b = getBlockNoCreate(p);
1310 if(b->dayNightDiffed())
1313 catch(InvalidPositionException &e){}
1319 Updates usage timers
1321 void Map::timerUpdate(float dtime)
1323 JMutexAutoLock lock(m_sector_mutex);
1325 core::map<v2s16, MapSector*>::Iterator si;
1327 si = m_sectors.getIterator();
1328 for(; si.atEnd() == false; si++)
1330 MapSector *sector = si.getNode()->getValue();
1331 sector->usage_timer += dtime;
1335 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1338 Wait for caches to be removed before continuing.
1340 This disables the existence of caches while locked
1342 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1344 core::list<v2s16>::Iterator j;
1345 for(j=list.begin(); j!=list.end(); j++)
1347 MapSector *sector = m_sectors[*j];
1350 sector->deleteBlocks();
1355 If sector is in sector cache, remove it from there
1357 if(m_sector_cache == sector)
1359 m_sector_cache = NULL;
1362 Remove from map and delete
1364 m_sectors.remove(*j);
1370 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1371 core::list<v3s16> *deleted_blocks)
1373 JMutexAutoLock lock(m_sector_mutex);
1375 core::list<v2s16> sector_deletion_queue;
1376 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1377 for(; i.atEnd() == false; i++)
1379 MapSector *sector = i.getNode()->getValue();
1381 Delete sector from memory if it hasn't been used in a long time
1383 if(sector->usage_timer > timeout)
1385 sector_deletion_queue.push_back(i.getNode()->getKey());
1387 if(deleted_blocks != NULL)
1389 // Collect positions of blocks of sector
1390 MapSector *sector = i.getNode()->getValue();
1391 core::list<MapBlock*> blocks;
1392 sector->getBlocks(blocks);
1393 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1394 i != blocks.end(); i++)
1396 deleted_blocks->push_back((*i)->getPos());
1401 deleteSectors(sector_deletion_queue, only_blocks);
1402 return sector_deletion_queue.getSize();
1405 void Map::PrintInfo(std::ostream &out)
1410 #define WATER_DROP_BOOST 4
1412 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1414 DSTACK(__FUNCTION_NAME);
1415 //TimeTaker timer("transformLiquids()");
1418 u32 initial_size = m_transforming_liquid.size();
1420 /*if(initial_size != 0)
1421 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1423 while(m_transforming_liquid.size() != 0)
1429 Get a queued transforming liquid node
1431 v3s16 p0 = m_transforming_liquid.pop_front();
1433 MapNode n0 = getNode(p0);
1435 // Don't deal with non-liquids
1436 if(content_liquid(n0.d) == false)
1439 bool is_source = !content_flowing_liquid(n0.d);
1441 u8 liquid_level = 8;
1442 if(is_source == false)
1443 liquid_level = n0.param2 & 0x0f;
1445 // Turn possible source into non-source
1446 u8 nonsource_c = make_liquid_flowing(n0.d);
1449 If not source, check that some node flows into this one
1450 and what is the level of liquid in this one
1452 if(is_source == false)
1454 s8 new_liquid_level_max = -1;
1456 v3s16 dirs_from[5] = {
1457 v3s16(0,1,0), // top
1458 v3s16(0,0,1), // back
1459 v3s16(1,0,0), // right
1460 v3s16(0,0,-1), // front
1461 v3s16(-1,0,0), // left
1463 for(u16 i=0; i<5; i++)
1468 bool from_top = (i==0);
1470 v3s16 p2 = p0 + dirs_from[i];
1471 MapNode n2 = getNode(p2);
1473 if(content_liquid(n2.d))
1475 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1476 // Check that the liquids are the same type
1477 if(n2_nonsource_c != nonsource_c)
1479 dstream<<"WARNING: Not handling: different liquids"
1480 " collide"<<std::endl;
1483 bool n2_is_source = !content_flowing_liquid(n2.d);
1484 s8 n2_liquid_level = 8;
1485 if(n2_is_source == false)
1486 n2_liquid_level = n2.param2 & 0x07;
1488 s8 new_liquid_level = -1;
1491 //new_liquid_level = 7;
1492 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1493 new_liquid_level = 7;
1495 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1497 else if(n2_liquid_level > 0)
1499 new_liquid_level = n2_liquid_level - 1;
1502 if(new_liquid_level > new_liquid_level_max)
1503 new_liquid_level_max = new_liquid_level;
1506 }catch(InvalidPositionException &e)
1512 If liquid level should be something else, update it and
1513 add all the neighboring water nodes to the transform queue.
1515 if(new_liquid_level_max != liquid_level)
1517 if(new_liquid_level_max == -1)
1519 // Remove water alltoghether
1526 n0.param2 = new_liquid_level_max;
1530 // Block has been modified
1532 v3s16 blockpos = getNodeBlockPos(p0);
1533 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1535 modified_blocks.insert(blockpos, block);
1539 Add neighboring non-source liquid nodes to transform queue.
1542 v3s16(0,0,1), // back
1543 v3s16(0,1,0), // top
1544 v3s16(1,0,0), // right
1545 v3s16(0,0,-1), // front
1546 v3s16(0,-1,0), // bottom
1547 v3s16(-1,0,0), // left
1549 for(u16 i=0; i<6; i++)
1554 v3s16 p2 = p0 + dirs[i];
1556 MapNode n2 = getNode(p2);
1557 if(content_flowing_liquid(n2.d))
1559 m_transforming_liquid.push_back(p2);
1562 }catch(InvalidPositionException &e)
1569 // Get a new one from queue if the node has turned into non-water
1570 if(content_liquid(n0.d) == false)
1574 Flow water from this node
1576 v3s16 dirs_to[5] = {
1577 v3s16(0,-1,0), // bottom
1578 v3s16(0,0,1), // back
1579 v3s16(1,0,0), // right
1580 v3s16(0,0,-1), // front
1581 v3s16(-1,0,0), // left
1583 for(u16 i=0; i<5; i++)
1588 bool to_bottom = (i == 0);
1590 // If liquid is at lowest possible height, it's not going
1591 // anywhere except down
1592 if(liquid_level == 0 && to_bottom == false)
1595 u8 liquid_next_level = 0;
1596 // If going to bottom
1599 //liquid_next_level = 7;
1600 if(liquid_level >= 7 - WATER_DROP_BOOST)
1601 liquid_next_level = 7;
1603 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1606 liquid_next_level = liquid_level - 1;
1608 bool n2_changed = false;
1609 bool flowed = false;
1611 v3s16 p2 = p0 + dirs_to[i];
1613 MapNode n2 = getNode(p2);
1614 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1616 if(content_liquid(n2.d))
1618 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1619 // Check that the liquids are the same type
1620 if(n2_nonsource_c != nonsource_c)
1622 dstream<<"WARNING: Not handling: different liquids"
1623 " collide"<<std::endl;
1626 bool n2_is_source = !content_flowing_liquid(n2.d);
1627 u8 n2_liquid_level = 8;
1628 if(n2_is_source == false)
1629 n2_liquid_level = n2.param2 & 0x07;
1638 // Just flow into the source, nothing changes.
1639 // n2_changed is not set because destination didn't change
1644 if(liquid_next_level > liquid_level)
1646 n2.param2 = liquid_next_level;
1654 else if(n2.d == CONTENT_AIR)
1657 n2.param2 = liquid_next_level;
1664 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1668 m_transforming_liquid.push_back(p2);
1670 v3s16 blockpos = getNodeBlockPos(p2);
1671 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1673 modified_blocks.insert(blockpos, block);
1676 // If n2_changed to bottom, don't flow anywhere else
1677 if(to_bottom && flowed && !is_source)
1680 }catch(InvalidPositionException &e)
1686 if(loopcount >= initial_size * 1 || loopcount >= 1000)
1689 }catch(InvalidPositionException &e)
1693 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1700 ServerMap::ServerMap(std::string savedir):
1706 //m_chunksize = 16; // Too slow
1707 //m_chunksize = 8; // Takes a few seconds
1708 m_chunksize = 4; // Too small?
1711 // TODO: Save to and load from a file
1712 m_seed = (((u64)(myrand()%0xffff)<<0)
1713 + ((u64)(myrand()%0xffff)<<16)
1714 + ((u64)(myrand()%0xffff)<<32)
1715 + ((u64)(myrand()%0xffff)<<48));
1718 Experimental and debug stuff
1725 Try to load map; if not found, create a new one.
1728 m_savedir = savedir;
1729 m_map_saving_enabled = false;
1733 // If directory exists, check contents and load if possible
1734 if(fs::PathExists(m_savedir))
1736 // If directory is empty, it is safe to save into it.
1737 if(fs::GetDirListing(m_savedir).size() == 0)
1739 dstream<<DTIME<<"Server: Empty save directory is valid."
1741 m_map_saving_enabled = true;
1745 // Load map metadata (seed, chunksize)
1748 // Load chunk metadata
1751 /*// Load sector (0,0) and throw and exception on fail
1752 if(loadSectorFull(v2s16(0,0)) == false)
1753 throw LoadError("Failed to load sector (0,0)");*/
1755 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1756 "metadata and sector (0,0) from "<<savedir<<
1757 ", assuming valid save directory."
1760 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1761 <<"and chunk metadata from "<<savedir
1762 <<", assuming valid save directory."
1765 m_map_saving_enabled = true;
1766 // Map loaded, not creating new one
1770 // If directory doesn't exist, it is safe to save to it
1772 m_map_saving_enabled = true;
1775 catch(std::exception &e)
1777 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1778 <<", exception: "<<e.what()<<std::endl;
1779 dstream<<"Please remove the map or fix it."<<std::endl;
1780 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1783 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1785 // Create zero sector
1786 emergeSector(v2s16(0,0));
1788 // Initially write whole map
1792 ServerMap::~ServerMap()
1796 if(m_map_saving_enabled)
1799 // Save only changed parts
1801 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1805 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1808 catch(std::exception &e)
1810 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1811 <<", exception: "<<e.what()<<std::endl;
1817 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1818 for(; i.atEnd() == false; i++)
1820 MapChunk *chunk = i.getNode()->getValue();
1826 Some helper functions for the map generator
1829 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1831 v3s16 em = vmanip.m_area.getExtent();
1832 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1833 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1834 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1836 for(y=y_nodes_max; y>=y_nodes_min; y--)
1838 MapNode &n = vmanip.m_data[i];
1839 if(content_walkable(n.d))
1842 vmanip.m_area.add_y(em, i, -1);
1844 if(y >= y_nodes_min)
1850 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1852 v3s16 em = vmanip.m_area.getExtent();
1853 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1854 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1855 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1857 for(y=y_nodes_max; y>=y_nodes_min; y--)
1859 MapNode &n = vmanip.m_data[i];
1860 if(content_walkable(n.d)
1861 && n.d != CONTENT_TREE
1862 && n.d != CONTENT_LEAVES)
1865 vmanip.m_area.add_y(em, i, -1);
1867 if(y >= y_nodes_min)
1873 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1875 MapNode treenode(CONTENT_TREE);
1876 MapNode leavesnode(CONTENT_LEAVES);
1878 vmanip.emerge(VoxelArea(p0-v3s16(2,0,2),p0+v3s16(2,7+2,2)));
1880 s16 trunk_h = myrand_range(4, 7);
1882 for(s16 ii=0; ii<trunk_h; ii++)
1884 if(vmanip.m_area.contains(p1))
1885 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1889 // p1 is now the last piece of the trunk
1892 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1893 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1894 Buffer<u8> leaves_d(leaves_a.getVolume());
1895 for(s32 i=0; i<leaves_a.getVolume(); i++)
1898 // Force leaves at near the end of the trunk
1901 for(s16 z=-d; z<=d; z++)
1902 for(s16 y=-d; y<=d; y++)
1903 for(s16 x=-d; x<=d; x++)
1905 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1909 // Add leaves randomly
1910 for(u32 iii=0; iii<7; iii++)
1915 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1916 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1917 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1920 for(s16 z=0; z<=d; z++)
1921 for(s16 y=0; y<=d; y++)
1922 for(s16 x=0; x<=d; x++)
1924 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1928 // Blit leaves to vmanip
1929 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1930 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1931 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1935 if(vmanip.m_area.contains(p) == false)
1937 u32 vi = vmanip.m_area.index(p);
1938 if(vmanip.m_data[vi].d != CONTENT_AIR)
1940 u32 i = leaves_a.index(x,y,z);
1941 if(leaves_d[i] == 1)
1942 vmanip.m_data[vi] = leavesnode;
1947 Noise functions. Make sure seed is mangled differently in each one.
1950 // Amount of trees per area in nodes
1951 double tree_amount_2d(u64 seed, v2s16 p)
1953 double noise = noise2d_perlin(
1954 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1956 double zeroval = -0.3;
1960 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1963 #define AVERAGE_MUD_AMOUNT 4.0
1965 double get_mud_amount(u64 seed, v2f p)
1967 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
1968 0.5+p.X/200, 0.5+p.Y/200,
1972 bool get_have_sand(u64 seed, v2f p)
1974 double sandnoise = noise2d_perlin(
1975 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
1976 seed+59420, 3, 0.50);
1977 return (sandnoise > -0.15);
1980 // -1->0, 0->1, 1->0
1981 double contour(double v)
1989 // -1->0, -r->1, 0->1, r->1, 1->0
1990 double contour_flat_top(double v, double r)
1995 double rmax = 0.999;
2001 return ((1.0-r)-v) / (1.0-r);
2002 //return easeCurve(((1.0-r)-v) / (1.0-r));
2005 double base_rock_level_2d(u64 seed, v2f p)
2007 // The ground level (return value)
2008 double h = WATER_LEVEL-1.5;
2010 // Raises from 0 when parameter is -1...1
2011 /*double m2 = contour_flat_top(-0.8 + 2.0 * noise2d_perlin(
2012 0.0+(float)p.X/1500., 0.0+(float)p.Y/1500.,
2013 (seed>>32)+34758, 5, 0.55), 0.10);*/
2018 double m1 = 200.0 + 300.0 * noise2d_perlin(
2019 0.0+(float)p.X/1000., 0.0+(float)p.Y/1000.,
2020 (seed>>32)+98525, 8, 0.5);
2025 /*double tm2 = contour_flat_top(-1.0 + 3.0 * noise2d_perlin(
2026 0.0+(float)p.X/300., 0.0+(float)p.Y/300.,
2027 (seed>>32)+78593, 5, 0.55), 0.15);
2033 double m3 = 100.0 - 600.0 * noise2d_perlin_abs(
2034 0.324+(float)p.X/2000., 0.423+(float)p.Y/2000.,
2035 (seed>>32)+985251, 9, 0.55);
2043 // More mountain ranges
2045 double a1 = d*2.0 - d*7 * noise2d_perlin_abs(
2046 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2047 seed+850342, 7, 0.55);
2049 a1 = d + sqrt(a1-d);*/
2050 a1 = (1.0 - exp(-a1/d))*d;
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 // Very steep mountain ranges
2079 double a1 = d*2 - d*6.5 * noise2d_perlin_abs(
2080 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2081 seed+850342, 6, 0.6);
2083 a1 = d + sqrt(a1-d);*/
2084 a1 = (1.0 - exp(-a1/d))*d;
2089 /*double a = noise2d_perlin_abs(
2090 0.94+(float)p.X/2000., 0.26+(float)p.Y/2000.,
2091 (seed>>32)+65012102, 8, 0.50);
2092 double m4 = 100.0 - 400.0 * a;
2099 The sutff before this comment is usually not used.
2100 The stuff after this comment is usually used.
2105 // Pretty neat looking mountains
2106 double m4 = 100.0 - 400.0 * noise2d_perlin_abs(
2107 0.324+(float)p.X/2000., 0.423+(float)p.Y/2000.,
2108 (seed>>32)+65012102, 7, 0.6);
2115 // Some kind of hill chains or something
2117 double a1 = 30 - 130. * noise2d_perlin_abs(
2118 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2119 seed+850342, 6, 0.63);
2122 a1 = d + sqrt(a1-d);
2131 double base = -2. + 30. * noise2d_perlin(
2132 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2133 (seed>>32)+653876, 7, 0.6);
2140 Combined with turbulence, this thing here is able to make very
2141 awesome terrain, albeit rarely.
2144 double higher = 40. * noise2d_perlin(
2145 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2146 seed+39292, 6, 0.50);
2147 /*double higher = 50. * noise2d_perlin_abs(
2148 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2149 seed+85039, 5, 0.63);*/
2154 // Steepness factor of cliffs
2155 double b = 1.0 + 1.0 * noise2d_perlin(
2156 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2158 b = rangelim(b, 0.0, 1000.0);
2162 b = rangelim(b, 3.0, 1000.0);
2163 //dstream<<"b="<<b<<std::endl;
2165 // Offset to more low
2166 //double a_off = -0.30;
2167 double a_off = -0.20;
2168 // High/low selector
2169 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2170 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2171 seed-359, 7, 0.70));
2176 b = rangelim(b, 3.0, 20.0);*/
2178 double a = -1.5 + 5.0 * (noise2d_perlin_abs(
2179 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2182 /*double a = 5.0 * (noise2d_perlin(
2183 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2184 seed-359, 5, 0.6));*/
2185 //a = contour_flat_top(a, 0.2);
2188 a = rangelim(a, 0.0, 1.0);
2191 //dstream<<"a="<<a<<std::endl;
2193 /*double h2 = higher * a;
2197 h += base*(1.0-a) + higher*a;
2210 double base_rock_level_2d(u64 seed, v2s16 p)
2212 return base_rock_level_2d(seed, v2f((float)p.X, (float)p.Y));
2215 v2f base_ground_turbulence(u64 seed, v3f p)
2221 // Cut off at a minimum height
2224 double min = WATER_LEVEL;
2227 else if(p.Y < min + d)
2233 double vv = 0.75 + 1.0 * noise3d_perlin(
2237 seed+1324381, 4, 0.5);
2238 double vve = rangelim(vv, 0.0, 1.0);
2239 /*double vv = 1.0 - 2.0 * noise3d_perlin_abs(
2243 seed+1324031, 4, 0.5);
2244 double vve = 1.0 - exp(-MYMAX(0, vv*2.0));*/
2245 //double vve = rangelim(vv, 0, 1.0);
2246 //dstream<<"vve="<<vve<<std::endl;
2248 /*// Limit turbulence near water level
2249 double a = contour((p.Y-WATER_LEVEL)/10.0);
2250 vve = (1.-a) * vve;*/
2252 // Increase turbulence in elevated heights
2253 double ah = WATER_LEVEL + 40;
2262 double v1 = f * noise3d_perlin(
2268 double v2 = f * noise3d_perlin(
2274 return v2f(v1*vve, v2*vve);
2280 bool is_carved(u64 seed, v3f p)
2283 double v1 = noise3d_perlin_abs(
2287 seed+657890854, 5, 0.7);
2296 double v4 = contour(f*noise3d_perlin(
2300 seed+87592, 5, 0.7));
2301 // Tilted 90 degrees
2302 double v5 = contour(f*noise3d_perlin(
2306 seed+98594, 5, 0.7));
2315 bool is_underground_mud(u64 seed, v3f p)
2317 double v1 = noise3d_perlin_abs(
2321 seed+83401, 5, 0.75);
2326 if depth_guess!=NULL, it is set to a guessed value of how deep
2327 underground the position is.
2329 bool is_base_ground(u64 seed, v3f p, double *depth_guess=NULL)
2332 // This is used for testing the output of the cave function
2338 return is_carved(seed, p);
2342 // This is used for testing the output of the underground mud function
2348 return is_underground_mud(seed, p);
2352 bool is_ground = true;
2355 if(is_carved(seed, p))
2359 if(depth_guess || is_ground == true)
2361 v2f t = base_ground_turbulence(seed, p);
2363 double surface_y_f = base_rock_level_2d(seed, v2f(p.X+t.X, p.Z+t.Y));
2368 // Find highest surface near current
2375 double s2 = surface_y_f;
2376 for(u32 i=0; i<4; i++)
2379 // Get turbulence at around there
2380 v2f t2 = base_ground_turbulence(seed, p+dir);
2381 // Get ground height
2382 v2f l = v2f(p.X+t2.X+dir.X, p.Z+t2.Y+dir.Z);
2383 double s = base_rock_level_2d(seed, l);
2387 *depth_guess = s2 - p.Y;
2393 // Check a bit lower also, take highest surface
2394 v2f t2 = base_ground_turbulence(seed, p + v3f(0,-2,0));
2395 double s2 = base_rock_level_2d(seed, v2f(p.X+t2.X, p.Z+t2.Y));
2396 if(s2 > surface_y_f)
2397 *depth_guess = s2 - p.Y;
2399 *depth_guess = surface_y_f - p.Y;
2404 *depth_guess = surface_y_f - p.Y;
2407 if(p.Y > surface_y_f)
2413 // Guess surface point
2414 v3f p2(p.X, surface_y_f, p.Z);
2415 v2f t2 = base_ground_turbulence
2417 double s1 = base_rock_level_2d(seed, v2f(p.X+v1,p.Z+v2));
2423 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2426 This is the main map generation method
2429 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2430 core::map<v3s16, MapBlock*> &changed_blocks,
2433 DSTACK(__FUNCTION_NAME);
2435 // Shall be not used now
2441 Don't generate if already fully generated
2445 MapChunk *chunk = getChunk(chunkpos);
2446 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2448 dstream<<"generateChunkRaw(): Chunk "
2449 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2450 <<" already generated"<<std::endl;
2455 dstream<<"generateChunkRaw(): Generating chunk "
2456 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2459 TimeTaker timer("generateChunkRaw()");
2461 // The distance how far into the neighbors the generator is allowed to go.
2462 s16 max_spread_amount_sectors = 2;
2463 assert(max_spread_amount_sectors <= m_chunksize);
2464 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2466 // Minimum amount of space left on sides for mud to fall in
2467 //s16 min_mud_fall_space = 2;
2469 // Maximum diameter of stone obstacles in X and Z
2470 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2471 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2473 s16 y_blocks_min = -4;
2474 s16 y_blocks_max = 3;
2475 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2476 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2477 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2479 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2480 s16 sectorpos_base_size = m_chunksize;
2482 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2483 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2484 v2s16 sectorpos_bigbase =
2485 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2486 s16 sectorpos_bigbase_size =
2487 sectorpos_base_size + 2 * max_spread_amount_sectors;
2489 v3s16 bigarea_blocks_min(
2490 sectorpos_bigbase.X,
2495 v3s16 bigarea_blocks_max(
2496 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2498 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2501 // Relative values to control amount of stuff in one chunk
2502 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2503 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2504 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2505 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2506 *(u32)h_blocks*MAP_BLOCKSIZE;
2509 The limiting edges of the lighting update, inclusive.
2511 s16 lighting_min_d = 0-max_spread_amount;
2512 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2515 Create the whole area of this and the neighboring chunks
2518 TimeTaker timer("generateChunkRaw() create area");
2520 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2521 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2523 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2524 ServerMapSector *sector = createSector(sectorpos);
2527 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2529 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2530 MapBlock *block = createBlock(blockpos);
2532 // Lighting won't be calculated
2533 //block->setLightingExpired(true);
2534 // Lighting will be calculated
2535 block->setLightingExpired(false);
2538 Block gets sunlight if this is true.
2540 This should be set to true when the top side of a block
2541 is completely exposed to the sky.
2543 Actually this doesn't matter now because the
2544 initial lighting is done here.
2546 block->setIsUnderground(y != y_blocks_max);
2552 Now we have a big empty area.
2554 Make a ManualMapVoxelManipulator that contains this and the
2558 ManualMapVoxelManipulator vmanip(this);
2559 // Add the area we just generated
2561 TimeTaker timer("generateChunkRaw() initialEmerge");
2562 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2566 vmanip.clearFlag(0xff);
2568 TimeTaker timer_generate("generateChunkRaw() generate");
2570 // Maximum height of the stone surface and obstacles.
2571 // This is used to disable dungeon generation from going too high.
2572 s16 stone_surface_max_y = 0;
2575 Generate general ground level to full area
2580 TimeTaker timer1("ground level");
2581 dstream<<"Generating base ground..."<<std::endl;
2583 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2584 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2587 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2590 Skip if already generated
2593 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2594 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2598 v2f p2df(p2d.X, p2d.Y);
2601 // Use fast index incrementing
2602 v3s16 em = vmanip.m_area.getExtent();
2603 s16 min = y_nodes_min;
2604 s16 max = y_nodes_max;
2607 //float surface_y_f = base_rock_level_2d(m_seed, p2df);
2608 u32 i = vmanip.m_area.index(v3s16(p2d.X, min, p2d.Y));
2609 for(s16 y=min; y<=max; y++)
2612 bool is = is_base_ground(m_seed, v3f(p2df.X,y,p2df.Y));
2614 vmanip.m_data[i].d = CONTENT_STONE;
2616 vmanip.m_data[i].d = CONTENT_AIR;
2619 double v = noise3d_perlin(
2620 0.5+(float)p2d.X/200,
2622 0.5+(float)p2d.Y/200,
2623 m_seed+293, 6, 0.55);
2625 vmanip.m_data[i].d = CONTENT_STONE;
2627 vmanip.m_data[i].d = CONTENT_AIR;
2630 /*double v1 = 5 * noise3d_perlin(
2631 0.5+(float)p2df.X/200,
2633 0.5+(float)p2df.Y/200,
2634 m_seed+293, 6, 0.55);
2636 double v2 = 5 * noise3d_perlin(
2637 0.5+(float)p2df.X/200,
2639 0.5+(float)p2df.Y/200,
2640 m_seed+293, 6, 0.55);*/
2645 float surface_y_f = base_rock_level_2d(m_seed, p2df+v2f(v1,v2));
2647 if(y <= surface_y_f)
2648 vmanip.m_data[i].d = CONTENT_STONE;
2650 vmanip.m_data[i].d = CONTENT_AIR;
2653 vmanip.m_area.add_y(em, i, 1);
2659 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2662 Skip if already generated
2665 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2666 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2670 // Ground height at this point
2671 float surface_y_f = 0.0;
2673 // Use perlin noise for ground height
2674 surface_y_f = base_rock_level_2d(m_seed, p2d);
2676 /*// Experimental stuff
2678 float a = highlands_level_2d(m_seed, p2d);
2683 // Convert to integer
2684 s16 surface_y = (s16)surface_y_f;
2687 if(surface_y > stone_surface_max_y)
2688 stone_surface_max_y = surface_y;
2691 Fill ground with stone
2694 // Use fast index incrementing
2695 v3s16 em = vmanip.m_area.getExtent();
2696 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2697 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2699 vmanip.m_data[i].d = CONTENT_STONE;
2701 vmanip.m_area.add_y(em, i, 1);
2710 Randomize some parameters
2713 s32 stone_obstacle_count = 0;
2714 /*s32 stone_obstacle_count =
2715 rangelim((1.0+noise2d(m_seed+897,
2716 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2718 s16 stone_obstacle_max_height = 0;
2719 /*s16 stone_obstacle_max_height =
2720 rangelim((1.0+noise2d(m_seed+5902,
2721 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2724 Loop this part, it will make stuff look older and newer nicely
2727 for(u32 i_age=0; i_age<age_count; i_age++)
2732 //TimeTaker timer1("stone obstacles");
2735 Add some random stone obstacles
2738 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2740 // Randomize max height so usually stuff will be quite low
2741 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2743 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2744 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2747 myrand_range(5, stone_obstacle_max_size),
2748 myrand_range(0, maxheight_randomized),
2749 myrand_range(5, stone_obstacle_max_size)
2752 // Don't make stupid small rectangle bumps
2757 myrand_range(1+ob_size.X/2+2,
2758 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2759 myrand_range(1+ob_size.Z/2+2,
2760 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2763 // Minimum space left on top of the obstacle
2764 s16 min_head_space = 12;
2766 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2767 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2769 // Node position in 2d
2770 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2772 // Find stone ground level
2773 // (ignore everything else than mud in already generated chunks)
2774 // and mud amount over the stone level
2778 v3s16 em = vmanip.m_area.getExtent();
2779 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2781 // Go to ground level
2782 for(y=y_nodes_max; y>=y_nodes_min; y--)
2784 MapNode *n = &vmanip.m_data[i];
2785 /*if(content_walkable(n.d)
2786 && n.d != CONTENT_MUD
2787 && n.d != CONTENT_GRASS)
2789 if(n->d == CONTENT_STONE)
2792 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2796 Change to mud because otherwise we might
2797 be throwing mud on grass at the next
2803 vmanip.m_area.add_y(em, i, -1);
2805 if(y >= y_nodes_min)
2808 surface_y = y_nodes_min;
2816 v3s16 em = vmanip.m_area.getExtent();
2817 s16 y_start = surface_y+1;
2818 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2822 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2824 MapNode &n = vmanip.m_data[i];
2825 n.d = CONTENT_STONE;
2827 if(y > stone_surface_max_y)
2828 stone_surface_max_y = y;
2831 if(count >= ob_size.Y)
2834 vmanip.m_area.add_y(em, i, 1);
2838 for(; y<=y_nodes_max - min_head_space; y++)
2840 MapNode &n = vmanip.m_data[i];
2843 if(count >= mud_amount)
2846 vmanip.m_area.add_y(em, i, 1);
2856 //TimeTaker timer1("dungeons");
2861 u32 dungeons_count = relative_volume / 600000;
2862 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2863 if(stone_surface_max_y < WATER_LEVEL)
2865 /*u32 dungeons_count = 0;
2866 u32 bruises_count = 0;*/
2867 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2869 s16 min_tunnel_diameter = 2;
2870 s16 max_tunnel_diameter = 6;
2871 u16 tunnel_routepoints = 25;
2873 bool bruise_surface = (jj < bruises_count);
2877 min_tunnel_diameter = 5;
2878 max_tunnel_diameter = myrand_range(10, 20);
2879 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2880 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2882 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2883 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2885 tunnel_routepoints = 5;
2888 // Allowed route area size in nodes
2890 sectorpos_base_size*MAP_BLOCKSIZE,
2891 h_blocks*MAP_BLOCKSIZE,
2892 sectorpos_base_size*MAP_BLOCKSIZE
2895 // Area starting point in nodes
2897 sectorpos_base.X*MAP_BLOCKSIZE,
2898 y_blocks_min*MAP_BLOCKSIZE,
2899 sectorpos_base.Y*MAP_BLOCKSIZE
2903 //(this should be more than the maximum radius of the tunnel)
2904 //s16 insure = 5; // Didn't work with max_d = 20
2906 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2907 ar += v3s16(1,0,1) * more * 2;
2908 of -= v3s16(1,0,1) * more;
2910 s16 route_y_min = 0;
2911 // Allow half a diameter + 7 over stone surface
2912 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2914 /*// If dungeons, don't go through surface too often
2915 if(bruise_surface == false)
2916 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2918 // Limit maximum to area
2919 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2923 /*// Minimum is at y=0
2924 route_y_min = -of.Y - 0;*/
2925 // Minimum is at y=max_tunnel_diameter/4
2926 //route_y_min = -of.Y + max_tunnel_diameter/4;
2927 //s16 min = -of.Y + max_tunnel_diameter/4;
2928 s16 min = -of.Y + 0;
2929 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2930 route_y_min = rangelim(route_y_min, 0, route_y_max);
2933 /*dstream<<"route_y_min = "<<route_y_min
2934 <<", route_y_max = "<<route_y_max<<std::endl;*/
2936 s16 route_start_y_min = route_y_min;
2937 s16 route_start_y_max = route_y_max;
2939 // Start every 2nd dungeon from surface
2940 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2942 if(coming_from_surface)
2944 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2947 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2948 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2950 // Randomize starting position
2952 (float)(myrand()%ar.X)+0.5,
2953 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2954 (float)(myrand()%ar.Z)+0.5
2957 MapNode airnode(CONTENT_AIR);
2960 Generate some tunnel starting from orp
2963 for(u16 j=0; j<tunnel_routepoints; j++)
2966 s16 min_d = min_tunnel_diameter;
2967 s16 max_d = max_tunnel_diameter;
2968 s16 rs = myrand_range(min_d, max_d);
2973 maxlen = v3s16(rs*7,rs*7,rs*7);
2977 maxlen = v3s16(15, myrand_range(1, 20), 15);
2982 if(coming_from_surface && j < 3)
2985 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2986 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2987 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2993 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2994 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2995 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3002 else if(rp.X >= ar.X)
3004 if(rp.Y < route_y_min)
3006 else if(rp.Y >= route_y_max)
3007 rp.Y = route_y_max-1;
3010 else if(rp.Z >= ar.Z)
3014 for(float f=0; f<1.0; f+=1.0/vec.getLength())
3016 v3f fp = orp + vec * f;
3017 v3s16 cp(fp.X, fp.Y, fp.Z);
3020 s16 d1 = d0 + rs - 1;
3021 for(s16 z0=d0; z0<=d1; z0++)
3023 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
3024 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
3025 for(s16 x0=-si; x0<=si-1; x0++)
3027 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
3028 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
3029 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
3030 //s16 si2 = rs - abs(x0);
3031 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
3037 /*if(isInArea(p, ar) == false)
3039 // Check only height
3040 if(y < 0 || y >= ar.Y)
3044 //assert(vmanip.m_area.contains(p));
3045 if(vmanip.m_area.contains(p) == false)
3047 dstream<<"WARNING: "<<__FUNCTION_NAME
3048 <<":"<<__LINE__<<": "
3049 <<"point not in area"
3054 // Just set it to air, it will be changed to
3056 u32 i = vmanip.m_area.index(p);
3057 vmanip.m_data[i] = airnode;
3059 if(bruise_surface == false)
3062 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
3077 //TimeTaker timer1("ore veins");
3082 for(u32 jj=0; jj<relative_volume/1000; jj++)
3084 s16 max_vein_diameter = 3;
3086 // Allowed route area size in nodes
3088 sectorpos_base_size*MAP_BLOCKSIZE,
3089 h_blocks*MAP_BLOCKSIZE,
3090 sectorpos_base_size*MAP_BLOCKSIZE
3093 // Area starting point in nodes
3095 sectorpos_base.X*MAP_BLOCKSIZE,
3096 y_blocks_min*MAP_BLOCKSIZE,
3097 sectorpos_base.Y*MAP_BLOCKSIZE
3101 //(this should be more than the maximum radius of the tunnel)
3103 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
3104 ar += v3s16(1,0,1) * more * 2;
3105 of -= v3s16(1,0,1) * more;
3107 // Randomize starting position
3109 (float)(myrand()%ar.X)+0.5,
3110 (float)(myrand()%ar.Y)+0.5,
3111 (float)(myrand()%ar.Z)+0.5
3114 // Randomize mineral
3117 mineral = MINERAL_COAL;
3119 mineral = MINERAL_IRON;
3122 Generate some vein starting from orp
3125 for(u16 j=0; j<2; j++)
3128 (float)(myrand()%ar.X)+0.5,
3129 (float)(myrand()%ar.Y)+0.5,
3130 (float)(myrand()%ar.Z)+0.5
3132 v3f vec = rp - orp;*/
3134 v3s16 maxlen(5, 5, 5);
3136 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
3137 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
3138 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3143 else if(rp.X >= ar.X)
3147 else if(rp.Y >= ar.Y)
3151 else if(rp.Z >= ar.Z)
3157 s16 max_d = max_vein_diameter;
3158 s16 rs = myrand_range(min_d, max_d);
3160 for(float f=0; f<1.0; f+=1.0/vec.getLength())
3162 v3f fp = orp + vec * f;
3163 v3s16 cp(fp.X, fp.Y, fp.Z);
3165 s16 d1 = d0 + rs - 1;
3166 for(s16 z0=d0; z0<=d1; z0++)
3168 s16 si = rs - abs(z0);
3169 for(s16 x0=-si; x0<=si-1; x0++)
3171 s16 si2 = rs - abs(x0);
3172 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3174 // Don't put mineral to every place
3182 /*if(isInArea(p, ar) == false)
3184 // Check only height
3185 if(y < 0 || y >= ar.Y)
3189 assert(vmanip.m_area.contains(p));
3191 // Just set it to air, it will be changed to
3193 u32 i = vmanip.m_area.index(p);
3194 MapNode *n = &vmanip.m_data[i];
3195 if(n->d == CONTENT_STONE)
3210 //TimeTaker timer1("add mud");
3213 Add mud to the central chunk
3216 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3217 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
3219 // Node position in 2d
3220 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3222 // Randomize mud amount
3223 s16 mud_add_amount = get_mud_amount(m_seed, v2f(p2d.X,p2d.Y))/age_count;
3225 // Find ground level
3226 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3229 If topmost node is grass, change it to mud.
3230 It might be if it was flown to there from a neighboring
3231 chunk and then converted.
3234 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
3235 MapNode *n = &vmanip.m_data[i];
3236 if(n->d == CONTENT_GRASS)
3245 v3s16 em = vmanip.m_area.getExtent();
3246 s16 y_start = surface_y+1;
3247 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3248 for(s16 y=y_start; y<=y_nodes_max; y++)
3250 if(mudcount >= mud_add_amount)
3253 MapNode &n = vmanip.m_data[i];
3257 vmanip.m_area.add_y(em, i, 1);
3266 //TimeTaker timer1("flow mud");
3269 Flow mud away from steep edges
3272 // Limit area by 1 because mud is flown into neighbors.
3273 s16 mudflow_minpos = 0-max_spread_amount+1;
3274 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
3276 // Iterate a few times
3277 for(s16 k=0; k<3; k++)
3280 for(s16 x=mudflow_minpos;
3283 for(s16 z=mudflow_minpos;
3287 // Invert coordinates every 2nd iteration
3290 x = mudflow_maxpos - (x-mudflow_minpos);
3291 z = mudflow_maxpos - (z-mudflow_minpos);
3294 // Node position in 2d
3295 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3297 v3s16 em = vmanip.m_area.getExtent();
3298 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3305 for(; y>=y_nodes_min; y--)
3307 n = &vmanip.m_data[i];
3308 //if(content_walkable(n->d))
3310 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3313 vmanip.m_area.add_y(em, i, -1);
3316 // Stop if out of area
3317 //if(vmanip.m_area.contains(i) == false)
3321 /*// If not mud, do nothing to it
3322 MapNode *n = &vmanip.m_data[i];
3323 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3327 Don't flow it if the stuff under it is not mud
3331 vmanip.m_area.add_y(em, i2, -1);
3332 // Cancel if out of area
3333 if(vmanip.m_area.contains(i2) == false)
3335 MapNode *n2 = &vmanip.m_data[i2];
3336 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
3340 // Make it exactly mud
3343 /*s16 recurse_count = 0;
3347 v3s16(0,0,1), // back
3348 v3s16(1,0,0), // right
3349 v3s16(0,0,-1), // front
3350 v3s16(-1,0,0), // left
3353 // Theck that upper is air or doesn't exist.
3354 // Cancel dropping if upper keeps it in place
3356 vmanip.m_area.add_y(em, i3, 1);
3357 if(vmanip.m_area.contains(i3) == true
3358 && content_walkable(vmanip.m_data[i3].d) == true)
3365 for(u32 di=0; di<4; di++)
3367 v3s16 dirp = dirs4[di];
3370 vmanip.m_area.add_p(em, i2, dirp);
3371 // Fail if out of area
3372 if(vmanip.m_area.contains(i2) == false)
3374 // Check that side is air
3375 MapNode *n2 = &vmanip.m_data[i2];
3376 if(content_walkable(n2->d))
3378 // Check that under side is air
3379 vmanip.m_area.add_y(em, i2, -1);
3380 if(vmanip.m_area.contains(i2) == false)
3382 n2 = &vmanip.m_data[i2];
3383 if(content_walkable(n2->d))
3385 /*// Check that under that is air (need a drop of 2)
3386 vmanip.m_area.add_y(em, i2, -1);
3387 if(vmanip.m_area.contains(i2) == false)
3389 n2 = &vmanip.m_data[i2];
3390 if(content_walkable(n2->d))
3392 // Loop further down until not air
3394 vmanip.m_area.add_y(em, i2, -1);
3395 // Fail if out of area
3396 if(vmanip.m_area.contains(i2) == false)
3398 n2 = &vmanip.m_data[i2];
3399 }while(content_walkable(n2->d) == false);
3400 // Loop one up so that we're in air
3401 vmanip.m_area.add_y(em, i2, 1);
3402 n2 = &vmanip.m_data[i2];
3404 // Move mud to new place
3406 // Set old place to be air
3407 *n = MapNode(CONTENT_AIR);
3420 //TimeTaker timer1("add water");
3423 Add water to the central chunk (and a bit more)
3426 for(s16 x=0-max_spread_amount;
3427 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3429 for(s16 z=0-max_spread_amount;
3430 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3433 // Node position in 2d
3434 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3436 // Find ground level
3437 //s16 surface_y = find_ground_level(vmanip, p2d);
3440 If ground level is over water level, skip.
3441 NOTE: This leaves caves near water without water,
3442 which looks especially crappy when the nearby water
3443 won't start flowing either for some reason
3445 /*if(surface_y > WATER_LEVEL)
3452 v3s16 em = vmanip.m_area.getExtent();
3453 u8 light = LIGHT_MAX;
3454 // Start at global water surface level
3455 s16 y_start = WATER_LEVEL;
3456 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3457 MapNode *n = &vmanip.m_data[i];
3459 /*// Add first one to transforming liquid queue, if water
3460 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3462 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3463 m_transforming_liquid.push_back(p);
3466 for(s16 y=y_start; y>=y_nodes_min; y--)
3468 n = &vmanip.m_data[i];
3470 // Stop when there is no water and no air
3471 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3472 && n->d != CONTENT_WATER)
3474 /*// Add bottom one to transforming liquid queue
3475 vmanip.m_area.add_y(em, i, 1);
3476 n = &vmanip.m_data[i];
3477 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3479 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3480 m_transforming_liquid.push_back(p);
3486 // Make water only not in dungeons
3487 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3489 n->d = CONTENT_WATERSOURCE;
3490 //n->setLight(LIGHTBANK_DAY, light);
3492 // Add to transforming liquid queue (in case it'd
3494 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3495 m_transforming_liquid.push_back(p);
3499 vmanip.m_area.add_y(em, i, -1);
3512 //TimeTaker timer1("convert mud to sand");
3518 //s16 mud_add_amount = myrand_range(2, 4);
3519 //s16 mud_add_amount = 0;
3521 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3522 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3523 for(s16 x=0-max_spread_amount+1;
3524 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3526 for(s16 z=0-max_spread_amount+1;
3527 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3530 // Node position in 2d
3531 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3533 // Determine whether to have sand here
3534 bool have_sand = get_have_sand(p2d);
3536 if(have_sand == false)
3539 // Find ground level
3540 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3542 if(surface_y > WATER_LEVEL + 2)
3546 v3s16 em = vmanip.m_area.getExtent();
3547 s16 y_start = surface_y;
3548 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3549 u32 not_sand_counter = 0;
3550 for(s16 y=y_start; y>=y_nodes_min; y--)
3552 MapNode *n = &vmanip.m_data[i];
3553 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3555 n->d = CONTENT_SAND;
3560 if(not_sand_counter > 3)
3564 vmanip.m_area.add_y(em, i, -1);
3573 //TimeTaker timer1("generate trees");
3579 // Divide area into parts
3581 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3582 double area = sidelen * sidelen;
3583 for(s16 x0=0; x0<div; x0++)
3584 for(s16 z0=0; z0<div; z0++)
3586 // Center position of part of division
3588 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3589 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3591 // Minimum edge of part of division
3593 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3594 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3596 // Maximum edge of part of division
3598 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3599 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3602 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3603 // Put trees in random places on part of division
3604 for(u32 i=0; i<tree_count; i++)
3606 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3607 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3608 s16 y = find_ground_level(vmanip, v2s16(x,z));
3609 // Don't make a tree under water level
3614 Trees grow only on mud and grass
3617 u32 i = vmanip.m_area.index(v3s16(p));
3618 MapNode *n = &vmanip.m_data[i];
3619 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3624 make_tree(vmanip, p);
3627 /*u32 tree_max = relative_area / 60;
3628 //u32 count = myrand_range(0, tree_max);
3629 for(u32 i=0; i<count; i++)
3631 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3632 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3633 x += sectorpos_base.X*MAP_BLOCKSIZE;
3634 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3635 s16 y = find_ground_level(vmanip, v2s16(x,z));
3636 // Don't make a tree under water level
3641 make_tree(vmanip, p);
3649 //TimeTaker timer1("grow grass");
3655 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3656 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3657 for(s16 x=0-max_spread_amount;
3658 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3660 for(s16 z=0-max_spread_amount;
3661 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3664 // Node position in 2d
3665 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3668 Find the lowest surface to which enough light ends up
3671 Basically just wait until not air and not leaves.
3675 v3s16 em = vmanip.m_area.getExtent();
3676 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3678 // Go to ground level
3679 for(y=y_nodes_max; y>=y_nodes_min; y--)
3681 MapNode &n = vmanip.m_data[i];
3682 if(n.d != CONTENT_AIR
3683 && n.d != CONTENT_LEAVES)
3685 vmanip.m_area.add_y(em, i, -1);
3687 if(y >= y_nodes_min)
3690 surface_y = y_nodes_min;
3693 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3694 MapNode *n = &vmanip.m_data[i];
3695 if(n->d == CONTENT_MUD)
3696 n->d = CONTENT_GRASS;
3702 Initial lighting (sunlight)
3705 core::map<v3s16, bool> light_sources;
3708 // 750ms @cs=8, can't optimize more
3709 TimeTaker timer1("initial lighting");
3713 Go through the edges and add all nodes that have light to light_sources
3717 for(s16 i=0; i<4; i++)
3719 for(s16 j=lighting_min_d;
3726 if(i == 0 || i == 1)
3728 x = (i==0) ? lighting_min_d : lighting_max_d;
3737 z = (i==0) ? lighting_min_d : lighting_max_d;
3744 // Node position in 2d
3745 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3748 v3s16 em = vmanip.m_area.getExtent();
3749 s16 y_start = y_nodes_max;
3750 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3751 for(s16 y=y_start; y>=y_nodes_min; y--)
3753 MapNode *n = &vmanip.m_data[i];
3754 if(n->getLight(LIGHTBANK_DAY) != 0)
3756 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3758 //NOTE: This is broken, at least the index has to
3767 Go through the edges and apply sunlight to them, not caring
3772 for(s16 i=0; i<4; i++)
3774 for(s16 j=lighting_min_d;
3781 if(i == 0 || i == 1)
3783 x = (i==0) ? lighting_min_d : lighting_max_d;
3792 z = (i==0) ? lighting_min_d : lighting_max_d;
3799 // Node position in 2d
3800 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3802 // Loop from top to down
3804 u8 light = LIGHT_SUN;
3805 v3s16 em = vmanip.m_area.getExtent();
3806 s16 y_start = y_nodes_max;
3807 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3808 for(s16 y=y_start; y>=y_nodes_min; y--)
3810 MapNode *n = &vmanip.m_data[i];
3811 if(light_propagates_content(n->d) == false)
3815 else if(light != LIGHT_SUN
3816 || sunlight_propagates_content(n->d) == false)
3822 n->setLight(LIGHTBANK_DAY, light);
3823 n->setLight(LIGHTBANK_NIGHT, 0);
3827 // Insert light source
3828 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3831 // Increment index by y
3832 vmanip.m_area.add_y(em, i, -1);
3838 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3839 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3840 /*for(s16 x=0-max_spread_amount+1;
3841 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3843 for(s16 z=0-max_spread_amount+1;
3844 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3848 This has to be 1 smaller than the actual area, because
3849 neighboring nodes are checked.
3851 for(s16 x=lighting_min_d+1;
3852 x<=lighting_max_d-1;
3854 for(s16 z=lighting_min_d+1;
3855 z<=lighting_max_d-1;
3858 // Node position in 2d
3859 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3862 Apply initial sunlight
3865 u8 light = LIGHT_SUN;
3866 bool add_to_sources = false;
3867 v3s16 em = vmanip.m_area.getExtent();
3868 s16 y_start = y_nodes_max;
3869 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3870 for(s16 y=y_start; y>=y_nodes_min; y--)
3872 MapNode *n = &vmanip.m_data[i];
3874 if(light_propagates_content(n->d) == false)
3878 else if(light != LIGHT_SUN
3879 || sunlight_propagates_content(n->d) == false)
3885 // This doesn't take much time
3886 if(add_to_sources == false)
3889 Check sides. If side is not air or water, start
3890 adding to light_sources.
3893 v3s16(0,0,1), // back
3894 v3s16(1,0,0), // right
3895 v3s16(0,0,-1), // front
3896 v3s16(-1,0,0), // left
3898 for(u32 di=0; di<4; di++)
3900 v3s16 dirp = dirs4[di];
3902 vmanip.m_area.add_p(em, i2, dirp);
3903 MapNode *n2 = &vmanip.m_data[i2];
3905 n2->d != CONTENT_AIR
3906 && n2->d != CONTENT_WATERSOURCE
3907 && n2->d != CONTENT_WATER
3909 add_to_sources = true;
3915 n->setLight(LIGHTBANK_DAY, light);
3916 n->setLight(LIGHTBANK_NIGHT, 0);
3918 // This doesn't take much time
3919 if(light != 0 && add_to_sources)
3921 // Insert light source
3922 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3925 // Increment index by y
3926 vmanip.m_area.add_y(em, i, -1);
3934 // Spread light around
3936 TimeTaker timer("generateChunkRaw() spreadLight");
3937 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3944 timer_generate.stop();
3947 Blit generated stuff to map
3951 //TimeTaker timer("generateChunkRaw() blitBackAll");
3952 vmanip.blitBackAll(&changed_blocks);
3956 Update day/night difference cache of the MapBlocks
3959 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3960 i.atEnd() == false; i++)
3962 MapBlock *block = i.getNode()->getValue();
3963 block->updateDayNightDiff();
3970 Create chunk metadata
3973 for(s16 x=-1; x<=1; x++)
3974 for(s16 y=-1; y<=1; y++)
3976 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3977 // Add chunk meta information
3978 MapChunk *chunk = getChunk(chunkpos0);
3981 chunk = new MapChunk();
3982 m_chunks.insert(chunkpos0, chunk);
3984 //chunk->setIsVolatile(true);
3985 if(chunk->getGenLevel() > GENERATED_PARTLY)
3986 chunk->setGenLevel(GENERATED_PARTLY);
3990 Set central chunk non-volatile
3992 MapChunk *chunk = getChunk(chunkpos);
3995 //chunk->setIsVolatile(false);
3996 chunk->setGenLevel(GENERATED_FULLY);
3999 Save changed parts of map
4004 Return central chunk (which was requested)
4009 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
4010 core::map<v3s16, MapBlock*> &changed_blocks)
4012 dstream<<"generateChunk(): Generating chunk "
4013 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
4016 // Shall be not used now
4019 /*for(s16 x=-1; x<=1; x++)
4020 for(s16 y=-1; y<=1; y++)*/
4021 for(s16 x=-0; x<=0; x++)
4022 for(s16 y=-0; y<=0; y++)
4024 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
4025 MapChunk *chunk = getChunk(chunkpos0);
4026 // Skip if already generated
4027 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
4029 generateChunkRaw(chunkpos0, changed_blocks);
4032 assert(chunkNonVolatile(chunkpos1));
4034 MapChunk *chunk = getChunk(chunkpos1);
4038 ServerMapSector * ServerMap::createSector(v2s16 p2d)
4040 DSTACK("%s: p2d=(%d,%d)",
4045 Check if it exists already in memory
4047 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
4052 Try to load it from disk (with blocks)
4054 if(loadSectorFull(p2d) == true)
4056 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
4059 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
4060 throw InvalidPositionException("");
4066 Do not create over-limit
4068 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4069 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4070 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4071 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4072 throw InvalidPositionException("createSector(): pos. over limit");
4075 Generate blank sector
4078 sector = new ServerMapSector(this, p2d);
4080 // Sector position on map in nodes
4081 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4086 m_sectors.insert(p2d, sector);
4091 MapSector * ServerMap::emergeSector(v2s16 p2d,
4092 core::map<v3s16, MapBlock*> &changed_blocks)
4094 DSTACK("%s: p2d=(%d,%d)",
4101 v2s16 chunkpos = sector_to_chunk(p2d);
4102 /*bool chunk_nonvolatile = false;
4103 MapChunk *chunk = getChunk(chunkpos);
4104 if(chunk && chunk->getIsVolatile() == false)
4105 chunk_nonvolatile = true;*/
4106 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
4109 If chunk is not fully generated, generate chunk
4111 if(chunk_nonvolatile == false)
4113 // Generate chunk and neighbors
4114 generateChunk(chunkpos, changed_blocks);
4118 Return sector if it exists now
4120 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4125 Try to load it from disk
4127 if(loadSectorFull(p2d) == true)
4129 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4132 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
4133 throw InvalidPositionException("");
4139 generateChunk should have generated the sector
4143 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
4144 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
4148 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
4151 generateChunkRaw(chunkpos, changed_blocks, true);
4154 Return sector if it exists now
4156 sector = getSectorNoGenerateNoEx(p2d);
4160 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
4166 dstream<<"WARNING: Creating an empty sector."<<std::endl;
4168 return createSector(p2d);
4175 //return generateSector();
4179 NOTE: This is not used for main map generation, only for blocks
4180 that are very high or low
4182 MapBlock * ServerMap::generateBlock(
4184 MapBlock *original_dummy,
4185 ServerMapSector *sector,
4186 core::map<v3s16, MapBlock*> &changed_blocks,
4187 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4190 DSTACK("%s: p=(%d,%d,%d)",
4194 /*dstream<<"generateBlock(): "
4195 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4198 MapBlock *block = original_dummy;
4200 v2s16 p2d(p.X, p.Z);
4202 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4203 v3s16 p_nodes = p * MAP_BLOCKSIZE;
4206 Do not generate over-limit
4208 if(blockpos_over_limit(p))
4210 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4211 throw InvalidPositionException("generateBlock(): pos. over limit");
4215 If block doesn't exist, create one.
4216 If it exists, it is a dummy. In that case unDummify() it.
4218 NOTE: This already sets the map as the parent of the block
4222 block = sector->createBlankBlockNoInsert(block_y);
4226 // Remove the block so that nobody can get a half-generated one.
4227 sector->removeBlock(block);
4228 // Allocate the block to contain the generated data
4232 u8 water_material = CONTENT_WATERSOURCE;
4234 s32 lowest_ground_y = 32767;
4235 s32 highest_ground_y = -32768;
4241 } block_type = BT_SURFACE;
4243 {// ground_timer (0ms or ~100ms)
4244 TimeTaker ground_timer("Ground generation");
4247 Approximate whether this block is a surface block, an air
4248 block or a ground block.
4250 This shall never mark a surface block as non-surface.
4255 Estimate surface at different positions of the block, to
4256 try to accomodate the effect of turbulence.
4269 v3f p_nodes_f = intToFloat(p_nodes, 1);
4270 float surface_y_max = -1000000;
4271 float surface_y_min = 1000000;
4272 for(u32 i=0; i<sizeof(checklist)/sizeof(checklist[0]); i++)
4274 v3f p_map_f = p_nodes_f + checklist[i]*MAP_BLOCKSIZE;
4277 /*bool is_ground =*/ is_base_ground(m_seed, p_map_f, &depth_guess);
4279 // Estimate the surface height
4280 float surface_y_f = p_map_f.Y + depth_guess;
4282 if(surface_y_f > surface_y_max)
4283 surface_y_max = surface_y_f;
4284 if(surface_y_f < surface_y_min)
4285 surface_y_min = surface_y_f;
4288 float block_low_y_f = p_nodes_f.Y;
4289 float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE;
4291 /*dstream<<"surface_y_max="<<surface_y_max
4292 <<", surface_y_min="<<surface_y_min
4293 <<", block_low_y_f="<<block_low_y_f
4294 <<", block_high_y_f="<<block_high_y_f
4297 // A fuzzyness value
4298 // Must accomodate mud and turbulence holes
4300 // Must accomodate a bit less
4303 if(block_high_y_f < surface_y_min - d_down)
4305 //dstream<<"BT_GROUND"<<std::endl;
4307 block_type = BT_GROUND;
4309 else if(block_low_y_f >= surface_y_max + d_up
4310 && block_low_y_f > WATER_LEVEL + d_up)
4312 //dstream<<"BT_SKY"<<std::endl;
4314 block_type = BT_SKY;
4318 //dstream<<"BT_SURFACE"<<std::endl;
4320 block_type = BT_SURFACE;
4323 if(/*block_type == BT_GROUND ||*/ block_type == BT_SKY)
4325 lowest_ground_y = surface_y_min;
4326 highest_ground_y = surface_y_max;
4330 if(block_type == BT_SURFACE || block_type == BT_GROUND)
4333 Generate ground precisely
4336 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4337 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4339 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4341 //s16 surface_y = 0;
4343 /*s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4344 + AVERAGE_MUD_AMOUNT;
4346 if(surface_y < lowest_ground_y)
4347 lowest_ground_y = surface_y;
4348 if(surface_y > highest_ground_y)
4349 highest_ground_y = surface_y;*/
4351 v2s16 real_p2d = v2s16(x0,z0) + p2d*MAP_BLOCKSIZE;
4353 //s32 surface_depth = AVERAGE_MUD_AMOUNT;
4354 s16 surface_depth = get_mud_amount(m_seed, v2f(real_p2d.X,real_p2d.Y));
4356 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4359 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4360 v3s16 real_pos = v3s16(x0,y0,z0) + p_nodes;
4365 NOTE: If there are some man-made structures above the
4366 newly created block, they won't be taken into account.
4368 /*if(real_y > surface_y)
4369 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);*/
4375 v3f real_pos_f = intToFloat(real_pos, 1);
4376 v2f real_pos_f_2d(real_pos_f.X, real_pos_f.Z);
4378 bool is_ground = is_base_ground(m_seed,
4379 real_pos_f, &depth_guess);
4381 // Estimate the surface height
4382 float surface_y_f = (float)real_y + depth_guess;
4383 s16 surface_y = real_y + depth_guess;
4385 // Get some statistics of surface height
4386 if(surface_y < lowest_ground_y)
4387 lowest_ground_y = surface_y;
4388 if(surface_y > highest_ground_y)
4389 highest_ground_y = surface_y;
4391 // If node is not ground, it's air or water
4392 if(is_ground == false)
4394 // If under water level, it's water
4395 if(real_y < WATER_LEVEL)
4397 n.d = water_material;
4398 n.setLight(LIGHTBANK_DAY,
4399 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4401 Add to transforming liquid queue (in case it'd
4404 m_transforming_liquid.push_back(real_pos);
4410 // Else it's ground or dungeons (air)
4413 // If it's surface_depth under ground, it's stone
4414 if((float)real_y <= surface_y_f - surface_depth - 0.75)
4416 if(is_underground_mud(m_seed, real_pos_f))
4419 n.d = CONTENT_STONE;
4421 else if(surface_y_f <= WATER_LEVEL + 2.1
4422 && get_have_sand(m_seed, real_pos_f_2d))
4428 /*// It is mud if it is under the first ground
4429 // level or under water
4430 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4436 n.d = CONTENT_GRASS;
4441 /*// If under water level, it's mud
4442 if(real_y < WATER_LEVEL)
4444 // Only the topmost node is grass
4445 else if(real_y <= surface_y - 1)
4448 n.d = CONTENT_GRASS;*/
4452 block->setNode(v3s16(x0,y0,z0), n);
4455 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4460 NOTE: If there are some man-made structures above the
4461 newly created block, they won't be taken into account.
4463 if(real_y > surface_y)
4464 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4470 // If node is over heightmap y, it's air or water
4471 if(real_y > surface_y)
4473 // If under water level, it's water
4474 if(real_y < WATER_LEVEL)
4476 n.d = water_material;
4477 n.setLight(LIGHTBANK_DAY,
4478 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4480 Add to transforming liquid queue (in case it'd
4483 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4484 m_transforming_liquid.push_back(real_pos);
4490 // Else it's ground or dungeons (air)
4493 // If it's surface_depth under ground, it's stone
4494 if(real_y <= surface_y - surface_depth)
4496 n.d = CONTENT_STONE;
4500 // It is mud if it is under the first ground
4501 // level or under water
4502 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4508 n.d = CONTENT_GRASS;
4511 //n.d = CONTENT_MUD;
4513 /*// If under water level, it's mud
4514 if(real_y < WATER_LEVEL)
4516 // Only the topmost node is grass
4517 else if(real_y <= surface_y - 1)
4520 n.d = CONTENT_GRASS;*/
4524 block->setNode(v3s16(x0,y0,z0), n);
4529 else // BT_GROUND, BT_SKY or anything else
4532 if(block_type == BT_GROUND)
4534 //n_fill.d = CONTENT_STONE;
4536 else if(block_type == BT_SKY)
4538 n_fill.d = CONTENT_AIR;
4539 n_fill.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4543 n_fill.d = CONTENT_MESE;
4547 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4548 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4549 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4551 //MapNode n = block->getNode(v3s16(x0,y0,z0));
4552 block->setNode(v3s16(x0,y0,z0), n_fill);
4559 Calculate some helper variables
4562 // Completely underground if the highest part of block is under lowest
4564 // This has to be very sure; it's probably one too strict now but
4565 // that's just better.
4566 bool completely_underground =
4567 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4569 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4571 bool mostly_underwater_surface = false;
4572 if(highest_ground_y < WATER_LEVEL
4573 && some_part_underground && !completely_underground)
4574 mostly_underwater_surface = true;
4577 Get local attributes
4580 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4582 //float caves_amount = 0.5;
4587 NOTE: BEWARE: Too big amount of attribute points slows verything
4589 1 interpolation from 5000 points takes 2-3ms.
4591 //TimeTaker timer("generateBlock() local attribute retrieval");
4592 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4593 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4594 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4598 //dstream<<"generateBlock(): Done"<<std::endl;
4601 // Set to true if has caves.
4602 // Set when some non-air is changed to air when making caves.
4603 bool has_dungeons = false;
4609 // Initialize temporary table
4610 const s32 ued = MAP_BLOCKSIZE;
4611 bool underground_emptiness[ued*ued*ued];
4612 for(s32 i=0; i<ued*ued*ued; i++)
4614 underground_emptiness[i] = 0;
4621 Initialize orp and ors. Try to find if some neighboring
4622 MapBlock has a tunnel ended in its side
4626 (float)(myrand()%ued)+0.5,
4627 (float)(myrand()%ued)+0.5,
4628 (float)(myrand()%ued)+0.5
4631 bool found_existing = false;
4637 for(s16 y=0; y<ued; y++)
4638 for(s16 x=0; x<ued; x++)
4640 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4641 if(getNode(ap).d == CONTENT_AIR)
4643 orp = v3f(x+1,y+1,0);
4644 found_existing = true;
4645 goto continue_generating;
4649 catch(InvalidPositionException &e){}
4655 for(s16 y=0; y<ued; y++)
4656 for(s16 x=0; x<ued; x++)
4658 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4659 if(getNode(ap).d == CONTENT_AIR)
4661 orp = v3f(x+1,y+1,ued-1);
4662 found_existing = true;
4663 goto continue_generating;
4667 catch(InvalidPositionException &e){}
4673 for(s16 y=0; y<ued; y++)
4674 for(s16 z=0; z<ued; z++)
4676 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4677 if(getNode(ap).d == CONTENT_AIR)
4679 orp = v3f(0,y+1,z+1);
4680 found_existing = true;
4681 goto continue_generating;
4685 catch(InvalidPositionException &e){}
4691 for(s16 y=0; y<ued; y++)
4692 for(s16 z=0; z<ued; z++)
4694 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4695 if(getNode(ap).d == CONTENT_AIR)
4697 orp = v3f(ued-1,y+1,z+1);
4698 found_existing = true;
4699 goto continue_generating;
4703 catch(InvalidPositionException &e){}
4709 for(s16 x=0; x<ued; x++)
4710 for(s16 z=0; z<ued; z++)
4712 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4713 if(getNode(ap).d == CONTENT_AIR)
4715 orp = v3f(x+1,0,z+1);
4716 found_existing = true;
4717 goto continue_generating;
4721 catch(InvalidPositionException &e){}
4727 for(s16 x=0; x<ued; x++)
4728 for(s16 z=0; z<ued; z++)
4730 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4731 if(getNode(ap).d == CONTENT_AIR)
4733 orp = v3f(x+1,ued-1,z+1);
4734 found_existing = true;
4735 goto continue_generating;
4739 catch(InvalidPositionException &e){}
4741 continue_generating:
4744 Choose whether to actually generate dungeon
4746 bool do_generate_dungeons = true;
4747 // Don't generate if no part is underground
4748 if(!some_part_underground)
4750 do_generate_dungeons = false;
4752 // Don't generate if mostly underwater surface
4753 /*else if(mostly_underwater_surface)
4755 do_generate_dungeons = false;
4757 // Partly underground = cave
4758 else if(!completely_underground)
4760 //do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4761 do_generate_dungeons = false;
4763 // Found existing dungeon underground
4764 else if(found_existing && completely_underground)
4766 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4768 // Underground and no dungeons found
4771 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4774 if(do_generate_dungeons)
4777 Generate some tunnel starting from orp and ors
4779 for(u16 i=0; i<3; i++)
4782 (float)(myrand()%ued)+0.5,
4783 (float)(myrand()%ued)+0.5,
4784 (float)(myrand()%ued)+0.5
4788 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4792 for(float f=0; f<1.0; f+=0.04)
4794 v3f fp = orp + vec * f;
4795 v3s16 cp(fp.X, fp.Y, fp.Z);
4797 s16 d1 = d0 + rs - 1;
4798 for(s16 z0=d0; z0<=d1; z0++)
4800 s16 si = rs - abs(z0);
4801 for(s16 x0=-si; x0<=si-1; x0++)
4803 s16 si2 = rs - abs(x0);
4804 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4810 if(isInArea(p, ued) == false)
4812 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4825 Apply temporary cave data to block
4828 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4829 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4831 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4833 MapNode n = block->getNode(v3s16(x0,y0,z0));
4836 if(underground_emptiness[
4837 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4838 +ued*(y0*ued/MAP_BLOCKSIZE)
4839 +(x0*ued/MAP_BLOCKSIZE)])
4841 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4844 has_dungeons = true;
4850 block->setNode(v3s16(x0,y0,z0), n);
4856 This is used for guessing whether or not the block should
4857 receive sunlight from the top if the block above doesn't exist
4859 block->setIsUnderground(completely_underground);
4862 Force lighting update if some part of block is partly
4863 underground and has caves.
4865 /*if(some_part_underground && !completely_underground && has_dungeons)
4867 //dstream<<"Half-ground caves"<<std::endl;
4868 lighting_invalidated_blocks[block->getPos()] = block;
4871 // DEBUG: Always update lighting
4872 //lighting_invalidated_blocks[block->getPos()] = block;
4878 if(some_part_underground)
4880 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4885 for(s16 i=0; i<underground_level/4 + 1; i++)
4887 if(myrand()%50 == 0)
4890 (myrand()%(MAP_BLOCKSIZE-2))+1,
4891 (myrand()%(MAP_BLOCKSIZE-2))+1,
4892 (myrand()%(MAP_BLOCKSIZE-2))+1
4898 for(u16 i=0; i<27; i++)
4900 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4902 block->setNode(cp+g_27dirs[i], n);
4910 u16 coal_amount = 60;
4911 u16 coal_rareness = 120 / coal_amount;
4912 if(coal_rareness == 0)
4914 if(myrand()%coal_rareness == 0)
4916 u16 a = myrand() % 16;
4917 u16 amount = coal_amount * a*a*a / 1000;
4918 for(s16 i=0; i<amount; i++)
4921 (myrand()%(MAP_BLOCKSIZE-2))+1,
4922 (myrand()%(MAP_BLOCKSIZE-2))+1,
4923 (myrand()%(MAP_BLOCKSIZE-2))+1
4927 n.d = CONTENT_STONE;
4928 n.param = MINERAL_COAL;
4930 for(u16 i=0; i<27; i++)
4932 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4934 block->setNode(cp+g_27dirs[i], n);
4942 u16 iron_amount = 40;
4943 u16 iron_rareness = 80 / iron_amount;
4944 if(iron_rareness == 0)
4946 if(myrand()%iron_rareness == 0)
4948 u16 a = myrand() % 16;
4949 u16 amount = iron_amount * a*a*a / 1000;
4950 for(s16 i=0; i<amount; i++)
4953 (myrand()%(MAP_BLOCKSIZE-2))+1,
4954 (myrand()%(MAP_BLOCKSIZE-2))+1,
4955 (myrand()%(MAP_BLOCKSIZE-2))+1
4959 n.d = CONTENT_STONE;
4960 n.param = MINERAL_IRON;
4962 for(u16 i=0; i<27; i++)
4964 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4966 block->setNode(cp+g_27dirs[i], n);
4973 Create a few rats in empty blocks underground
4975 if(completely_underground)
4977 //for(u16 i=0; i<2; i++)
4980 (myrand()%(MAP_BLOCKSIZE-2))+1,
4981 (myrand()%(MAP_BLOCKSIZE-2))+1,
4982 (myrand()%(MAP_BLOCKSIZE-2))+1
4985 // Check that the place is empty
4986 //if(!is_ground_content(block->getNode(cp).d))
4989 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4990 block->addObject(obj);
4998 sector->insertBlock(block);
5000 // Lighting is invalid after generation for surface blocks
5001 if(block_type == BT_SURFACE)
5004 block->setLightingExpired(true);
5005 lighting_invalidated_blocks.insert(p, block);
5007 block->setLightingExpired(false);
5010 // Lighting is not invalid for other blocks
5013 block->setLightingExpired(false);
5020 if(some_part_underground && !completely_underground)
5022 MapVoxelManipulator vm(this);
5024 double a = tree_amount_2d(m_seed, v2s16(p_nodes.X+8, p_nodes.Z+8));
5025 u16 tree_count = (u16)(a*MAP_BLOCKSIZE*MAP_BLOCKSIZE);
5026 for(u16 i=0; i<tree_count/2; i++)
5028 v3s16 tree_p = p_nodes + v3s16(
5029 myrand_range(0,MAP_BLOCKSIZE-1),
5031 myrand_range(0,MAP_BLOCKSIZE-1)
5034 /*bool is_ground =*/ is_base_ground(m_seed,
5035 intToFloat(tree_p, 1), &depth_guess);
5036 tree_p.Y += (depth_guess - 0.5);
5037 if(tree_p.Y <= WATER_LEVEL)
5039 make_tree(vm, tree_p);
5042 vm.blitBack(changed_blocks);
5051 <<"lighting_invalidated_blocks.size()"
5055 <<" "<<lighting_invalidated_blocks.size()
5056 <<", "<<has_dungeons
5057 <<", "<<completely_underground
5058 <<", "<<some_part_underground
5065 MapBlock * ServerMap::createBlock(v3s16 p)
5067 DSTACK("%s: p=(%d,%d,%d)",
5068 __FUNCTION_NAME, p.X, p.Y, p.Z);
5071 Do not create over-limit
5073 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5074 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5075 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5076 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5077 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5078 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
5079 throw InvalidPositionException("createBlock(): pos. over limit");
5081 v2s16 p2d(p.X, p.Z);
5084 This will create or load a sector if not found in memory.
5085 If block exists on disk, it will be loaded.
5087 NOTE: On old save formats, this will be slow, as it generates
5088 lighting on blocks for them.
5090 ServerMapSector *sector;
5092 sector = (ServerMapSector*)createSector(p2d);
5093 assert(sector->getId() == MAPSECTOR_SERVER);
5095 catch(InvalidPositionException &e)
5097 dstream<<"createBlock: createSector() failed"<<std::endl;
5101 NOTE: This should not be done, or at least the exception
5102 should not be passed on as std::exception, because it
5103 won't be catched at all.
5105 /*catch(std::exception &e)
5107 dstream<<"createBlock: createSector() failed: "
5108 <<e.what()<<std::endl;
5113 Try to get a block from the sector
5116 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
5120 block = sector->createBlankBlock(block_y);
5124 MapBlock * ServerMap::emergeBlock(
5126 bool only_from_disk,
5127 core::map<v3s16, MapBlock*> &changed_blocks,
5128 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
5131 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
5133 p.X, p.Y, p.Z, only_from_disk);
5136 Do not generate over-limit
5138 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5139 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5140 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5141 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5142 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5143 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
5144 throw InvalidPositionException("emergeBlock(): pos. over limit");
5146 v2s16 p2d(p.X, p.Z);
5149 This will create or load a sector if not found in memory.
5150 If block exists on disk, it will be loaded.
5152 ServerMapSector *sector;
5154 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
5155 assert(sector->getId() == MAPSECTOR_SERVER);
5157 catch(InvalidPositionException &e)
5159 dstream<<"emergeBlock: emergeSector() failed: "
5160 <<e.what()<<std::endl;
5161 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
5163 <<"You could try to delete it."<<std::endl;
5166 catch(VersionMismatchException &e)
5168 dstream<<"emergeBlock: emergeSector() failed: "
5169 <<e.what()<<std::endl;
5170 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
5172 <<"You could try to delete it."<<std::endl;
5176 NOTE: This should not be done, or at least the exception
5177 should not be passed on as std::exception, because it
5178 won't be catched at all.
5180 /*catch(std::exception &e)
5182 dstream<<"emergeBlock: emergeSector() failed: "
5183 <<e.what()<<std::endl;
5184 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
5186 <<"You could try to delete it."<<std::endl;
5191 Try to get a block from the sector
5194 bool does_not_exist = false;
5195 bool lighting_expired = false;
5196 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
5200 does_not_exist = true;
5202 else if(block->isDummy() == true)
5204 does_not_exist = true;
5206 else if(block->getLightingExpired())
5208 lighting_expired = true;
5213 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
5218 If block was not found on disk and not going to generate a
5219 new one, make sure there is a dummy block in place.
5221 if(only_from_disk && (does_not_exist || lighting_expired))
5223 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
5227 // Create dummy block
5228 block = new MapBlock(this, p, true);
5230 // Add block to sector
5231 sector->insertBlock(block);
5237 //dstream<<"Not found on disk, generating."<<std::endl;
5239 //TimeTaker("emergeBlock() generate");
5241 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
5244 If the block doesn't exist, generate the block.
5248 block = generateBlock(p, block, sector, changed_blocks,
5249 lighting_invalidated_blocks);
5251 lighting_expired = block->getLightingExpired();
5254 if(lighting_expired)
5256 lighting_invalidated_blocks.insert(p, block);
5260 Initially update sunlight
5263 if(lighting_expired)
5265 core::map<v3s16, bool> light_sources;
5266 bool black_air_left = false;
5267 bool bottom_invalid =
5268 block->propagateSunlight(light_sources, true,
5269 &black_air_left, true);
5271 // If sunlight didn't reach everywhere and part of block is
5272 // above ground, lighting has to be properly updated
5273 //if(black_air_left && some_part_underground)
5276 lighting_invalidated_blocks[block->getPos()] = block;
5281 lighting_invalidated_blocks[block->getPos()] = block;
5288 s16 ServerMap::findGroundLevel(v2s16 p2d)
5291 Uh, just do something random...
5293 // Find existing map from top to down
5296 v3s16 p(p2d.X, max, p2d.Y);
5297 for(; p.Y>min; p.Y--)
5299 MapNode n = getNodeNoEx(p);
5300 if(n.d != CONTENT_IGNORE)
5305 // If this node is not air, go to plan b
5306 if(getNodeNoEx(p).d != CONTENT_AIR)
5308 // Search existing walkable and return it
5309 for(; p.Y>min; p.Y--)
5311 MapNode n = getNodeNoEx(p);
5312 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
5318 Plan B: Get from map generator perlin noise function
5320 double level = base_rock_level_2d(m_seed, p2d);
5324 void ServerMap::createDir(std::string path)
5326 if(fs::CreateDir(path) == false)
5328 m_dout<<DTIME<<"ServerMap: Failed to create directory "
5329 <<"\""<<path<<"\""<<std::endl;
5330 throw BaseException("ServerMap failed to create directory");
5334 std::string ServerMap::getSectorSubDir(v2s16 pos)
5337 snprintf(cc, 9, "%.4x%.4x",
5338 (unsigned int)pos.X&0xffff,
5339 (unsigned int)pos.Y&0xffff);
5341 return std::string(cc);
5344 std::string ServerMap::getSectorDir(v2s16 pos)
5346 return m_savedir + "/sectors/" + getSectorSubDir(pos);
5349 v2s16 ServerMap::getSectorPos(std::string dirname)
5351 if(dirname.size() != 8)
5352 throw InvalidFilenameException("Invalid sector directory name");
5354 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
5356 throw InvalidFilenameException("Invalid sector directory name");
5357 v2s16 pos((s16)x, (s16)y);
5361 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
5363 v2s16 p2d = getSectorPos(sectordir);
5365 if(blockfile.size() != 4){
5366 throw InvalidFilenameException("Invalid block filename");
5369 int r = sscanf(blockfile.c_str(), "%4x", &y);
5371 throw InvalidFilenameException("Invalid block filename");
5372 return v3s16(p2d.X, y, p2d.Y);
5375 void ServerMap::save(bool only_changed)
5377 DSTACK(__FUNCTION_NAME);
5378 if(m_map_saving_enabled == false)
5380 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
5384 if(only_changed == false)
5385 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
5391 u32 sector_meta_count = 0;
5392 u32 block_count = 0;
5395 JMutexAutoLock lock(m_sector_mutex);
5397 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
5398 for(; i.atEnd() == false; i++)
5400 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
5401 assert(sector->getId() == MAPSECTOR_SERVER);
5403 if(sector->differs_from_disk || only_changed == false)
5405 saveSectorMeta(sector);
5406 sector_meta_count++;
5408 core::list<MapBlock*> blocks;
5409 sector->getBlocks(blocks);
5410 core::list<MapBlock*>::Iterator j;
5411 for(j=blocks.begin(); j!=blocks.end(); j++)
5413 MapBlock *block = *j;
5414 if(block->getChangedFlag() || only_changed == false)
5419 /*dstream<<"ServerMap: Written block ("
5420 <<block->getPos().X<<","
5421 <<block->getPos().Y<<","
5422 <<block->getPos().Z<<")"
5431 Only print if something happened or saved whole map
5433 if(only_changed == false || sector_meta_count != 0
5434 || block_count != 0)
5436 dstream<<DTIME<<"ServerMap: Written: "
5437 <<sector_meta_count<<" sector metadata files, "
5438 <<block_count<<" block files"
5443 void ServerMap::loadAll()
5445 DSTACK(__FUNCTION_NAME);
5446 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5451 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5453 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5455 JMutexAutoLock lock(m_sector_mutex);
5458 s32 printed_counter = -100000;
5459 s32 count = list.size();
5461 std::vector<fs::DirListNode>::iterator i;
5462 for(i=list.begin(); i!=list.end(); i++)
5464 if(counter > printed_counter + 10)
5466 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5467 printed_counter = counter;
5471 MapSector *sector = NULL;
5473 // We want directories
5477 sector = loadSectorMeta(i->name);
5479 catch(InvalidFilenameException &e)
5481 // This catches unknown crap in directory
5484 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5485 (m_savedir+"/sectors/"+i->name);
5486 std::vector<fs::DirListNode>::iterator i2;
5487 for(i2=list2.begin(); i2!=list2.end(); i2++)
5493 loadBlock(i->name, i2->name, sector);
5495 catch(InvalidFilenameException &e)
5497 // This catches unknown crap in directory
5501 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5505 void ServerMap::saveMasterHeightmap()
5507 DSTACK(__FUNCTION_NAME);
5509 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5511 createDir(m_savedir);
5513 /*std::string fullpath = m_savedir + "/master_heightmap";
5514 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5515 if(o.good() == false)
5516 throw FileNotGoodException("Cannot open master heightmap");*/
5518 // Format used for writing
5519 //u8 version = SER_FMT_VER_HIGHEST;
5522 void ServerMap::loadMasterHeightmap()
5524 DSTACK(__FUNCTION_NAME);
5526 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5528 /*std::string fullpath = m_savedir + "/master_heightmap";
5529 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5530 if(is.good() == false)
5531 throw FileNotGoodException("Cannot open master heightmap");*/
5535 void ServerMap::saveMapMeta()
5537 DSTACK(__FUNCTION_NAME);
5539 dstream<<"INFO: ServerMap::saveMapMeta(): "
5540 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5543 createDir(m_savedir);
5545 std::string fullpath = m_savedir + "/map_meta.txt";
5546 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5547 if(os.good() == false)
5549 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5550 <<"could not open"<<fullpath<<std::endl;
5551 throw FileNotGoodException("Cannot open chunk metadata");
5555 params.setU64("seed", m_seed);
5556 params.setS32("chunksize", m_chunksize);
5558 params.writeLines(os);
5560 os<<"[end_of_params]\n";
5564 void ServerMap::loadMapMeta()
5566 DSTACK(__FUNCTION_NAME);
5568 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
5571 std::string fullpath = m_savedir + "/map_meta.txt";
5572 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5573 if(is.good() == false)
5575 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5576 <<"could not open"<<fullpath<<std::endl;
5577 throw FileNotGoodException("Cannot open chunk metadata");
5585 throw SerializationError
5586 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5588 std::getline(is, line);
5589 std::string trimmedline = trim(line);
5590 if(trimmedline == "[end_of_params]")
5592 params.parseConfigLine(line);
5595 m_seed = params.getU64("seed");
5596 m_chunksize = params.getS32("chunksize");
5598 dstream<<"INFO: ServerMap::loadMapMeta(): "
5599 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5603 void ServerMap::saveChunkMeta()
5605 DSTACK(__FUNCTION_NAME);
5607 u32 count = m_chunks.size();
5609 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5610 <<count<<" chunks"<<std::endl;
5612 createDir(m_savedir);
5614 std::string fullpath = m_savedir + "/chunk_meta";
5615 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5616 if(os.good() == false)
5618 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5619 <<"could not open"<<fullpath<<std::endl;
5620 throw FileNotGoodException("Cannot open chunk metadata");
5626 os.write((char*)&version, 1);
5631 writeU32(buf, count);
5632 os.write((char*)buf, 4);
5634 for(core::map<v2s16, MapChunk*>::Iterator
5635 i = m_chunks.getIterator();
5636 i.atEnd()==false; i++)
5638 v2s16 p = i.getNode()->getKey();
5639 MapChunk *chunk = i.getNode()->getValue();
5642 os.write((char*)buf, 4);
5644 chunk->serialize(os, version);
5648 void ServerMap::loadChunkMeta()
5650 DSTACK(__FUNCTION_NAME);
5652 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5655 std::string fullpath = m_savedir + "/chunk_meta";
5656 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5657 if(is.good() == false)
5659 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5660 <<"could not open"<<fullpath<<std::endl;
5661 throw FileNotGoodException("Cannot open chunk metadata");
5667 is.read((char*)&version, 1);
5672 is.read((char*)buf, 4);
5673 u32 count = readU32(buf);
5675 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5676 <<count<<" chunks"<<std::endl;
5678 for(u32 i=0; i<count; i++)
5681 MapChunk *chunk = new MapChunk();
5683 is.read((char*)buf, 4);
5686 chunk->deSerialize(is, version);
5687 m_chunks.insert(p, chunk);
5691 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5693 DSTACK(__FUNCTION_NAME);
5694 // Format used for writing
5695 u8 version = SER_FMT_VER_HIGHEST;
5697 v2s16 pos = sector->getPos();
5698 createDir(m_savedir);
5699 createDir(m_savedir+"/sectors");
5700 std::string dir = getSectorDir(pos);
5703 std::string fullpath = dir + "/meta";
5704 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5705 if(o.good() == false)
5706 throw FileNotGoodException("Cannot open sector metafile");
5708 sector->serialize(o, version);
5710 sector->differs_from_disk = false;
5713 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5715 DSTACK(__FUNCTION_NAME);
5717 v2s16 p2d = getSectorPos(dirname);
5718 std::string dir = m_savedir + "/sectors/" + dirname;
5720 std::string fullpath = dir + "/meta";
5721 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5722 if(is.good() == false)
5723 throw FileNotGoodException("Cannot open sector metafile");
5725 ServerMapSector *sector = ServerMapSector::deSerialize
5726 (is, this, p2d, m_sectors);
5728 sector->differs_from_disk = false;
5733 bool ServerMap::loadSectorFull(v2s16 p2d)
5735 DSTACK(__FUNCTION_NAME);
5736 std::string sectorsubdir = getSectorSubDir(p2d);
5738 MapSector *sector = NULL;
5740 JMutexAutoLock lock(m_sector_mutex);
5743 sector = loadSectorMeta(sectorsubdir);
5745 catch(InvalidFilenameException &e)
5749 catch(FileNotGoodException &e)
5753 catch(std::exception &e)
5761 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5762 (m_savedir+"/sectors/"+sectorsubdir);
5763 std::vector<fs::DirListNode>::iterator i2;
5764 for(i2=list2.begin(); i2!=list2.end(); i2++)
5770 loadBlock(sectorsubdir, i2->name, sector);
5772 catch(InvalidFilenameException &e)
5774 // This catches unknown crap in directory
5780 void ServerMap::saveBlock(MapBlock *block)
5782 DSTACK(__FUNCTION_NAME);
5784 Dummy blocks are not written
5786 if(block->isDummy())
5788 /*v3s16 p = block->getPos();
5789 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5790 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5794 // Format used for writing
5795 u8 version = SER_FMT_VER_HIGHEST;
5797 v3s16 p3d = block->getPos();
5798 v2s16 p2d(p3d.X, p3d.Z);
5799 createDir(m_savedir);
5800 createDir(m_savedir+"/sectors");
5801 std::string dir = getSectorDir(p2d);
5804 // Block file is map/sectors/xxxxxxxx/xxxx
5806 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5807 std::string fullpath = dir + "/" + cc;
5808 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5809 if(o.good() == false)
5810 throw FileNotGoodException("Cannot open block data");
5813 [0] u8 serialization version
5816 o.write((char*)&version, 1);
5818 block->serialize(o, version);
5821 Versions up from 9 have block objects.
5825 block->serializeObjects(o, version);
5828 // We just wrote it to the disk
5829 block->resetChangedFlag();
5832 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5834 DSTACK(__FUNCTION_NAME);
5838 // Block file is map/sectors/xxxxxxxx/xxxx
5839 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5840 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5841 if(is.good() == false)
5842 throw FileNotGoodException("Cannot open block file");
5844 v3s16 p3d = getBlockPos(sectordir, blockfile);
5845 v2s16 p2d(p3d.X, p3d.Z);
5847 assert(sector->getPos() == p2d);
5849 u8 version = SER_FMT_VER_INVALID;
5850 is.read((char*)&version, 1);
5853 throw SerializationError("ServerMap::loadBlock(): Failed"
5854 " to read MapBlock version");
5856 /*u32 block_size = MapBlock::serializedLength(version);
5857 SharedBuffer<u8> data(block_size);
5858 is.read((char*)*data, block_size);*/
5860 // This will always return a sector because we're the server
5861 //MapSector *sector = emergeSector(p2d);
5863 MapBlock *block = NULL;
5864 bool created_new = false;
5866 block = sector->getBlockNoCreate(p3d.Y);
5868 catch(InvalidPositionException &e)
5870 block = sector->createBlankBlockNoInsert(p3d.Y);
5874 // deserialize block data
5875 block->deSerialize(is, version);
5878 Versions up from 9 have block objects.
5882 block->updateObjects(is, version, NULL, 0);
5886 sector->insertBlock(block);
5889 Convert old formats to new and save
5892 // Save old format blocks in new format
5893 if(version < SER_FMT_VER_HIGHEST)
5898 // We just loaded it from the disk, so it's up-to-date.
5899 block->resetChangedFlag();
5902 catch(SerializationError &e)
5904 dstream<<"WARNING: Invalid block data on disk "
5905 "(SerializationError). Ignoring. "
5906 "A new one will be generated."
5911 void ServerMap::PrintInfo(std::ostream &out)
5922 ClientMap::ClientMap(
5924 MapDrawControl &control,
5925 scene::ISceneNode* parent,
5926 scene::ISceneManager* mgr,
5930 scene::ISceneNode(parent, mgr, id),
5933 m_camera_position(0,0,0),
5934 m_camera_direction(0,0,1)
5936 m_camera_mutex.Init();
5937 assert(m_camera_mutex.IsInitialized());
5939 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5940 BS*1000000,BS*1000000,BS*1000000);
5943 ClientMap::~ClientMap()
5945 /*JMutexAutoLock lock(mesh_mutex);
5954 MapSector * ClientMap::emergeSector(v2s16 p2d)
5956 DSTACK(__FUNCTION_NAME);
5957 // Check that it doesn't exist already
5959 return getSectorNoGenerate(p2d);
5961 catch(InvalidPositionException &e)
5966 ClientMapSector *sector = new ClientMapSector(this, p2d);
5969 JMutexAutoLock lock(m_sector_mutex);
5970 m_sectors.insert(p2d, sector);
5976 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5978 DSTACK(__FUNCTION_NAME);
5979 ClientMapSector *sector = NULL;
5981 JMutexAutoLock lock(m_sector_mutex);
5983 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5987 sector = (ClientMapSector*)n->getValue();
5988 assert(sector->getId() == MAPSECTOR_CLIENT);
5992 sector = new ClientMapSector(this, p2d);
5994 JMutexAutoLock lock(m_sector_mutex);
5995 m_sectors.insert(p2d, sector);
5999 sector->deSerialize(is);
6002 void ClientMap::OnRegisterSceneNode()
6006 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
6007 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
6010 ISceneNode::OnRegisterSceneNode();
6013 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
6015 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
6016 DSTACK(__FUNCTION_NAME);
6018 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
6021 Get time for measuring timeout.
6023 Measuring time is very useful for long delays when the
6024 machine is swapping a lot.
6026 int time1 = time(0);
6028 u32 daynight_ratio = m_client->getDayNightRatio();
6030 m_camera_mutex.Lock();
6031 v3f camera_position = m_camera_position;
6032 v3f camera_direction = m_camera_direction;
6033 m_camera_mutex.Unlock();
6036 Get all blocks and draw all visible ones
6039 v3s16 cam_pos_nodes(
6040 camera_position.X / BS,
6041 camera_position.Y / BS,
6042 camera_position.Z / BS);
6044 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
6046 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
6047 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
6049 // Take a fair amount as we will be dropping more out later
6051 p_nodes_min.X / MAP_BLOCKSIZE - 1,
6052 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
6053 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
6055 p_nodes_max.X / MAP_BLOCKSIZE + 1,
6056 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
6057 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
6059 u32 vertex_count = 0;
6061 // For limiting number of mesh updates per frame
6062 u32 mesh_update_count = 0;
6064 u32 blocks_would_have_drawn = 0;
6065 u32 blocks_drawn = 0;
6067 //NOTE: The sectors map should be locked but we're not doing it
6068 // because it'd cause too much delays
6070 int timecheck_counter = 0;
6071 core::map<v2s16, MapSector*>::Iterator si;
6072 si = m_sectors.getIterator();
6073 for(; si.atEnd() == false; si++)
6076 timecheck_counter++;
6077 if(timecheck_counter > 50)
6079 timecheck_counter = 0;
6080 int time2 = time(0);
6081 if(time2 > time1 + 4)
6083 dstream<<"ClientMap::renderMap(): "
6084 "Rendering takes ages, returning."
6091 MapSector *sector = si.getNode()->getValue();
6092 v2s16 sp = sector->getPos();
6094 if(m_control.range_all == false)
6096 if(sp.X < p_blocks_min.X
6097 || sp.X > p_blocks_max.X
6098 || sp.Y < p_blocks_min.Z
6099 || sp.Y > p_blocks_max.Z)
6103 core::list< MapBlock * > sectorblocks;
6104 sector->getBlocks(sectorblocks);
6110 core::list< MapBlock * >::Iterator i;
6111 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6113 MapBlock *block = *i;
6116 Compare block position to camera position, skip
6117 if not seen on display
6120 float range = 100000 * BS;
6121 if(m_control.range_all == false)
6122 range = m_control.wanted_range * BS;
6125 if(isBlockInSight(block->getPos(), camera_position,
6126 camera_direction, range, &d) == false)
6132 /*if(m_control.range_all == false &&
6133 d - 0.5*BS*MAP_BLOCKSIZE > range)
6138 Update expired mesh (used for day/night change)
6141 bool mesh_expired = false;
6144 JMutexAutoLock lock(block->mesh_mutex);
6146 mesh_expired = block->getMeshExpired();
6148 // Mesh has not been expired and there is no mesh:
6149 // block has no content
6150 if(block->mesh == NULL && mesh_expired == false)
6154 f32 faraway = BS*50;
6155 //f32 faraway = m_control.wanted_range * BS;
6158 This has to be done with the mesh_mutex unlocked
6160 // Pretty random but this should work somewhat nicely
6161 if(mesh_expired && (
6162 (mesh_update_count < 3
6163 && (d < faraway || mesh_update_count < 2)
6166 (m_control.range_all && mesh_update_count < 20)
6169 /*if(mesh_expired && mesh_update_count < 6
6170 && (d < faraway || mesh_update_count < 3))*/
6172 mesh_update_count++;
6174 // Mesh has been expired: generate new mesh
6175 //block->updateMeshes(daynight_i);
6176 block->updateMesh(daynight_ratio);
6178 mesh_expired = false;
6182 Don't draw an expired mesh that is far away
6184 /*if(mesh_expired && d >= faraway)
6187 // Instead, delete it
6188 JMutexAutoLock lock(block->mesh_mutex);
6191 block->mesh->drop();
6194 // And continue to next block
6199 Draw the faces of the block
6202 JMutexAutoLock lock(block->mesh_mutex);
6204 scene::SMesh *mesh = block->mesh;
6209 blocks_would_have_drawn++;
6210 if(blocks_drawn >= m_control.wanted_max_blocks
6211 && m_control.range_all == false
6212 && d > m_control.wanted_min_range * BS)
6216 u32 c = mesh->getMeshBufferCount();
6218 for(u32 i=0; i<c; i++)
6220 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
6221 const video::SMaterial& material = buf->getMaterial();
6222 video::IMaterialRenderer* rnd =
6223 driver->getMaterialRenderer(material.MaterialType);
6224 bool transparent = (rnd && rnd->isTransparent());
6225 // Render transparent on transparent pass and likewise.
6226 if(transparent == is_transparent_pass)
6229 This *shouldn't* hurt too much because Irrlicht
6230 doesn't change opengl textures if the old
6231 material is set again.
6233 driver->setMaterial(buf->getMaterial());
6234 driver->drawMeshBuffer(buf);
6235 vertex_count += buf->getVertexCount();
6239 } // foreach sectorblocks
6242 m_control.blocks_drawn = blocks_drawn;
6243 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
6245 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
6246 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
6249 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
6250 core::map<v3s16, MapBlock*> *affected_blocks)
6252 bool changed = false;
6254 Add it to all blocks touching it
6257 v3s16(0,0,0), // this
6258 v3s16(0,0,1), // back
6259 v3s16(0,1,0), // top
6260 v3s16(1,0,0), // right
6261 v3s16(0,0,-1), // front
6262 v3s16(0,-1,0), // bottom
6263 v3s16(-1,0,0), // left
6265 for(u16 i=0; i<7; i++)
6267 v3s16 p2 = p + dirs[i];
6268 // Block position of neighbor (or requested) node
6269 v3s16 blockpos = getNodeBlockPos(p2);
6270 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6271 if(blockref == NULL)
6273 // Relative position of requested node
6274 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
6275 if(blockref->setTempMod(relpos, mod))
6280 if(changed && affected_blocks!=NULL)
6282 for(u16 i=0; i<7; i++)
6284 v3s16 p2 = p + dirs[i];
6285 // Block position of neighbor (or requested) node
6286 v3s16 blockpos = getNodeBlockPos(p2);
6287 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6288 if(blockref == NULL)
6290 affected_blocks->insert(blockpos, blockref);
6296 bool ClientMap::clearTempMod(v3s16 p,
6297 core::map<v3s16, MapBlock*> *affected_blocks)
6299 bool changed = false;
6301 v3s16(0,0,0), // this
6302 v3s16(0,0,1), // back
6303 v3s16(0,1,0), // top
6304 v3s16(1,0,0), // right
6305 v3s16(0,0,-1), // front
6306 v3s16(0,-1,0), // bottom
6307 v3s16(-1,0,0), // left
6309 for(u16 i=0; i<7; i++)
6311 v3s16 p2 = p + dirs[i];
6312 // Block position of neighbor (or requested) node
6313 v3s16 blockpos = getNodeBlockPos(p2);
6314 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6315 if(blockref == NULL)
6317 // Relative position of requested node
6318 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
6319 if(blockref->clearTempMod(relpos))
6324 if(changed && affected_blocks!=NULL)
6326 for(u16 i=0; i<7; i++)
6328 v3s16 p2 = p + dirs[i];
6329 // Block position of neighbor (or requested) node
6330 v3s16 blockpos = getNodeBlockPos(p2);
6331 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6332 if(blockref == NULL)
6334 affected_blocks->insert(blockpos, blockref);
6340 void ClientMap::expireMeshes(bool only_daynight_diffed)
6342 TimeTaker timer("expireMeshes()");
6344 core::map<v2s16, MapSector*>::Iterator si;
6345 si = m_sectors.getIterator();
6346 for(; si.atEnd() == false; si++)
6348 MapSector *sector = si.getNode()->getValue();
6350 core::list< MapBlock * > sectorblocks;
6351 sector->getBlocks(sectorblocks);
6353 core::list< MapBlock * >::Iterator i;
6354 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6356 MapBlock *block = *i;
6358 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
6364 JMutexAutoLock lock(block->mesh_mutex);
6365 if(block->mesh != NULL)
6367 /*block->mesh->drop();
6368 block->mesh = NULL;*/
6369 block->setMeshExpired(true);
6376 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6378 assert(mapType() == MAPTYPE_CLIENT);
6381 v3s16 p = blockpos + v3s16(0,0,0);
6382 MapBlock *b = getBlockNoCreate(p);
6383 b->updateMesh(daynight_ratio);
6385 catch(InvalidPositionException &e){}
6388 v3s16 p = blockpos + v3s16(-1,0,0);
6389 MapBlock *b = getBlockNoCreate(p);
6390 b->updateMesh(daynight_ratio);
6392 catch(InvalidPositionException &e){}
6394 v3s16 p = blockpos + v3s16(0,-1,0);
6395 MapBlock *b = getBlockNoCreate(p);
6396 b->updateMesh(daynight_ratio);
6398 catch(InvalidPositionException &e){}
6400 v3s16 p = blockpos + v3s16(0,0,-1);
6401 MapBlock *b = getBlockNoCreate(p);
6402 b->updateMesh(daynight_ratio);
6404 catch(InvalidPositionException &e){}
6407 v3s16 p = blockpos + v3s16(1,0,0);
6408 MapBlock *b = getBlockNoCreate(p);
6409 b->updateMesh(daynight_ratio);
6411 catch(InvalidPositionException &e){}
6413 v3s16 p = blockpos + v3s16(0,1,0);
6414 MapBlock *b = getBlockNoCreate(p);
6415 b->updateMesh(daynight_ratio);
6417 catch(InvalidPositionException &e){}
6419 v3s16 p = blockpos + v3s16(0,0,1);
6420 MapBlock *b = getBlockNoCreate(p);
6421 b->updateMesh(daynight_ratio);
6423 catch(InvalidPositionException &e){}*/
6426 void ClientMap::PrintInfo(std::ostream &out)
6437 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6442 MapVoxelManipulator::~MapVoxelManipulator()
6444 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6448 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6450 TimeTaker timer1("emerge", &emerge_time);
6452 // Units of these are MapBlocks
6453 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6454 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6456 VoxelArea block_area_nodes
6457 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6459 addArea(block_area_nodes);
6461 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6462 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6463 for(s32 x=p_min.X; x<=p_max.X; x++)
6466 core::map<v3s16, bool>::Node *n;
6467 n = m_loaded_blocks.find(p);
6471 bool block_data_inexistent = false;
6474 TimeTaker timer1("emerge load", &emerge_load_time);
6476 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6477 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6480 dstream<<std::endl;*/
6482 MapBlock *block = m_map->getBlockNoCreate(p);
6483 if(block->isDummy())
6484 block_data_inexistent = true;
6486 block->copyTo(*this);
6488 catch(InvalidPositionException &e)
6490 block_data_inexistent = true;
6493 if(block_data_inexistent)
6495 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6496 // Fill with VOXELFLAG_INEXISTENT
6497 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6498 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6500 s32 i = m_area.index(a.MinEdge.X,y,z);
6501 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6505 m_loaded_blocks.insert(p, !block_data_inexistent);
6508 //dstream<<"emerge done"<<std::endl;
6512 SUGG: Add an option to only update eg. water and air nodes.
6513 This will make it interfere less with important stuff if
6516 void MapVoxelManipulator::blitBack
6517 (core::map<v3s16, MapBlock*> & modified_blocks)
6519 if(m_area.getExtent() == v3s16(0,0,0))
6522 //TimeTaker timer1("blitBack");
6524 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6525 <<m_loaded_blocks.size()<<std::endl;*/
6528 Initialize block cache
6530 v3s16 blockpos_last;
6531 MapBlock *block = NULL;
6532 bool block_checked_in_modified = false;
6534 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6535 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6536 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6540 u8 f = m_flags[m_area.index(p)];
6541 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6544 MapNode &n = m_data[m_area.index(p)];
6546 v3s16 blockpos = getNodeBlockPos(p);
6551 if(block == NULL || blockpos != blockpos_last){
6552 block = m_map->getBlockNoCreate(blockpos);
6553 blockpos_last = blockpos;
6554 block_checked_in_modified = false;
6557 // Calculate relative position in block
6558 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6560 // Don't continue if nothing has changed here
6561 if(block->getNode(relpos) == n)
6564 //m_map->setNode(m_area.MinEdge + p, n);
6565 block->setNode(relpos, n);
6568 Make sure block is in modified_blocks
6570 if(block_checked_in_modified == false)
6572 modified_blocks[blockpos] = block;
6573 block_checked_in_modified = true;
6576 catch(InvalidPositionException &e)
6582 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6583 MapVoxelManipulator(map)
6587 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6591 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6593 // Just create the area so that it can be pointed to
6594 VoxelManipulator::emerge(a, caller_id);
6597 void ManualMapVoxelManipulator::initialEmerge(
6598 v3s16 blockpos_min, v3s16 blockpos_max)
6600 TimeTaker timer1("initialEmerge", &emerge_time);
6602 // Units of these are MapBlocks
6603 v3s16 p_min = blockpos_min;
6604 v3s16 p_max = blockpos_max;
6606 VoxelArea block_area_nodes
6607 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6609 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6612 dstream<<"initialEmerge: area: ";
6613 block_area_nodes.print(dstream);
6614 dstream<<" ("<<size_MB<<"MB)";
6618 addArea(block_area_nodes);
6620 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6621 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6622 for(s32 x=p_min.X; x<=p_max.X; x++)
6625 core::map<v3s16, bool>::Node *n;
6626 n = m_loaded_blocks.find(p);
6630 bool block_data_inexistent = false;
6633 TimeTaker timer1("emerge load", &emerge_load_time);
6635 MapBlock *block = m_map->getBlockNoCreate(p);
6636 if(block->isDummy())
6637 block_data_inexistent = true;
6639 block->copyTo(*this);
6641 catch(InvalidPositionException &e)
6643 block_data_inexistent = true;
6646 if(block_data_inexistent)
6649 Mark area inexistent
6651 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6652 // Fill with VOXELFLAG_INEXISTENT
6653 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6654 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6656 s32 i = m_area.index(a.MinEdge.X,y,z);
6657 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6661 m_loaded_blocks.insert(p, !block_data_inexistent);
6665 void ManualMapVoxelManipulator::blitBackAll(
6666 core::map<v3s16, MapBlock*> * modified_blocks)
6668 if(m_area.getExtent() == v3s16(0,0,0))
6672 Copy data of all blocks
6674 for(core::map<v3s16, bool>::Iterator
6675 i = m_loaded_blocks.getIterator();
6676 i.atEnd() == false; i++)
6678 bool existed = i.getNode()->getValue();
6679 if(existed == false)
6681 v3s16 p = i.getNode()->getKey();
6682 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6685 dstream<<"WARNING: "<<__FUNCTION_NAME
6686 <<": got NULL block "
6687 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6692 block->copyFrom(*this);
6695 modified_blocks->insert(p, block);