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;
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);
2032 double m3 = 150.0 - 500.0 * noise2d_perlin_abs(
2033 0.324+(float)p.X/2000., 0.423+(float)p.Y/2000.,
2034 (seed>>32)+985251, 9, 0.55);
2040 // Some kind of hill chains or something
2042 double a1 = 30 - 130. * noise2d_perlin_abs(
2043 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2044 seed+850342, 6, 0.63);
2047 a1 = d + sqrt(a1-d);
2056 double base = -5. + 25. * noise2d_perlin(
2057 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2058 (seed>>32)+653876, 7, 0.6);
2064 double higher = 40. * noise2d_perlin(
2065 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2066 seed+39292, 7, 0.55);
2067 /*double higher = 50. * noise2d_perlin_abs(
2068 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2069 seed+85039, 5, 0.63);*/
2074 // Steepness factor of cliffs
2075 double b = 1.0 + 1.0 * noise2d_perlin(
2076 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2078 b = rangelim(b, 0.0, 1000.0);
2082 b = rangelim(b, 3.0, 1000.0);
2083 //dstream<<"b="<<b<<std::endl;
2085 // Offset to more low
2086 //double a_off = -0.30;
2087 double a_off = -0.00;
2088 // High/low selector
2089 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2090 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2091 seed-359, 6, 0.70));
2096 b = rangelim(b, 3.0, 20.0);*/
2098 double a = -1.5 + 5.0 * (noise2d_perlin_abs(
2099 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2102 /*double a = 5.0 * (noise2d_perlin(
2103 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2104 seed-359, 5, 0.6));*/
2105 //a = contour_flat_top(a, 0.2);
2108 a = rangelim(a, 0.0, 1.0);
2111 //dstream<<"a="<<a<<std::endl;
2113 /*double h2 = higher * a;
2117 h += base*(1.0-a) + higher*a;
2130 double base_rock_level_2d(u64 seed, v2s16 p)
2132 return base_rock_level_2d(seed, v2f((float)p.X, (float)p.Y));
2135 v2f base_ground_turbulence(u64 seed, v3f p)
2140 double vv = 1.0 - 1.0 * noise3d_perlin_abs(
2144 seed+1324031, 4, 0.5);
2145 //double vve = 1.0 - exp(-MYMAX(0, vv*2.0));
2146 double vve = MYMAX(0, vv);
2147 //dstream<<"vve="<<vve<<std::endl;
2149 double v1 = f * noise3d_perlin(
2155 double v2 = f * noise3d_perlin(
2161 return v2f(v1*vve, v2*vve);
2167 bool is_carved(u64 seed, v3f p)
2170 double v1 = noise3d_perlin_abs(
2174 seed+657890854, 5, 0.7);
2183 double v4 = contour(f*noise3d_perlin(
2187 seed+87592, 5, 0.7));
2188 // Tilted 90 degrees
2189 double v5 = contour(f*noise3d_perlin(
2193 seed+98594, 5, 0.7));
2202 bool is_underground_mud(u64 seed, v3f p)
2204 double v1 = noise3d_perlin_abs(
2208 seed+83401, 5, 0.75);
2213 if depth_guess!=NULL, it is set to a guessed value of how deep
2214 underground the position is.
2216 bool is_base_ground(u64 seed, v3f p, double *depth_guess=NULL)
2219 // This is used for testing the output of the cave function
2225 return is_carved(seed, p);
2229 // This is used for testing the output of the underground mud function
2235 return is_underground_mud(seed, p);
2239 v2f t = base_ground_turbulence(seed, p);
2241 double surface_y_f = base_rock_level_2d(seed, v2f(p.X+t.X, p.Z+t.Y));
2244 *depth_guess = surface_y_f - p.Y;*/
2248 // Find highest surface near current
2255 double s2 = surface_y_f;
2256 for(u32 i=0; i<4; i++)
2259 // Get turbulence at around there
2260 v2f t2 = base_ground_turbulence(seed, p+dir);
2261 // Get ground height
2262 v2f l = v2f(p.X+t2.X+dir.X, p.Z+t2.Y+dir.Z);
2263 double s = base_rock_level_2d(seed, l);
2267 *depth_guess = s2 - p.Y;
2272 // Check a bit lower also, take highest surface
2273 v2f t2 = base_ground_turbulence(seed, p + v3f(0,-2,0));
2274 double s2 = base_rock_level_2d(seed, v2f(p.X+t2.X, p.Z+t2.Y));
2275 if(s2 > surface_y_f)
2276 *depth_guess = s2 - p.Y;
2278 *depth_guess = surface_y_f - p.Y;
2283 // Guess surface point
2284 v3f p2(p.X, surface_y_f, p.Z);
2285 v2f t2 = base_ground_turbulence
2287 double s1 = base_rock_level_2d(seed, v2f(p.X+v1,p.Z+v2));
2290 bool is_ground = (p.Y <= surface_y_f);
2293 if(is_carved(seed, p))
2300 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2303 This is the main map generation method
2306 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2307 core::map<v3s16, MapBlock*> &changed_blocks,
2310 DSTACK(__FUNCTION_NAME);
2312 // Shall be not used now
2318 Don't generate if already fully generated
2322 MapChunk *chunk = getChunk(chunkpos);
2323 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2325 dstream<<"generateChunkRaw(): Chunk "
2326 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2327 <<" already generated"<<std::endl;
2332 dstream<<"generateChunkRaw(): Generating chunk "
2333 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2336 TimeTaker timer("generateChunkRaw()");
2338 // The distance how far into the neighbors the generator is allowed to go.
2339 s16 max_spread_amount_sectors = 2;
2340 assert(max_spread_amount_sectors <= m_chunksize);
2341 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2343 // Minimum amount of space left on sides for mud to fall in
2344 //s16 min_mud_fall_space = 2;
2346 // Maximum diameter of stone obstacles in X and Z
2347 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2348 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2350 s16 y_blocks_min = -4;
2351 s16 y_blocks_max = 3;
2352 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2353 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2354 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2356 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2357 s16 sectorpos_base_size = m_chunksize;
2359 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2360 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2361 v2s16 sectorpos_bigbase =
2362 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2363 s16 sectorpos_bigbase_size =
2364 sectorpos_base_size + 2 * max_spread_amount_sectors;
2366 v3s16 bigarea_blocks_min(
2367 sectorpos_bigbase.X,
2372 v3s16 bigarea_blocks_max(
2373 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2375 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2378 // Relative values to control amount of stuff in one chunk
2379 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2380 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2381 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2382 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2383 *(u32)h_blocks*MAP_BLOCKSIZE;
2386 The limiting edges of the lighting update, inclusive.
2388 s16 lighting_min_d = 0-max_spread_amount;
2389 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2392 Create the whole area of this and the neighboring chunks
2395 TimeTaker timer("generateChunkRaw() create area");
2397 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2398 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2400 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2401 ServerMapSector *sector = createSector(sectorpos);
2404 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2406 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2407 MapBlock *block = createBlock(blockpos);
2409 // Lighting won't be calculated
2410 //block->setLightingExpired(true);
2411 // Lighting will be calculated
2412 block->setLightingExpired(false);
2415 Block gets sunlight if this is true.
2417 This should be set to true when the top side of a block
2418 is completely exposed to the sky.
2420 Actually this doesn't matter now because the
2421 initial lighting is done here.
2423 block->setIsUnderground(y != y_blocks_max);
2429 Now we have a big empty area.
2431 Make a ManualMapVoxelManipulator that contains this and the
2435 ManualMapVoxelManipulator vmanip(this);
2436 // Add the area we just generated
2438 TimeTaker timer("generateChunkRaw() initialEmerge");
2439 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2443 vmanip.clearFlag(0xff);
2445 TimeTaker timer_generate("generateChunkRaw() generate");
2447 // Maximum height of the stone surface and obstacles.
2448 // This is used to disable dungeon generation from going too high.
2449 s16 stone_surface_max_y = 0;
2452 Generate general ground level to full area
2457 TimeTaker timer1("ground level");
2458 dstream<<"Generating base ground..."<<std::endl;
2460 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2461 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2464 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2467 Skip if already generated
2470 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2471 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2475 v2f p2df(p2d.X, p2d.Y);
2478 // Use fast index incrementing
2479 v3s16 em = vmanip.m_area.getExtent();
2480 s16 min = y_nodes_min;
2481 s16 max = y_nodes_max;
2484 //float surface_y_f = base_rock_level_2d(m_seed, p2df);
2485 u32 i = vmanip.m_area.index(v3s16(p2d.X, min, p2d.Y));
2486 for(s16 y=min; y<=max; y++)
2489 bool is = is_base_ground(m_seed, v3f(p2df.X,y,p2df.Y));
2491 vmanip.m_data[i].d = CONTENT_STONE;
2493 vmanip.m_data[i].d = CONTENT_AIR;
2496 double v = noise3d_perlin(
2497 0.5+(float)p2d.X/200,
2499 0.5+(float)p2d.Y/200,
2500 m_seed+293, 6, 0.55);
2502 vmanip.m_data[i].d = CONTENT_STONE;
2504 vmanip.m_data[i].d = CONTENT_AIR;
2507 /*double v1 = 5 * noise3d_perlin(
2508 0.5+(float)p2df.X/200,
2510 0.5+(float)p2df.Y/200,
2511 m_seed+293, 6, 0.55);
2513 double v2 = 5 * noise3d_perlin(
2514 0.5+(float)p2df.X/200,
2516 0.5+(float)p2df.Y/200,
2517 m_seed+293, 6, 0.55);*/
2522 float surface_y_f = base_rock_level_2d(m_seed, p2df+v2f(v1,v2));
2524 if(y <= surface_y_f)
2525 vmanip.m_data[i].d = CONTENT_STONE;
2527 vmanip.m_data[i].d = CONTENT_AIR;
2530 vmanip.m_area.add_y(em, i, 1);
2536 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2539 Skip if already generated
2542 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2543 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2547 // Ground height at this point
2548 float surface_y_f = 0.0;
2550 // Use perlin noise for ground height
2551 surface_y_f = base_rock_level_2d(m_seed, p2d);
2553 /*// Experimental stuff
2555 float a = highlands_level_2d(m_seed, p2d);
2560 // Convert to integer
2561 s16 surface_y = (s16)surface_y_f;
2564 if(surface_y > stone_surface_max_y)
2565 stone_surface_max_y = surface_y;
2568 Fill ground with stone
2571 // Use fast index incrementing
2572 v3s16 em = vmanip.m_area.getExtent();
2573 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2574 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2576 vmanip.m_data[i].d = CONTENT_STONE;
2578 vmanip.m_area.add_y(em, i, 1);
2587 Randomize some parameters
2590 s32 stone_obstacle_count = 0;
2591 /*s32 stone_obstacle_count =
2592 rangelim((1.0+noise2d(m_seed+897,
2593 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2595 s16 stone_obstacle_max_height = 0;
2596 /*s16 stone_obstacle_max_height =
2597 rangelim((1.0+noise2d(m_seed+5902,
2598 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2601 Loop this part, it will make stuff look older and newer nicely
2604 for(u32 i_age=0; i_age<age_count; i_age++)
2609 //TimeTaker timer1("stone obstacles");
2612 Add some random stone obstacles
2615 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2617 // Randomize max height so usually stuff will be quite low
2618 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2620 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2621 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2624 myrand_range(5, stone_obstacle_max_size),
2625 myrand_range(0, maxheight_randomized),
2626 myrand_range(5, stone_obstacle_max_size)
2629 // Don't make stupid small rectangle bumps
2634 myrand_range(1+ob_size.X/2+2,
2635 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2636 myrand_range(1+ob_size.Z/2+2,
2637 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2640 // Minimum space left on top of the obstacle
2641 s16 min_head_space = 12;
2643 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2644 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2646 // Node position in 2d
2647 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2649 // Find stone ground level
2650 // (ignore everything else than mud in already generated chunks)
2651 // and mud amount over the stone level
2655 v3s16 em = vmanip.m_area.getExtent();
2656 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2658 // Go to ground level
2659 for(y=y_nodes_max; y>=y_nodes_min; y--)
2661 MapNode *n = &vmanip.m_data[i];
2662 /*if(content_walkable(n.d)
2663 && n.d != CONTENT_MUD
2664 && n.d != CONTENT_GRASS)
2666 if(n->d == CONTENT_STONE)
2669 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2673 Change to mud because otherwise we might
2674 be throwing mud on grass at the next
2680 vmanip.m_area.add_y(em, i, -1);
2682 if(y >= y_nodes_min)
2685 surface_y = y_nodes_min;
2693 v3s16 em = vmanip.m_area.getExtent();
2694 s16 y_start = surface_y+1;
2695 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2699 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2701 MapNode &n = vmanip.m_data[i];
2702 n.d = CONTENT_STONE;
2704 if(y > stone_surface_max_y)
2705 stone_surface_max_y = y;
2708 if(count >= ob_size.Y)
2711 vmanip.m_area.add_y(em, i, 1);
2715 for(; y<=y_nodes_max - min_head_space; y++)
2717 MapNode &n = vmanip.m_data[i];
2720 if(count >= mud_amount)
2723 vmanip.m_area.add_y(em, i, 1);
2733 //TimeTaker timer1("dungeons");
2738 u32 dungeons_count = relative_volume / 600000;
2739 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2740 if(stone_surface_max_y < WATER_LEVEL)
2742 /*u32 dungeons_count = 0;
2743 u32 bruises_count = 0;*/
2744 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2746 s16 min_tunnel_diameter = 2;
2747 s16 max_tunnel_diameter = 6;
2748 u16 tunnel_routepoints = 25;
2750 bool bruise_surface = (jj < bruises_count);
2754 min_tunnel_diameter = 5;
2755 max_tunnel_diameter = myrand_range(10, 20);
2756 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2757 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2759 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2760 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2762 tunnel_routepoints = 5;
2765 // Allowed route area size in nodes
2767 sectorpos_base_size*MAP_BLOCKSIZE,
2768 h_blocks*MAP_BLOCKSIZE,
2769 sectorpos_base_size*MAP_BLOCKSIZE
2772 // Area starting point in nodes
2774 sectorpos_base.X*MAP_BLOCKSIZE,
2775 y_blocks_min*MAP_BLOCKSIZE,
2776 sectorpos_base.Y*MAP_BLOCKSIZE
2780 //(this should be more than the maximum radius of the tunnel)
2781 //s16 insure = 5; // Didn't work with max_d = 20
2783 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2784 ar += v3s16(1,0,1) * more * 2;
2785 of -= v3s16(1,0,1) * more;
2787 s16 route_y_min = 0;
2788 // Allow half a diameter + 7 over stone surface
2789 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2791 /*// If dungeons, don't go through surface too often
2792 if(bruise_surface == false)
2793 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2795 // Limit maximum to area
2796 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2800 /*// Minimum is at y=0
2801 route_y_min = -of.Y - 0;*/
2802 // Minimum is at y=max_tunnel_diameter/4
2803 //route_y_min = -of.Y + max_tunnel_diameter/4;
2804 //s16 min = -of.Y + max_tunnel_diameter/4;
2805 s16 min = -of.Y + 0;
2806 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2807 route_y_min = rangelim(route_y_min, 0, route_y_max);
2810 /*dstream<<"route_y_min = "<<route_y_min
2811 <<", route_y_max = "<<route_y_max<<std::endl;*/
2813 s16 route_start_y_min = route_y_min;
2814 s16 route_start_y_max = route_y_max;
2816 // Start every 2nd dungeon from surface
2817 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2819 if(coming_from_surface)
2821 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2824 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2825 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2827 // Randomize starting position
2829 (float)(myrand()%ar.X)+0.5,
2830 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2831 (float)(myrand()%ar.Z)+0.5
2834 MapNode airnode(CONTENT_AIR);
2837 Generate some tunnel starting from orp
2840 for(u16 j=0; j<tunnel_routepoints; j++)
2843 s16 min_d = min_tunnel_diameter;
2844 s16 max_d = max_tunnel_diameter;
2845 s16 rs = myrand_range(min_d, max_d);
2850 maxlen = v3s16(rs*7,rs*7,rs*7);
2854 maxlen = v3s16(15, myrand_range(1, 20), 15);
2859 if(coming_from_surface && j < 3)
2862 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2863 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2864 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2870 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2871 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2872 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2879 else if(rp.X >= ar.X)
2881 if(rp.Y < route_y_min)
2883 else if(rp.Y >= route_y_max)
2884 rp.Y = route_y_max-1;
2887 else if(rp.Z >= ar.Z)
2891 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2893 v3f fp = orp + vec * f;
2894 v3s16 cp(fp.X, fp.Y, fp.Z);
2897 s16 d1 = d0 + rs - 1;
2898 for(s16 z0=d0; z0<=d1; z0++)
2900 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2901 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2902 for(s16 x0=-si; x0<=si-1; x0++)
2904 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2905 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2906 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2907 //s16 si2 = rs - abs(x0);
2908 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2914 /*if(isInArea(p, ar) == false)
2916 // Check only height
2917 if(y < 0 || y >= ar.Y)
2921 //assert(vmanip.m_area.contains(p));
2922 if(vmanip.m_area.contains(p) == false)
2924 dstream<<"WARNING: "<<__FUNCTION_NAME
2925 <<":"<<__LINE__<<": "
2926 <<"point not in area"
2931 // Just set it to air, it will be changed to
2933 u32 i = vmanip.m_area.index(p);
2934 vmanip.m_data[i] = airnode;
2936 if(bruise_surface == false)
2939 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2954 //TimeTaker timer1("ore veins");
2959 for(u32 jj=0; jj<relative_volume/1000; jj++)
2961 s16 max_vein_diameter = 3;
2963 // Allowed route area size in nodes
2965 sectorpos_base_size*MAP_BLOCKSIZE,
2966 h_blocks*MAP_BLOCKSIZE,
2967 sectorpos_base_size*MAP_BLOCKSIZE
2970 // Area starting point in nodes
2972 sectorpos_base.X*MAP_BLOCKSIZE,
2973 y_blocks_min*MAP_BLOCKSIZE,
2974 sectorpos_base.Y*MAP_BLOCKSIZE
2978 //(this should be more than the maximum radius of the tunnel)
2980 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2981 ar += v3s16(1,0,1) * more * 2;
2982 of -= v3s16(1,0,1) * more;
2984 // Randomize starting position
2986 (float)(myrand()%ar.X)+0.5,
2987 (float)(myrand()%ar.Y)+0.5,
2988 (float)(myrand()%ar.Z)+0.5
2991 // Randomize mineral
2994 mineral = MINERAL_COAL;
2996 mineral = MINERAL_IRON;
2999 Generate some vein starting from orp
3002 for(u16 j=0; j<2; j++)
3005 (float)(myrand()%ar.X)+0.5,
3006 (float)(myrand()%ar.Y)+0.5,
3007 (float)(myrand()%ar.Z)+0.5
3009 v3f vec = rp - orp;*/
3011 v3s16 maxlen(5, 5, 5);
3013 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
3014 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
3015 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
3020 else if(rp.X >= ar.X)
3024 else if(rp.Y >= ar.Y)
3028 else if(rp.Z >= ar.Z)
3034 s16 max_d = max_vein_diameter;
3035 s16 rs = myrand_range(min_d, max_d);
3037 for(float f=0; f<1.0; f+=1.0/vec.getLength())
3039 v3f fp = orp + vec * f;
3040 v3s16 cp(fp.X, fp.Y, fp.Z);
3042 s16 d1 = d0 + rs - 1;
3043 for(s16 z0=d0; z0<=d1; z0++)
3045 s16 si = rs - abs(z0);
3046 for(s16 x0=-si; x0<=si-1; x0++)
3048 s16 si2 = rs - abs(x0);
3049 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3051 // Don't put mineral to every place
3059 /*if(isInArea(p, ar) == false)
3061 // Check only height
3062 if(y < 0 || y >= ar.Y)
3066 assert(vmanip.m_area.contains(p));
3068 // Just set it to air, it will be changed to
3070 u32 i = vmanip.m_area.index(p);
3071 MapNode *n = &vmanip.m_data[i];
3072 if(n->d == CONTENT_STONE)
3087 //TimeTaker timer1("add mud");
3090 Add mud to the central chunk
3093 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3094 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
3096 // Node position in 2d
3097 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3099 // Randomize mud amount
3100 s16 mud_add_amount = get_mud_amount(m_seed, v2f(p2d.X,p2d.Y))/age_count;
3102 // Find ground level
3103 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3106 If topmost node is grass, change it to mud.
3107 It might be if it was flown to there from a neighboring
3108 chunk and then converted.
3111 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
3112 MapNode *n = &vmanip.m_data[i];
3113 if(n->d == CONTENT_GRASS)
3122 v3s16 em = vmanip.m_area.getExtent();
3123 s16 y_start = surface_y+1;
3124 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3125 for(s16 y=y_start; y<=y_nodes_max; y++)
3127 if(mudcount >= mud_add_amount)
3130 MapNode &n = vmanip.m_data[i];
3134 vmanip.m_area.add_y(em, i, 1);
3143 //TimeTaker timer1("flow mud");
3146 Flow mud away from steep edges
3149 // Limit area by 1 because mud is flown into neighbors.
3150 s16 mudflow_minpos = 0-max_spread_amount+1;
3151 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
3153 // Iterate a few times
3154 for(s16 k=0; k<3; k++)
3157 for(s16 x=mudflow_minpos;
3160 for(s16 z=mudflow_minpos;
3164 // Invert coordinates every 2nd iteration
3167 x = mudflow_maxpos - (x-mudflow_minpos);
3168 z = mudflow_maxpos - (z-mudflow_minpos);
3171 // Node position in 2d
3172 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3174 v3s16 em = vmanip.m_area.getExtent();
3175 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3182 for(; y>=y_nodes_min; y--)
3184 n = &vmanip.m_data[i];
3185 //if(content_walkable(n->d))
3187 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3190 vmanip.m_area.add_y(em, i, -1);
3193 // Stop if out of area
3194 //if(vmanip.m_area.contains(i) == false)
3198 /*// If not mud, do nothing to it
3199 MapNode *n = &vmanip.m_data[i];
3200 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3204 Don't flow it if the stuff under it is not mud
3208 vmanip.m_area.add_y(em, i2, -1);
3209 // Cancel if out of area
3210 if(vmanip.m_area.contains(i2) == false)
3212 MapNode *n2 = &vmanip.m_data[i2];
3213 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
3217 // Make it exactly mud
3220 /*s16 recurse_count = 0;
3224 v3s16(0,0,1), // back
3225 v3s16(1,0,0), // right
3226 v3s16(0,0,-1), // front
3227 v3s16(-1,0,0), // left
3230 // Theck that upper is air or doesn't exist.
3231 // Cancel dropping if upper keeps it in place
3233 vmanip.m_area.add_y(em, i3, 1);
3234 if(vmanip.m_area.contains(i3) == true
3235 && content_walkable(vmanip.m_data[i3].d) == true)
3242 for(u32 di=0; di<4; di++)
3244 v3s16 dirp = dirs4[di];
3247 vmanip.m_area.add_p(em, i2, dirp);
3248 // Fail if out of area
3249 if(vmanip.m_area.contains(i2) == false)
3251 // Check that side is air
3252 MapNode *n2 = &vmanip.m_data[i2];
3253 if(content_walkable(n2->d))
3255 // Check that under side is air
3256 vmanip.m_area.add_y(em, i2, -1);
3257 if(vmanip.m_area.contains(i2) == false)
3259 n2 = &vmanip.m_data[i2];
3260 if(content_walkable(n2->d))
3262 /*// Check that under that is air (need a drop of 2)
3263 vmanip.m_area.add_y(em, i2, -1);
3264 if(vmanip.m_area.contains(i2) == false)
3266 n2 = &vmanip.m_data[i2];
3267 if(content_walkable(n2->d))
3269 // Loop further down until not air
3271 vmanip.m_area.add_y(em, i2, -1);
3272 // Fail if out of area
3273 if(vmanip.m_area.contains(i2) == false)
3275 n2 = &vmanip.m_data[i2];
3276 }while(content_walkable(n2->d) == false);
3277 // Loop one up so that we're in air
3278 vmanip.m_area.add_y(em, i2, 1);
3279 n2 = &vmanip.m_data[i2];
3281 // Move mud to new place
3283 // Set old place to be air
3284 *n = MapNode(CONTENT_AIR);
3297 //TimeTaker timer1("add water");
3300 Add water to the central chunk (and a bit more)
3303 for(s16 x=0-max_spread_amount;
3304 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3306 for(s16 z=0-max_spread_amount;
3307 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3310 // Node position in 2d
3311 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3313 // Find ground level
3314 //s16 surface_y = find_ground_level(vmanip, p2d);
3317 If ground level is over water level, skip.
3318 NOTE: This leaves caves near water without water,
3319 which looks especially crappy when the nearby water
3320 won't start flowing either for some reason
3322 /*if(surface_y > WATER_LEVEL)
3329 v3s16 em = vmanip.m_area.getExtent();
3330 u8 light = LIGHT_MAX;
3331 // Start at global water surface level
3332 s16 y_start = WATER_LEVEL;
3333 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3334 MapNode *n = &vmanip.m_data[i];
3336 /*// Add first one to transforming liquid queue, if water
3337 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3339 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3340 m_transforming_liquid.push_back(p);
3343 for(s16 y=y_start; y>=y_nodes_min; y--)
3345 n = &vmanip.m_data[i];
3347 // Stop when there is no water and no air
3348 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3349 && n->d != CONTENT_WATER)
3351 /*// Add bottom one to transforming liquid queue
3352 vmanip.m_area.add_y(em, i, 1);
3353 n = &vmanip.m_data[i];
3354 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3356 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3357 m_transforming_liquid.push_back(p);
3363 // Make water only not in dungeons
3364 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3366 n->d = CONTENT_WATERSOURCE;
3367 //n->setLight(LIGHTBANK_DAY, light);
3369 // Add to transforming liquid queue (in case it'd
3371 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3372 m_transforming_liquid.push_back(p);
3376 vmanip.m_area.add_y(em, i, -1);
3389 //TimeTaker timer1("convert mud to sand");
3395 //s16 mud_add_amount = myrand_range(2, 4);
3396 //s16 mud_add_amount = 0;
3398 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3399 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3400 for(s16 x=0-max_spread_amount+1;
3401 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3403 for(s16 z=0-max_spread_amount+1;
3404 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3407 // Node position in 2d
3408 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3410 // Determine whether to have sand here
3411 bool have_sand = get_have_sand(p2d);
3413 if(have_sand == false)
3416 // Find ground level
3417 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3419 if(surface_y > WATER_LEVEL + 2)
3423 v3s16 em = vmanip.m_area.getExtent();
3424 s16 y_start = surface_y;
3425 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3426 u32 not_sand_counter = 0;
3427 for(s16 y=y_start; y>=y_nodes_min; y--)
3429 MapNode *n = &vmanip.m_data[i];
3430 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3432 n->d = CONTENT_SAND;
3437 if(not_sand_counter > 3)
3441 vmanip.m_area.add_y(em, i, -1);
3450 //TimeTaker timer1("generate trees");
3456 // Divide area into parts
3458 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3459 double area = sidelen * sidelen;
3460 for(s16 x0=0; x0<div; x0++)
3461 for(s16 z0=0; z0<div; z0++)
3463 // Center position of part of division
3465 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3466 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3468 // Minimum edge of part of division
3470 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3471 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3473 // Maximum edge of part of division
3475 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3476 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3479 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3480 // Put trees in random places on part of division
3481 for(u32 i=0; i<tree_count; i++)
3483 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3484 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3485 s16 y = find_ground_level(vmanip, v2s16(x,z));
3486 // Don't make a tree under water level
3491 Trees grow only on mud and grass
3494 u32 i = vmanip.m_area.index(v3s16(p));
3495 MapNode *n = &vmanip.m_data[i];
3496 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3501 make_tree(vmanip, p);
3504 /*u32 tree_max = relative_area / 60;
3505 //u32 count = myrand_range(0, tree_max);
3506 for(u32 i=0; i<count; i++)
3508 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3509 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3510 x += sectorpos_base.X*MAP_BLOCKSIZE;
3511 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3512 s16 y = find_ground_level(vmanip, v2s16(x,z));
3513 // Don't make a tree under water level
3518 make_tree(vmanip, p);
3526 //TimeTaker timer1("grow grass");
3532 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3533 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3534 for(s16 x=0-max_spread_amount;
3535 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3537 for(s16 z=0-max_spread_amount;
3538 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3541 // Node position in 2d
3542 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3545 Find the lowest surface to which enough light ends up
3548 Basically just wait until not air and not leaves.
3552 v3s16 em = vmanip.m_area.getExtent();
3553 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3555 // Go to ground level
3556 for(y=y_nodes_max; y>=y_nodes_min; y--)
3558 MapNode &n = vmanip.m_data[i];
3559 if(n.d != CONTENT_AIR
3560 && n.d != CONTENT_LEAVES)
3562 vmanip.m_area.add_y(em, i, -1);
3564 if(y >= y_nodes_min)
3567 surface_y = y_nodes_min;
3570 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3571 MapNode *n = &vmanip.m_data[i];
3572 if(n->d == CONTENT_MUD)
3573 n->d = CONTENT_GRASS;
3579 Initial lighting (sunlight)
3582 core::map<v3s16, bool> light_sources;
3585 // 750ms @cs=8, can't optimize more
3586 TimeTaker timer1("initial lighting");
3590 Go through the edges and add all nodes that have light to light_sources
3594 for(s16 i=0; i<4; i++)
3596 for(s16 j=lighting_min_d;
3603 if(i == 0 || i == 1)
3605 x = (i==0) ? lighting_min_d : lighting_max_d;
3614 z = (i==0) ? lighting_min_d : lighting_max_d;
3621 // Node position in 2d
3622 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3625 v3s16 em = vmanip.m_area.getExtent();
3626 s16 y_start = y_nodes_max;
3627 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3628 for(s16 y=y_start; y>=y_nodes_min; y--)
3630 MapNode *n = &vmanip.m_data[i];
3631 if(n->getLight(LIGHTBANK_DAY) != 0)
3633 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3635 //NOTE: This is broken, at least the index has to
3644 Go through the edges and apply sunlight to them, not caring
3649 for(s16 i=0; i<4; i++)
3651 for(s16 j=lighting_min_d;
3658 if(i == 0 || i == 1)
3660 x = (i==0) ? lighting_min_d : lighting_max_d;
3669 z = (i==0) ? lighting_min_d : lighting_max_d;
3676 // Node position in 2d
3677 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3679 // Loop from top to down
3681 u8 light = LIGHT_SUN;
3682 v3s16 em = vmanip.m_area.getExtent();
3683 s16 y_start = y_nodes_max;
3684 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3685 for(s16 y=y_start; y>=y_nodes_min; y--)
3687 MapNode *n = &vmanip.m_data[i];
3688 if(light_propagates_content(n->d) == false)
3692 else if(light != LIGHT_SUN
3693 || sunlight_propagates_content(n->d) == false)
3699 n->setLight(LIGHTBANK_DAY, light);
3700 n->setLight(LIGHTBANK_NIGHT, 0);
3704 // Insert light source
3705 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3708 // Increment index by y
3709 vmanip.m_area.add_y(em, i, -1);
3715 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3716 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3717 /*for(s16 x=0-max_spread_amount+1;
3718 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3720 for(s16 z=0-max_spread_amount+1;
3721 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3725 This has to be 1 smaller than the actual area, because
3726 neighboring nodes are checked.
3728 for(s16 x=lighting_min_d+1;
3729 x<=lighting_max_d-1;
3731 for(s16 z=lighting_min_d+1;
3732 z<=lighting_max_d-1;
3735 // Node position in 2d
3736 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3739 Apply initial sunlight
3742 u8 light = LIGHT_SUN;
3743 bool add_to_sources = false;
3744 v3s16 em = vmanip.m_area.getExtent();
3745 s16 y_start = y_nodes_max;
3746 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3747 for(s16 y=y_start; y>=y_nodes_min; y--)
3749 MapNode *n = &vmanip.m_data[i];
3751 if(light_propagates_content(n->d) == false)
3755 else if(light != LIGHT_SUN
3756 || sunlight_propagates_content(n->d) == false)
3762 // This doesn't take much time
3763 if(add_to_sources == false)
3766 Check sides. If side is not air or water, start
3767 adding to light_sources.
3770 v3s16(0,0,1), // back
3771 v3s16(1,0,0), // right
3772 v3s16(0,0,-1), // front
3773 v3s16(-1,0,0), // left
3775 for(u32 di=0; di<4; di++)
3777 v3s16 dirp = dirs4[di];
3779 vmanip.m_area.add_p(em, i2, dirp);
3780 MapNode *n2 = &vmanip.m_data[i2];
3782 n2->d != CONTENT_AIR
3783 && n2->d != CONTENT_WATERSOURCE
3784 && n2->d != CONTENT_WATER
3786 add_to_sources = true;
3792 n->setLight(LIGHTBANK_DAY, light);
3793 n->setLight(LIGHTBANK_NIGHT, 0);
3795 // This doesn't take much time
3796 if(light != 0 && add_to_sources)
3798 // Insert light source
3799 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3802 // Increment index by y
3803 vmanip.m_area.add_y(em, i, -1);
3811 // Spread light around
3813 TimeTaker timer("generateChunkRaw() spreadLight");
3814 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3821 timer_generate.stop();
3824 Blit generated stuff to map
3828 //TimeTaker timer("generateChunkRaw() blitBackAll");
3829 vmanip.blitBackAll(&changed_blocks);
3833 Update day/night difference cache of the MapBlocks
3836 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3837 i.atEnd() == false; i++)
3839 MapBlock *block = i.getNode()->getValue();
3840 block->updateDayNightDiff();
3847 Create chunk metadata
3850 for(s16 x=-1; x<=1; x++)
3851 for(s16 y=-1; y<=1; y++)
3853 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3854 // Add chunk meta information
3855 MapChunk *chunk = getChunk(chunkpos0);
3858 chunk = new MapChunk();
3859 m_chunks.insert(chunkpos0, chunk);
3861 //chunk->setIsVolatile(true);
3862 if(chunk->getGenLevel() > GENERATED_PARTLY)
3863 chunk->setGenLevel(GENERATED_PARTLY);
3867 Set central chunk non-volatile
3869 MapChunk *chunk = getChunk(chunkpos);
3872 //chunk->setIsVolatile(false);
3873 chunk->setGenLevel(GENERATED_FULLY);
3876 Save changed parts of map
3881 Return central chunk (which was requested)
3886 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3887 core::map<v3s16, MapBlock*> &changed_blocks)
3889 dstream<<"generateChunk(): Generating chunk "
3890 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3893 // Shall be not used now
3896 /*for(s16 x=-1; x<=1; x++)
3897 for(s16 y=-1; y<=1; y++)*/
3898 for(s16 x=-0; x<=0; x++)
3899 for(s16 y=-0; y<=0; y++)
3901 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3902 MapChunk *chunk = getChunk(chunkpos0);
3903 // Skip if already generated
3904 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3906 generateChunkRaw(chunkpos0, changed_blocks);
3909 assert(chunkNonVolatile(chunkpos1));
3911 MapChunk *chunk = getChunk(chunkpos1);
3915 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3917 DSTACK("%s: p2d=(%d,%d)",
3922 Check if it exists already in memory
3924 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3929 Try to load it from disk (with blocks)
3931 if(loadSectorFull(p2d) == true)
3933 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3936 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3937 throw InvalidPositionException("");
3943 Do not create over-limit
3945 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3946 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3947 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3948 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3949 throw InvalidPositionException("createSector(): pos. over limit");
3952 Generate blank sector
3955 sector = new ServerMapSector(this, p2d);
3957 // Sector position on map in nodes
3958 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3963 m_sectors.insert(p2d, sector);
3968 MapSector * ServerMap::emergeSector(v2s16 p2d,
3969 core::map<v3s16, MapBlock*> &changed_blocks)
3971 DSTACK("%s: p2d=(%d,%d)",
3978 v2s16 chunkpos = sector_to_chunk(p2d);
3979 /*bool chunk_nonvolatile = false;
3980 MapChunk *chunk = getChunk(chunkpos);
3981 if(chunk && chunk->getIsVolatile() == false)
3982 chunk_nonvolatile = true;*/
3983 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3986 If chunk is not fully generated, generate chunk
3988 if(chunk_nonvolatile == false)
3990 // Generate chunk and neighbors
3991 generateChunk(chunkpos, changed_blocks);
3995 Return sector if it exists now
3997 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4002 Try to load it from disk
4004 if(loadSectorFull(p2d) == true)
4006 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4009 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
4010 throw InvalidPositionException("");
4016 generateChunk should have generated the sector
4020 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
4021 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
4025 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
4028 generateChunkRaw(chunkpos, changed_blocks, true);
4031 Return sector if it exists now
4033 sector = getSectorNoGenerateNoEx(p2d);
4037 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
4043 dstream<<"WARNING: Creating an empty sector."<<std::endl;
4045 return createSector(p2d);
4052 //return generateSector();
4056 NOTE: This is not used for main map generation, only for blocks
4057 that are very high or low
4059 MapBlock * ServerMap::generateBlock(
4061 MapBlock *original_dummy,
4062 ServerMapSector *sector,
4063 core::map<v3s16, MapBlock*> &changed_blocks,
4064 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4067 DSTACK("%s: p=(%d,%d,%d)",
4071 /*dstream<<"generateBlock(): "
4072 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4075 MapBlock *block = original_dummy;
4077 v2s16 p2d(p.X, p.Z);
4079 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4080 v3s16 p_nodes = p * MAP_BLOCKSIZE;
4083 Do not generate over-limit
4085 if(blockpos_over_limit(p))
4087 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4088 throw InvalidPositionException("generateBlock(): pos. over limit");
4092 If block doesn't exist, create one.
4093 If it exists, it is a dummy. In that case unDummify() it.
4095 NOTE: This already sets the map as the parent of the block
4099 block = sector->createBlankBlockNoInsert(block_y);
4103 // Remove the block so that nobody can get a half-generated one.
4104 sector->removeBlock(block);
4105 // Allocate the block to contain the generated data
4109 u8 water_material = CONTENT_WATERSOURCE;
4111 s32 lowest_ground_y = 32767;
4112 s32 highest_ground_y = -32768;
4118 } block_type = BT_SURFACE;
4120 {// ground_timer (0ms or ~100ms)
4121 //TimeTaker ground_timer("Ground generation");
4124 Approximate whether this block is a surface block, an air
4125 block or a ground block.
4127 This shall never mark a surface block as non-surface.
4132 Estimate surface at different positions of the block, to
4133 try to accomodate the effect of turbulence.
4146 v3f p_nodes_f = intToFloat(p_nodes, 1);
4147 float surface_y_max = -1000000;
4148 float surface_y_min = 1000000;
4149 for(u32 i=0; i<sizeof(checklist)/sizeof(checklist[0]); i++)
4151 v3f p_map_f = p_nodes_f + checklist[i]*MAP_BLOCKSIZE;
4154 bool is_ground = is_base_ground(m_seed, p_map_f, &depth_guess);
4156 // Estimate the surface height
4157 float surface_y_f = p_map_f.Y + depth_guess;
4159 if(surface_y_f > surface_y_max)
4160 surface_y_max = surface_y_f;
4161 if(surface_y_f < surface_y_min)
4162 surface_y_min = surface_y_f;
4165 float block_low_y_f = p_nodes_f.Y;
4166 float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE;
4168 /*dstream<<"surface_y_max="<<surface_y_max
4169 <<", surface_y_min="<<surface_y_min
4170 <<", block_low_y_f="<<block_low_y_f
4171 <<", block_high_y_f="<<block_high_y_f
4174 // A fuzzyness value
4175 // Must accomodate mud and turbulence holes
4177 // Must accomodate a bit less
4180 if(block_high_y_f < surface_y_min - d_down)
4182 //dstream<<"BT_GROUND"<<std::endl;
4184 block_type = BT_GROUND;
4186 else if(block_low_y_f >= surface_y_max + d_up
4187 && block_low_y_f > WATER_LEVEL + d_up)
4189 //dstream<<"BT_SKY"<<std::endl;
4191 block_type = BT_SKY;
4195 //dstream<<"BT_SURFACE"<<std::endl;
4197 block_type = BT_SURFACE;
4200 if(/*block_type == BT_GROUND ||*/ block_type == BT_SKY)
4202 lowest_ground_y = surface_y_min;
4203 highest_ground_y = surface_y_max;
4207 if(block_type == BT_SURFACE || block_type == BT_GROUND)
4210 Generate ground precisely
4213 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4214 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4216 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4218 //s16 surface_y = 0;
4220 /*s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4221 + AVERAGE_MUD_AMOUNT;
4223 if(surface_y < lowest_ground_y)
4224 lowest_ground_y = surface_y;
4225 if(surface_y > highest_ground_y)
4226 highest_ground_y = surface_y;*/
4228 v2s16 real_p2d = v2s16(x0,z0) + p2d*MAP_BLOCKSIZE;
4230 //s32 surface_depth = AVERAGE_MUD_AMOUNT;
4231 s16 surface_depth = get_mud_amount(m_seed, v2f(real_p2d.X,real_p2d.Y));
4233 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4236 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4237 v3s16 real_pos = v3s16(x0,y0,z0) + p_nodes;
4242 NOTE: If there are some man-made structures above the
4243 newly created block, they won't be taken into account.
4245 /*if(real_y > surface_y)
4246 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);*/
4252 v3f real_pos_f = intToFloat(real_pos, 1);
4253 v2f real_pos_f_2d(real_pos_f.X, real_pos_f.Z);
4255 bool is_ground = is_base_ground(m_seed,
4256 real_pos_f, &depth_guess);
4258 // Estimate the surface height
4259 float surface_y_f = (float)real_y + depth_guess;
4260 s16 surface_y = real_y + depth_guess;
4262 // Get some statistics of surface height
4263 if(surface_y < lowest_ground_y)
4264 lowest_ground_y = surface_y;
4265 if(surface_y > highest_ground_y)
4266 highest_ground_y = surface_y;
4268 // If node is not ground, it's air or water
4269 if(is_ground == false)
4271 // If under water level, it's water
4272 if(real_y < WATER_LEVEL)
4274 n.d = water_material;
4275 n.setLight(LIGHTBANK_DAY,
4276 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4278 Add to transforming liquid queue (in case it'd
4281 m_transforming_liquid.push_back(real_pos);
4287 // Else it's ground or dungeons (air)
4290 // If it's surface_depth under ground, it's stone
4291 if((float)real_y <= surface_y_f - surface_depth - 0.75)
4293 if(is_underground_mud(m_seed, real_pos_f))
4296 n.d = CONTENT_STONE;
4298 else if(surface_y_f <= WATER_LEVEL + 2.1
4299 && get_have_sand(m_seed, real_pos_f_2d))
4305 /*// It is mud if it is under the first ground
4306 // level or under water
4307 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4313 n.d = CONTENT_GRASS;
4318 /*// If under water level, it's mud
4319 if(real_y < WATER_LEVEL)
4321 // Only the topmost node is grass
4322 else if(real_y <= surface_y - 1)
4325 n.d = CONTENT_GRASS;*/
4329 block->setNode(v3s16(x0,y0,z0), n);
4332 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4337 NOTE: If there are some man-made structures above the
4338 newly created block, they won't be taken into account.
4340 if(real_y > surface_y)
4341 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4347 // If node is over heightmap y, it's air or water
4348 if(real_y > surface_y)
4350 // If under water level, it's water
4351 if(real_y < WATER_LEVEL)
4353 n.d = water_material;
4354 n.setLight(LIGHTBANK_DAY,
4355 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4357 Add to transforming liquid queue (in case it'd
4360 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4361 m_transforming_liquid.push_back(real_pos);
4367 // Else it's ground or dungeons (air)
4370 // If it's surface_depth under ground, it's stone
4371 if(real_y <= surface_y - surface_depth)
4373 n.d = CONTENT_STONE;
4377 // It is mud if it is under the first ground
4378 // level or under water
4379 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4385 n.d = CONTENT_GRASS;
4388 //n.d = CONTENT_MUD;
4390 /*// If under water level, it's mud
4391 if(real_y < WATER_LEVEL)
4393 // Only the topmost node is grass
4394 else if(real_y <= surface_y - 1)
4397 n.d = CONTENT_GRASS;*/
4401 block->setNode(v3s16(x0,y0,z0), n);
4406 else // BT_GROUND, BT_SKY or anything else
4409 if(block_type == BT_GROUND)
4411 //n_fill.d = CONTENT_STONE;
4413 else if(block_type == BT_SKY)
4415 n_fill.d = CONTENT_AIR;
4416 n_fill.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4420 n_fill.d = CONTENT_MESE;
4424 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4425 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4426 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4428 //MapNode n = block->getNode(v3s16(x0,y0,z0));
4429 block->setNode(v3s16(x0,y0,z0), n_fill);
4436 Calculate some helper variables
4439 // Completely underground if the highest part of block is under lowest
4441 // This has to be very sure; it's probably one too strict now but
4442 // that's just better.
4443 bool completely_underground =
4444 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4446 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4448 bool mostly_underwater_surface = false;
4449 if(highest_ground_y < WATER_LEVEL
4450 && some_part_underground && !completely_underground)
4451 mostly_underwater_surface = true;
4454 Get local attributes
4457 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4459 float caves_amount = 0.5;
4464 NOTE: BEWARE: Too big amount of attribute points slows verything
4466 1 interpolation from 5000 points takes 2-3ms.
4468 //TimeTaker timer("generateBlock() local attribute retrieval");
4469 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4470 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4471 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4475 //dstream<<"generateBlock(): Done"<<std::endl;
4477 // Set to true if has caves.
4478 // Set when some non-air is changed to air when making caves.
4479 bool has_dungeons = false;
4486 // Initialize temporary table
4487 const s32 ued = MAP_BLOCKSIZE;
4488 bool underground_emptiness[ued*ued*ued];
4489 for(s32 i=0; i<ued*ued*ued; i++)
4491 underground_emptiness[i] = 0;
4498 Initialize orp and ors. Try to find if some neighboring
4499 MapBlock has a tunnel ended in its side
4503 (float)(myrand()%ued)+0.5,
4504 (float)(myrand()%ued)+0.5,
4505 (float)(myrand()%ued)+0.5
4508 bool found_existing = false;
4514 for(s16 y=0; y<ued; y++)
4515 for(s16 x=0; x<ued; x++)
4517 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4518 if(getNode(ap).d == CONTENT_AIR)
4520 orp = v3f(x+1,y+1,0);
4521 found_existing = true;
4522 goto continue_generating;
4526 catch(InvalidPositionException &e){}
4532 for(s16 y=0; y<ued; y++)
4533 for(s16 x=0; x<ued; x++)
4535 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4536 if(getNode(ap).d == CONTENT_AIR)
4538 orp = v3f(x+1,y+1,ued-1);
4539 found_existing = true;
4540 goto continue_generating;
4544 catch(InvalidPositionException &e){}
4550 for(s16 y=0; y<ued; y++)
4551 for(s16 z=0; z<ued; z++)
4553 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4554 if(getNode(ap).d == CONTENT_AIR)
4556 orp = v3f(0,y+1,z+1);
4557 found_existing = true;
4558 goto continue_generating;
4562 catch(InvalidPositionException &e){}
4568 for(s16 y=0; y<ued; y++)
4569 for(s16 z=0; z<ued; z++)
4571 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4572 if(getNode(ap).d == CONTENT_AIR)
4574 orp = v3f(ued-1,y+1,z+1);
4575 found_existing = true;
4576 goto continue_generating;
4580 catch(InvalidPositionException &e){}
4586 for(s16 x=0; x<ued; x++)
4587 for(s16 z=0; z<ued; z++)
4589 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4590 if(getNode(ap).d == CONTENT_AIR)
4592 orp = v3f(x+1,0,z+1);
4593 found_existing = true;
4594 goto continue_generating;
4598 catch(InvalidPositionException &e){}
4604 for(s16 x=0; x<ued; x++)
4605 for(s16 z=0; z<ued; z++)
4607 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4608 if(getNode(ap).d == CONTENT_AIR)
4610 orp = v3f(x+1,ued-1,z+1);
4611 found_existing = true;
4612 goto continue_generating;
4616 catch(InvalidPositionException &e){}
4618 continue_generating:
4621 Choose whether to actually generate dungeon
4623 bool do_generate_dungeons = true;
4624 // Don't generate if no part is underground
4625 if(!some_part_underground)
4627 do_generate_dungeons = false;
4629 // Don't generate if mostly underwater surface
4630 /*else if(mostly_underwater_surface)
4632 do_generate_dungeons = false;
4634 // Partly underground = cave
4635 else if(!completely_underground)
4637 //do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4638 do_generate_dungeons = false;
4640 // Found existing dungeon underground
4641 else if(found_existing && completely_underground)
4643 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4645 // Underground and no dungeons found
4648 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4651 if(do_generate_dungeons)
4654 Generate some tunnel starting from orp and ors
4656 for(u16 i=0; i<3; i++)
4659 (float)(myrand()%ued)+0.5,
4660 (float)(myrand()%ued)+0.5,
4661 (float)(myrand()%ued)+0.5
4665 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4669 for(float f=0; f<1.0; f+=0.04)
4671 v3f fp = orp + vec * f;
4672 v3s16 cp(fp.X, fp.Y, fp.Z);
4674 s16 d1 = d0 + rs - 1;
4675 for(s16 z0=d0; z0<=d1; z0++)
4677 s16 si = rs - abs(z0);
4678 for(s16 x0=-si; x0<=si-1; x0++)
4680 s16 si2 = rs - abs(x0);
4681 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4687 if(isInArea(p, ued) == false)
4689 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4702 Apply temporary cave data to block
4705 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4706 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4708 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4710 MapNode n = block->getNode(v3s16(x0,y0,z0));
4713 if(underground_emptiness[
4714 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4715 +ued*(y0*ued/MAP_BLOCKSIZE)
4716 +(x0*ued/MAP_BLOCKSIZE)])
4718 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4721 has_dungeons = true;
4727 block->setNode(v3s16(x0,y0,z0), n);
4733 This is used for guessing whether or not the block should
4734 receive sunlight from the top if the block above doesn't exist
4736 block->setIsUnderground(completely_underground);
4739 Force lighting update if some part of block is partly
4740 underground and has caves.
4742 /*if(some_part_underground && !completely_underground && has_dungeons)
4744 //dstream<<"Half-ground caves"<<std::endl;
4745 lighting_invalidated_blocks[block->getPos()] = block;
4748 // DEBUG: Always update lighting
4749 //lighting_invalidated_blocks[block->getPos()] = block;
4755 if(some_part_underground)
4757 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4762 for(s16 i=0; i<underground_level/4 + 1; i++)
4764 if(myrand()%50 == 0)
4767 (myrand()%(MAP_BLOCKSIZE-2))+1,
4768 (myrand()%(MAP_BLOCKSIZE-2))+1,
4769 (myrand()%(MAP_BLOCKSIZE-2))+1
4775 for(u16 i=0; i<27; i++)
4777 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4779 block->setNode(cp+g_27dirs[i], n);
4787 u16 coal_amount = 60;
4788 u16 coal_rareness = 120 / coal_amount;
4789 if(coal_rareness == 0)
4791 if(myrand()%coal_rareness == 0)
4793 u16 a = myrand() % 16;
4794 u16 amount = coal_amount * a*a*a / 1000;
4795 for(s16 i=0; i<amount; i++)
4798 (myrand()%(MAP_BLOCKSIZE-2))+1,
4799 (myrand()%(MAP_BLOCKSIZE-2))+1,
4800 (myrand()%(MAP_BLOCKSIZE-2))+1
4804 n.d = CONTENT_STONE;
4805 n.param = MINERAL_COAL;
4807 for(u16 i=0; i<27; i++)
4809 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4811 block->setNode(cp+g_27dirs[i], n);
4819 u16 iron_amount = 40;
4820 u16 iron_rareness = 80 / iron_amount;
4821 if(iron_rareness == 0)
4823 if(myrand()%iron_rareness == 0)
4825 u16 a = myrand() % 16;
4826 u16 amount = iron_amount * a*a*a / 1000;
4827 for(s16 i=0; i<amount; i++)
4830 (myrand()%(MAP_BLOCKSIZE-2))+1,
4831 (myrand()%(MAP_BLOCKSIZE-2))+1,
4832 (myrand()%(MAP_BLOCKSIZE-2))+1
4836 n.d = CONTENT_STONE;
4837 n.param = MINERAL_IRON;
4839 for(u16 i=0; i<27; i++)
4841 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4843 block->setNode(cp+g_27dirs[i], n);
4850 Create a few rats in empty blocks underground
4852 if(completely_underground)
4854 //for(u16 i=0; i<2; i++)
4857 (myrand()%(MAP_BLOCKSIZE-2))+1,
4858 (myrand()%(MAP_BLOCKSIZE-2))+1,
4859 (myrand()%(MAP_BLOCKSIZE-2))+1
4862 // Check that the place is empty
4863 //if(!is_ground_content(block->getNode(cp).d))
4866 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4867 block->addObject(obj);
4875 sector->insertBlock(block);
4877 // Lighting is invalid after generation for surface blocks
4878 if(block_type == BT_SURFACE)
4880 block->setLightingExpired(true);
4881 lighting_invalidated_blocks.insert(p, block);
4883 // Lighting is not invalid for other blocks
4886 block->setLightingExpired(false);
4893 if(some_part_underground && !completely_underground)
4895 MapVoxelManipulator vm(this);
4897 double a = tree_amount_2d(m_seed, v2s16(p_nodes.X+8, p_nodes.Z+8));
4898 u16 tree_count = (u16)(a*MAP_BLOCKSIZE*MAP_BLOCKSIZE);
4899 for(u16 i=0; i<tree_count/2; i++)
4901 v3s16 tree_p = p_nodes + v3s16(
4902 myrand_range(0,MAP_BLOCKSIZE-1),
4904 myrand_range(0,MAP_BLOCKSIZE-1)
4907 /*bool is_ground =*/ is_base_ground(m_seed,
4908 intToFloat(tree_p, 1), &depth_guess);
4909 tree_p.Y += (depth_guess - 0.5);
4910 if(tree_p.Y <= WATER_LEVEL)
4912 make_tree(vm, tree_p);
4915 vm.blitBack(changed_blocks);
4924 <<"lighting_invalidated_blocks.size()"
4928 <<" "<<lighting_invalidated_blocks.size()
4929 <<", "<<has_dungeons
4930 <<", "<<completely_underground
4931 <<", "<<some_part_underground
4938 MapBlock * ServerMap::createBlock(v3s16 p)
4940 DSTACK("%s: p=(%d,%d,%d)",
4941 __FUNCTION_NAME, p.X, p.Y, p.Z);
4944 Do not create over-limit
4946 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4947 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4948 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4949 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4950 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4951 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4952 throw InvalidPositionException("createBlock(): pos. over limit");
4954 v2s16 p2d(p.X, p.Z);
4957 This will create or load a sector if not found in memory.
4958 If block exists on disk, it will be loaded.
4960 NOTE: On old save formats, this will be slow, as it generates
4961 lighting on blocks for them.
4963 ServerMapSector *sector;
4965 sector = (ServerMapSector*)createSector(p2d);
4966 assert(sector->getId() == MAPSECTOR_SERVER);
4968 catch(InvalidPositionException &e)
4970 dstream<<"createBlock: createSector() failed"<<std::endl;
4974 NOTE: This should not be done, or at least the exception
4975 should not be passed on as std::exception, because it
4976 won't be catched at all.
4978 /*catch(std::exception &e)
4980 dstream<<"createBlock: createSector() failed: "
4981 <<e.what()<<std::endl;
4986 Try to get a block from the sector
4989 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4993 block = sector->createBlankBlock(block_y);
4997 MapBlock * ServerMap::emergeBlock(
4999 bool only_from_disk,
5000 core::map<v3s16, MapBlock*> &changed_blocks,
5001 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
5004 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
5006 p.X, p.Y, p.Z, only_from_disk);
5009 Do not generate over-limit
5011 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5012 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5013 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5014 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5015 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
5016 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
5017 throw InvalidPositionException("emergeBlock(): pos. over limit");
5019 v2s16 p2d(p.X, p.Z);
5022 This will create or load a sector if not found in memory.
5023 If block exists on disk, it will be loaded.
5025 ServerMapSector *sector;
5027 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
5028 assert(sector->getId() == MAPSECTOR_SERVER);
5030 catch(InvalidPositionException &e)
5032 dstream<<"emergeBlock: emergeSector() failed: "
5033 <<e.what()<<std::endl;
5034 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
5036 <<"You could try to delete it."<<std::endl;
5039 catch(VersionMismatchException &e)
5041 dstream<<"emergeBlock: emergeSector() failed: "
5042 <<e.what()<<std::endl;
5043 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
5045 <<"You could try to delete it."<<std::endl;
5049 NOTE: This should not be done, or at least the exception
5050 should not be passed on as std::exception, because it
5051 won't be catched at all.
5053 /*catch(std::exception &e)
5055 dstream<<"emergeBlock: emergeSector() failed: "
5056 <<e.what()<<std::endl;
5057 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
5059 <<"You could try to delete it."<<std::endl;
5064 Try to get a block from the sector
5067 bool does_not_exist = false;
5068 bool lighting_expired = false;
5069 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
5073 does_not_exist = true;
5075 else if(block->isDummy() == true)
5077 does_not_exist = true;
5079 else if(block->getLightingExpired())
5081 lighting_expired = true;
5086 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
5091 If block was not found on disk and not going to generate a
5092 new one, make sure there is a dummy block in place.
5094 if(only_from_disk && (does_not_exist || lighting_expired))
5096 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
5100 // Create dummy block
5101 block = new MapBlock(this, p, true);
5103 // Add block to sector
5104 sector->insertBlock(block);
5110 //dstream<<"Not found on disk, generating."<<std::endl;
5112 //TimeTaker("emergeBlock() generate");
5114 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
5117 If the block doesn't exist, generate the block.
5121 block = generateBlock(p, block, sector, changed_blocks,
5122 lighting_invalidated_blocks);
5124 lighting_expired = block->getLightingExpired();
5127 if(lighting_expired)
5129 lighting_invalidated_blocks.insert(p, block);
5133 Initially update sunlight
5136 if(lighting_expired)
5138 core::map<v3s16, bool> light_sources;
5139 bool black_air_left = false;
5140 bool bottom_invalid =
5141 block->propagateSunlight(light_sources, true,
5142 &black_air_left, true);
5144 // If sunlight didn't reach everywhere and part of block is
5145 // above ground, lighting has to be properly updated
5146 //if(black_air_left && some_part_underground)
5149 lighting_invalidated_blocks[block->getPos()] = block;
5154 lighting_invalidated_blocks[block->getPos()] = block;
5161 s16 ServerMap::findGroundLevel(v2s16 p2d)
5164 Uh, just do something random...
5166 // Find existing map from top to down
5169 v3s16 p(p2d.X, max, p2d.Y);
5170 for(; p.Y>min; p.Y--)
5172 MapNode n = getNodeNoEx(p);
5173 if(n.d != CONTENT_IGNORE)
5178 // If this node is not air, go to plan b
5179 if(getNodeNoEx(p).d != CONTENT_AIR)
5181 // Search existing walkable and return it
5182 for(; p.Y>min; p.Y--)
5184 MapNode n = getNodeNoEx(p);
5185 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
5191 Plan B: Get from map generator perlin noise function
5193 double level = base_rock_level_2d(m_seed, p2d);
5197 void ServerMap::createDir(std::string path)
5199 if(fs::CreateDir(path) == false)
5201 m_dout<<DTIME<<"ServerMap: Failed to create directory "
5202 <<"\""<<path<<"\""<<std::endl;
5203 throw BaseException("ServerMap failed to create directory");
5207 std::string ServerMap::getSectorSubDir(v2s16 pos)
5210 snprintf(cc, 9, "%.4x%.4x",
5211 (unsigned int)pos.X&0xffff,
5212 (unsigned int)pos.Y&0xffff);
5214 return std::string(cc);
5217 std::string ServerMap::getSectorDir(v2s16 pos)
5219 return m_savedir + "/sectors/" + getSectorSubDir(pos);
5222 v2s16 ServerMap::getSectorPos(std::string dirname)
5224 if(dirname.size() != 8)
5225 throw InvalidFilenameException("Invalid sector directory name");
5227 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
5229 throw InvalidFilenameException("Invalid sector directory name");
5230 v2s16 pos((s16)x, (s16)y);
5234 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
5236 v2s16 p2d = getSectorPos(sectordir);
5238 if(blockfile.size() != 4){
5239 throw InvalidFilenameException("Invalid block filename");
5242 int r = sscanf(blockfile.c_str(), "%4x", &y);
5244 throw InvalidFilenameException("Invalid block filename");
5245 return v3s16(p2d.X, y, p2d.Y);
5248 void ServerMap::save(bool only_changed)
5250 DSTACK(__FUNCTION_NAME);
5251 if(m_map_saving_enabled == false)
5253 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
5257 if(only_changed == false)
5258 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
5264 u32 sector_meta_count = 0;
5265 u32 block_count = 0;
5268 JMutexAutoLock lock(m_sector_mutex);
5270 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
5271 for(; i.atEnd() == false; i++)
5273 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
5274 assert(sector->getId() == MAPSECTOR_SERVER);
5276 if(sector->differs_from_disk || only_changed == false)
5278 saveSectorMeta(sector);
5279 sector_meta_count++;
5281 core::list<MapBlock*> blocks;
5282 sector->getBlocks(blocks);
5283 core::list<MapBlock*>::Iterator j;
5284 for(j=blocks.begin(); j!=blocks.end(); j++)
5286 MapBlock *block = *j;
5287 if(block->getChangedFlag() || only_changed == false)
5292 /*dstream<<"ServerMap: Written block ("
5293 <<block->getPos().X<<","
5294 <<block->getPos().Y<<","
5295 <<block->getPos().Z<<")"
5304 Only print if something happened or saved whole map
5306 if(only_changed == false || sector_meta_count != 0
5307 || block_count != 0)
5309 dstream<<DTIME<<"ServerMap: Written: "
5310 <<sector_meta_count<<" sector metadata files, "
5311 <<block_count<<" block files"
5316 void ServerMap::loadAll()
5318 DSTACK(__FUNCTION_NAME);
5319 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5324 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5326 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5328 JMutexAutoLock lock(m_sector_mutex);
5331 s32 printed_counter = -100000;
5332 s32 count = list.size();
5334 std::vector<fs::DirListNode>::iterator i;
5335 for(i=list.begin(); i!=list.end(); i++)
5337 if(counter > printed_counter + 10)
5339 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5340 printed_counter = counter;
5344 MapSector *sector = NULL;
5346 // We want directories
5350 sector = loadSectorMeta(i->name);
5352 catch(InvalidFilenameException &e)
5354 // This catches unknown crap in directory
5357 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5358 (m_savedir+"/sectors/"+i->name);
5359 std::vector<fs::DirListNode>::iterator i2;
5360 for(i2=list2.begin(); i2!=list2.end(); i2++)
5366 loadBlock(i->name, i2->name, sector);
5368 catch(InvalidFilenameException &e)
5370 // This catches unknown crap in directory
5374 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5378 void ServerMap::saveMasterHeightmap()
5380 DSTACK(__FUNCTION_NAME);
5382 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5384 createDir(m_savedir);
5386 /*std::string fullpath = m_savedir + "/master_heightmap";
5387 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5388 if(o.good() == false)
5389 throw FileNotGoodException("Cannot open master heightmap");*/
5391 // Format used for writing
5392 //u8 version = SER_FMT_VER_HIGHEST;
5395 void ServerMap::loadMasterHeightmap()
5397 DSTACK(__FUNCTION_NAME);
5399 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5401 /*std::string fullpath = m_savedir + "/master_heightmap";
5402 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5403 if(is.good() == false)
5404 throw FileNotGoodException("Cannot open master heightmap");*/
5408 void ServerMap::saveMapMeta()
5410 DSTACK(__FUNCTION_NAME);
5412 dstream<<"INFO: ServerMap::saveMapMeta(): "
5413 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5416 createDir(m_savedir);
5418 std::string fullpath = m_savedir + "/map_meta.txt";
5419 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5420 if(os.good() == false)
5422 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5423 <<"could not open"<<fullpath<<std::endl;
5424 throw FileNotGoodException("Cannot open chunk metadata");
5428 params.setU64("seed", m_seed);
5429 params.setS32("chunksize", m_chunksize);
5431 params.writeLines(os);
5433 os<<"[end_of_params]\n";
5437 void ServerMap::loadMapMeta()
5439 DSTACK(__FUNCTION_NAME);
5441 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
5444 std::string fullpath = m_savedir + "/map_meta.txt";
5445 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5446 if(is.good() == false)
5448 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5449 <<"could not open"<<fullpath<<std::endl;
5450 throw FileNotGoodException("Cannot open chunk metadata");
5458 throw SerializationError
5459 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5461 std::getline(is, line);
5462 std::string trimmedline = trim(line);
5463 if(trimmedline == "[end_of_params]")
5465 params.parseConfigLine(line);
5468 m_seed = params.getU64("seed");
5469 m_chunksize = params.getS32("chunksize");
5471 dstream<<"INFO: ServerMap::loadMapMeta(): "
5472 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5476 void ServerMap::saveChunkMeta()
5478 DSTACK(__FUNCTION_NAME);
5480 u32 count = m_chunks.size();
5482 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5483 <<count<<" chunks"<<std::endl;
5485 createDir(m_savedir);
5487 std::string fullpath = m_savedir + "/chunk_meta";
5488 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5489 if(os.good() == false)
5491 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5492 <<"could not open"<<fullpath<<std::endl;
5493 throw FileNotGoodException("Cannot open chunk metadata");
5499 os.write((char*)&version, 1);
5504 writeU32(buf, count);
5505 os.write((char*)buf, 4);
5507 for(core::map<v2s16, MapChunk*>::Iterator
5508 i = m_chunks.getIterator();
5509 i.atEnd()==false; i++)
5511 v2s16 p = i.getNode()->getKey();
5512 MapChunk *chunk = i.getNode()->getValue();
5515 os.write((char*)buf, 4);
5517 chunk->serialize(os, version);
5521 void ServerMap::loadChunkMeta()
5523 DSTACK(__FUNCTION_NAME);
5525 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5528 std::string fullpath = m_savedir + "/chunk_meta";
5529 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5530 if(is.good() == false)
5532 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5533 <<"could not open"<<fullpath<<std::endl;
5534 throw FileNotGoodException("Cannot open chunk metadata");
5540 is.read((char*)&version, 1);
5545 is.read((char*)buf, 4);
5546 u32 count = readU32(buf);
5548 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5549 <<count<<" chunks"<<std::endl;
5551 for(u32 i=0; i<count; i++)
5554 MapChunk *chunk = new MapChunk();
5556 is.read((char*)buf, 4);
5559 chunk->deSerialize(is, version);
5560 m_chunks.insert(p, chunk);
5564 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5566 DSTACK(__FUNCTION_NAME);
5567 // Format used for writing
5568 u8 version = SER_FMT_VER_HIGHEST;
5570 v2s16 pos = sector->getPos();
5571 createDir(m_savedir);
5572 createDir(m_savedir+"/sectors");
5573 std::string dir = getSectorDir(pos);
5576 std::string fullpath = dir + "/meta";
5577 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5578 if(o.good() == false)
5579 throw FileNotGoodException("Cannot open sector metafile");
5581 sector->serialize(o, version);
5583 sector->differs_from_disk = false;
5586 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5588 DSTACK(__FUNCTION_NAME);
5590 v2s16 p2d = getSectorPos(dirname);
5591 std::string dir = m_savedir + "/sectors/" + dirname;
5593 std::string fullpath = dir + "/meta";
5594 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5595 if(is.good() == false)
5596 throw FileNotGoodException("Cannot open sector metafile");
5598 ServerMapSector *sector = ServerMapSector::deSerialize
5599 (is, this, p2d, m_sectors);
5601 sector->differs_from_disk = false;
5606 bool ServerMap::loadSectorFull(v2s16 p2d)
5608 DSTACK(__FUNCTION_NAME);
5609 std::string sectorsubdir = getSectorSubDir(p2d);
5611 MapSector *sector = NULL;
5613 JMutexAutoLock lock(m_sector_mutex);
5616 sector = loadSectorMeta(sectorsubdir);
5618 catch(InvalidFilenameException &e)
5622 catch(FileNotGoodException &e)
5626 catch(std::exception &e)
5634 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5635 (m_savedir+"/sectors/"+sectorsubdir);
5636 std::vector<fs::DirListNode>::iterator i2;
5637 for(i2=list2.begin(); i2!=list2.end(); i2++)
5643 loadBlock(sectorsubdir, i2->name, sector);
5645 catch(InvalidFilenameException &e)
5647 // This catches unknown crap in directory
5653 void ServerMap::saveBlock(MapBlock *block)
5655 DSTACK(__FUNCTION_NAME);
5657 Dummy blocks are not written
5659 if(block->isDummy())
5661 /*v3s16 p = block->getPos();
5662 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5663 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5667 // Format used for writing
5668 u8 version = SER_FMT_VER_HIGHEST;
5670 v3s16 p3d = block->getPos();
5671 v2s16 p2d(p3d.X, p3d.Z);
5672 createDir(m_savedir);
5673 createDir(m_savedir+"/sectors");
5674 std::string dir = getSectorDir(p2d);
5677 // Block file is map/sectors/xxxxxxxx/xxxx
5679 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5680 std::string fullpath = dir + "/" + cc;
5681 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5682 if(o.good() == false)
5683 throw FileNotGoodException("Cannot open block data");
5686 [0] u8 serialization version
5689 o.write((char*)&version, 1);
5691 block->serialize(o, version);
5694 Versions up from 9 have block objects.
5698 block->serializeObjects(o, version);
5701 // We just wrote it to the disk
5702 block->resetChangedFlag();
5705 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5707 DSTACK(__FUNCTION_NAME);
5711 // Block file is map/sectors/xxxxxxxx/xxxx
5712 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5713 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5714 if(is.good() == false)
5715 throw FileNotGoodException("Cannot open block file");
5717 v3s16 p3d = getBlockPos(sectordir, blockfile);
5718 v2s16 p2d(p3d.X, p3d.Z);
5720 assert(sector->getPos() == p2d);
5722 u8 version = SER_FMT_VER_INVALID;
5723 is.read((char*)&version, 1);
5726 throw SerializationError("ServerMap::loadBlock(): Failed"
5727 " to read MapBlock version");
5729 /*u32 block_size = MapBlock::serializedLength(version);
5730 SharedBuffer<u8> data(block_size);
5731 is.read((char*)*data, block_size);*/
5733 // This will always return a sector because we're the server
5734 //MapSector *sector = emergeSector(p2d);
5736 MapBlock *block = NULL;
5737 bool created_new = false;
5739 block = sector->getBlockNoCreate(p3d.Y);
5741 catch(InvalidPositionException &e)
5743 block = sector->createBlankBlockNoInsert(p3d.Y);
5747 // deserialize block data
5748 block->deSerialize(is, version);
5751 Versions up from 9 have block objects.
5755 block->updateObjects(is, version, NULL, 0);
5759 sector->insertBlock(block);
5762 Convert old formats to new and save
5765 // Save old format blocks in new format
5766 if(version < SER_FMT_VER_HIGHEST)
5771 // We just loaded it from the disk, so it's up-to-date.
5772 block->resetChangedFlag();
5775 catch(SerializationError &e)
5777 dstream<<"WARNING: Invalid block data on disk "
5778 "(SerializationError). Ignoring. "
5779 "A new one will be generated."
5784 void ServerMap::PrintInfo(std::ostream &out)
5795 ClientMap::ClientMap(
5797 MapDrawControl &control,
5798 scene::ISceneNode* parent,
5799 scene::ISceneManager* mgr,
5803 scene::ISceneNode(parent, mgr, id),
5806 m_camera_position(0,0,0),
5807 m_camera_direction(0,0,1)
5809 m_camera_mutex.Init();
5810 assert(m_camera_mutex.IsInitialized());
5812 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5813 BS*1000000,BS*1000000,BS*1000000);
5816 ClientMap::~ClientMap()
5818 /*JMutexAutoLock lock(mesh_mutex);
5827 MapSector * ClientMap::emergeSector(v2s16 p2d)
5829 DSTACK(__FUNCTION_NAME);
5830 // Check that it doesn't exist already
5832 return getSectorNoGenerate(p2d);
5834 catch(InvalidPositionException &e)
5839 ClientMapSector *sector = new ClientMapSector(this, p2d);
5842 JMutexAutoLock lock(m_sector_mutex);
5843 m_sectors.insert(p2d, sector);
5849 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5851 DSTACK(__FUNCTION_NAME);
5852 ClientMapSector *sector = NULL;
5854 JMutexAutoLock lock(m_sector_mutex);
5856 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5860 sector = (ClientMapSector*)n->getValue();
5861 assert(sector->getId() == MAPSECTOR_CLIENT);
5865 sector = new ClientMapSector(this, p2d);
5867 JMutexAutoLock lock(m_sector_mutex);
5868 m_sectors.insert(p2d, sector);
5872 sector->deSerialize(is);
5875 void ClientMap::OnRegisterSceneNode()
5879 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5880 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5883 ISceneNode::OnRegisterSceneNode();
5886 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5888 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5889 DSTACK(__FUNCTION_NAME);
5891 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5894 Get time for measuring timeout.
5896 Measuring time is very useful for long delays when the
5897 machine is swapping a lot.
5899 int time1 = time(0);
5901 u32 daynight_ratio = m_client->getDayNightRatio();
5903 m_camera_mutex.Lock();
5904 v3f camera_position = m_camera_position;
5905 v3f camera_direction = m_camera_direction;
5906 m_camera_mutex.Unlock();
5909 Get all blocks and draw all visible ones
5912 v3s16 cam_pos_nodes(
5913 camera_position.X / BS,
5914 camera_position.Y / BS,
5915 camera_position.Z / BS);
5917 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5919 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5920 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5922 // Take a fair amount as we will be dropping more out later
5924 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5925 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5926 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5928 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5929 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5930 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5932 u32 vertex_count = 0;
5934 // For limiting number of mesh updates per frame
5935 u32 mesh_update_count = 0;
5937 u32 blocks_would_have_drawn = 0;
5938 u32 blocks_drawn = 0;
5940 //NOTE: The sectors map should be locked but we're not doing it
5941 // because it'd cause too much delays
5943 int timecheck_counter = 0;
5944 core::map<v2s16, MapSector*>::Iterator si;
5945 si = m_sectors.getIterator();
5946 for(; si.atEnd() == false; si++)
5949 timecheck_counter++;
5950 if(timecheck_counter > 50)
5952 timecheck_counter = 0;
5953 int time2 = time(0);
5954 if(time2 > time1 + 4)
5956 dstream<<"ClientMap::renderMap(): "
5957 "Rendering takes ages, returning."
5964 MapSector *sector = si.getNode()->getValue();
5965 v2s16 sp = sector->getPos();
5967 if(m_control.range_all == false)
5969 if(sp.X < p_blocks_min.X
5970 || sp.X > p_blocks_max.X
5971 || sp.Y < p_blocks_min.Z
5972 || sp.Y > p_blocks_max.Z)
5976 core::list< MapBlock * > sectorblocks;
5977 sector->getBlocks(sectorblocks);
5983 core::list< MapBlock * >::Iterator i;
5984 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5986 MapBlock *block = *i;
5989 Compare block position to camera position, skip
5990 if not seen on display
5993 float range = 100000 * BS;
5994 if(m_control.range_all == false)
5995 range = m_control.wanted_range * BS;
5998 if(isBlockInSight(block->getPos(), camera_position,
5999 camera_direction, range, &d) == false)
6005 /*if(m_control.range_all == false &&
6006 d - 0.5*BS*MAP_BLOCKSIZE > range)
6011 Update expired mesh (used for day/night change)
6014 bool mesh_expired = false;
6017 JMutexAutoLock lock(block->mesh_mutex);
6019 mesh_expired = block->getMeshExpired();
6021 // Mesh has not been expired and there is no mesh:
6022 // block has no content
6023 if(block->mesh == NULL && mesh_expired == false)
6027 f32 faraway = BS*50;
6028 //f32 faraway = m_control.wanted_range * BS;
6031 This has to be done with the mesh_mutex unlocked
6033 // Pretty random but this should work somewhat nicely
6034 if(mesh_expired && (
6035 (mesh_update_count < 3
6036 && (d < faraway || mesh_update_count < 2)
6039 (m_control.range_all && mesh_update_count < 20)
6042 /*if(mesh_expired && mesh_update_count < 6
6043 && (d < faraway || mesh_update_count < 3))*/
6045 mesh_update_count++;
6047 // Mesh has been expired: generate new mesh
6048 //block->updateMeshes(daynight_i);
6049 block->updateMesh(daynight_ratio);
6051 mesh_expired = false;
6055 Don't draw an expired mesh that is far away
6057 /*if(mesh_expired && d >= faraway)
6060 // Instead, delete it
6061 JMutexAutoLock lock(block->mesh_mutex);
6064 block->mesh->drop();
6067 // And continue to next block
6072 Draw the faces of the block
6075 JMutexAutoLock lock(block->mesh_mutex);
6077 scene::SMesh *mesh = block->mesh;
6082 blocks_would_have_drawn++;
6083 if(blocks_drawn >= m_control.wanted_max_blocks
6084 && m_control.range_all == false
6085 && d > m_control.wanted_min_range * BS)
6089 u32 c = mesh->getMeshBufferCount();
6091 for(u32 i=0; i<c; i++)
6093 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
6094 const video::SMaterial& material = buf->getMaterial();
6095 video::IMaterialRenderer* rnd =
6096 driver->getMaterialRenderer(material.MaterialType);
6097 bool transparent = (rnd && rnd->isTransparent());
6098 // Render transparent on transparent pass and likewise.
6099 if(transparent == is_transparent_pass)
6102 This *shouldn't* hurt too much because Irrlicht
6103 doesn't change opengl textures if the old
6104 material is set again.
6106 driver->setMaterial(buf->getMaterial());
6107 driver->drawMeshBuffer(buf);
6108 vertex_count += buf->getVertexCount();
6112 } // foreach sectorblocks
6115 m_control.blocks_drawn = blocks_drawn;
6116 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
6118 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
6119 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
6122 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
6123 core::map<v3s16, MapBlock*> *affected_blocks)
6125 bool changed = false;
6127 Add it to all blocks touching it
6130 v3s16(0,0,0), // this
6131 v3s16(0,0,1), // back
6132 v3s16(0,1,0), // top
6133 v3s16(1,0,0), // right
6134 v3s16(0,0,-1), // front
6135 v3s16(0,-1,0), // bottom
6136 v3s16(-1,0,0), // left
6138 for(u16 i=0; i<7; i++)
6140 v3s16 p2 = p + dirs[i];
6141 // Block position of neighbor (or requested) node
6142 v3s16 blockpos = getNodeBlockPos(p2);
6143 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6144 if(blockref == NULL)
6146 // Relative position of requested node
6147 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
6148 if(blockref->setTempMod(relpos, mod))
6153 if(changed && affected_blocks!=NULL)
6155 for(u16 i=0; i<7; i++)
6157 v3s16 p2 = p + dirs[i];
6158 // Block position of neighbor (or requested) node
6159 v3s16 blockpos = getNodeBlockPos(p2);
6160 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6161 if(blockref == NULL)
6163 affected_blocks->insert(blockpos, blockref);
6169 bool ClientMap::clearTempMod(v3s16 p,
6170 core::map<v3s16, MapBlock*> *affected_blocks)
6172 bool changed = false;
6174 v3s16(0,0,0), // this
6175 v3s16(0,0,1), // back
6176 v3s16(0,1,0), // top
6177 v3s16(1,0,0), // right
6178 v3s16(0,0,-1), // front
6179 v3s16(0,-1,0), // bottom
6180 v3s16(-1,0,0), // left
6182 for(u16 i=0; i<7; i++)
6184 v3s16 p2 = p + dirs[i];
6185 // Block position of neighbor (or requested) node
6186 v3s16 blockpos = getNodeBlockPos(p2);
6187 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6188 if(blockref == NULL)
6190 // Relative position of requested node
6191 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
6192 if(blockref->clearTempMod(relpos))
6197 if(changed && affected_blocks!=NULL)
6199 for(u16 i=0; i<7; i++)
6201 v3s16 p2 = p + dirs[i];
6202 // Block position of neighbor (or requested) node
6203 v3s16 blockpos = getNodeBlockPos(p2);
6204 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6205 if(blockref == NULL)
6207 affected_blocks->insert(blockpos, blockref);
6213 void ClientMap::expireMeshes(bool only_daynight_diffed)
6215 TimeTaker timer("expireMeshes()");
6217 core::map<v2s16, MapSector*>::Iterator si;
6218 si = m_sectors.getIterator();
6219 for(; si.atEnd() == false; si++)
6221 MapSector *sector = si.getNode()->getValue();
6223 core::list< MapBlock * > sectorblocks;
6224 sector->getBlocks(sectorblocks);
6226 core::list< MapBlock * >::Iterator i;
6227 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6229 MapBlock *block = *i;
6231 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
6237 JMutexAutoLock lock(block->mesh_mutex);
6238 if(block->mesh != NULL)
6240 /*block->mesh->drop();
6241 block->mesh = NULL;*/
6242 block->setMeshExpired(true);
6249 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6251 assert(mapType() == MAPTYPE_CLIENT);
6254 v3s16 p = blockpos + v3s16(0,0,0);
6255 MapBlock *b = getBlockNoCreate(p);
6256 b->updateMesh(daynight_ratio);
6258 catch(InvalidPositionException &e){}
6261 v3s16 p = blockpos + v3s16(-1,0,0);
6262 MapBlock *b = getBlockNoCreate(p);
6263 b->updateMesh(daynight_ratio);
6265 catch(InvalidPositionException &e){}
6267 v3s16 p = blockpos + v3s16(0,-1,0);
6268 MapBlock *b = getBlockNoCreate(p);
6269 b->updateMesh(daynight_ratio);
6271 catch(InvalidPositionException &e){}
6273 v3s16 p = blockpos + v3s16(0,0,-1);
6274 MapBlock *b = getBlockNoCreate(p);
6275 b->updateMesh(daynight_ratio);
6277 catch(InvalidPositionException &e){}
6280 v3s16 p = blockpos + v3s16(1,0,0);
6281 MapBlock *b = getBlockNoCreate(p);
6282 b->updateMesh(daynight_ratio);
6284 catch(InvalidPositionException &e){}
6286 v3s16 p = blockpos + v3s16(0,1,0);
6287 MapBlock *b = getBlockNoCreate(p);
6288 b->updateMesh(daynight_ratio);
6290 catch(InvalidPositionException &e){}
6292 v3s16 p = blockpos + v3s16(0,0,1);
6293 MapBlock *b = getBlockNoCreate(p);
6294 b->updateMesh(daynight_ratio);
6296 catch(InvalidPositionException &e){}*/
6299 void ClientMap::PrintInfo(std::ostream &out)
6310 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6315 MapVoxelManipulator::~MapVoxelManipulator()
6317 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6321 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6323 TimeTaker timer1("emerge", &emerge_time);
6325 // Units of these are MapBlocks
6326 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6327 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6329 VoxelArea block_area_nodes
6330 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6332 addArea(block_area_nodes);
6334 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6335 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6336 for(s32 x=p_min.X; x<=p_max.X; x++)
6339 core::map<v3s16, bool>::Node *n;
6340 n = m_loaded_blocks.find(p);
6344 bool block_data_inexistent = false;
6347 TimeTaker timer1("emerge load", &emerge_load_time);
6349 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6350 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6353 dstream<<std::endl;*/
6355 MapBlock *block = m_map->getBlockNoCreate(p);
6356 if(block->isDummy())
6357 block_data_inexistent = true;
6359 block->copyTo(*this);
6361 catch(InvalidPositionException &e)
6363 block_data_inexistent = true;
6366 if(block_data_inexistent)
6368 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6369 // Fill with VOXELFLAG_INEXISTENT
6370 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6371 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6373 s32 i = m_area.index(a.MinEdge.X,y,z);
6374 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6378 m_loaded_blocks.insert(p, !block_data_inexistent);
6381 //dstream<<"emerge done"<<std::endl;
6385 SUGG: Add an option to only update eg. water and air nodes.
6386 This will make it interfere less with important stuff if
6389 void MapVoxelManipulator::blitBack
6390 (core::map<v3s16, MapBlock*> & modified_blocks)
6392 if(m_area.getExtent() == v3s16(0,0,0))
6395 //TimeTaker timer1("blitBack");
6397 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6398 <<m_loaded_blocks.size()<<std::endl;*/
6401 Initialize block cache
6403 v3s16 blockpos_last;
6404 MapBlock *block = NULL;
6405 bool block_checked_in_modified = false;
6407 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6408 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6409 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6413 u8 f = m_flags[m_area.index(p)];
6414 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6417 MapNode &n = m_data[m_area.index(p)];
6419 v3s16 blockpos = getNodeBlockPos(p);
6424 if(block == NULL || blockpos != blockpos_last){
6425 block = m_map->getBlockNoCreate(blockpos);
6426 blockpos_last = blockpos;
6427 block_checked_in_modified = false;
6430 // Calculate relative position in block
6431 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6433 // Don't continue if nothing has changed here
6434 if(block->getNode(relpos) == n)
6437 //m_map->setNode(m_area.MinEdge + p, n);
6438 block->setNode(relpos, n);
6441 Make sure block is in modified_blocks
6443 if(block_checked_in_modified == false)
6445 modified_blocks[blockpos] = block;
6446 block_checked_in_modified = true;
6449 catch(InvalidPositionException &e)
6455 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6456 MapVoxelManipulator(map)
6460 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6464 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6466 // Just create the area so that it can be pointed to
6467 VoxelManipulator::emerge(a, caller_id);
6470 void ManualMapVoxelManipulator::initialEmerge(
6471 v3s16 blockpos_min, v3s16 blockpos_max)
6473 TimeTaker timer1("initialEmerge", &emerge_time);
6475 // Units of these are MapBlocks
6476 v3s16 p_min = blockpos_min;
6477 v3s16 p_max = blockpos_max;
6479 VoxelArea block_area_nodes
6480 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6482 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6485 dstream<<"initialEmerge: area: ";
6486 block_area_nodes.print(dstream);
6487 dstream<<" ("<<size_MB<<"MB)";
6491 addArea(block_area_nodes);
6493 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6494 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6495 for(s32 x=p_min.X; x<=p_max.X; x++)
6498 core::map<v3s16, bool>::Node *n;
6499 n = m_loaded_blocks.find(p);
6503 bool block_data_inexistent = false;
6506 TimeTaker timer1("emerge load", &emerge_load_time);
6508 MapBlock *block = m_map->getBlockNoCreate(p);
6509 if(block->isDummy())
6510 block_data_inexistent = true;
6512 block->copyTo(*this);
6514 catch(InvalidPositionException &e)
6516 block_data_inexistent = true;
6519 if(block_data_inexistent)
6522 Mark area inexistent
6524 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6525 // Fill with VOXELFLAG_INEXISTENT
6526 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6527 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6529 s32 i = m_area.index(a.MinEdge.X,y,z);
6530 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6534 m_loaded_blocks.insert(p, !block_data_inexistent);
6538 void ManualMapVoxelManipulator::blitBackAll(
6539 core::map<v3s16, MapBlock*> * modified_blocks)
6541 if(m_area.getExtent() == v3s16(0,0,0))
6545 Copy data of all blocks
6547 for(core::map<v3s16, bool>::Iterator
6548 i = m_loaded_blocks.getIterator();
6549 i.atEnd() == false; i++)
6551 bool existed = i.getNode()->getValue();
6552 if(existed == false)
6554 v3s16 p = i.getNode()->getKey();
6555 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6558 dstream<<"WARNING: "<<__FUNCTION_NAME
6559 <<": got NULL block "
6560 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6565 block->copyFrom(*this);
6568 modified_blocks->insert(p, block);