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 If node lets sunlight through and is under sunlight, it has
947 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
949 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
953 Set the node on the map
962 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
965 NodeMetadata *meta = meta_proto->clone();
966 setNodeMetadata(p, meta);
970 If node is under sunlight and doesn't let sunlight through,
971 take all sunlighted nodes under it and clear light from them
972 and from where the light has been spread.
973 TODO: This could be optimized by mass-unlighting instead
976 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
980 //m_dout<<DTIME<<"y="<<y<<std::endl;
981 v3s16 n2pos(p.X, y, p.Z);
987 catch(InvalidPositionException &e)
992 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
994 unLightNeighbors(LIGHTBANK_DAY,
995 n2pos, n2.getLight(LIGHTBANK_DAY),
996 light_sources, modified_blocks);
997 n2.setLight(LIGHTBANK_DAY, 0);
1005 for(s32 i=0; i<2; i++)
1007 enum LightBank bank = banks[i];
1010 Spread light from all nodes that might be capable of doing so
1012 spreadLight(bank, light_sources, modified_blocks);
1016 Update information about whether day and night light differ
1018 for(core::map<v3s16, MapBlock*>::Iterator
1019 i = modified_blocks.getIterator();
1020 i.atEnd() == false; i++)
1022 MapBlock *block = i.getNode()->getValue();
1023 block->updateDayNightDiff();
1027 Add neighboring liquid nodes and the node itself if it is
1028 liquid (=water node was added) to transform queue.
1031 v3s16(0,0,0), // self
1032 v3s16(0,0,1), // back
1033 v3s16(0,1,0), // top
1034 v3s16(1,0,0), // right
1035 v3s16(0,0,-1), // front
1036 v3s16(0,-1,0), // bottom
1037 v3s16(-1,0,0), // left
1039 for(u16 i=0; i<7; i++)
1044 v3s16 p2 = p + dirs[i];
1046 MapNode n2 = getNode(p2);
1047 if(content_liquid(n2.d))
1049 m_transforming_liquid.push_back(p2);
1052 }catch(InvalidPositionException &e)
1060 void Map::removeNodeAndUpdate(v3s16 p,
1061 core::map<v3s16, MapBlock*> &modified_blocks)
1063 /*PrintInfo(m_dout);
1064 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1065 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1067 bool node_under_sunlight = true;
1069 v3s16 toppos = p + v3s16(0,1,0);
1071 // Node will be replaced with this
1072 u8 replace_material = CONTENT_AIR;
1075 If there is a node at top and it doesn't have sunlight,
1076 there will be no sunlight going down.
1079 MapNode topnode = getNode(toppos);
1081 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1082 node_under_sunlight = false;
1084 catch(InvalidPositionException &e)
1088 core::map<v3s16, bool> light_sources;
1090 enum LightBank banks[] =
1095 for(s32 i=0; i<2; i++)
1097 enum LightBank bank = banks[i];
1100 Unlight neighbors (in case the node is a light source)
1102 unLightNeighbors(bank, p,
1103 getNode(p).getLight(bank),
1104 light_sources, modified_blocks);
1108 Remove node metadata
1111 removeNodeMetadata(p);
1115 This also clears the lighting.
1119 n.d = replace_material;
1122 for(s32 i=0; i<2; i++)
1124 enum LightBank bank = banks[i];
1127 Recalculate lighting
1129 spreadLight(bank, light_sources, modified_blocks);
1132 // Add the block of the removed node to modified_blocks
1133 v3s16 blockpos = getNodeBlockPos(p);
1134 MapBlock * block = getBlockNoCreate(blockpos);
1135 assert(block != NULL);
1136 modified_blocks.insert(blockpos, block);
1139 If the removed node was under sunlight, propagate the
1140 sunlight down from it and then light all neighbors
1141 of the propagated blocks.
1143 if(node_under_sunlight)
1145 s16 ybottom = propagateSunlight(p, modified_blocks);
1146 /*m_dout<<DTIME<<"Node was under sunlight. "
1147 "Propagating sunlight";
1148 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1150 for(; y >= ybottom; y--)
1152 v3s16 p2(p.X, y, p.Z);
1153 /*m_dout<<DTIME<<"lighting neighbors of node ("
1154 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1156 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1161 // Set the lighting of this node to 0
1162 // TODO: Is this needed? Lighting is cleared up there already.
1164 MapNode n = getNode(p);
1165 n.setLight(LIGHTBANK_DAY, 0);
1168 catch(InvalidPositionException &e)
1174 for(s32 i=0; i<2; i++)
1176 enum LightBank bank = banks[i];
1178 // Get the brightest neighbour node and propagate light from it
1179 v3s16 n2p = getBrightestNeighbour(bank, p);
1181 MapNode n2 = getNode(n2p);
1182 lightNeighbors(bank, n2p, modified_blocks);
1184 catch(InvalidPositionException &e)
1190 Update information about whether day and night light differ
1192 for(core::map<v3s16, MapBlock*>::Iterator
1193 i = modified_blocks.getIterator();
1194 i.atEnd() == false; i++)
1196 MapBlock *block = i.getNode()->getValue();
1197 block->updateDayNightDiff();
1201 Add neighboring liquid nodes to transform queue.
1204 v3s16(0,0,1), // back
1205 v3s16(0,1,0), // top
1206 v3s16(1,0,0), // right
1207 v3s16(0,0,-1), // front
1208 v3s16(0,-1,0), // bottom
1209 v3s16(-1,0,0), // left
1211 for(u16 i=0; i<6; i++)
1216 v3s16 p2 = p + dirs[i];
1218 MapNode n2 = getNode(p2);
1219 if(content_liquid(n2.d))
1221 m_transforming_liquid.push_back(p2);
1224 }catch(InvalidPositionException &e)
1230 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1233 event.type = MEET_ADDNODE;
1237 bool succeeded = true;
1239 core::map<v3s16, MapBlock*> modified_blocks;
1240 addNodeAndUpdate(p, n, modified_blocks);
1242 // Copy modified_blocks to event
1243 for(core::map<v3s16, MapBlock*>::Iterator
1244 i = modified_blocks.getIterator();
1245 i.atEnd()==false; i++)
1247 event.modified_blocks.insert(i.getNode()->getKey(), false);
1250 catch(InvalidPositionException &e){
1254 dispatchEvent(&event);
1259 bool Map::removeNodeWithEvent(v3s16 p)
1262 event.type = MEET_REMOVENODE;
1265 bool succeeded = true;
1267 core::map<v3s16, MapBlock*> modified_blocks;
1268 removeNodeAndUpdate(p, modified_blocks);
1270 // Copy modified_blocks to event
1271 for(core::map<v3s16, MapBlock*>::Iterator
1272 i = modified_blocks.getIterator();
1273 i.atEnd()==false; i++)
1275 event.modified_blocks.insert(i.getNode()->getKey(), false);
1278 catch(InvalidPositionException &e){
1282 dispatchEvent(&event);
1287 bool Map::dayNightDiffed(v3s16 blockpos)
1290 v3s16 p = blockpos + v3s16(0,0,0);
1291 MapBlock *b = getBlockNoCreate(p);
1292 if(b->dayNightDiffed())
1295 catch(InvalidPositionException &e){}
1298 v3s16 p = blockpos + v3s16(-1,0,0);
1299 MapBlock *b = getBlockNoCreate(p);
1300 if(b->dayNightDiffed())
1303 catch(InvalidPositionException &e){}
1305 v3s16 p = blockpos + v3s16(0,-1,0);
1306 MapBlock *b = getBlockNoCreate(p);
1307 if(b->dayNightDiffed())
1310 catch(InvalidPositionException &e){}
1312 v3s16 p = blockpos + v3s16(0,0,-1);
1313 MapBlock *b = getBlockNoCreate(p);
1314 if(b->dayNightDiffed())
1317 catch(InvalidPositionException &e){}
1320 v3s16 p = blockpos + v3s16(1,0,0);
1321 MapBlock *b = getBlockNoCreate(p);
1322 if(b->dayNightDiffed())
1325 catch(InvalidPositionException &e){}
1327 v3s16 p = blockpos + v3s16(0,1,0);
1328 MapBlock *b = getBlockNoCreate(p);
1329 if(b->dayNightDiffed())
1332 catch(InvalidPositionException &e){}
1334 v3s16 p = blockpos + v3s16(0,0,1);
1335 MapBlock *b = getBlockNoCreate(p);
1336 if(b->dayNightDiffed())
1339 catch(InvalidPositionException &e){}
1345 Updates usage timers
1347 void Map::timerUpdate(float dtime)
1349 JMutexAutoLock lock(m_sector_mutex);
1351 core::map<v2s16, MapSector*>::Iterator si;
1353 si = m_sectors.getIterator();
1354 for(; si.atEnd() == false; si++)
1356 MapSector *sector = si.getNode()->getValue();
1357 sector->usage_timer += dtime;
1361 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1364 Wait for caches to be removed before continuing.
1366 This disables the existence of caches while locked
1368 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1370 core::list<v2s16>::Iterator j;
1371 for(j=list.begin(); j!=list.end(); j++)
1373 MapSector *sector = m_sectors[*j];
1376 sector->deleteBlocks();
1381 If sector is in sector cache, remove it from there
1383 if(m_sector_cache == sector)
1385 m_sector_cache = NULL;
1388 Remove from map and delete
1390 m_sectors.remove(*j);
1396 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1397 core::list<v3s16> *deleted_blocks)
1399 JMutexAutoLock lock(m_sector_mutex);
1401 core::list<v2s16> sector_deletion_queue;
1402 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1403 for(; i.atEnd() == false; i++)
1405 MapSector *sector = i.getNode()->getValue();
1407 Delete sector from memory if it hasn't been used in a long time
1409 if(sector->usage_timer > timeout)
1411 sector_deletion_queue.push_back(i.getNode()->getKey());
1413 if(deleted_blocks != NULL)
1415 // Collect positions of blocks of sector
1416 MapSector *sector = i.getNode()->getValue();
1417 core::list<MapBlock*> blocks;
1418 sector->getBlocks(blocks);
1419 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1420 i != blocks.end(); i++)
1422 deleted_blocks->push_back((*i)->getPos());
1427 deleteSectors(sector_deletion_queue, only_blocks);
1428 return sector_deletion_queue.getSize();
1431 void Map::PrintInfo(std::ostream &out)
1436 #define WATER_DROP_BOOST 4
1438 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1440 DSTACK(__FUNCTION_NAME);
1441 //TimeTaker timer("transformLiquids()");
1444 u32 initial_size = m_transforming_liquid.size();
1446 /*if(initial_size != 0)
1447 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1449 while(m_transforming_liquid.size() != 0)
1452 Get a queued transforming liquid node
1454 v3s16 p0 = m_transforming_liquid.pop_front();
1456 MapNode n0 = getNode(p0);
1458 // Don't deal with non-liquids
1459 if(content_liquid(n0.d) == false)
1462 bool is_source = !content_flowing_liquid(n0.d);
1464 u8 liquid_level = 8;
1465 if(is_source == false)
1466 liquid_level = n0.param2 & 0x0f;
1468 // Turn possible source into non-source
1469 u8 nonsource_c = make_liquid_flowing(n0.d);
1472 If not source, check that some node flows into this one
1473 and what is the level of liquid in this one
1475 if(is_source == false)
1477 s8 new_liquid_level_max = -1;
1479 v3s16 dirs_from[5] = {
1480 v3s16(0,1,0), // top
1481 v3s16(0,0,1), // back
1482 v3s16(1,0,0), // right
1483 v3s16(0,0,-1), // front
1484 v3s16(-1,0,0), // left
1486 for(u16 i=0; i<5; i++)
1491 bool from_top = (i==0);
1493 v3s16 p2 = p0 + dirs_from[i];
1494 MapNode n2 = getNode(p2);
1496 if(content_liquid(n2.d))
1498 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1499 // Check that the liquids are the same type
1500 if(n2_nonsource_c != nonsource_c)
1502 dstream<<"WARNING: Not handling: different liquids"
1503 " collide"<<std::endl;
1506 bool n2_is_source = !content_flowing_liquid(n2.d);
1507 s8 n2_liquid_level = 8;
1508 if(n2_is_source == false)
1509 n2_liquid_level = n2.param2 & 0x07;
1511 s8 new_liquid_level = -1;
1514 //new_liquid_level = 7;
1515 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1516 new_liquid_level = 7;
1518 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1520 else if(n2_liquid_level > 0)
1522 new_liquid_level = n2_liquid_level - 1;
1525 if(new_liquid_level > new_liquid_level_max)
1526 new_liquid_level_max = new_liquid_level;
1529 }catch(InvalidPositionException &e)
1535 If liquid level should be something else, update it and
1536 add all the neighboring water nodes to the transform queue.
1538 if(new_liquid_level_max != liquid_level)
1540 if(new_liquid_level_max == -1)
1542 // Remove water alltoghether
1549 n0.param2 = new_liquid_level_max;
1553 // Block has been modified
1555 v3s16 blockpos = getNodeBlockPos(p0);
1556 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1558 modified_blocks.insert(blockpos, block);
1562 Add neighboring non-source liquid nodes to transform queue.
1565 v3s16(0,0,1), // back
1566 v3s16(0,1,0), // top
1567 v3s16(1,0,0), // right
1568 v3s16(0,0,-1), // front
1569 v3s16(0,-1,0), // bottom
1570 v3s16(-1,0,0), // left
1572 for(u16 i=0; i<6; i++)
1577 v3s16 p2 = p0 + dirs[i];
1579 MapNode n2 = getNode(p2);
1580 if(content_flowing_liquid(n2.d))
1582 m_transforming_liquid.push_back(p2);
1585 }catch(InvalidPositionException &e)
1592 // Get a new one from queue if the node has turned into non-water
1593 if(content_liquid(n0.d) == false)
1597 Flow water from this node
1599 v3s16 dirs_to[5] = {
1600 v3s16(0,-1,0), // bottom
1601 v3s16(0,0,1), // back
1602 v3s16(1,0,0), // right
1603 v3s16(0,0,-1), // front
1604 v3s16(-1,0,0), // left
1606 for(u16 i=0; i<5; i++)
1611 bool to_bottom = (i == 0);
1613 // If liquid is at lowest possible height, it's not going
1614 // anywhere except down
1615 if(liquid_level == 0 && to_bottom == false)
1618 u8 liquid_next_level = 0;
1619 // If going to bottom
1622 //liquid_next_level = 7;
1623 if(liquid_level >= 7 - WATER_DROP_BOOST)
1624 liquid_next_level = 7;
1626 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1629 liquid_next_level = liquid_level - 1;
1631 bool n2_changed = false;
1632 bool flowed = false;
1634 v3s16 p2 = p0 + dirs_to[i];
1636 MapNode n2 = getNode(p2);
1637 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1639 if(content_liquid(n2.d))
1641 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1642 // Check that the liquids are the same type
1643 if(n2_nonsource_c != nonsource_c)
1645 dstream<<"WARNING: Not handling: different liquids"
1646 " collide"<<std::endl;
1649 bool n2_is_source = !content_flowing_liquid(n2.d);
1650 u8 n2_liquid_level = 8;
1651 if(n2_is_source == false)
1652 n2_liquid_level = n2.param2 & 0x07;
1661 // Just flow into the source, nothing changes.
1662 // n2_changed is not set because destination didn't change
1667 if(liquid_next_level > liquid_level)
1669 n2.param2 = liquid_next_level;
1677 else if(n2.d == CONTENT_AIR)
1680 n2.param2 = liquid_next_level;
1687 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1691 m_transforming_liquid.push_back(p2);
1693 v3s16 blockpos = getNodeBlockPos(p2);
1694 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1696 modified_blocks.insert(blockpos, block);
1699 // If n2_changed to bottom, don't flow anywhere else
1700 if(to_bottom && flowed && !is_source)
1703 }catch(InvalidPositionException &e)
1709 //if(loopcount >= 100000)
1710 if(loopcount >= initial_size * 1)
1713 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1716 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1718 v3s16 blockpos = getNodeBlockPos(p);
1719 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1720 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1723 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1727 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1731 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1733 v3s16 blockpos = getNodeBlockPos(p);
1734 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1735 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1738 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1742 block->m_node_metadata.set(p_rel, meta);
1745 void Map::removeNodeMetadata(v3s16 p)
1747 v3s16 blockpos = getNodeBlockPos(p);
1748 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1749 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1752 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1756 block->m_node_metadata.remove(p_rel);
1759 void Map::nodeMetadataStep(float dtime,
1760 core::map<v3s16, MapBlock*> &changed_blocks)
1764 Currently there is no way to ensure that all the necessary
1765 blocks are loaded when this is run. (They might get unloaded)
1766 NOTE: ^- Actually, that might not be so. In a quick test it
1767 reloaded a block with a furnace when I walked back to it from
1770 core::map<v2s16, MapSector*>::Iterator si;
1771 si = m_sectors.getIterator();
1772 for(; si.atEnd() == false; si++)
1774 MapSector *sector = si.getNode()->getValue();
1775 core::list< MapBlock * > sectorblocks;
1776 sector->getBlocks(sectorblocks);
1777 core::list< MapBlock * >::Iterator i;
1778 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1780 MapBlock *block = *i;
1781 bool changed = block->m_node_metadata.step(dtime);
1783 changed_blocks[block->getPos()] = block;
1792 ServerMap::ServerMap(std::string savedir):
1798 //m_chunksize = 16; // Too slow
1799 m_chunksize = 8; // Takes a few seconds
1803 // TODO: Save to and load from a file
1804 m_seed = (((u64)(myrand()%0xffff)<<0)
1805 + ((u64)(myrand()%0xffff)<<16)
1806 + ((u64)(myrand()%0xffff)<<32)
1807 + ((u64)(myrand()%0xffff)<<48));
1810 Experimental and debug stuff
1817 Try to load map; if not found, create a new one.
1820 m_savedir = savedir;
1821 m_map_saving_enabled = false;
1825 // If directory exists, check contents and load if possible
1826 if(fs::PathExists(m_savedir))
1828 // If directory is empty, it is safe to save into it.
1829 if(fs::GetDirListing(m_savedir).size() == 0)
1831 dstream<<DTIME<<"Server: Empty save directory is valid."
1833 m_map_saving_enabled = true;
1837 // Load map metadata (seed, chunksize)
1840 // Load chunk metadata
1843 /*// Load sector (0,0) and throw and exception on fail
1844 if(loadSectorFull(v2s16(0,0)) == false)
1845 throw LoadError("Failed to load sector (0,0)");*/
1847 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1848 "metadata and sector (0,0) from "<<savedir<<
1849 ", assuming valid save directory."
1852 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1853 <<"and chunk metadata from "<<savedir
1854 <<", assuming valid save directory."
1857 m_map_saving_enabled = true;
1858 // Map loaded, not creating new one
1862 // If directory doesn't exist, it is safe to save to it
1864 m_map_saving_enabled = true;
1867 catch(std::exception &e)
1869 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1870 <<", exception: "<<e.what()<<std::endl;
1871 dstream<<"Please remove the map or fix it."<<std::endl;
1872 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1875 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1877 // Create zero sector
1878 emergeSector(v2s16(0,0));
1880 // Initially write whole map
1884 ServerMap::~ServerMap()
1888 if(m_map_saving_enabled)
1891 // Save only changed parts
1893 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1897 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1900 catch(std::exception &e)
1902 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1903 <<", exception: "<<e.what()<<std::endl;
1909 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1910 for(; i.atEnd() == false; i++)
1912 MapChunk *chunk = i.getNode()->getValue();
1918 Some helper functions for the map generator
1921 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1923 v3s16 em = vmanip.m_area.getExtent();
1924 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1925 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1926 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1928 for(y=y_nodes_max; y>=y_nodes_min; y--)
1930 MapNode &n = vmanip.m_data[i];
1931 if(content_walkable(n.d))
1934 vmanip.m_area.add_y(em, i, -1);
1936 if(y >= y_nodes_min)
1942 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1944 v3s16 em = vmanip.m_area.getExtent();
1945 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1946 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1947 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1949 for(y=y_nodes_max; y>=y_nodes_min; y--)
1951 MapNode &n = vmanip.m_data[i];
1952 if(content_walkable(n.d)
1953 && n.d != CONTENT_TREE
1954 && n.d != CONTENT_LEAVES)
1957 vmanip.m_area.add_y(em, i, -1);
1959 if(y >= y_nodes_min)
1965 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1967 MapNode treenode(CONTENT_TREE);
1968 MapNode leavesnode(CONTENT_LEAVES);
1970 s16 trunk_h = myrand_range(3, 6);
1972 for(s16 ii=0; ii<trunk_h; ii++)
1974 if(vmanip.m_area.contains(p1))
1975 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1979 // p1 is now the last piece of the trunk
1982 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1983 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1984 Buffer<u8> leaves_d(leaves_a.getVolume());
1985 for(s32 i=0; i<leaves_a.getVolume(); i++)
1988 // Force leaves at near the end of the trunk
1991 for(s16 z=-d; z<=d; z++)
1992 for(s16 y=-d; y<=d; y++)
1993 for(s16 x=-d; x<=d; x++)
1995 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1999 // Add leaves randomly
2000 for(u32 iii=0; iii<7; iii++)
2005 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2006 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2007 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2010 for(s16 z=0; z<=d; z++)
2011 for(s16 y=0; y<=d; y++)
2012 for(s16 x=0; x<=d; x++)
2014 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2018 // Blit leaves to vmanip
2019 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2020 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2021 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2025 if(vmanip.m_area.contains(p) == false)
2027 u32 vi = vmanip.m_area.index(p);
2028 if(vmanip.m_data[vi].d != CONTENT_AIR)
2030 u32 i = leaves_a.index(x,y,z);
2031 if(leaves_d[i] == 1)
2032 vmanip.m_data[vi] = leavesnode;
2037 Noise functions. Make sure seed is mangled differently in each one.
2040 // Amount of trees per area in nodes
2041 double tree_amount_2d(u64 seed, v2s16 p)
2043 double noise = noise2d_perlin(
2044 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2046 double zeroval = -0.3;
2050 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2053 #define AVERAGE_MUD_AMOUNT 4
2055 double base_rock_level_2d(u64 seed, v2s16 p)
2057 // The base ground level
2058 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2059 + 25. * noise2d_perlin(
2060 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2061 (seed>>32)+654879876, 6, 0.6);
2063 /*// A bit hillier one
2064 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2065 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2066 (seed>>27)+90340, 6, 0.69);
2070 // Higher ground level
2071 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2072 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2073 seed+85039, 5, 0.69);
2074 //higher = 30; // For debugging
2076 // Limit higher to at least base
2080 // Steepness factor of cliffs
2081 double b = 1.0 + 1.0 * noise2d_perlin(
2082 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2084 b = rangelim(b, 0.0, 1000.0);
2087 b = rangelim(b, 3.0, 1000.0);
2088 //dstream<<"b="<<b<<std::endl;
2091 // Offset to more low
2092 double a_off = -0.2;
2093 // High/low selector
2094 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2095 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2096 seed-359, 6, 0.7));*/
2097 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2098 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2099 seed-359, 5, 0.60));
2101 a = rangelim(a, 0.0, 1.0);
2103 //dstream<<"a="<<a<<std::endl;
2105 double h = base*(1.0-a) + higher*a;
2113 Adds random objects to block, depending on the content of the block
2115 void addRandomObjects(MapBlock *block)
2117 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2118 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2120 bool last_node_walkable = false;
2121 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2124 MapNode n = block->getNodeNoEx(p);
2125 if(n.d == CONTENT_IGNORE)
2127 if(content_features(n.d).liquid_type != LIQUID_NONE)
2129 if(content_features(n.d).walkable)
2131 last_node_walkable = true;
2134 if(last_node_walkable)
2136 // If block contains light information
2137 if(content_features(n.d).param_type == CPT_LIGHT)
2139 if(n.getLight(LIGHTBANK_DAY) <= 3)
2141 if(myrand() % 300 == 0)
2143 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2145 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2146 std::string data = obj->getStaticData();
2147 StaticObject s_obj(obj->getType(),
2148 obj->getBasePosition(), data);
2150 block->m_static_objects.insert(0, s_obj);
2151 block->m_static_objects.insert(0, s_obj);
2152 block->m_static_objects.insert(0, s_obj);
2153 block->m_static_objects.insert(0, s_obj);
2154 block->m_static_objects.insert(0, s_obj);
2155 block->m_static_objects.insert(0, s_obj);
2161 last_node_walkable = false;
2164 block->setChangedFlag();
2167 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2170 This is the main map generation method
2173 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2174 core::map<v3s16, MapBlock*> &changed_blocks,
2177 DSTACK(__FUNCTION_NAME);
2180 Don't generate if already fully generated
2184 MapChunk *chunk = getChunk(chunkpos);
2185 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2187 dstream<<"generateChunkRaw(): Chunk "
2188 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2189 <<" already generated"<<std::endl;
2194 dstream<<"generateChunkRaw(): Generating chunk "
2195 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2198 TimeTaker timer("generateChunkRaw()");
2200 // The distance how far into the neighbors the generator is allowed to go.
2201 s16 max_spread_amount_sectors = 2;
2202 assert(max_spread_amount_sectors <= m_chunksize);
2203 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2205 // Minimum amount of space left on sides for mud to fall in
2206 //s16 min_mud_fall_space = 2;
2208 // Maximum diameter of stone obstacles in X and Z
2209 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2210 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2212 s16 y_blocks_min = -4;
2213 s16 y_blocks_max = 3;
2214 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2215 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2216 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2218 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2219 s16 sectorpos_base_size = m_chunksize;
2221 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2222 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2223 v2s16 sectorpos_bigbase =
2224 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2225 s16 sectorpos_bigbase_size =
2226 sectorpos_base_size + 2 * max_spread_amount_sectors;
2228 v3s16 bigarea_blocks_min(
2229 sectorpos_bigbase.X,
2234 v3s16 bigarea_blocks_max(
2235 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2237 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2240 // Relative values to control amount of stuff in one chunk
2241 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2242 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2243 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2244 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2245 *(u32)h_blocks*MAP_BLOCKSIZE;
2248 The limiting edges of the lighting update, inclusive.
2250 s16 lighting_min_d = 0-max_spread_amount;
2251 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2254 Create the whole area of this and the neighboring chunks
2257 TimeTaker timer("generateChunkRaw() create area");
2259 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2260 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2262 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2263 ServerMapSector *sector = createSector(sectorpos);
2266 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2268 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2269 MapBlock *block = createBlock(blockpos);
2271 // Lighting won't be calculated
2272 //block->setLightingExpired(true);
2273 // Lighting will be calculated
2274 block->setLightingExpired(false);
2277 Block gets sunlight if this is true.
2279 This should be set to true when the top side of a block
2280 is completely exposed to the sky.
2282 Actually this doesn't matter now because the
2283 initial lighting is done here.
2285 block->setIsUnderground(y != y_blocks_max);
2291 Now we have a big empty area.
2293 Make a ManualMapVoxelManipulator that contains this and the
2297 ManualMapVoxelManipulator vmanip(this);
2298 // Add the area we just generated
2300 TimeTaker timer("generateChunkRaw() initialEmerge");
2301 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2305 vmanip.clearFlag(0xff);
2307 TimeTaker timer_generate("generateChunkRaw() generate");
2309 // Maximum height of the stone surface and obstacles.
2310 // This is used to disable cave generation from going too high.
2311 s16 stone_surface_max_y = 0;
2314 Generate general ground level to full area
2319 //TimeTaker timer1("ground level");
2321 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2322 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2325 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2328 Skip of already generated
2331 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2332 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2336 // Ground height at this point
2337 float surface_y_f = 0.0;
2339 // Use perlin noise for ground height
2340 surface_y_f = base_rock_level_2d(m_seed, p2d);
2342 /*// Experimental stuff
2344 float a = highlands_level_2d(m_seed, p2d);
2349 // Convert to integer
2350 s16 surface_y = (s16)surface_y_f;
2353 if(surface_y > stone_surface_max_y)
2354 stone_surface_max_y = surface_y;
2357 Fill ground with stone
2360 // Use fast index incrementing
2361 v3s16 em = vmanip.m_area.getExtent();
2362 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2363 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2365 vmanip.m_data[i].d = CONTENT_STONE;
2367 vmanip.m_area.add_y(em, i, 1);
2375 Randomize some parameters
2378 //s32 stone_obstacle_count = 0;
2379 /*s32 stone_obstacle_count =
2380 rangelim((1.0+noise2d(m_seed+897,
2381 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2383 //s16 stone_obstacle_max_height = 0;
2384 /*s16 stone_obstacle_max_height =
2385 rangelim((1.0+noise2d(m_seed+5902,
2386 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2389 Loop this part, it will make stuff look older and newer nicely
2391 //for(u32 i_age=0; i_age<1; i_age++)
2392 for(u32 i_age=0; i_age<2; i_age++)
2394 /******************************
2395 BEGINNING OF AGING LOOP
2396 ******************************/
2401 //TimeTaker timer1("stone obstacles");
2404 Add some random stone obstacles
2407 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2409 // Randomize max height so usually stuff will be quite low
2410 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2412 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2413 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2416 myrand_range(5, stone_obstacle_max_size),
2417 myrand_range(0, maxheight_randomized),
2418 myrand_range(5, stone_obstacle_max_size)
2421 // Don't make stupid small rectangle bumps
2426 myrand_range(1+ob_size.X/2+2,
2427 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2428 myrand_range(1+ob_size.Z/2+2,
2429 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2432 // Minimum space left on top of the obstacle
2433 s16 min_head_space = 12;
2435 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2436 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2438 // Node position in 2d
2439 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2441 // Find stone ground level
2442 // (ignore everything else than mud in already generated chunks)
2443 // and mud amount over the stone level
2447 v3s16 em = vmanip.m_area.getExtent();
2448 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2450 // Go to ground level
2451 for(y=y_nodes_max; y>=y_nodes_min; y--)
2453 MapNode *n = &vmanip.m_data[i];
2454 /*if(content_walkable(n.d)
2455 && n.d != CONTENT_MUD
2456 && n.d != CONTENT_GRASS)
2458 if(n->d == CONTENT_STONE)
2461 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2465 Change to mud because otherwise we might
2466 be throwing mud on grass at the next
2472 vmanip.m_area.add_y(em, i, -1);
2474 if(y >= y_nodes_min)
2477 surface_y = y_nodes_min;
2485 v3s16 em = vmanip.m_area.getExtent();
2486 s16 y_start = surface_y+1;
2487 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2491 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2493 MapNode &n = vmanip.m_data[i];
2494 n.d = CONTENT_STONE;
2496 if(y > stone_surface_max_y)
2497 stone_surface_max_y = y;
2500 if(count >= ob_size.Y)
2503 vmanip.m_area.add_y(em, i, 1);
2507 for(; y<=y_nodes_max - min_head_space; y++)
2509 MapNode &n = vmanip.m_data[i];
2512 if(count >= mud_amount)
2515 vmanip.m_area.add_y(em, i, 1);
2527 //TimeTaker timer1("caves");
2532 u32 caves_count = relative_volume / 400000;
2533 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2534 if(stone_surface_max_y < WATER_LEVEL)
2536 /*u32 caves_count = 0;
2537 u32 bruises_count = 0;*/
2538 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2540 s16 min_tunnel_diameter = 3;
2541 s16 max_tunnel_diameter = 5;
2542 u16 tunnel_routepoints = 20;
2544 v3f main_direction(0,0,0);
2546 bool bruise_surface = (jj > caves_count);
2550 min_tunnel_diameter = 5;
2551 max_tunnel_diameter = myrand_range(10, 20);
2552 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2553 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2555 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2556 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2558 tunnel_routepoints = 5;
2564 // Allowed route area size in nodes
2566 sectorpos_base_size*MAP_BLOCKSIZE,
2567 h_blocks*MAP_BLOCKSIZE,
2568 sectorpos_base_size*MAP_BLOCKSIZE
2571 // Area starting point in nodes
2573 sectorpos_base.X*MAP_BLOCKSIZE,
2574 y_blocks_min*MAP_BLOCKSIZE,
2575 sectorpos_base.Y*MAP_BLOCKSIZE
2579 //(this should be more than the maximum radius of the tunnel)
2580 //s16 insure = 5; // Didn't work with max_d = 20
2582 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2583 ar += v3s16(1,0,1) * more * 2;
2584 of -= v3s16(1,0,1) * more;
2586 s16 route_y_min = 0;
2587 // Allow half a diameter + 7 over stone surface
2588 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2590 /*// If caves, don't go through surface too often
2591 if(bruise_surface == false)
2592 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2594 // Limit maximum to area
2595 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2599 /*// Minimum is at y=0
2600 route_y_min = -of.Y - 0;*/
2601 // Minimum is at y=max_tunnel_diameter/4
2602 //route_y_min = -of.Y + max_tunnel_diameter/4;
2603 //s16 min = -of.Y + max_tunnel_diameter/4;
2604 s16 min = -of.Y + 0;
2605 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2606 route_y_min = rangelim(route_y_min, 0, route_y_max);
2609 /*dstream<<"route_y_min = "<<route_y_min
2610 <<", route_y_max = "<<route_y_max<<std::endl;*/
2612 s16 route_start_y_min = route_y_min;
2613 s16 route_start_y_max = route_y_max;
2615 // Start every 2nd cave from surface
2616 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2618 if(coming_from_surface)
2620 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2623 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2624 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2626 // Randomize starting position
2628 (float)(myrand()%ar.X)+0.5,
2629 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2630 (float)(myrand()%ar.Z)+0.5
2633 MapNode airnode(CONTENT_AIR);
2636 Generate some tunnel starting from orp
2639 for(u16 j=0; j<tunnel_routepoints; j++)
2641 if(j%7==0 && bruise_surface == false)
2643 main_direction = v3f(
2644 ((float)(myrand()%20)-(float)10)/10,
2645 ((float)(myrand()%20)-(float)10)/30,
2646 ((float)(myrand()%20)-(float)10)/10
2648 main_direction *= (float)myrand_range(1, 3);
2652 s16 min_d = min_tunnel_diameter;
2653 s16 max_d = max_tunnel_diameter;
2654 s16 rs = myrand_range(min_d, max_d);
2659 maxlen = v3s16(rs*7,rs*7,rs*7);
2663 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2668 if(coming_from_surface && j < 3)
2671 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2672 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2673 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2679 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2680 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2681 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2685 vec += main_direction;
2690 else if(rp.X >= ar.X)
2692 if(rp.Y < route_y_min)
2694 else if(rp.Y >= route_y_max)
2695 rp.Y = route_y_max-1;
2698 else if(rp.Z >= ar.Z)
2702 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2704 v3f fp = orp + vec * f;
2705 v3s16 cp(fp.X, fp.Y, fp.Z);
2708 s16 d1 = d0 + rs - 1;
2709 for(s16 z0=d0; z0<=d1; z0++)
2711 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2712 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2713 for(s16 x0=-si; x0<=si-1; x0++)
2715 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2716 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2717 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2718 //s16 si2 = rs - abs(x0);
2719 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2725 /*if(isInArea(p, ar) == false)
2727 // Check only height
2728 if(y < 0 || y >= ar.Y)
2732 //assert(vmanip.m_area.contains(p));
2733 if(vmanip.m_area.contains(p) == false)
2735 dstream<<"WARNING: "<<__FUNCTION_NAME
2736 <<":"<<__LINE__<<": "
2737 <<"point not in area"
2742 // Just set it to air, it will be changed to
2744 u32 i = vmanip.m_area.index(p);
2745 vmanip.m_data[i] = airnode;
2747 if(bruise_surface == false)
2750 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2765 //TimeTaker timer1("ore veins");
2770 for(u32 jj=0; jj<relative_volume/1000; jj++)
2772 s16 max_vein_diameter = 3;
2774 // Allowed route area size in nodes
2776 sectorpos_base_size*MAP_BLOCKSIZE,
2777 h_blocks*MAP_BLOCKSIZE,
2778 sectorpos_base_size*MAP_BLOCKSIZE
2781 // Area starting point in nodes
2783 sectorpos_base.X*MAP_BLOCKSIZE,
2784 y_blocks_min*MAP_BLOCKSIZE,
2785 sectorpos_base.Y*MAP_BLOCKSIZE
2789 //(this should be more than the maximum radius of the tunnel)
2791 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2792 ar += v3s16(1,0,1) * more * 2;
2793 of -= v3s16(1,0,1) * more;
2795 // Randomize starting position
2797 (float)(myrand()%ar.X)+0.5,
2798 (float)(myrand()%ar.Y)+0.5,
2799 (float)(myrand()%ar.Z)+0.5
2802 // Randomize mineral
2805 mineral = MINERAL_COAL;
2807 mineral = MINERAL_IRON;
2810 Generate some vein starting from orp
2813 for(u16 j=0; j<2; j++)
2816 (float)(myrand()%ar.X)+0.5,
2817 (float)(myrand()%ar.Y)+0.5,
2818 (float)(myrand()%ar.Z)+0.5
2820 v3f vec = rp - orp;*/
2822 v3s16 maxlen(5, 5, 5);
2824 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2825 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2826 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2831 else if(rp.X >= ar.X)
2835 else if(rp.Y >= ar.Y)
2839 else if(rp.Z >= ar.Z)
2845 s16 max_d = max_vein_diameter;
2846 s16 rs = myrand_range(min_d, max_d);
2848 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2850 v3f fp = orp + vec * f;
2851 v3s16 cp(fp.X, fp.Y, fp.Z);
2853 s16 d1 = d0 + rs - 1;
2854 for(s16 z0=d0; z0<=d1; z0++)
2856 s16 si = rs - abs(z0);
2857 for(s16 x0=-si; x0<=si-1; x0++)
2859 s16 si2 = rs - abs(x0);
2860 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2862 // Don't put mineral to every place
2870 /*if(isInArea(p, ar) == false)
2872 // Check only height
2873 if(y < 0 || y >= ar.Y)
2877 assert(vmanip.m_area.contains(p));
2879 // Just set it to air, it will be changed to
2881 u32 i = vmanip.m_area.index(p);
2882 MapNode *n = &vmanip.m_data[i];
2883 if(n->d == CONTENT_STONE)
2898 //TimeTaker timer1("add mud");
2901 Add mud to the central chunk
2904 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2905 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2907 // Node position in 2d
2908 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2910 // Randomize mud amount
2911 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2912 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2913 m_seed+1, 3, 0.55));
2915 // Find ground level
2916 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2919 If topmost node is grass, change it to mud.
2920 It might be if it was flown to there from a neighboring
2921 chunk and then converted.
2924 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2925 MapNode *n = &vmanip.m_data[i];
2926 if(n->d == CONTENT_GRASS)
2935 v3s16 em = vmanip.m_area.getExtent();
2936 s16 y_start = surface_y+1;
2937 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2938 for(s16 y=y_start; y<=y_nodes_max; y++)
2940 if(mudcount >= mud_add_amount)
2943 MapNode &n = vmanip.m_data[i];
2947 vmanip.m_area.add_y(em, i, 1);
2956 TimeTaker timer1("flow mud");
2959 Flow mud away from steep edges
2962 // Limit area by 1 because mud is flown into neighbors.
2963 s16 mudflow_minpos = 0-max_spread_amount+1;
2964 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2966 // Iterate a few times
2967 for(s16 k=0; k<3; k++)
2970 for(s16 x=mudflow_minpos;
2973 for(s16 z=mudflow_minpos;
2977 // Invert coordinates every 2nd iteration
2980 x = mudflow_maxpos - (x-mudflow_minpos);
2981 z = mudflow_maxpos - (z-mudflow_minpos);
2984 // Node position in 2d
2985 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2987 v3s16 em = vmanip.m_area.getExtent();
2988 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2995 for(; y>=y_nodes_min; y--)
2997 n = &vmanip.m_data[i];
2998 //if(content_walkable(n->d))
3000 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3003 vmanip.m_area.add_y(em, i, -1);
3006 // Stop if out of area
3007 //if(vmanip.m_area.contains(i) == false)
3011 /*// If not mud, do nothing to it
3012 MapNode *n = &vmanip.m_data[i];
3013 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3017 Don't flow it if the stuff under it is not mud
3021 vmanip.m_area.add_y(em, i2, -1);
3022 // Cancel if out of area
3023 if(vmanip.m_area.contains(i2) == false)
3025 MapNode *n2 = &vmanip.m_data[i2];
3026 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
3030 // Make it exactly mud
3033 /*s16 recurse_count = 0;
3037 v3s16(0,0,1), // back
3038 v3s16(1,0,0), // right
3039 v3s16(0,0,-1), // front
3040 v3s16(-1,0,0), // left
3043 // Theck that upper is air or doesn't exist.
3044 // Cancel dropping if upper keeps it in place
3046 vmanip.m_area.add_y(em, i3, 1);
3047 if(vmanip.m_area.contains(i3) == true
3048 && content_walkable(vmanip.m_data[i3].d) == true)
3055 for(u32 di=0; di<4; di++)
3057 v3s16 dirp = dirs4[di];
3060 vmanip.m_area.add_p(em, i2, dirp);
3061 // Fail if out of area
3062 if(vmanip.m_area.contains(i2) == false)
3064 // Check that side is air
3065 MapNode *n2 = &vmanip.m_data[i2];
3066 if(content_walkable(n2->d))
3068 // Check that under side is air
3069 vmanip.m_area.add_y(em, i2, -1);
3070 if(vmanip.m_area.contains(i2) == false)
3072 n2 = &vmanip.m_data[i2];
3073 if(content_walkable(n2->d))
3075 /*// Check that under that is air (need a drop of 2)
3076 vmanip.m_area.add_y(em, i2, -1);
3077 if(vmanip.m_area.contains(i2) == false)
3079 n2 = &vmanip.m_data[i2];
3080 if(content_walkable(n2->d))
3082 // Loop further down until not air
3084 vmanip.m_area.add_y(em, i2, -1);
3085 // Fail if out of area
3086 if(vmanip.m_area.contains(i2) == false)
3088 n2 = &vmanip.m_data[i2];
3089 }while(content_walkable(n2->d) == false);
3090 // Loop one up so that we're in air
3091 vmanip.m_area.add_y(em, i2, 1);
3092 n2 = &vmanip.m_data[i2];
3094 // Move mud to new place
3096 // Set old place to be air
3097 *n = MapNode(CONTENT_AIR);
3110 //TimeTaker timer1("add water");
3113 Add water to the central chunk (and a bit more)
3116 for(s16 x=0-max_spread_amount;
3117 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3119 for(s16 z=0-max_spread_amount;
3120 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3123 // Node position in 2d
3124 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3126 // Find ground level
3127 //s16 surface_y = find_ground_level(vmanip, p2d);
3130 If ground level is over water level, skip.
3131 NOTE: This leaves caves near water without water,
3132 which looks especially crappy when the nearby water
3133 won't start flowing either for some reason
3135 /*if(surface_y > WATER_LEVEL)
3142 v3s16 em = vmanip.m_area.getExtent();
3143 u8 light = LIGHT_MAX;
3144 // Start at global water surface level
3145 s16 y_start = WATER_LEVEL;
3146 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3147 MapNode *n = &vmanip.m_data[i];
3149 /*// Add first one to transforming liquid queue, if water
3150 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3152 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3153 m_transforming_liquid.push_back(p);
3156 for(s16 y=y_start; y>=y_nodes_min; y--)
3158 n = &vmanip.m_data[i];
3160 // Stop when there is no water and no air
3161 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3162 && n->d != CONTENT_WATER)
3164 /*// Add bottom one to transforming liquid queue
3165 vmanip.m_area.add_y(em, i, 1);
3166 n = &vmanip.m_data[i];
3167 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3169 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3170 m_transforming_liquid.push_back(p);
3176 // Make water only not in caves
3177 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3179 n->d = CONTENT_WATERSOURCE;
3180 //n->setLight(LIGHTBANK_DAY, light);
3182 // Add to transforming liquid queue (in case it'd
3184 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3185 m_transforming_liquid.push_back(p);
3189 vmanip.m_area.add_y(em, i, -1);
3200 /***********************
3202 ************************/
3205 //TimeTaker timer1("convert mud to sand");
3211 //s16 mud_add_amount = myrand_range(2, 4);
3212 //s16 mud_add_amount = 0;
3214 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3215 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3216 for(s16 x=0-max_spread_amount+1;
3217 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3219 for(s16 z=0-max_spread_amount+1;
3220 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3223 // Node position in 2d
3224 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3226 // Determine whether to have sand here
3227 double sandnoise = noise2d_perlin(
3228 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3229 m_seed+59420, 3, 0.50);
3231 bool have_sand = (sandnoise > -0.15);
3233 if(have_sand == false)
3236 // Find ground level
3237 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3239 if(surface_y > WATER_LEVEL + 2)
3243 v3s16 em = vmanip.m_area.getExtent();
3244 s16 y_start = surface_y;
3245 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3246 u32 not_sand_counter = 0;
3247 for(s16 y=y_start; y>=y_nodes_min; y--)
3249 MapNode *n = &vmanip.m_data[i];
3250 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3252 n->d = CONTENT_SAND;
3257 if(not_sand_counter > 3)
3261 vmanip.m_area.add_y(em, i, -1);
3270 //TimeTaker timer1("generate trees");
3276 // Divide area into parts
3278 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3279 double area = sidelen * sidelen;
3280 for(s16 x0=0; x0<div; x0++)
3281 for(s16 z0=0; z0<div; z0++)
3283 // Center position of part of division
3285 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3286 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3288 // Minimum edge of part of division
3290 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3291 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3293 // Maximum edge of part of division
3295 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3296 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3299 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3300 // Put trees in random places on part of division
3301 for(u32 i=0; i<tree_count; i++)
3303 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3304 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3305 s16 y = find_ground_level(vmanip, v2s16(x,z));
3306 // Don't make a tree under water level
3309 // Don't make a tree so high that it doesn't fit
3310 if(y > y_nodes_max - 6)
3314 Trees grow only on mud and grass
3317 u32 i = vmanip.m_area.index(v3s16(p));
3318 MapNode *n = &vmanip.m_data[i];
3319 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3324 make_tree(vmanip, p);
3327 /*u32 tree_max = relative_area / 60;
3328 //u32 count = myrand_range(0, tree_max);
3329 for(u32 i=0; i<count; i++)
3331 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3332 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3333 x += sectorpos_base.X*MAP_BLOCKSIZE;
3334 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3335 s16 y = find_ground_level(vmanip, v2s16(x,z));
3336 // Don't make a tree under water level
3341 make_tree(vmanip, p);
3349 //TimeTaker timer1("grow grass");
3355 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3356 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3357 for(s16 x=0-max_spread_amount;
3358 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3360 for(s16 z=0-max_spread_amount;
3361 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3364 // Node position in 2d
3365 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3368 Find the lowest surface to which enough light ends up
3371 Basically just wait until not air and not leaves.
3375 v3s16 em = vmanip.m_area.getExtent();
3376 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3378 // Go to ground level
3379 for(y=y_nodes_max; y>=y_nodes_min; y--)
3381 MapNode &n = vmanip.m_data[i];
3382 if(n.d != CONTENT_AIR
3383 && n.d != CONTENT_LEAVES)
3385 vmanip.m_area.add_y(em, i, -1);
3387 if(y >= y_nodes_min)
3390 surface_y = y_nodes_min;
3393 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3394 MapNode *n = &vmanip.m_data[i];
3395 if(n->d == CONTENT_MUD)
3396 n->d = CONTENT_GRASS;
3402 Initial lighting (sunlight)
3405 core::map<v3s16, bool> light_sources;
3408 // 750ms @cs=8, can't optimize more
3409 TimeTaker timer1("initial lighting");
3413 Go through the edges and add all nodes that have light to light_sources
3417 for(s16 i=0; i<4; i++)
3419 for(s16 j=lighting_min_d;
3426 if(i == 0 || i == 1)
3428 x = (i==0) ? lighting_min_d : lighting_max_d;
3437 z = (i==0) ? lighting_min_d : lighting_max_d;
3444 // Node position in 2d
3445 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3448 v3s16 em = vmanip.m_area.getExtent();
3449 s16 y_start = y_nodes_max;
3450 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3451 for(s16 y=y_start; y>=y_nodes_min; y--)
3453 MapNode *n = &vmanip.m_data[i];
3454 if(n->getLight(LIGHTBANK_DAY) != 0)
3456 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3458 //NOTE: This is broken, at least the index has to
3467 Go through the edges and apply sunlight to them, not caring
3472 for(s16 i=0; i<4; i++)
3474 for(s16 j=lighting_min_d;
3481 if(i == 0 || i == 1)
3483 x = (i==0) ? lighting_min_d : lighting_max_d;
3492 z = (i==0) ? lighting_min_d : lighting_max_d;
3499 // Node position in 2d
3500 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3502 // Loop from top to down
3504 u8 light = LIGHT_SUN;
3505 v3s16 em = vmanip.m_area.getExtent();
3506 s16 y_start = y_nodes_max;
3507 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3508 for(s16 y=y_start; y>=y_nodes_min; y--)
3510 MapNode *n = &vmanip.m_data[i];
3511 if(light_propagates_content(n->d) == false)
3515 else if(light != LIGHT_SUN
3516 || sunlight_propagates_content(n->d) == false)
3522 n->setLight(LIGHTBANK_DAY, light);
3523 n->setLight(LIGHTBANK_NIGHT, 0);
3527 // Insert light source
3528 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3531 // Increment index by y
3532 vmanip.m_area.add_y(em, i, -1);
3538 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3539 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3540 /*for(s16 x=0-max_spread_amount+1;
3541 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3543 for(s16 z=0-max_spread_amount+1;
3544 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3548 This has to be 1 smaller than the actual area, because
3549 neighboring nodes are checked.
3551 for(s16 x=lighting_min_d+1;
3552 x<=lighting_max_d-1;
3554 for(s16 z=lighting_min_d+1;
3555 z<=lighting_max_d-1;
3558 // Node position in 2d
3559 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3562 Apply initial sunlight
3565 u8 light = LIGHT_SUN;
3566 bool add_to_sources = false;
3567 v3s16 em = vmanip.m_area.getExtent();
3568 s16 y_start = y_nodes_max;
3569 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3570 for(s16 y=y_start; y>=y_nodes_min; y--)
3572 MapNode *n = &vmanip.m_data[i];
3574 if(light_propagates_content(n->d) == false)
3578 else if(light != LIGHT_SUN
3579 || sunlight_propagates_content(n->d) == false)
3585 // This doesn't take much time
3586 if(add_to_sources == false)
3589 Check sides. If side is not air or water, start
3590 adding to light_sources.
3593 v3s16(0,0,1), // back
3594 v3s16(1,0,0), // right
3595 v3s16(0,0,-1), // front
3596 v3s16(-1,0,0), // left
3598 for(u32 di=0; di<4; di++)
3600 v3s16 dirp = dirs4[di];
3602 vmanip.m_area.add_p(em, i2, dirp);
3603 MapNode *n2 = &vmanip.m_data[i2];
3605 n2->d != CONTENT_AIR
3606 && n2->d != CONTENT_WATERSOURCE
3607 && n2->d != CONTENT_WATER
3609 add_to_sources = true;
3615 n->setLight(LIGHTBANK_DAY, light);
3616 n->setLight(LIGHTBANK_NIGHT, 0);
3618 // This doesn't take much time
3619 if(light != 0 && add_to_sources)
3621 // Insert light source
3622 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3625 // Increment index by y
3626 vmanip.m_area.add_y(em, i, -1);
3633 for(s16 x=lighting_min_d+1;
3634 x<=lighting_max_d-1;
3636 for(s16 z=lighting_min_d+1;
3637 z<=lighting_max_d-1;
3640 // Node position in 2d
3641 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3644 Apply initial sunlight
3647 u8 light = LIGHT_SUN;
3648 v3s16 em = vmanip.m_area.getExtent();
3649 s16 y_start = y_nodes_max;
3650 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3651 for(s16 y=y_start; y>=y_nodes_min; y--)
3653 MapNode *n = &vmanip.m_data[i];
3655 if(light_propagates_content(n->d) == false)
3659 else if(light != LIGHT_SUN
3660 || sunlight_propagates_content(n->d) == false)
3666 n->setLight(LIGHTBANK_DAY, light);
3667 n->setLight(LIGHTBANK_NIGHT, 0);
3669 // This doesn't take much time
3672 // Insert light source
3673 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3676 // Increment index by y
3677 vmanip.m_area.add_y(em, i, -1);
3685 // Spread light around
3687 TimeTaker timer("generateChunkRaw() spreadLight");
3688 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3695 timer_generate.stop();
3698 Blit generated stuff to map
3702 //TimeTaker timer("generateChunkRaw() blitBackAll");
3703 vmanip.blitBackAll(&changed_blocks);
3707 Update day/night difference cache of the MapBlocks
3710 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3711 i.atEnd() == false; i++)
3713 MapBlock *block = i.getNode()->getValue();
3714 block->updateDayNightDiff();
3719 Add random objects to blocks
3722 for(s16 x=0; x<sectorpos_base_size; x++)
3723 for(s16 z=0; z<sectorpos_base_size; z++)
3725 v2s16 sectorpos = sectorpos_base + v2s16(x,z);
3726 ServerMapSector *sector = createSector(sectorpos);
3729 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3731 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3732 MapBlock *block = createBlock(blockpos);
3733 addRandomObjects(block);
3739 Create chunk metadata
3742 for(s16 x=-1; x<=1; x++)
3743 for(s16 y=-1; y<=1; y++)
3745 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3746 // Add chunk meta information
3747 MapChunk *chunk = getChunk(chunkpos0);
3750 chunk = new MapChunk();
3751 m_chunks.insert(chunkpos0, chunk);
3753 //chunk->setIsVolatile(true);
3754 if(chunk->getGenLevel() > GENERATED_PARTLY)
3755 chunk->setGenLevel(GENERATED_PARTLY);
3759 Set central chunk non-volatile
3761 MapChunk *chunk = getChunk(chunkpos);
3764 //chunk->setIsVolatile(false);
3765 chunk->setGenLevel(GENERATED_FULLY);
3768 Save changed parts of map
3773 Return central chunk (which was requested)
3778 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3779 core::map<v3s16, MapBlock*> &changed_blocks)
3781 dstream<<"generateChunk(): Generating chunk "
3782 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3785 /*for(s16 x=-1; x<=1; x++)
3786 for(s16 y=-1; y<=1; y++)*/
3787 for(s16 x=-0; x<=0; x++)
3788 for(s16 y=-0; y<=0; y++)
3790 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3791 MapChunk *chunk = getChunk(chunkpos0);
3792 // Skip if already generated
3793 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3795 generateChunkRaw(chunkpos0, changed_blocks);
3798 assert(chunkNonVolatile(chunkpos1));
3800 MapChunk *chunk = getChunk(chunkpos1);
3804 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3806 DSTACK("%s: p2d=(%d,%d)",
3811 Check if it exists already in memory
3813 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3818 Try to load it from disk (with blocks)
3820 if(loadSectorFull(p2d) == true)
3822 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3825 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3826 throw InvalidPositionException("");
3832 Do not create over-limit
3834 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3835 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3836 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3837 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3838 throw InvalidPositionException("createSector(): pos. over limit");
3841 Generate blank sector
3844 sector = new ServerMapSector(this, p2d);
3846 // Sector position on map in nodes
3847 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3852 m_sectors.insert(p2d, sector);
3857 MapSector * ServerMap::emergeSector(v2s16 p2d,
3858 core::map<v3s16, MapBlock*> &changed_blocks)
3860 DSTACK("%s: p2d=(%d,%d)",
3867 v2s16 chunkpos = sector_to_chunk(p2d);
3868 /*bool chunk_nonvolatile = false;
3869 MapChunk *chunk = getChunk(chunkpos);
3870 if(chunk && chunk->getIsVolatile() == false)
3871 chunk_nonvolatile = true;*/
3872 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3875 If chunk is not fully generated, generate chunk
3877 if(chunk_nonvolatile == false)
3879 // Generate chunk and neighbors
3880 generateChunk(chunkpos, changed_blocks);
3884 Return sector if it exists now
3886 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3891 Try to load it from disk
3893 if(loadSectorFull(p2d) == true)
3895 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3898 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3899 throw InvalidPositionException("");
3905 generateChunk should have generated the sector
3909 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3910 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3914 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3916 return createSector(p2d);
3921 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3924 generateChunkRaw(chunkpos, changed_blocks, true);
3927 Return sector if it exists now
3929 sector = getSectorNoGenerateNoEx(p2d);
3933 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3941 //return generateSector();
3945 NOTE: This is not used for main map generation, only for blocks
3946 that are very high or low
3948 MapBlock * ServerMap::generateBlock(
3950 MapBlock *original_dummy,
3951 ServerMapSector *sector,
3952 core::map<v3s16, MapBlock*> &changed_blocks,
3953 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3956 DSTACK("%s: p=(%d,%d,%d)",
3960 /*dstream<<"generateBlock(): "
3961 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3964 MapBlock *block = original_dummy;
3966 v2s16 p2d(p.X, p.Z);
3968 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3971 Do not generate over-limit
3973 if(blockpos_over_limit(p))
3975 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3976 throw InvalidPositionException("generateBlock(): pos. over limit");
3980 If block doesn't exist, create one.
3981 If it exists, it is a dummy. In that case unDummify() it.
3983 NOTE: This already sets the map as the parent of the block
3987 block = sector->createBlankBlockNoInsert(block_y);
3991 // Remove the block so that nobody can get a half-generated one.
3992 sector->removeBlock(block);
3993 // Allocate the block to contain the generated data
3997 u8 water_material = CONTENT_WATERSOURCE;
3999 s32 lowest_ground_y = 32767;
4000 s32 highest_ground_y = -32768;
4002 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4003 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4005 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4007 //s16 surface_y = 0;
4009 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4010 + AVERAGE_MUD_AMOUNT;
4012 if(surface_y < lowest_ground_y)
4013 lowest_ground_y = surface_y;
4014 if(surface_y > highest_ground_y)
4015 highest_ground_y = surface_y;
4017 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4019 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4021 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4026 NOTE: If there are some man-made structures above the
4027 newly created block, they won't be taken into account.
4029 if(real_y > surface_y)
4030 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4036 // If node is over heightmap y, it's air or water
4037 if(real_y > surface_y)
4039 // If under water level, it's water
4040 if(real_y < WATER_LEVEL)
4042 n.d = water_material;
4043 n.setLight(LIGHTBANK_DAY,
4044 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4046 Add to transforming liquid queue (in case it'd
4049 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4050 m_transforming_liquid.push_back(real_pos);
4056 // Else it's ground or caves (air)
4059 // If it's surface_depth under ground, it's stone
4060 if(real_y <= surface_y - surface_depth)
4062 n.d = CONTENT_STONE;
4066 // It is mud if it is under the first ground
4067 // level or under water
4068 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4074 n.d = CONTENT_GRASS;
4077 //n.d = CONTENT_MUD;
4079 /*// If under water level, it's mud
4080 if(real_y < WATER_LEVEL)
4082 // Only the topmost node is grass
4083 else if(real_y <= surface_y - 1)
4086 n.d = CONTENT_GRASS;*/
4090 block->setNode(v3s16(x0,y0,z0), n);
4095 Calculate some helper variables
4098 // Completely underground if the highest part of block is under lowest
4100 // This has to be very sure; it's probably one too strict now but
4101 // that's just better.
4102 bool completely_underground =
4103 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4105 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4107 bool mostly_underwater_surface = false;
4108 if(highest_ground_y < WATER_LEVEL
4109 && some_part_underground && !completely_underground)
4110 mostly_underwater_surface = true;
4113 Get local attributes
4116 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4118 float caves_amount = 0.5;
4123 NOTE: BEWARE: Too big amount of attribute points slows verything
4125 1 interpolation from 5000 points takes 2-3ms.
4127 //TimeTaker timer("generateBlock() local attribute retrieval");
4128 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4129 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4130 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4134 //dstream<<"generateBlock(): Done"<<std::endl;
4140 // Initialize temporary table
4141 const s32 ued = MAP_BLOCKSIZE;
4142 bool underground_emptiness[ued*ued*ued];
4143 for(s32 i=0; i<ued*ued*ued; i++)
4145 underground_emptiness[i] = 0;
4152 Initialize orp and ors. Try to find if some neighboring
4153 MapBlock has a tunnel ended in its side
4157 (float)(myrand()%ued)+0.5,
4158 (float)(myrand()%ued)+0.5,
4159 (float)(myrand()%ued)+0.5
4162 bool found_existing = false;
4168 for(s16 y=0; y<ued; y++)
4169 for(s16 x=0; x<ued; x++)
4171 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4172 if(getNode(ap).d == CONTENT_AIR)
4174 orp = v3f(x+1,y+1,0);
4175 found_existing = true;
4176 goto continue_generating;
4180 catch(InvalidPositionException &e){}
4186 for(s16 y=0; y<ued; y++)
4187 for(s16 x=0; x<ued; x++)
4189 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4190 if(getNode(ap).d == CONTENT_AIR)
4192 orp = v3f(x+1,y+1,ued-1);
4193 found_existing = true;
4194 goto continue_generating;
4198 catch(InvalidPositionException &e){}
4204 for(s16 y=0; y<ued; y++)
4205 for(s16 z=0; z<ued; z++)
4207 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4208 if(getNode(ap).d == CONTENT_AIR)
4210 orp = v3f(0,y+1,z+1);
4211 found_existing = true;
4212 goto continue_generating;
4216 catch(InvalidPositionException &e){}
4222 for(s16 y=0; y<ued; y++)
4223 for(s16 z=0; z<ued; z++)
4225 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4226 if(getNode(ap).d == CONTENT_AIR)
4228 orp = v3f(ued-1,y+1,z+1);
4229 found_existing = true;
4230 goto continue_generating;
4234 catch(InvalidPositionException &e){}
4240 for(s16 x=0; x<ued; x++)
4241 for(s16 z=0; z<ued; z++)
4243 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4244 if(getNode(ap).d == CONTENT_AIR)
4246 orp = v3f(x+1,0,z+1);
4247 found_existing = true;
4248 goto continue_generating;
4252 catch(InvalidPositionException &e){}
4258 for(s16 x=0; x<ued; x++)
4259 for(s16 z=0; z<ued; z++)
4261 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4262 if(getNode(ap).d == CONTENT_AIR)
4264 orp = v3f(x+1,ued-1,z+1);
4265 found_existing = true;
4266 goto continue_generating;
4270 catch(InvalidPositionException &e){}
4272 continue_generating:
4275 Choose whether to actually generate cave
4277 bool do_generate_caves = true;
4278 // Don't generate if no part is underground
4279 if(!some_part_underground)
4281 do_generate_caves = false;
4283 // Don't generate if mostly underwater surface
4284 /*else if(mostly_underwater_surface)
4286 do_generate_caves = false;
4288 // Partly underground = cave
4289 else if(!completely_underground)
4291 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4293 // Found existing cave underground
4294 else if(found_existing && completely_underground)
4296 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4298 // Underground and no caves found
4301 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4304 if(do_generate_caves)
4307 Generate some tunnel starting from orp and ors
4309 for(u16 i=0; i<3; i++)
4312 (float)(myrand()%ued)+0.5,
4313 (float)(myrand()%ued)+0.5,
4314 (float)(myrand()%ued)+0.5
4318 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4322 for(float f=0; f<1.0; f+=0.04)
4324 v3f fp = orp + vec * f;
4325 v3s16 cp(fp.X, fp.Y, fp.Z);
4327 s16 d1 = d0 + rs - 1;
4328 for(s16 z0=d0; z0<=d1; z0++)
4330 s16 si = rs - abs(z0);
4331 for(s16 x0=-si; x0<=si-1; x0++)
4333 s16 si2 = rs - abs(x0);
4334 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4340 if(isInArea(p, ued) == false)
4342 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4354 // Set to true if has caves.
4355 // Set when some non-air is changed to air when making caves.
4356 bool has_caves = false;
4359 Apply temporary cave data to block
4362 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4363 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4365 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4367 MapNode n = block->getNode(v3s16(x0,y0,z0));
4370 if(underground_emptiness[
4371 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4372 +ued*(y0*ued/MAP_BLOCKSIZE)
4373 +(x0*ued/MAP_BLOCKSIZE)])
4375 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4384 block->setNode(v3s16(x0,y0,z0), n);
4389 This is used for guessing whether or not the block should
4390 receive sunlight from the top if the block above doesn't exist
4392 block->setIsUnderground(completely_underground);
4395 Force lighting update if some part of block is partly
4396 underground and has caves.
4398 /*if(some_part_underground && !completely_underground && has_caves)
4400 //dstream<<"Half-ground caves"<<std::endl;
4401 lighting_invalidated_blocks[block->getPos()] = block;
4404 // DEBUG: Always update lighting
4405 //lighting_invalidated_blocks[block->getPos()] = block;
4411 if(some_part_underground)
4413 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4418 for(s16 i=0; i<underground_level/4 + 1; i++)
4420 if(myrand()%50 == 0)
4423 (myrand()%(MAP_BLOCKSIZE-2))+1,
4424 (myrand()%(MAP_BLOCKSIZE-2))+1,
4425 (myrand()%(MAP_BLOCKSIZE-2))+1
4431 for(u16 i=0; i<27; i++)
4433 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4435 block->setNode(cp+g_27dirs[i], n);
4443 u16 coal_amount = 30;
4444 u16 coal_rareness = 60 / coal_amount;
4445 if(coal_rareness == 0)
4447 if(myrand()%coal_rareness == 0)
4449 u16 a = myrand() % 16;
4450 u16 amount = coal_amount * a*a*a / 1000;
4451 for(s16 i=0; i<amount; i++)
4454 (myrand()%(MAP_BLOCKSIZE-2))+1,
4455 (myrand()%(MAP_BLOCKSIZE-2))+1,
4456 (myrand()%(MAP_BLOCKSIZE-2))+1
4460 n.d = CONTENT_STONE;
4461 n.param = MINERAL_COAL;
4463 for(u16 i=0; i<27; i++)
4465 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4467 block->setNode(cp+g_27dirs[i], n);
4475 //TODO: change to iron_amount or whatever
4476 u16 iron_amount = 15;
4477 u16 iron_rareness = 60 / iron_amount;
4478 if(iron_rareness == 0)
4480 if(myrand()%iron_rareness == 0)
4482 u16 a = myrand() % 16;
4483 u16 amount = iron_amount * a*a*a / 1000;
4484 for(s16 i=0; i<amount; i++)
4487 (myrand()%(MAP_BLOCKSIZE-2))+1,
4488 (myrand()%(MAP_BLOCKSIZE-2))+1,
4489 (myrand()%(MAP_BLOCKSIZE-2))+1
4493 n.d = CONTENT_STONE;
4494 n.param = MINERAL_IRON;
4496 for(u16 i=0; i<27; i++)
4498 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4500 block->setNode(cp+g_27dirs[i], n);
4507 Create a few rats in empty blocks underground
4509 if(completely_underground)
4511 //for(u16 i=0; i<2; i++)
4514 (myrand()%(MAP_BLOCKSIZE-2))+1,
4515 (myrand()%(MAP_BLOCKSIZE-2))+1,
4516 (myrand()%(MAP_BLOCKSIZE-2))+1
4519 // Check that the place is empty
4520 //if(!is_ground_content(block->getNode(cp).d))
4523 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4524 block->addObject(obj);
4530 Add block to sector.
4532 sector->insertBlock(block);
4534 // Lighting is invalid after generation.
4535 block->setLightingExpired(true);
4542 <<"lighting_invalidated_blocks.size()"
4546 <<" "<<lighting_invalidated_blocks.size()
4548 <<", "<<completely_underground
4549 <<", "<<some_part_underground
4556 MapBlock * ServerMap::createBlock(v3s16 p)
4558 DSTACK("%s: p=(%d,%d,%d)",
4559 __FUNCTION_NAME, p.X, p.Y, p.Z);
4562 Do not create over-limit
4564 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4565 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4566 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4567 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4568 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4569 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4570 throw InvalidPositionException("createBlock(): pos. over limit");
4572 v2s16 p2d(p.X, p.Z);
4575 This will create or load a sector if not found in memory.
4576 If block exists on disk, it will be loaded.
4578 NOTE: On old save formats, this will be slow, as it generates
4579 lighting on blocks for them.
4581 ServerMapSector *sector;
4583 sector = (ServerMapSector*)createSector(p2d);
4584 assert(sector->getId() == MAPSECTOR_SERVER);
4586 catch(InvalidPositionException &e)
4588 dstream<<"createBlock: createSector() failed"<<std::endl;
4592 NOTE: This should not be done, or at least the exception
4593 should not be passed on as std::exception, because it
4594 won't be catched at all.
4596 /*catch(std::exception &e)
4598 dstream<<"createBlock: createSector() failed: "
4599 <<e.what()<<std::endl;
4604 Try to get a block from the sector
4607 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4611 block = sector->createBlankBlock(block_y);
4615 MapBlock * ServerMap::emergeBlock(
4617 bool only_from_disk,
4618 core::map<v3s16, MapBlock*> &changed_blocks,
4619 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4622 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4624 p.X, p.Y, p.Z, only_from_disk);
4627 Do not generate over-limit
4629 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4630 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4631 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4632 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4633 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4634 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4635 throw InvalidPositionException("emergeBlock(): pos. over limit");
4637 v2s16 p2d(p.X, p.Z);
4640 This will create or load a sector if not found in memory.
4641 If block exists on disk, it will be loaded.
4643 ServerMapSector *sector;
4645 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4646 assert(sector->getId() == MAPSECTOR_SERVER);
4648 catch(InvalidPositionException &e)
4650 dstream<<"emergeBlock: emergeSector() failed: "
4651 <<e.what()<<std::endl;
4652 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4654 <<"You could try to delete it."<<std::endl;
4657 catch(VersionMismatchException &e)
4659 dstream<<"emergeBlock: emergeSector() failed: "
4660 <<e.what()<<std::endl;
4661 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4663 <<"You could try to delete it."<<std::endl;
4667 NOTE: This should not be done, or at least the exception
4668 should not be passed on as std::exception, because it
4669 won't be catched at all.
4671 /*catch(std::exception &e)
4673 dstream<<"emergeBlock: emergeSector() failed: "
4674 <<e.what()<<std::endl;
4675 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4677 <<"You could try to delete it."<<std::endl;
4682 Try to get a block from the sector
4685 bool does_not_exist = false;
4686 bool lighting_expired = false;
4687 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4691 does_not_exist = true;
4693 else if(block->isDummy() == true)
4695 does_not_exist = true;
4697 else if(block->getLightingExpired())
4699 lighting_expired = true;
4704 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4709 If block was not found on disk and not going to generate a
4710 new one, make sure there is a dummy block in place.
4712 if(only_from_disk && (does_not_exist || lighting_expired))
4714 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4718 // Create dummy block
4719 block = new MapBlock(this, p, true);
4721 // Add block to sector
4722 sector->insertBlock(block);
4728 //dstream<<"Not found on disk, generating."<<std::endl;
4730 //TimeTaker("emergeBlock() generate");
4732 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4735 If the block doesn't exist, generate the block.
4739 block = generateBlock(p, block, sector, changed_blocks,
4740 lighting_invalidated_blocks);
4743 if(lighting_expired)
4745 lighting_invalidated_blocks.insert(p, block);
4749 Initially update sunlight
4753 core::map<v3s16, bool> light_sources;
4754 bool black_air_left = false;
4755 bool bottom_invalid =
4756 block->propagateSunlight(light_sources, true,
4757 &black_air_left, true);
4759 // If sunlight didn't reach everywhere and part of block is
4760 // above ground, lighting has to be properly updated
4761 //if(black_air_left && some_part_underground)
4764 lighting_invalidated_blocks[block->getPos()] = block;
4769 lighting_invalidated_blocks[block->getPos()] = block;
4776 s16 ServerMap::findGroundLevel(v2s16 p2d)
4779 Uh, just do something random...
4781 // Find existing map from top to down
4784 v3s16 p(p2d.X, max, p2d.Y);
4785 for(; p.Y>min; p.Y--)
4787 MapNode n = getNodeNoEx(p);
4788 if(n.d != CONTENT_IGNORE)
4793 // If this node is not air, go to plan b
4794 if(getNodeNoEx(p).d != CONTENT_AIR)
4796 // Search existing walkable and return it
4797 for(; p.Y>min; p.Y--)
4799 MapNode n = getNodeNoEx(p);
4800 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4806 Plan B: Get from map generator perlin noise function
4808 double level = base_rock_level_2d(m_seed, p2d);
4812 void ServerMap::createDir(std::string path)
4814 if(fs::CreateDir(path) == false)
4816 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4817 <<"\""<<path<<"\""<<std::endl;
4818 throw BaseException("ServerMap failed to create directory");
4822 std::string ServerMap::getSectorSubDir(v2s16 pos)
4825 snprintf(cc, 9, "%.4x%.4x",
4826 (unsigned int)pos.X&0xffff,
4827 (unsigned int)pos.Y&0xffff);
4829 return std::string(cc);
4832 std::string ServerMap::getSectorDir(v2s16 pos)
4834 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4837 v2s16 ServerMap::getSectorPos(std::string dirname)
4839 if(dirname.size() != 8)
4840 throw InvalidFilenameException("Invalid sector directory name");
4842 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4844 throw InvalidFilenameException("Invalid sector directory name");
4845 v2s16 pos((s16)x, (s16)y);
4849 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4851 v2s16 p2d = getSectorPos(sectordir);
4853 if(blockfile.size() != 4){
4854 throw InvalidFilenameException("Invalid block filename");
4857 int r = sscanf(blockfile.c_str(), "%4x", &y);
4859 throw InvalidFilenameException("Invalid block filename");
4860 return v3s16(p2d.X, y, p2d.Y);
4863 void ServerMap::save(bool only_changed)
4865 DSTACK(__FUNCTION_NAME);
4866 if(m_map_saving_enabled == false)
4868 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4872 if(only_changed == false)
4873 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4879 u32 sector_meta_count = 0;
4880 u32 block_count = 0;
4883 JMutexAutoLock lock(m_sector_mutex);
4885 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4886 for(; i.atEnd() == false; i++)
4888 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4889 assert(sector->getId() == MAPSECTOR_SERVER);
4891 if(sector->differs_from_disk || only_changed == false)
4893 saveSectorMeta(sector);
4894 sector_meta_count++;
4896 core::list<MapBlock*> blocks;
4897 sector->getBlocks(blocks);
4898 core::list<MapBlock*>::Iterator j;
4899 for(j=blocks.begin(); j!=blocks.end(); j++)
4901 MapBlock *block = *j;
4902 if(block->getChangedFlag() || only_changed == false)
4907 /*dstream<<"ServerMap: Written block ("
4908 <<block->getPos().X<<","
4909 <<block->getPos().Y<<","
4910 <<block->getPos().Z<<")"
4919 Only print if something happened or saved whole map
4921 if(only_changed == false || sector_meta_count != 0
4922 || block_count != 0)
4924 dstream<<DTIME<<"ServerMap: Written: "
4925 <<sector_meta_count<<" sector metadata files, "
4926 <<block_count<<" block files"
4931 void ServerMap::loadAll()
4933 DSTACK(__FUNCTION_NAME);
4934 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4939 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4941 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4943 JMutexAutoLock lock(m_sector_mutex);
4946 s32 printed_counter = -100000;
4947 s32 count = list.size();
4949 std::vector<fs::DirListNode>::iterator i;
4950 for(i=list.begin(); i!=list.end(); i++)
4952 if(counter > printed_counter + 10)
4954 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4955 printed_counter = counter;
4959 MapSector *sector = NULL;
4961 // We want directories
4965 sector = loadSectorMeta(i->name);
4967 catch(InvalidFilenameException &e)
4969 // This catches unknown crap in directory
4972 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4973 (m_savedir+"/sectors/"+i->name);
4974 std::vector<fs::DirListNode>::iterator i2;
4975 for(i2=list2.begin(); i2!=list2.end(); i2++)
4981 loadBlock(i->name, i2->name, sector);
4983 catch(InvalidFilenameException &e)
4985 // This catches unknown crap in directory
4989 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4993 void ServerMap::saveMasterHeightmap()
4995 DSTACK(__FUNCTION_NAME);
4997 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4999 createDir(m_savedir);
5001 /*std::string fullpath = m_savedir + "/master_heightmap";
5002 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5003 if(o.good() == false)
5004 throw FileNotGoodException("Cannot open master heightmap");*/
5006 // Format used for writing
5007 //u8 version = SER_FMT_VER_HIGHEST;
5010 void ServerMap::loadMasterHeightmap()
5012 DSTACK(__FUNCTION_NAME);
5014 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5016 /*std::string fullpath = m_savedir + "/master_heightmap";
5017 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5018 if(is.good() == false)
5019 throw FileNotGoodException("Cannot open master heightmap");*/
5023 void ServerMap::saveMapMeta()
5025 DSTACK(__FUNCTION_NAME);
5027 dstream<<"INFO: ServerMap::saveMapMeta(): "
5028 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5031 createDir(m_savedir);
5033 std::string fullpath = m_savedir + "/map_meta.txt";
5034 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5035 if(os.good() == false)
5037 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5038 <<"could not open"<<fullpath<<std::endl;
5039 throw FileNotGoodException("Cannot open chunk metadata");
5043 params.setU64("seed", m_seed);
5044 params.setS32("chunksize", m_chunksize);
5046 params.writeLines(os);
5048 os<<"[end_of_params]\n";
5052 void ServerMap::loadMapMeta()
5054 DSTACK(__FUNCTION_NAME);
5056 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
5059 std::string fullpath = m_savedir + "/map_meta.txt";
5060 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5061 if(is.good() == false)
5063 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5064 <<"could not open"<<fullpath<<std::endl;
5065 throw FileNotGoodException("Cannot open chunk metadata");
5073 throw SerializationError
5074 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5076 std::getline(is, line);
5077 std::string trimmedline = trim(line);
5078 if(trimmedline == "[end_of_params]")
5080 params.parseConfigLine(line);
5083 m_seed = params.getU64("seed");
5084 m_chunksize = params.getS32("chunksize");
5086 dstream<<"INFO: ServerMap::loadMapMeta(): "
5087 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5091 void ServerMap::saveChunkMeta()
5093 DSTACK(__FUNCTION_NAME);
5095 u32 count = m_chunks.size();
5097 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5098 <<count<<" chunks"<<std::endl;
5100 createDir(m_savedir);
5102 std::string fullpath = m_savedir + "/chunk_meta";
5103 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5104 if(os.good() == false)
5106 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5107 <<"could not open"<<fullpath<<std::endl;
5108 throw FileNotGoodException("Cannot open chunk metadata");
5114 os.write((char*)&version, 1);
5119 writeU32(buf, count);
5120 os.write((char*)buf, 4);
5122 for(core::map<v2s16, MapChunk*>::Iterator
5123 i = m_chunks.getIterator();
5124 i.atEnd()==false; i++)
5126 v2s16 p = i.getNode()->getKey();
5127 MapChunk *chunk = i.getNode()->getValue();
5130 os.write((char*)buf, 4);
5132 chunk->serialize(os, version);
5136 void ServerMap::loadChunkMeta()
5138 DSTACK(__FUNCTION_NAME);
5140 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5143 std::string fullpath = m_savedir + "/chunk_meta";
5144 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5145 if(is.good() == false)
5147 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5148 <<"could not open"<<fullpath<<std::endl;
5149 throw FileNotGoodException("Cannot open chunk metadata");
5155 is.read((char*)&version, 1);
5160 is.read((char*)buf, 4);
5161 u32 count = readU32(buf);
5163 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5164 <<count<<" chunks"<<std::endl;
5166 for(u32 i=0; i<count; i++)
5169 MapChunk *chunk = new MapChunk();
5171 is.read((char*)buf, 4);
5174 chunk->deSerialize(is, version);
5175 m_chunks.insert(p, chunk);
5179 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5181 DSTACK(__FUNCTION_NAME);
5182 // Format used for writing
5183 u8 version = SER_FMT_VER_HIGHEST;
5185 v2s16 pos = sector->getPos();
5186 createDir(m_savedir);
5187 createDir(m_savedir+"/sectors");
5188 std::string dir = getSectorDir(pos);
5191 std::string fullpath = dir + "/meta";
5192 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5193 if(o.good() == false)
5194 throw FileNotGoodException("Cannot open sector metafile");
5196 sector->serialize(o, version);
5198 sector->differs_from_disk = false;
5201 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5203 DSTACK(__FUNCTION_NAME);
5205 v2s16 p2d = getSectorPos(dirname);
5206 std::string dir = m_savedir + "/sectors/" + dirname;
5208 std::string fullpath = dir + "/meta";
5209 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5210 if(is.good() == false)
5211 throw FileNotGoodException("Cannot open sector metafile");
5213 ServerMapSector *sector = ServerMapSector::deSerialize
5214 (is, this, p2d, m_sectors);
5216 sector->differs_from_disk = false;
5221 bool ServerMap::loadSectorFull(v2s16 p2d)
5223 DSTACK(__FUNCTION_NAME);
5224 std::string sectorsubdir = getSectorSubDir(p2d);
5226 MapSector *sector = NULL;
5228 JMutexAutoLock lock(m_sector_mutex);
5231 sector = loadSectorMeta(sectorsubdir);
5233 catch(InvalidFilenameException &e)
5237 catch(FileNotGoodException &e)
5241 catch(std::exception &e)
5249 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5250 (m_savedir+"/sectors/"+sectorsubdir);
5251 std::vector<fs::DirListNode>::iterator i2;
5252 for(i2=list2.begin(); i2!=list2.end(); i2++)
5258 loadBlock(sectorsubdir, i2->name, sector);
5260 catch(InvalidFilenameException &e)
5262 // This catches unknown crap in directory
5268 void ServerMap::saveBlock(MapBlock *block)
5270 DSTACK(__FUNCTION_NAME);
5272 Dummy blocks are not written
5274 if(block->isDummy())
5276 /*v3s16 p = block->getPos();
5277 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5278 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5282 // Format used for writing
5283 u8 version = SER_FMT_VER_HIGHEST;
5285 v3s16 p3d = block->getPos();
5286 v2s16 p2d(p3d.X, p3d.Z);
5287 createDir(m_savedir);
5288 createDir(m_savedir+"/sectors");
5289 std::string dir = getSectorDir(p2d);
5292 // Block file is map/sectors/xxxxxxxx/xxxx
5294 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5295 std::string fullpath = dir + "/" + cc;
5296 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5297 if(o.good() == false)
5298 throw FileNotGoodException("Cannot open block data");
5301 [0] u8 serialization version
5304 o.write((char*)&version, 1);
5306 block->serialize(o, version);
5309 Versions up from 9 have block objects.
5313 block->serializeObjects(o, version);
5317 Versions up from 15 have static objects.
5321 block->m_static_objects.serialize(o);
5324 // We just wrote it to the disk
5325 block->resetChangedFlag();
5328 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5330 DSTACK(__FUNCTION_NAME);
5332 // Block file is map/sectors/xxxxxxxx/xxxx
5333 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5336 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5337 if(is.good() == false)
5338 throw FileNotGoodException("Cannot open block file");
5340 v3s16 p3d = getBlockPos(sectordir, blockfile);
5341 v2s16 p2d(p3d.X, p3d.Z);
5343 assert(sector->getPos() == p2d);
5345 u8 version = SER_FMT_VER_INVALID;
5346 is.read((char*)&version, 1);
5349 throw SerializationError("ServerMap::loadBlock(): Failed"
5350 " to read MapBlock version");
5352 /*u32 block_size = MapBlock::serializedLength(version);
5353 SharedBuffer<u8> data(block_size);
5354 is.read((char*)*data, block_size);*/
5356 // This will always return a sector because we're the server
5357 //MapSector *sector = emergeSector(p2d);
5359 MapBlock *block = NULL;
5360 bool created_new = false;
5362 block = sector->getBlockNoCreate(p3d.Y);
5364 catch(InvalidPositionException &e)
5366 block = sector->createBlankBlockNoInsert(p3d.Y);
5370 // deserialize block data
5371 block->deSerialize(is, version);
5374 Versions up from 9 have block objects.
5378 block->updateObjects(is, version, NULL, 0);
5382 Versions up from 15 have static objects.
5386 block->m_static_objects.deSerialize(is);
5390 sector->insertBlock(block);
5393 Convert old formats to new and save
5396 // Save old format blocks in new format
5397 if(version < SER_FMT_VER_HIGHEST)
5402 // We just loaded it from the disk, so it's up-to-date.
5403 block->resetChangedFlag();
5406 catch(SerializationError &e)
5408 dstream<<"WARNING: Invalid block data on disk "
5409 "(SerializationError). Ignoring. "
5410 "A new one will be generated."
5413 // TODO: Backup file; name is in fullpath.
5417 void ServerMap::PrintInfo(std::ostream &out)
5428 ClientMap::ClientMap(
5430 MapDrawControl &control,
5431 scene::ISceneNode* parent,
5432 scene::ISceneManager* mgr,
5436 scene::ISceneNode(parent, mgr, id),
5439 m_camera_position(0,0,0),
5440 m_camera_direction(0,0,1)
5442 m_camera_mutex.Init();
5443 assert(m_camera_mutex.IsInitialized());
5445 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5446 BS*1000000,BS*1000000,BS*1000000);
5449 ClientMap::~ClientMap()
5451 /*JMutexAutoLock lock(mesh_mutex);
5460 MapSector * ClientMap::emergeSector(v2s16 p2d)
5462 DSTACK(__FUNCTION_NAME);
5463 // Check that it doesn't exist already
5465 return getSectorNoGenerate(p2d);
5467 catch(InvalidPositionException &e)
5472 ClientMapSector *sector = new ClientMapSector(this, p2d);
5475 JMutexAutoLock lock(m_sector_mutex);
5476 m_sectors.insert(p2d, sector);
5482 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5484 DSTACK(__FUNCTION_NAME);
5485 ClientMapSector *sector = NULL;
5487 JMutexAutoLock lock(m_sector_mutex);
5489 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5493 sector = (ClientMapSector*)n->getValue();
5494 assert(sector->getId() == MAPSECTOR_CLIENT);
5498 sector = new ClientMapSector(this, p2d);
5500 JMutexAutoLock lock(m_sector_mutex);
5501 m_sectors.insert(p2d, sector);
5505 sector->deSerialize(is);
5508 void ClientMap::OnRegisterSceneNode()
5512 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5513 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5516 ISceneNode::OnRegisterSceneNode();
5519 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5521 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5522 DSTACK(__FUNCTION_NAME);
5524 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5527 Get time for measuring timeout.
5529 Measuring time is very useful for long delays when the
5530 machine is swapping a lot.
5532 int time1 = time(0);
5534 //u32 daynight_ratio = m_client->getDayNightRatio();
5536 m_camera_mutex.Lock();
5537 v3f camera_position = m_camera_position;
5538 v3f camera_direction = m_camera_direction;
5539 m_camera_mutex.Unlock();
5542 Get all blocks and draw all visible ones
5545 v3s16 cam_pos_nodes(
5546 camera_position.X / BS,
5547 camera_position.Y / BS,
5548 camera_position.Z / BS);
5550 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5552 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5553 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5555 // Take a fair amount as we will be dropping more out later
5557 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5558 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5559 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5561 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5562 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5563 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5565 u32 vertex_count = 0;
5567 // For limiting number of mesh updates per frame
5568 u32 mesh_update_count = 0;
5570 u32 blocks_would_have_drawn = 0;
5571 u32 blocks_drawn = 0;
5573 //NOTE: The sectors map should be locked but we're not doing it
5574 // because it'd cause too much delays
5576 int timecheck_counter = 0;
5577 core::map<v2s16, MapSector*>::Iterator si;
5578 si = m_sectors.getIterator();
5579 for(; si.atEnd() == false; si++)
5582 timecheck_counter++;
5583 if(timecheck_counter > 50)
5585 timecheck_counter = 0;
5586 int time2 = time(0);
5587 if(time2 > time1 + 4)
5589 dstream<<"ClientMap::renderMap(): "
5590 "Rendering takes ages, returning."
5597 MapSector *sector = si.getNode()->getValue();
5598 v2s16 sp = sector->getPos();
5600 if(m_control.range_all == false)
5602 if(sp.X < p_blocks_min.X
5603 || sp.X > p_blocks_max.X
5604 || sp.Y < p_blocks_min.Z
5605 || sp.Y > p_blocks_max.Z)
5609 core::list< MapBlock * > sectorblocks;
5610 sector->getBlocks(sectorblocks);
5616 core::list< MapBlock * >::Iterator i;
5617 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5619 MapBlock *block = *i;
5622 Compare block position to camera position, skip
5623 if not seen on display
5626 float range = 100000 * BS;
5627 if(m_control.range_all == false)
5628 range = m_control.wanted_range * BS;
5631 if(isBlockInSight(block->getPos(), camera_position,
5632 camera_direction, range, &d) == false)
5637 // This is ugly (spherical distance limit?)
5638 /*if(m_control.range_all == false &&
5639 d - 0.5*BS*MAP_BLOCKSIZE > range)
5644 Update expired mesh (used for day/night change)
5646 It doesn't work exactly like it should now with the
5647 tasked mesh update but whatever.
5650 bool mesh_expired = false;
5653 JMutexAutoLock lock(block->mesh_mutex);
5655 mesh_expired = block->getMeshExpired();
5657 // Mesh has not been expired and there is no mesh:
5658 // block has no content
5659 if(block->mesh == NULL && mesh_expired == false)
5663 f32 faraway = BS*50;
5664 //f32 faraway = m_control.wanted_range * BS;
5667 This has to be done with the mesh_mutex unlocked
5669 // Pretty random but this should work somewhat nicely
5670 if(mesh_expired && (
5671 (mesh_update_count < 3
5672 && (d < faraway || mesh_update_count < 2)
5675 (m_control.range_all && mesh_update_count < 20)
5678 /*if(mesh_expired && mesh_update_count < 6
5679 && (d < faraway || mesh_update_count < 3))*/
5681 mesh_update_count++;
5683 // Mesh has been expired: generate new mesh
5684 //block->updateMesh(daynight_ratio);
5685 m_client->addUpdateMeshTask(block->getPos());
5687 mesh_expired = false;
5692 Draw the faces of the block
5695 JMutexAutoLock lock(block->mesh_mutex);
5697 scene::SMesh *mesh = block->mesh;
5702 blocks_would_have_drawn++;
5703 if(blocks_drawn >= m_control.wanted_max_blocks
5704 && m_control.range_all == false
5705 && d > m_control.wanted_min_range * BS)
5709 u32 c = mesh->getMeshBufferCount();
5711 for(u32 i=0; i<c; i++)
5713 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5714 const video::SMaterial& material = buf->getMaterial();
5715 video::IMaterialRenderer* rnd =
5716 driver->getMaterialRenderer(material.MaterialType);
5717 bool transparent = (rnd && rnd->isTransparent());
5718 // Render transparent on transparent pass and likewise.
5719 if(transparent == is_transparent_pass)
5722 This *shouldn't* hurt too much because Irrlicht
5723 doesn't change opengl textures if the old
5724 material is set again.
5726 driver->setMaterial(buf->getMaterial());
5727 driver->drawMeshBuffer(buf);
5728 vertex_count += buf->getVertexCount();
5732 } // foreach sectorblocks
5735 m_control.blocks_drawn = blocks_drawn;
5736 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5738 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5739 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5742 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5743 core::map<v3s16, MapBlock*> *affected_blocks)
5745 bool changed = false;
5747 Add it to all blocks touching it
5750 v3s16(0,0,0), // this
5751 v3s16(0,0,1), // back
5752 v3s16(0,1,0), // top
5753 v3s16(1,0,0), // right
5754 v3s16(0,0,-1), // front
5755 v3s16(0,-1,0), // bottom
5756 v3s16(-1,0,0), // left
5758 for(u16 i=0; i<7; i++)
5760 v3s16 p2 = p + dirs[i];
5761 // Block position of neighbor (or requested) node
5762 v3s16 blockpos = getNodeBlockPos(p2);
5763 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5764 if(blockref == NULL)
5766 // Relative position of requested node
5767 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5768 if(blockref->setTempMod(relpos, mod))
5773 if(changed && affected_blocks!=NULL)
5775 for(u16 i=0; i<7; i++)
5777 v3s16 p2 = p + dirs[i];
5778 // Block position of neighbor (or requested) node
5779 v3s16 blockpos = getNodeBlockPos(p2);
5780 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5781 if(blockref == NULL)
5783 affected_blocks->insert(blockpos, blockref);
5789 bool ClientMap::clearTempMod(v3s16 p,
5790 core::map<v3s16, MapBlock*> *affected_blocks)
5792 bool changed = false;
5794 v3s16(0,0,0), // this
5795 v3s16(0,0,1), // back
5796 v3s16(0,1,0), // top
5797 v3s16(1,0,0), // right
5798 v3s16(0,0,-1), // front
5799 v3s16(0,-1,0), // bottom
5800 v3s16(-1,0,0), // left
5802 for(u16 i=0; i<7; i++)
5804 v3s16 p2 = p + dirs[i];
5805 // Block position of neighbor (or requested) node
5806 v3s16 blockpos = getNodeBlockPos(p2);
5807 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5808 if(blockref == NULL)
5810 // Relative position of requested node
5811 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5812 if(blockref->clearTempMod(relpos))
5817 if(changed && affected_blocks!=NULL)
5819 for(u16 i=0; i<7; i++)
5821 v3s16 p2 = p + dirs[i];
5822 // Block position of neighbor (or requested) node
5823 v3s16 blockpos = getNodeBlockPos(p2);
5824 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5825 if(blockref == NULL)
5827 affected_blocks->insert(blockpos, blockref);
5833 void ClientMap::expireMeshes(bool only_daynight_diffed)
5835 TimeTaker timer("expireMeshes()");
5837 core::map<v2s16, MapSector*>::Iterator si;
5838 si = m_sectors.getIterator();
5839 for(; si.atEnd() == false; si++)
5841 MapSector *sector = si.getNode()->getValue();
5843 core::list< MapBlock * > sectorblocks;
5844 sector->getBlocks(sectorblocks);
5846 core::list< MapBlock * >::Iterator i;
5847 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5849 MapBlock *block = *i;
5851 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5857 JMutexAutoLock lock(block->mesh_mutex);
5858 if(block->mesh != NULL)
5860 /*block->mesh->drop();
5861 block->mesh = NULL;*/
5862 block->setMeshExpired(true);
5869 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5871 assert(mapType() == MAPTYPE_CLIENT);
5874 v3s16 p = blockpos + v3s16(0,0,0);
5875 MapBlock *b = getBlockNoCreate(p);
5876 b->updateMesh(daynight_ratio);
5877 //b->setMeshExpired(true);
5879 catch(InvalidPositionException &e){}
5882 v3s16 p = blockpos + v3s16(-1,0,0);
5883 MapBlock *b = getBlockNoCreate(p);
5884 b->updateMesh(daynight_ratio);
5885 //b->setMeshExpired(true);
5887 catch(InvalidPositionException &e){}
5889 v3s16 p = blockpos + v3s16(0,-1,0);
5890 MapBlock *b = getBlockNoCreate(p);
5891 b->updateMesh(daynight_ratio);
5892 //b->setMeshExpired(true);
5894 catch(InvalidPositionException &e){}
5896 v3s16 p = blockpos + v3s16(0,0,-1);
5897 MapBlock *b = getBlockNoCreate(p);
5898 b->updateMesh(daynight_ratio);
5899 //b->setMeshExpired(true);
5901 catch(InvalidPositionException &e){}
5906 Update mesh of block in which the node is, and if the node is at the
5907 leading edge, update the appropriate leading blocks too.
5909 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5917 v3s16 blockposes[4];
5918 for(u32 i=0; i<4; i++)
5920 v3s16 np = nodepos + dirs[i];
5921 blockposes[i] = getNodeBlockPos(np);
5922 // Don't update mesh of block if it has been done already
5923 bool already_updated = false;
5924 for(u32 j=0; j<i; j++)
5926 if(blockposes[j] == blockposes[i])
5928 already_updated = true;
5935 MapBlock *b = getBlockNoCreate(blockposes[i]);
5936 b->updateMesh(daynight_ratio);
5941 void ClientMap::PrintInfo(std::ostream &out)
5952 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5957 MapVoxelManipulator::~MapVoxelManipulator()
5959 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5963 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5965 TimeTaker timer1("emerge", &emerge_time);
5967 // Units of these are MapBlocks
5968 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5969 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5971 VoxelArea block_area_nodes
5972 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5974 addArea(block_area_nodes);
5976 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5977 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5978 for(s32 x=p_min.X; x<=p_max.X; x++)
5981 core::map<v3s16, bool>::Node *n;
5982 n = m_loaded_blocks.find(p);
5986 bool block_data_inexistent = false;
5989 TimeTaker timer1("emerge load", &emerge_load_time);
5991 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5992 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5995 dstream<<std::endl;*/
5997 MapBlock *block = m_map->getBlockNoCreate(p);
5998 if(block->isDummy())
5999 block_data_inexistent = true;
6001 block->copyTo(*this);
6003 catch(InvalidPositionException &e)
6005 block_data_inexistent = true;
6008 if(block_data_inexistent)
6010 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6011 // Fill with VOXELFLAG_INEXISTENT
6012 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6013 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6015 s32 i = m_area.index(a.MinEdge.X,y,z);
6016 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6020 m_loaded_blocks.insert(p, !block_data_inexistent);
6023 //dstream<<"emerge done"<<std::endl;
6027 SUGG: Add an option to only update eg. water and air nodes.
6028 This will make it interfere less with important stuff if
6031 void MapVoxelManipulator::blitBack
6032 (core::map<v3s16, MapBlock*> & modified_blocks)
6034 if(m_area.getExtent() == v3s16(0,0,0))
6037 //TimeTaker timer1("blitBack");
6039 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6040 <<m_loaded_blocks.size()<<std::endl;*/
6043 Initialize block cache
6045 v3s16 blockpos_last;
6046 MapBlock *block = NULL;
6047 bool block_checked_in_modified = false;
6049 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6050 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6051 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6055 u8 f = m_flags[m_area.index(p)];
6056 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6059 MapNode &n = m_data[m_area.index(p)];
6061 v3s16 blockpos = getNodeBlockPos(p);
6066 if(block == NULL || blockpos != blockpos_last){
6067 block = m_map->getBlockNoCreate(blockpos);
6068 blockpos_last = blockpos;
6069 block_checked_in_modified = false;
6072 // Calculate relative position in block
6073 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6075 // Don't continue if nothing has changed here
6076 if(block->getNode(relpos) == n)
6079 //m_map->setNode(m_area.MinEdge + p, n);
6080 block->setNode(relpos, n);
6083 Make sure block is in modified_blocks
6085 if(block_checked_in_modified == false)
6087 modified_blocks[blockpos] = block;
6088 block_checked_in_modified = true;
6091 catch(InvalidPositionException &e)
6097 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6098 MapVoxelManipulator(map)
6102 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6106 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6108 // Just create the area so that it can be pointed to
6109 VoxelManipulator::emerge(a, caller_id);
6112 void ManualMapVoxelManipulator::initialEmerge(
6113 v3s16 blockpos_min, v3s16 blockpos_max)
6115 TimeTaker timer1("initialEmerge", &emerge_time);
6117 // Units of these are MapBlocks
6118 v3s16 p_min = blockpos_min;
6119 v3s16 p_max = blockpos_max;
6121 VoxelArea block_area_nodes
6122 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6124 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6127 dstream<<"initialEmerge: area: ";
6128 block_area_nodes.print(dstream);
6129 dstream<<" ("<<size_MB<<"MB)";
6133 addArea(block_area_nodes);
6135 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6136 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6137 for(s32 x=p_min.X; x<=p_max.X; x++)
6140 core::map<v3s16, bool>::Node *n;
6141 n = m_loaded_blocks.find(p);
6145 bool block_data_inexistent = false;
6148 TimeTaker timer1("emerge load", &emerge_load_time);
6150 MapBlock *block = m_map->getBlockNoCreate(p);
6151 if(block->isDummy())
6152 block_data_inexistent = true;
6154 block->copyTo(*this);
6156 catch(InvalidPositionException &e)
6158 block_data_inexistent = true;
6161 if(block_data_inexistent)
6164 Mark area inexistent
6166 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6167 // Fill with VOXELFLAG_INEXISTENT
6168 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6169 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6171 s32 i = m_area.index(a.MinEdge.X,y,z);
6172 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6176 m_loaded_blocks.insert(p, !block_data_inexistent);
6180 void ManualMapVoxelManipulator::blitBackAll(
6181 core::map<v3s16, MapBlock*> * modified_blocks)
6183 if(m_area.getExtent() == v3s16(0,0,0))
6187 Copy data of all blocks
6189 for(core::map<v3s16, bool>::Iterator
6190 i = m_loaded_blocks.getIterator();
6191 i.atEnd() == false; i++)
6193 bool existed = i.getNode()->getValue();
6194 if(existed == false)
6196 v3s16 p = i.getNode()->getKey();
6197 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6200 dstream<<"WARNING: "<<__FUNCTION_NAME
6201 <<": got NULL block "
6202 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6207 block->copyFrom(*this);
6210 modified_blocks->insert(p, block);