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 void makeChunk(ChunkMakeData *data)
2175 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2176 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2177 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2178 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2179 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2180 *(u32)h_blocks*MAP_BLOCKSIZE;
2181 v3s16 bigarea_blocks_min(
2182 data->sectorpos_bigbase.X,
2184 data->sectorpos_bigbase.Y
2186 v3s16 bigarea_blocks_max(
2187 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2189 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2191 s16 lighting_min_d = 0-data->max_spread_amount;
2192 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2193 + data->max_spread_amount-1;
2196 data->vmanip.clearFlag(0xff);
2198 TimeTaker timer_generate("makeChunk() generate");
2200 // Maximum height of the stone surface and obstacles.
2201 // This is used to disable cave generation from going too high.
2202 s16 stone_surface_max_y = 0;
2205 Generate general ground level to full area
2210 //TimeTaker timer1("ground level");
2212 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2213 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2216 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2219 Skip of already generated
2222 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2223 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2227 // Ground height at this point
2228 float surface_y_f = 0.0;
2230 // Use perlin noise for ground height
2231 surface_y_f = base_rock_level_2d(data->seed, p2d);
2233 /*// Experimental stuff
2235 float a = highlands_level_2d(data->seed, p2d);
2240 // Convert to integer
2241 s16 surface_y = (s16)surface_y_f;
2244 if(surface_y > stone_surface_max_y)
2245 stone_surface_max_y = surface_y;
2248 Fill ground with stone
2251 // Use fast index incrementing
2252 v3s16 em = data->vmanip.m_area.getExtent();
2253 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2254 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2256 // Skip if already generated.
2257 // This is done here because there might be a cave at
2258 // any point in ground, which could look like it
2259 // wasn't generated.
2260 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2263 data->vmanip.m_data[i].d = CONTENT_STONE;
2265 data->vmanip.m_area.add_y(em, i, 1);
2273 Randomize some parameters
2276 //s32 stone_obstacle_count = 0;
2277 /*s32 stone_obstacle_count =
2278 rangelim((1.0+noise2d(data->seed+897,
2279 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2281 //s16 stone_obstacle_max_height = 0;
2282 /*s16 stone_obstacle_max_height =
2283 rangelim((1.0+noise2d(data->seed+5902,
2284 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2287 Loop this part, it will make stuff look older and newer nicely
2289 //for(u32 i_age=0; i_age<1; i_age++)
2290 for(u32 i_age=0; i_age<2; i_age++)
2292 /******************************
2293 BEGINNING OF AGING LOOP
2294 ******************************/
2298 //TimeTaker timer1("caves");
2303 u32 caves_count = relative_volume / 400000;
2304 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2305 if(stone_surface_max_y < WATER_LEVEL)
2307 /*u32 caves_count = 0;
2308 u32 bruises_count = 0;*/
2309 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2311 s16 min_tunnel_diameter = 3;
2312 s16 max_tunnel_diameter = 5;
2313 u16 tunnel_routepoints = 20;
2315 v3f main_direction(0,0,0);
2317 bool bruise_surface = (jj > caves_count);
2321 min_tunnel_diameter = 5;
2322 max_tunnel_diameter = myrand_range(10, 20);
2323 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2324 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2326 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2327 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2329 tunnel_routepoints = 5;
2335 // Allowed route area size in nodes
2337 data->sectorpos_base_size*MAP_BLOCKSIZE,
2338 h_blocks*MAP_BLOCKSIZE,
2339 data->sectorpos_base_size*MAP_BLOCKSIZE
2342 // Area starting point in nodes
2344 data->sectorpos_base.X*MAP_BLOCKSIZE,
2345 data->y_blocks_min*MAP_BLOCKSIZE,
2346 data->sectorpos_base.Y*MAP_BLOCKSIZE
2350 //(this should be more than the maximum radius of the tunnel)
2351 //s16 insure = 5; // Didn't work with max_d = 20
2353 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2354 ar += v3s16(1,0,1) * more * 2;
2355 of -= v3s16(1,0,1) * more;
2357 s16 route_y_min = 0;
2358 // Allow half a diameter + 7 over stone surface
2359 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2361 /*// If caves, don't go through surface too often
2362 if(bruise_surface == false)
2363 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2365 // Limit maximum to area
2366 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2370 /*// Minimum is at y=0
2371 route_y_min = -of.Y - 0;*/
2372 // Minimum is at y=max_tunnel_diameter/4
2373 //route_y_min = -of.Y + max_tunnel_diameter/4;
2374 //s16 min = -of.Y + max_tunnel_diameter/4;
2375 s16 min = -of.Y + 0;
2376 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2377 route_y_min = rangelim(route_y_min, 0, route_y_max);
2380 /*dstream<<"route_y_min = "<<route_y_min
2381 <<", route_y_max = "<<route_y_max<<std::endl;*/
2383 s16 route_start_y_min = route_y_min;
2384 s16 route_start_y_max = route_y_max;
2386 // Start every 2nd cave from surface
2387 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2389 if(coming_from_surface)
2391 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2394 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2395 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2397 // Randomize starting position
2399 (float)(myrand()%ar.X)+0.5,
2400 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2401 (float)(myrand()%ar.Z)+0.5
2404 MapNode airnode(CONTENT_AIR);
2407 Generate some tunnel starting from orp
2410 for(u16 j=0; j<tunnel_routepoints; j++)
2412 if(j%7==0 && bruise_surface == false)
2414 main_direction = v3f(
2415 ((float)(myrand()%20)-(float)10)/10,
2416 ((float)(myrand()%20)-(float)10)/30,
2417 ((float)(myrand()%20)-(float)10)/10
2419 main_direction *= (float)myrand_range(1, 3);
2423 s16 min_d = min_tunnel_diameter;
2424 s16 max_d = max_tunnel_diameter;
2425 s16 rs = myrand_range(min_d, max_d);
2430 maxlen = v3s16(rs*7,rs*7,rs*7);
2434 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2439 if(coming_from_surface && j < 3)
2442 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2443 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2444 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2450 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2451 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2452 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2456 vec += main_direction;
2461 else if(rp.X >= ar.X)
2463 if(rp.Y < route_y_min)
2465 else if(rp.Y >= route_y_max)
2466 rp.Y = route_y_max-1;
2469 else if(rp.Z >= ar.Z)
2473 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2475 v3f fp = orp + vec * f;
2476 v3s16 cp(fp.X, fp.Y, fp.Z);
2479 s16 d1 = d0 + rs - 1;
2480 for(s16 z0=d0; z0<=d1; z0++)
2482 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2483 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2484 for(s16 x0=-si; x0<=si-1; x0++)
2486 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2487 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2488 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2489 //s16 si2 = rs - abs(x0);
2490 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2496 /*if(isInArea(p, ar) == false)
2498 // Check only height
2499 if(y < 0 || y >= ar.Y)
2503 //assert(data->vmanip.m_area.contains(p));
2504 if(data->vmanip.m_area.contains(p) == false)
2506 dstream<<"WARNING: "<<__FUNCTION_NAME
2507 <<":"<<__LINE__<<": "
2508 <<"point not in area"
2513 // Just set it to air, it will be changed to
2515 u32 i = data->vmanip.m_area.index(p);
2516 data->vmanip.m_data[i] = airnode;
2518 if(bruise_surface == false)
2521 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2536 //TimeTaker timer1("ore veins");
2541 for(u32 jj=0; jj<relative_volume/1000; jj++)
2543 s16 max_vein_diameter = 3;
2545 // Allowed route area size in nodes
2547 data->sectorpos_base_size*MAP_BLOCKSIZE,
2548 h_blocks*MAP_BLOCKSIZE,
2549 data->sectorpos_base_size*MAP_BLOCKSIZE
2552 // Area starting point in nodes
2554 data->sectorpos_base.X*MAP_BLOCKSIZE,
2555 data->y_blocks_min*MAP_BLOCKSIZE,
2556 data->sectorpos_base.Y*MAP_BLOCKSIZE
2560 //(this should be more than the maximum radius of the tunnel)
2562 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2563 ar += v3s16(1,0,1) * more * 2;
2564 of -= v3s16(1,0,1) * more;
2566 // Randomize starting position
2568 (float)(myrand()%ar.X)+0.5,
2569 (float)(myrand()%ar.Y)+0.5,
2570 (float)(myrand()%ar.Z)+0.5
2573 // Randomize mineral
2576 mineral = MINERAL_COAL;
2578 mineral = MINERAL_IRON;
2581 Generate some vein starting from orp
2584 for(u16 j=0; j<2; j++)
2587 (float)(myrand()%ar.X)+0.5,
2588 (float)(myrand()%ar.Y)+0.5,
2589 (float)(myrand()%ar.Z)+0.5
2591 v3f vec = rp - orp;*/
2593 v3s16 maxlen(5, 5, 5);
2595 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2596 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2597 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2602 else if(rp.X >= ar.X)
2606 else if(rp.Y >= ar.Y)
2610 else if(rp.Z >= ar.Z)
2616 s16 max_d = max_vein_diameter;
2617 s16 rs = myrand_range(min_d, max_d);
2619 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2621 v3f fp = orp + vec * f;
2622 v3s16 cp(fp.X, fp.Y, fp.Z);
2624 s16 d1 = d0 + rs - 1;
2625 for(s16 z0=d0; z0<=d1; z0++)
2627 s16 si = rs - abs(z0);
2628 for(s16 x0=-si; x0<=si-1; x0++)
2630 s16 si2 = rs - abs(x0);
2631 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2633 // Don't put mineral to every place
2641 /*if(isInArea(p, ar) == false)
2643 // Check only height
2644 if(y < 0 || y >= ar.Y)
2648 assert(data->vmanip.m_area.contains(p));
2650 // Just set it to air, it will be changed to
2652 u32 i = data->vmanip.m_area.index(p);
2653 MapNode *n = &data->vmanip.m_data[i];
2654 if(n->d == CONTENT_STONE)
2669 //TimeTaker timer1("add mud");
2672 Add mud to the central chunk
2675 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2676 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2678 // Node position in 2d
2679 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2681 // Randomize mud amount
2682 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2683 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2684 data->seed+1, 3, 0.55));
2686 // Find ground level
2687 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2690 If topmost node is grass, change it to mud.
2691 It might be if it was flown to there from a neighboring
2692 chunk and then converted.
2695 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2696 MapNode *n = &data->vmanip.m_data[i];
2697 if(n->d == CONTENT_GRASS)
2706 v3s16 em = data->vmanip.m_area.getExtent();
2707 s16 y_start = surface_y+1;
2708 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2709 for(s16 y=y_start; y<=y_nodes_max; y++)
2711 if(mudcount >= mud_add_amount)
2714 MapNode &n = data->vmanip.m_data[i];
2718 data->vmanip.m_area.add_y(em, i, 1);
2727 TimeTaker timer1("flow mud");
2730 Flow mud away from steep edges
2733 // Limit area by 1 because mud is flown into neighbors.
2734 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2735 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2737 // Iterate a few times
2738 for(s16 k=0; k<3; k++)
2741 for(s16 x=mudflow_minpos;
2744 for(s16 z=mudflow_minpos;
2748 // Invert coordinates every 2nd iteration
2751 x = mudflow_maxpos - (x-mudflow_minpos);
2752 z = mudflow_maxpos - (z-mudflow_minpos);
2755 // Node position in 2d
2756 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2758 v3s16 em = data->vmanip.m_area.getExtent();
2759 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2766 for(; y>=y_nodes_min; y--)
2768 n = &data->vmanip.m_data[i];
2769 //if(content_walkable(n->d))
2771 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2774 data->vmanip.m_area.add_y(em, i, -1);
2777 // Stop if out of area
2778 //if(data->vmanip.m_area.contains(i) == false)
2782 /*// If not mud, do nothing to it
2783 MapNode *n = &data->vmanip.m_data[i];
2784 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2788 Don't flow it if the stuff under it is not mud
2792 data->vmanip.m_area.add_y(em, i2, -1);
2793 // Cancel if out of area
2794 if(data->vmanip.m_area.contains(i2) == false)
2796 MapNode *n2 = &data->vmanip.m_data[i2];
2797 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2801 // Make it exactly mud
2804 /*s16 recurse_count = 0;
2808 v3s16(0,0,1), // back
2809 v3s16(1,0,0), // right
2810 v3s16(0,0,-1), // front
2811 v3s16(-1,0,0), // left
2814 // Theck that upper is air or doesn't exist.
2815 // Cancel dropping if upper keeps it in place
2817 data->vmanip.m_area.add_y(em, i3, 1);
2818 if(data->vmanip.m_area.contains(i3) == true
2819 && content_walkable(data->vmanip.m_data[i3].d) == true)
2826 for(u32 di=0; di<4; di++)
2828 v3s16 dirp = dirs4[di];
2831 data->vmanip.m_area.add_p(em, i2, dirp);
2832 // Fail if out of area
2833 if(data->vmanip.m_area.contains(i2) == false)
2835 // Check that side is air
2836 MapNode *n2 = &data->vmanip.m_data[i2];
2837 if(content_walkable(n2->d))
2839 // Check that under side is air
2840 data->vmanip.m_area.add_y(em, i2, -1);
2841 if(data->vmanip.m_area.contains(i2) == false)
2843 n2 = &data->vmanip.m_data[i2];
2844 if(content_walkable(n2->d))
2846 /*// Check that under that is air (need a drop of 2)
2847 data->vmanip.m_area.add_y(em, i2, -1);
2848 if(data->vmanip.m_area.contains(i2) == false)
2850 n2 = &data->vmanip.m_data[i2];
2851 if(content_walkable(n2->d))
2853 // Loop further down until not air
2855 data->vmanip.m_area.add_y(em, i2, -1);
2856 // Fail if out of area
2857 if(data->vmanip.m_area.contains(i2) == false)
2859 n2 = &data->vmanip.m_data[i2];
2860 }while(content_walkable(n2->d) == false);
2861 // Loop one up so that we're in air
2862 data->vmanip.m_area.add_y(em, i2, 1);
2863 n2 = &data->vmanip.m_data[i2];
2865 // Move mud to new place
2867 // Set old place to be air
2868 *n = MapNode(CONTENT_AIR);
2881 //TimeTaker timer1("add water");
2884 Add water to the central chunk (and a bit more)
2887 for(s16 x=0-data->max_spread_amount;
2888 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2890 for(s16 z=0-data->max_spread_amount;
2891 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2894 // Node position in 2d
2895 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2897 // Find ground level
2898 //s16 surface_y = find_ground_level(data->vmanip, p2d);
2901 If ground level is over water level, skip.
2902 NOTE: This leaves caves near water without water,
2903 which looks especially crappy when the nearby water
2904 won't start flowing either for some reason
2906 /*if(surface_y > WATER_LEVEL)
2913 v3s16 em = data->vmanip.m_area.getExtent();
2914 u8 light = LIGHT_MAX;
2915 // Start at global water surface level
2916 s16 y_start = WATER_LEVEL;
2917 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2918 MapNode *n = &data->vmanip.m_data[i];
2920 for(s16 y=y_start; y>=y_nodes_min; y--)
2922 n = &data->vmanip.m_data[i];
2924 // Stop when there is no water and no air
2925 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2926 && n->d != CONTENT_WATER)
2932 // Make water only not in caves
2933 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
2935 n->d = CONTENT_WATERSOURCE;
2936 //n->setLight(LIGHTBANK_DAY, light);
2938 // Add to transforming liquid queue (in case it'd
2940 /*v3s16 p = v3s16(p2d.X, y, p2d.Y);
2941 m_transforming_liquid.push_back(p);*/
2945 data->vmanip.m_area.add_y(em, i, -1);
2956 /***********************
2958 ************************/
2961 //TimeTaker timer1("convert mud to sand");
2967 //s16 mud_add_amount = myrand_range(2, 4);
2968 //s16 mud_add_amount = 0;
2970 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2971 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
2972 for(s16 x=0-data->max_spread_amount+1;
2973 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
2975 for(s16 z=0-data->max_spread_amount+1;
2976 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
2979 // Node position in 2d
2980 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2982 // Determine whether to have sand here
2983 double sandnoise = noise2d_perlin(
2984 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
2985 data->seed+59420, 3, 0.50);
2987 bool have_sand = (sandnoise > -0.15);
2989 if(have_sand == false)
2992 // Find ground level
2993 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2995 if(surface_y > WATER_LEVEL + 2)
2999 v3s16 em = data->vmanip.m_area.getExtent();
3000 s16 y_start = surface_y;
3001 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3002 u32 not_sand_counter = 0;
3003 for(s16 y=y_start; y>=y_nodes_min; y--)
3005 MapNode *n = &data->vmanip.m_data[i];
3006 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3008 n->d = CONTENT_SAND;
3013 if(not_sand_counter > 3)
3017 data->vmanip.m_area.add_y(em, i, -1);
3026 //TimeTaker timer1("generate trees");
3032 // Divide area into parts
3034 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3035 double area = sidelen * sidelen;
3036 for(s16 x0=0; x0<div; x0++)
3037 for(s16 z0=0; z0<div; z0++)
3039 // Center position of part of division
3041 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3042 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3044 // Minimum edge of part of division
3046 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3047 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3049 // Maximum edge of part of division
3051 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3052 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3055 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3056 // Put trees in random places on part of division
3057 for(u32 i=0; i<tree_count; i++)
3059 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3060 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3061 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3062 // Don't make a tree under water level
3065 // Don't make a tree so high that it doesn't fit
3066 if(y > y_nodes_max - 6)
3070 Trees grow only on mud and grass
3073 u32 i = data->vmanip.m_area.index(v3s16(p));
3074 MapNode *n = &data->vmanip.m_data[i];
3075 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3080 make_tree(data->vmanip, p);
3083 /*u32 tree_max = relative_area / 60;
3084 //u32 count = myrand_range(0, tree_max);
3085 for(u32 i=0; i<count; i++)
3087 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3088 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3089 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3090 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3091 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3092 // Don't make a tree under water level
3097 make_tree(data->vmanip, p);
3105 //TimeTaker timer1("grow grass");
3111 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3112 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3113 for(s16 x=0-data->max_spread_amount;
3114 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3116 for(s16 z=0-data->max_spread_amount;
3117 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3120 // Node position in 2d
3121 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3124 Find the lowest surface to which enough light ends up
3127 Basically just wait until not air and not leaves.
3131 v3s16 em = data->vmanip.m_area.getExtent();
3132 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3134 // Go to ground level
3135 for(y=y_nodes_max; y>=y_nodes_min; y--)
3137 MapNode &n = data->vmanip.m_data[i];
3138 if(n.d != CONTENT_AIR
3139 && n.d != CONTENT_LEAVES)
3141 data->vmanip.m_area.add_y(em, i, -1);
3143 if(y >= y_nodes_min)
3146 surface_y = y_nodes_min;
3149 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3150 MapNode *n = &data->vmanip.m_data[i];
3151 if(n->d == CONTENT_MUD)
3152 n->d = CONTENT_GRASS;
3158 Initial lighting (sunlight)
3161 core::map<v3s16, bool> light_sources;
3164 // 750ms @cs=8, can't optimize more
3165 TimeTaker timer1("initial lighting");
3169 Go through the edges and add all nodes that have light to light_sources
3173 for(s16 i=0; i<4; i++)
3175 for(s16 j=lighting_min_d;
3182 if(i == 0 || i == 1)
3184 x = (i==0) ? lighting_min_d : lighting_max_d;
3193 z = (i==0) ? lighting_min_d : lighting_max_d;
3200 // Node position in 2d
3201 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3204 v3s16 em = data->vmanip.m_area.getExtent();
3205 s16 y_start = y_nodes_max;
3206 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3207 for(s16 y=y_start; y>=y_nodes_min; y--)
3209 MapNode *n = &data->vmanip.m_data[i];
3210 if(n->getLight(LIGHTBANK_DAY) != 0)
3212 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3214 //NOTE: This is broken, at least the index has to
3223 Go through the edges and apply sunlight to them, not caring
3228 for(s16 i=0; i<4; i++)
3230 for(s16 j=lighting_min_d;
3237 if(i == 0 || i == 1)
3239 x = (i==0) ? lighting_min_d : lighting_max_d;
3248 z = (i==0) ? lighting_min_d : lighting_max_d;
3255 // Node position in 2d
3256 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3258 // Loop from top to down
3260 u8 light = LIGHT_SUN;
3261 v3s16 em = data->vmanip.m_area.getExtent();
3262 s16 y_start = y_nodes_max;
3263 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3264 for(s16 y=y_start; y>=y_nodes_min; y--)
3266 MapNode *n = &data->vmanip.m_data[i];
3267 if(light_propagates_content(n->d) == false)
3271 else if(light != LIGHT_SUN
3272 || sunlight_propagates_content(n->d) == false)
3278 n->setLight(LIGHTBANK_DAY, light);
3279 n->setLight(LIGHTBANK_NIGHT, 0);
3283 // Insert light source
3284 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3287 // Increment index by y
3288 data->vmanip.m_area.add_y(em, i, -1);
3294 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3295 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3296 /*for(s16 x=0-data->max_spread_amount+1;
3297 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3299 for(s16 z=0-data->max_spread_amount+1;
3300 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3304 This has to be 1 smaller than the actual area, because
3305 neighboring nodes are checked.
3307 for(s16 x=lighting_min_d+1;
3308 x<=lighting_max_d-1;
3310 for(s16 z=lighting_min_d+1;
3311 z<=lighting_max_d-1;
3314 // Node position in 2d
3315 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3318 Apply initial sunlight
3321 u8 light = LIGHT_SUN;
3322 bool add_to_sources = false;
3323 v3s16 em = data->vmanip.m_area.getExtent();
3324 s16 y_start = y_nodes_max;
3325 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3326 for(s16 y=y_start; y>=y_nodes_min; y--)
3328 MapNode *n = &data->vmanip.m_data[i];
3330 if(light_propagates_content(n->d) == false)
3334 else if(light != LIGHT_SUN
3335 || sunlight_propagates_content(n->d) == false)
3341 // This doesn't take much time
3342 if(add_to_sources == false)
3345 Check sides. If side is not air or water, start
3346 adding to light_sources.
3349 v3s16(0,0,1), // back
3350 v3s16(1,0,0), // right
3351 v3s16(0,0,-1), // front
3352 v3s16(-1,0,0), // left
3354 for(u32 di=0; di<4; di++)
3356 v3s16 dirp = dirs4[di];
3358 data->vmanip.m_area.add_p(em, i2, dirp);
3359 MapNode *n2 = &data->vmanip.m_data[i2];
3361 n2->d != CONTENT_AIR
3362 && n2->d != CONTENT_WATERSOURCE
3363 && n2->d != CONTENT_WATER
3365 add_to_sources = true;
3371 n->setLight(LIGHTBANK_DAY, light);
3372 n->setLight(LIGHTBANK_NIGHT, 0);
3374 // This doesn't take much time
3375 if(light != 0 && add_to_sources)
3377 // Insert light source
3378 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3381 // Increment index by y
3382 data->vmanip.m_area.add_y(em, i, -1);
3390 // Spread light around
3392 TimeTaker timer("makeChunk() spreadLight");
3393 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3400 timer_generate.stop();
3403 //###################################################################
3404 //###################################################################
3405 //###################################################################
3406 //###################################################################
3407 //###################################################################
3408 //###################################################################
3409 //###################################################################
3410 //###################################################################
3411 //###################################################################
3412 //###################################################################
3413 //###################################################################
3414 //###################################################################
3415 //###################################################################
3416 //###################################################################
3417 //###################################################################
3419 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3421 // The distance how far into the neighbors the generator is allowed to go.
3422 s16 max_spread_amount_sectors = 2;
3423 assert(max_spread_amount_sectors <= m_chunksize);
3424 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3426 s16 y_blocks_min = -4;
3427 s16 y_blocks_max = 3;
3429 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3430 s16 sectorpos_base_size = m_chunksize;
3432 v2s16 sectorpos_bigbase =
3433 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3434 s16 sectorpos_bigbase_size =
3435 sectorpos_base_size + 2 * max_spread_amount_sectors;
3438 data.chunkpos = chunkpos;
3439 data.y_blocks_min = y_blocks_min;
3440 data.y_blocks_max = y_blocks_max;
3441 data.sectorpos_base = sectorpos_base;
3442 data.sectorpos_base_size = sectorpos_base_size;
3443 data.sectorpos_bigbase = sectorpos_bigbase;
3444 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3445 data.max_spread_amount = max_spread_amount;
3448 Create the whole area of this and the neighboring chunks
3451 TimeTaker timer("generateChunkRaw() create area");
3453 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3454 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3456 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3457 ServerMapSector *sector = createSector(sectorpos);
3460 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3462 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3463 MapBlock *block = createBlock(blockpos);
3465 // Lighting won't be calculated
3466 //block->setLightingExpired(true);
3467 // Lighting will be calculated
3468 block->setLightingExpired(false);
3471 Block gets sunlight if this is true.
3473 This should be set to true when the top side of a block
3474 is completely exposed to the sky.
3476 Actually this doesn't matter now because the
3477 initial lighting is done here.
3479 block->setIsUnderground(y != y_blocks_max);
3485 Now we have a big empty area.
3487 Make a ManualMapVoxelManipulator that contains this and the
3491 v3s16 bigarea_blocks_min(
3492 sectorpos_bigbase.X,
3496 v3s16 bigarea_blocks_max(
3497 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3499 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3502 data.vmanip.setMap(this);
3505 TimeTaker timer("generateChunkRaw() initialEmerge");
3506 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3511 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3512 core::map<v3s16, MapBlock*> &changed_blocks)
3515 Blit generated stuff to map
3519 //TimeTaker timer("generateChunkRaw() blitBackAll");
3520 data.vmanip.blitBackAll(&changed_blocks);
3524 Update day/night difference cache of the MapBlocks
3527 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3528 i.atEnd() == false; i++)
3530 MapBlock *block = i.getNode()->getValue();
3531 block->updateDayNightDiff();
3536 Add random objects to blocks
3539 for(s16 x=0; x<data.sectorpos_base_size; x++)
3540 for(s16 z=0; z<data.sectorpos_base_size; z++)
3542 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3543 ServerMapSector *sector = createSector(sectorpos);
3546 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3548 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3549 MapBlock *block = createBlock(blockpos);
3550 addRandomObjects(block);
3556 Create chunk metadata
3559 for(s16 x=-1; x<=1; x++)
3560 for(s16 y=-1; y<=1; y++)
3562 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3563 // Add chunk meta information
3564 MapChunk *chunk = getChunk(chunkpos0);
3567 chunk = new MapChunk();
3568 m_chunks.insert(chunkpos0, chunk);
3570 //chunk->setIsVolatile(true);
3571 if(chunk->getGenLevel() > GENERATED_PARTLY)
3572 chunk->setGenLevel(GENERATED_PARTLY);
3576 Set central chunk non-volatile
3578 MapChunk *chunk = getChunk(data.chunkpos);
3581 //chunk->setIsVolatile(false);
3582 chunk->setGenLevel(GENERATED_FULLY);
3585 Save changed parts of map
3593 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3594 core::map<v3s16, MapBlock*> &changed_blocks,
3597 DSTACK(__FUNCTION_NAME);
3600 Don't generate if already fully generated
3604 MapChunk *chunk = getChunk(chunkpos);
3605 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3607 dstream<<"generateChunkRaw(): Chunk "
3608 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3609 <<" already generated"<<std::endl;
3614 dstream<<"generateChunkRaw(): Generating chunk "
3615 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3618 TimeTaker timer("generateChunkRaw()");
3622 // Initialize generation
3623 initChunkMake(data, chunkpos);
3628 // Finalize generation
3629 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3632 Return central chunk (which was requested)
3638 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3639 core::map<v3s16, MapBlock*> &changed_blocks)
3641 dstream<<"generateChunk(): Generating chunk "
3642 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3645 /*for(s16 x=-1; x<=1; x++)
3646 for(s16 y=-1; y<=1; y++)*/
3647 for(s16 x=-0; x<=0; x++)
3648 for(s16 y=-0; y<=0; y++)
3650 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3651 MapChunk *chunk = getChunk(chunkpos0);
3652 // Skip if already generated
3653 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3655 generateChunkRaw(chunkpos0, changed_blocks);
3658 assert(chunkNonVolatile(chunkpos1));
3660 MapChunk *chunk = getChunk(chunkpos1);
3664 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3666 DSTACK("%s: p2d=(%d,%d)",
3671 Check if it exists already in memory
3673 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3678 Try to load it from disk (with blocks)
3680 if(loadSectorFull(p2d) == true)
3682 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3685 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3686 throw InvalidPositionException("");
3692 Do not create over-limit
3694 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3695 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3696 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3697 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3698 throw InvalidPositionException("createSector(): pos. over limit");
3701 Generate blank sector
3704 sector = new ServerMapSector(this, p2d);
3706 // Sector position on map in nodes
3707 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3712 m_sectors.insert(p2d, sector);
3717 MapSector * ServerMap::emergeSector(v2s16 p2d,
3718 core::map<v3s16, MapBlock*> &changed_blocks)
3720 DSTACK("%s: p2d=(%d,%d)",
3727 v2s16 chunkpos = sector_to_chunk(p2d);
3728 /*bool chunk_nonvolatile = false;
3729 MapChunk *chunk = getChunk(chunkpos);
3730 if(chunk && chunk->getIsVolatile() == false)
3731 chunk_nonvolatile = true;*/
3732 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3735 If chunk is not fully generated, generate chunk
3737 if(chunk_nonvolatile == false)
3739 // Generate chunk and neighbors
3740 generateChunk(chunkpos, changed_blocks);
3744 Return sector if it exists now
3746 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3751 Try to load it from disk
3753 if(loadSectorFull(p2d) == true)
3755 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3758 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3759 throw InvalidPositionException("");
3765 generateChunk should have generated the sector
3769 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3770 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3774 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3776 return createSector(p2d);
3781 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3784 generateChunkRaw(chunkpos, changed_blocks, true);
3787 Return sector if it exists now
3789 sector = getSectorNoGenerateNoEx(p2d);
3793 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3801 //return generateSector();
3805 NOTE: This is not used for main map generation, only for blocks
3806 that are very high or low
3808 MapBlock * ServerMap::generateBlock(
3810 MapBlock *original_dummy,
3811 ServerMapSector *sector,
3812 core::map<v3s16, MapBlock*> &changed_blocks,
3813 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3816 DSTACK("%s: p=(%d,%d,%d)",
3820 /*dstream<<"generateBlock(): "
3821 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3824 MapBlock *block = original_dummy;
3826 v2s16 p2d(p.X, p.Z);
3828 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3831 Do not generate over-limit
3833 if(blockpos_over_limit(p))
3835 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3836 throw InvalidPositionException("generateBlock(): pos. over limit");
3840 If block doesn't exist, create one.
3841 If it exists, it is a dummy. In that case unDummify() it.
3843 NOTE: This already sets the map as the parent of the block
3847 block = sector->createBlankBlockNoInsert(block_y);
3851 // Remove the block so that nobody can get a half-generated one.
3852 sector->removeBlock(block);
3853 // Allocate the block to contain the generated data
3857 u8 water_material = CONTENT_WATERSOURCE;
3859 s32 lowest_ground_y = 32767;
3860 s32 highest_ground_y = -32768;
3862 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3863 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3865 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3867 //s16 surface_y = 0;
3869 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3870 + AVERAGE_MUD_AMOUNT;
3872 if(surface_y < lowest_ground_y)
3873 lowest_ground_y = surface_y;
3874 if(surface_y > highest_ground_y)
3875 highest_ground_y = surface_y;
3877 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3879 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3881 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3886 NOTE: If there are some man-made structures above the
3887 newly created block, they won't be taken into account.
3889 if(real_y > surface_y)
3890 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3896 // If node is over heightmap y, it's air or water
3897 if(real_y > surface_y)
3899 // If under water level, it's water
3900 if(real_y < WATER_LEVEL)
3902 n.d = water_material;
3903 n.setLight(LIGHTBANK_DAY,
3904 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3906 Add to transforming liquid queue (in case it'd
3909 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3910 m_transforming_liquid.push_back(real_pos);
3916 // Else it's ground or caves (air)
3919 // If it's surface_depth under ground, it's stone
3920 if(real_y <= surface_y - surface_depth)
3922 n.d = CONTENT_STONE;
3926 // It is mud if it is under the first ground
3927 // level or under water
3928 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3934 n.d = CONTENT_GRASS;
3937 //n.d = CONTENT_MUD;
3939 /*// If under water level, it's mud
3940 if(real_y < WATER_LEVEL)
3942 // Only the topmost node is grass
3943 else if(real_y <= surface_y - 1)
3946 n.d = CONTENT_GRASS;*/
3950 block->setNode(v3s16(x0,y0,z0), n);
3955 Calculate some helper variables
3958 // Completely underground if the highest part of block is under lowest
3960 // This has to be very sure; it's probably one too strict now but
3961 // that's just better.
3962 bool completely_underground =
3963 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3965 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3967 bool mostly_underwater_surface = false;
3968 if(highest_ground_y < WATER_LEVEL
3969 && some_part_underground && !completely_underground)
3970 mostly_underwater_surface = true;
3973 Get local attributes
3976 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3978 float caves_amount = 0.5;
3983 NOTE: BEWARE: Too big amount of attribute points slows verything
3985 1 interpolation from 5000 points takes 2-3ms.
3987 //TimeTaker timer("generateBlock() local attribute retrieval");
3988 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3989 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3990 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3994 //dstream<<"generateBlock(): Done"<<std::endl;
4000 // Initialize temporary table
4001 const s32 ued = MAP_BLOCKSIZE;
4002 bool underground_emptiness[ued*ued*ued];
4003 for(s32 i=0; i<ued*ued*ued; i++)
4005 underground_emptiness[i] = 0;
4012 Initialize orp and ors. Try to find if some neighboring
4013 MapBlock has a tunnel ended in its side
4017 (float)(myrand()%ued)+0.5,
4018 (float)(myrand()%ued)+0.5,
4019 (float)(myrand()%ued)+0.5
4022 bool found_existing = false;
4028 for(s16 y=0; y<ued; y++)
4029 for(s16 x=0; x<ued; x++)
4031 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4032 if(getNode(ap).d == CONTENT_AIR)
4034 orp = v3f(x+1,y+1,0);
4035 found_existing = true;
4036 goto continue_generating;
4040 catch(InvalidPositionException &e){}
4046 for(s16 y=0; y<ued; y++)
4047 for(s16 x=0; x<ued; x++)
4049 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4050 if(getNode(ap).d == CONTENT_AIR)
4052 orp = v3f(x+1,y+1,ued-1);
4053 found_existing = true;
4054 goto continue_generating;
4058 catch(InvalidPositionException &e){}
4064 for(s16 y=0; y<ued; y++)
4065 for(s16 z=0; z<ued; z++)
4067 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4068 if(getNode(ap).d == CONTENT_AIR)
4070 orp = v3f(0,y+1,z+1);
4071 found_existing = true;
4072 goto continue_generating;
4076 catch(InvalidPositionException &e){}
4082 for(s16 y=0; y<ued; y++)
4083 for(s16 z=0; z<ued; z++)
4085 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4086 if(getNode(ap).d == CONTENT_AIR)
4088 orp = v3f(ued-1,y+1,z+1);
4089 found_existing = true;
4090 goto continue_generating;
4094 catch(InvalidPositionException &e){}
4100 for(s16 x=0; x<ued; x++)
4101 for(s16 z=0; z<ued; z++)
4103 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4104 if(getNode(ap).d == CONTENT_AIR)
4106 orp = v3f(x+1,0,z+1);
4107 found_existing = true;
4108 goto continue_generating;
4112 catch(InvalidPositionException &e){}
4118 for(s16 x=0; x<ued; x++)
4119 for(s16 z=0; z<ued; z++)
4121 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4122 if(getNode(ap).d == CONTENT_AIR)
4124 orp = v3f(x+1,ued-1,z+1);
4125 found_existing = true;
4126 goto continue_generating;
4130 catch(InvalidPositionException &e){}
4132 continue_generating:
4135 Choose whether to actually generate cave
4137 bool do_generate_caves = true;
4138 // Don't generate if no part is underground
4139 if(!some_part_underground)
4141 do_generate_caves = false;
4143 // Don't generate if mostly underwater surface
4144 /*else if(mostly_underwater_surface)
4146 do_generate_caves = false;
4148 // Partly underground = cave
4149 else if(!completely_underground)
4151 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4153 // Found existing cave underground
4154 else if(found_existing && completely_underground)
4156 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4158 // Underground and no caves found
4161 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4164 if(do_generate_caves)
4167 Generate some tunnel starting from orp and ors
4169 for(u16 i=0; i<3; i++)
4172 (float)(myrand()%ued)+0.5,
4173 (float)(myrand()%ued)+0.5,
4174 (float)(myrand()%ued)+0.5
4178 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4182 for(float f=0; f<1.0; f+=0.04)
4184 v3f fp = orp + vec * f;
4185 v3s16 cp(fp.X, fp.Y, fp.Z);
4187 s16 d1 = d0 + rs - 1;
4188 for(s16 z0=d0; z0<=d1; z0++)
4190 s16 si = rs - abs(z0);
4191 for(s16 x0=-si; x0<=si-1; x0++)
4193 s16 si2 = rs - abs(x0);
4194 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4200 if(isInArea(p, ued) == false)
4202 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4214 // Set to true if has caves.
4215 // Set when some non-air is changed to air when making caves.
4216 bool has_caves = false;
4219 Apply temporary cave data to block
4222 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4223 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4225 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4227 MapNode n = block->getNode(v3s16(x0,y0,z0));
4230 if(underground_emptiness[
4231 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4232 +ued*(y0*ued/MAP_BLOCKSIZE)
4233 +(x0*ued/MAP_BLOCKSIZE)])
4235 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4244 block->setNode(v3s16(x0,y0,z0), n);
4249 This is used for guessing whether or not the block should
4250 receive sunlight from the top if the block above doesn't exist
4252 block->setIsUnderground(completely_underground);
4255 Force lighting update if some part of block is partly
4256 underground and has caves.
4258 /*if(some_part_underground && !completely_underground && has_caves)
4260 //dstream<<"Half-ground caves"<<std::endl;
4261 lighting_invalidated_blocks[block->getPos()] = block;
4264 // DEBUG: Always update lighting
4265 //lighting_invalidated_blocks[block->getPos()] = block;
4271 if(some_part_underground)
4273 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4278 for(s16 i=0; i<underground_level/4 + 1; i++)
4280 if(myrand()%50 == 0)
4283 (myrand()%(MAP_BLOCKSIZE-2))+1,
4284 (myrand()%(MAP_BLOCKSIZE-2))+1,
4285 (myrand()%(MAP_BLOCKSIZE-2))+1
4291 for(u16 i=0; i<27; i++)
4293 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4295 block->setNode(cp+g_27dirs[i], n);
4303 u16 coal_amount = 30;
4304 u16 coal_rareness = 60 / coal_amount;
4305 if(coal_rareness == 0)
4307 if(myrand()%coal_rareness == 0)
4309 u16 a = myrand() % 16;
4310 u16 amount = coal_amount * a*a*a / 1000;
4311 for(s16 i=0; i<amount; i++)
4314 (myrand()%(MAP_BLOCKSIZE-2))+1,
4315 (myrand()%(MAP_BLOCKSIZE-2))+1,
4316 (myrand()%(MAP_BLOCKSIZE-2))+1
4320 n.d = CONTENT_STONE;
4321 n.param = MINERAL_COAL;
4323 for(u16 i=0; i<27; i++)
4325 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4327 block->setNode(cp+g_27dirs[i], n);
4335 //TODO: change to iron_amount or whatever
4336 u16 iron_amount = 15;
4337 u16 iron_rareness = 60 / iron_amount;
4338 if(iron_rareness == 0)
4340 if(myrand()%iron_rareness == 0)
4342 u16 a = myrand() % 16;
4343 u16 amount = iron_amount * a*a*a / 1000;
4344 for(s16 i=0; i<amount; i++)
4347 (myrand()%(MAP_BLOCKSIZE-2))+1,
4348 (myrand()%(MAP_BLOCKSIZE-2))+1,
4349 (myrand()%(MAP_BLOCKSIZE-2))+1
4353 n.d = CONTENT_STONE;
4354 n.param = MINERAL_IRON;
4356 for(u16 i=0; i<27; i++)
4358 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4360 block->setNode(cp+g_27dirs[i], n);
4367 Create a few rats in empty blocks underground
4369 if(completely_underground)
4371 //for(u16 i=0; i<2; i++)
4374 (myrand()%(MAP_BLOCKSIZE-2))+1,
4375 (myrand()%(MAP_BLOCKSIZE-2))+1,
4376 (myrand()%(MAP_BLOCKSIZE-2))+1
4379 // Check that the place is empty
4380 //if(!is_ground_content(block->getNode(cp).d))
4383 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4384 block->addObject(obj);
4390 Add block to sector.
4392 sector->insertBlock(block);
4394 // Lighting is invalid after generation.
4395 block->setLightingExpired(true);
4402 <<"lighting_invalidated_blocks.size()"
4406 <<" "<<lighting_invalidated_blocks.size()
4408 <<", "<<completely_underground
4409 <<", "<<some_part_underground
4416 MapBlock * ServerMap::createBlock(v3s16 p)
4418 DSTACK("%s: p=(%d,%d,%d)",
4419 __FUNCTION_NAME, p.X, p.Y, p.Z);
4422 Do not create over-limit
4424 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4425 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4426 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4427 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4428 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4429 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4430 throw InvalidPositionException("createBlock(): pos. over limit");
4432 v2s16 p2d(p.X, p.Z);
4435 This will create or load a sector if not found in memory.
4436 If block exists on disk, it will be loaded.
4438 NOTE: On old save formats, this will be slow, as it generates
4439 lighting on blocks for them.
4441 ServerMapSector *sector;
4443 sector = (ServerMapSector*)createSector(p2d);
4444 assert(sector->getId() == MAPSECTOR_SERVER);
4446 catch(InvalidPositionException &e)
4448 dstream<<"createBlock: createSector() failed"<<std::endl;
4452 NOTE: This should not be done, or at least the exception
4453 should not be passed on as std::exception, because it
4454 won't be catched at all.
4456 /*catch(std::exception &e)
4458 dstream<<"createBlock: createSector() failed: "
4459 <<e.what()<<std::endl;
4464 Try to get a block from the sector
4467 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4471 block = sector->createBlankBlock(block_y);
4475 MapBlock * ServerMap::emergeBlock(
4477 bool only_from_disk,
4478 core::map<v3s16, MapBlock*> &changed_blocks,
4479 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4482 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4484 p.X, p.Y, p.Z, only_from_disk);
4487 Do not generate over-limit
4489 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4490 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4491 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4492 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4493 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4494 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4495 throw InvalidPositionException("emergeBlock(): pos. over limit");
4497 v2s16 p2d(p.X, p.Z);
4500 This will create or load a sector if not found in memory.
4501 If block exists on disk, it will be loaded.
4503 ServerMapSector *sector;
4505 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4506 assert(sector->getId() == MAPSECTOR_SERVER);
4508 catch(InvalidPositionException &e)
4510 dstream<<"emergeBlock: emergeSector() failed: "
4511 <<e.what()<<std::endl;
4512 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4514 <<"You could try to delete it."<<std::endl;
4517 catch(VersionMismatchException &e)
4519 dstream<<"emergeBlock: emergeSector() failed: "
4520 <<e.what()<<std::endl;
4521 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4523 <<"You could try to delete it."<<std::endl;
4527 NOTE: This should not be done, or at least the exception
4528 should not be passed on as std::exception, because it
4529 won't be catched at all.
4531 /*catch(std::exception &e)
4533 dstream<<"emergeBlock: emergeSector() failed: "
4534 <<e.what()<<std::endl;
4535 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4537 <<"You could try to delete it."<<std::endl;
4542 Try to get a block from the sector
4545 bool does_not_exist = false;
4546 bool lighting_expired = false;
4547 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4551 does_not_exist = true;
4553 else if(block->isDummy() == true)
4555 does_not_exist = true;
4557 else if(block->getLightingExpired())
4559 lighting_expired = true;
4564 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4569 If block was not found on disk and not going to generate a
4570 new one, make sure there is a dummy block in place.
4572 if(only_from_disk && (does_not_exist || lighting_expired))
4574 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4578 // Create dummy block
4579 block = new MapBlock(this, p, true);
4581 // Add block to sector
4582 sector->insertBlock(block);
4588 //dstream<<"Not found on disk, generating."<<std::endl;
4590 //TimeTaker("emergeBlock() generate");
4592 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4595 If the block doesn't exist, generate the block.
4599 block = generateBlock(p, block, sector, changed_blocks,
4600 lighting_invalidated_blocks);
4603 if(lighting_expired)
4605 lighting_invalidated_blocks.insert(p, block);
4609 Initially update sunlight
4613 core::map<v3s16, bool> light_sources;
4614 bool black_air_left = false;
4615 bool bottom_invalid =
4616 block->propagateSunlight(light_sources, true,
4617 &black_air_left, true);
4619 // If sunlight didn't reach everywhere and part of block is
4620 // above ground, lighting has to be properly updated
4621 //if(black_air_left && some_part_underground)
4624 lighting_invalidated_blocks[block->getPos()] = block;
4629 lighting_invalidated_blocks[block->getPos()] = block;
4636 s16 ServerMap::findGroundLevel(v2s16 p2d)
4639 Uh, just do something random...
4641 // Find existing map from top to down
4644 v3s16 p(p2d.X, max, p2d.Y);
4645 for(; p.Y>min; p.Y--)
4647 MapNode n = getNodeNoEx(p);
4648 if(n.d != CONTENT_IGNORE)
4653 // If this node is not air, go to plan b
4654 if(getNodeNoEx(p).d != CONTENT_AIR)
4656 // Search existing walkable and return it
4657 for(; p.Y>min; p.Y--)
4659 MapNode n = getNodeNoEx(p);
4660 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4666 Plan B: Get from map generator perlin noise function
4668 double level = base_rock_level_2d(m_seed, p2d);
4672 void ServerMap::createDir(std::string path)
4674 if(fs::CreateDir(path) == false)
4676 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4677 <<"\""<<path<<"\""<<std::endl;
4678 throw BaseException("ServerMap failed to create directory");
4682 std::string ServerMap::getSectorSubDir(v2s16 pos)
4685 snprintf(cc, 9, "%.4x%.4x",
4686 (unsigned int)pos.X&0xffff,
4687 (unsigned int)pos.Y&0xffff);
4689 return std::string(cc);
4692 std::string ServerMap::getSectorDir(v2s16 pos)
4694 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4697 v2s16 ServerMap::getSectorPos(std::string dirname)
4699 if(dirname.size() != 8)
4700 throw InvalidFilenameException("Invalid sector directory name");
4702 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4704 throw InvalidFilenameException("Invalid sector directory name");
4705 v2s16 pos((s16)x, (s16)y);
4709 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4711 v2s16 p2d = getSectorPos(sectordir);
4713 if(blockfile.size() != 4){
4714 throw InvalidFilenameException("Invalid block filename");
4717 int r = sscanf(blockfile.c_str(), "%4x", &y);
4719 throw InvalidFilenameException("Invalid block filename");
4720 return v3s16(p2d.X, y, p2d.Y);
4723 void ServerMap::save(bool only_changed)
4725 DSTACK(__FUNCTION_NAME);
4726 if(m_map_saving_enabled == false)
4728 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4732 if(only_changed == false)
4733 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4739 u32 sector_meta_count = 0;
4740 u32 block_count = 0;
4743 JMutexAutoLock lock(m_sector_mutex);
4745 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4746 for(; i.atEnd() == false; i++)
4748 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4749 assert(sector->getId() == MAPSECTOR_SERVER);
4751 if(sector->differs_from_disk || only_changed == false)
4753 saveSectorMeta(sector);
4754 sector_meta_count++;
4756 core::list<MapBlock*> blocks;
4757 sector->getBlocks(blocks);
4758 core::list<MapBlock*>::Iterator j;
4759 for(j=blocks.begin(); j!=blocks.end(); j++)
4761 MapBlock *block = *j;
4762 if(block->getChangedFlag() || only_changed == false)
4767 /*dstream<<"ServerMap: Written block ("
4768 <<block->getPos().X<<","
4769 <<block->getPos().Y<<","
4770 <<block->getPos().Z<<")"
4779 Only print if something happened or saved whole map
4781 if(only_changed == false || sector_meta_count != 0
4782 || block_count != 0)
4784 dstream<<DTIME<<"ServerMap: Written: "
4785 <<sector_meta_count<<" sector metadata files, "
4786 <<block_count<<" block files"
4791 void ServerMap::loadAll()
4793 DSTACK(__FUNCTION_NAME);
4794 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4799 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4801 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4803 JMutexAutoLock lock(m_sector_mutex);
4806 s32 printed_counter = -100000;
4807 s32 count = list.size();
4809 std::vector<fs::DirListNode>::iterator i;
4810 for(i=list.begin(); i!=list.end(); i++)
4812 if(counter > printed_counter + 10)
4814 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4815 printed_counter = counter;
4819 MapSector *sector = NULL;
4821 // We want directories
4825 sector = loadSectorMeta(i->name);
4827 catch(InvalidFilenameException &e)
4829 // This catches unknown crap in directory
4832 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4833 (m_savedir+"/sectors/"+i->name);
4834 std::vector<fs::DirListNode>::iterator i2;
4835 for(i2=list2.begin(); i2!=list2.end(); i2++)
4841 loadBlock(i->name, i2->name, sector);
4843 catch(InvalidFilenameException &e)
4845 // This catches unknown crap in directory
4849 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4853 void ServerMap::saveMasterHeightmap()
4855 DSTACK(__FUNCTION_NAME);
4857 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4859 createDir(m_savedir);
4861 /*std::string fullpath = m_savedir + "/master_heightmap";
4862 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4863 if(o.good() == false)
4864 throw FileNotGoodException("Cannot open master heightmap");*/
4866 // Format used for writing
4867 //u8 version = SER_FMT_VER_HIGHEST;
4870 void ServerMap::loadMasterHeightmap()
4872 DSTACK(__FUNCTION_NAME);
4874 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4876 /*std::string fullpath = m_savedir + "/master_heightmap";
4877 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4878 if(is.good() == false)
4879 throw FileNotGoodException("Cannot open master heightmap");*/
4883 void ServerMap::saveMapMeta()
4885 DSTACK(__FUNCTION_NAME);
4887 dstream<<"INFO: ServerMap::saveMapMeta(): "
4888 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4891 createDir(m_savedir);
4893 std::string fullpath = m_savedir + "/map_meta.txt";
4894 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4895 if(os.good() == false)
4897 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4898 <<"could not open"<<fullpath<<std::endl;
4899 throw FileNotGoodException("Cannot open chunk metadata");
4903 params.setU64("seed", m_seed);
4904 params.setS32("chunksize", m_chunksize);
4906 params.writeLines(os);
4908 os<<"[end_of_params]\n";
4912 void ServerMap::loadMapMeta()
4914 DSTACK(__FUNCTION_NAME);
4916 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4919 std::string fullpath = m_savedir + "/map_meta.txt";
4920 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4921 if(is.good() == false)
4923 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4924 <<"could not open"<<fullpath<<std::endl;
4925 throw FileNotGoodException("Cannot open chunk metadata");
4933 throw SerializationError
4934 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4936 std::getline(is, line);
4937 std::string trimmedline = trim(line);
4938 if(trimmedline == "[end_of_params]")
4940 params.parseConfigLine(line);
4943 m_seed = params.getU64("seed");
4944 m_chunksize = params.getS32("chunksize");
4946 dstream<<"INFO: ServerMap::loadMapMeta(): "
4947 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4951 void ServerMap::saveChunkMeta()
4953 DSTACK(__FUNCTION_NAME);
4955 u32 count = m_chunks.size();
4957 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4958 <<count<<" chunks"<<std::endl;
4960 createDir(m_savedir);
4962 std::string fullpath = m_savedir + "/chunk_meta";
4963 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4964 if(os.good() == false)
4966 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4967 <<"could not open"<<fullpath<<std::endl;
4968 throw FileNotGoodException("Cannot open chunk metadata");
4974 os.write((char*)&version, 1);
4979 writeU32(buf, count);
4980 os.write((char*)buf, 4);
4982 for(core::map<v2s16, MapChunk*>::Iterator
4983 i = m_chunks.getIterator();
4984 i.atEnd()==false; i++)
4986 v2s16 p = i.getNode()->getKey();
4987 MapChunk *chunk = i.getNode()->getValue();
4990 os.write((char*)buf, 4);
4992 chunk->serialize(os, version);
4996 void ServerMap::loadChunkMeta()
4998 DSTACK(__FUNCTION_NAME);
5000 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5003 std::string fullpath = m_savedir + "/chunk_meta";
5004 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5005 if(is.good() == false)
5007 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5008 <<"could not open"<<fullpath<<std::endl;
5009 throw FileNotGoodException("Cannot open chunk metadata");
5015 is.read((char*)&version, 1);
5020 is.read((char*)buf, 4);
5021 u32 count = readU32(buf);
5023 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5024 <<count<<" chunks"<<std::endl;
5026 for(u32 i=0; i<count; i++)
5029 MapChunk *chunk = new MapChunk();
5031 is.read((char*)buf, 4);
5034 chunk->deSerialize(is, version);
5035 m_chunks.insert(p, chunk);
5039 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5041 DSTACK(__FUNCTION_NAME);
5042 // Format used for writing
5043 u8 version = SER_FMT_VER_HIGHEST;
5045 v2s16 pos = sector->getPos();
5046 createDir(m_savedir);
5047 createDir(m_savedir+"/sectors");
5048 std::string dir = getSectorDir(pos);
5051 std::string fullpath = dir + "/meta";
5052 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5053 if(o.good() == false)
5054 throw FileNotGoodException("Cannot open sector metafile");
5056 sector->serialize(o, version);
5058 sector->differs_from_disk = false;
5061 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5063 DSTACK(__FUNCTION_NAME);
5065 v2s16 p2d = getSectorPos(dirname);
5066 std::string dir = m_savedir + "/sectors/" + dirname;
5068 std::string fullpath = dir + "/meta";
5069 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5070 if(is.good() == false)
5071 throw FileNotGoodException("Cannot open sector metafile");
5073 ServerMapSector *sector = ServerMapSector::deSerialize
5074 (is, this, p2d, m_sectors);
5076 sector->differs_from_disk = false;
5081 bool ServerMap::loadSectorFull(v2s16 p2d)
5083 DSTACK(__FUNCTION_NAME);
5084 std::string sectorsubdir = getSectorSubDir(p2d);
5086 MapSector *sector = NULL;
5088 JMutexAutoLock lock(m_sector_mutex);
5091 sector = loadSectorMeta(sectorsubdir);
5093 catch(InvalidFilenameException &e)
5097 catch(FileNotGoodException &e)
5101 catch(std::exception &e)
5109 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5110 (m_savedir+"/sectors/"+sectorsubdir);
5111 std::vector<fs::DirListNode>::iterator i2;
5112 for(i2=list2.begin(); i2!=list2.end(); i2++)
5118 loadBlock(sectorsubdir, i2->name, sector);
5120 catch(InvalidFilenameException &e)
5122 // This catches unknown crap in directory
5128 void ServerMap::saveBlock(MapBlock *block)
5130 DSTACK(__FUNCTION_NAME);
5132 Dummy blocks are not written
5134 if(block->isDummy())
5136 /*v3s16 p = block->getPos();
5137 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5138 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5142 // Format used for writing
5143 u8 version = SER_FMT_VER_HIGHEST;
5145 v3s16 p3d = block->getPos();
5146 v2s16 p2d(p3d.X, p3d.Z);
5147 createDir(m_savedir);
5148 createDir(m_savedir+"/sectors");
5149 std::string dir = getSectorDir(p2d);
5152 // Block file is map/sectors/xxxxxxxx/xxxx
5154 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5155 std::string fullpath = dir + "/" + cc;
5156 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5157 if(o.good() == false)
5158 throw FileNotGoodException("Cannot open block data");
5161 [0] u8 serialization version
5164 o.write((char*)&version, 1);
5166 block->serialize(o, version);
5169 Versions up from 9 have block objects.
5173 block->serializeObjects(o, version);
5177 Versions up from 15 have static objects.
5181 block->m_static_objects.serialize(o);
5184 // We just wrote it to the disk
5185 block->resetChangedFlag();
5188 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5190 DSTACK(__FUNCTION_NAME);
5192 // Block file is map/sectors/xxxxxxxx/xxxx
5193 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5196 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5197 if(is.good() == false)
5198 throw FileNotGoodException("Cannot open block file");
5200 v3s16 p3d = getBlockPos(sectordir, blockfile);
5201 v2s16 p2d(p3d.X, p3d.Z);
5203 assert(sector->getPos() == p2d);
5205 u8 version = SER_FMT_VER_INVALID;
5206 is.read((char*)&version, 1);
5209 throw SerializationError("ServerMap::loadBlock(): Failed"
5210 " to read MapBlock version");
5212 /*u32 block_size = MapBlock::serializedLength(version);
5213 SharedBuffer<u8> data(block_size);
5214 is.read((char*)*data, block_size);*/
5216 // This will always return a sector because we're the server
5217 //MapSector *sector = emergeSector(p2d);
5219 MapBlock *block = NULL;
5220 bool created_new = false;
5222 block = sector->getBlockNoCreate(p3d.Y);
5224 catch(InvalidPositionException &e)
5226 block = sector->createBlankBlockNoInsert(p3d.Y);
5230 // deserialize block data
5231 block->deSerialize(is, version);
5234 Versions up from 9 have block objects.
5238 block->updateObjects(is, version, NULL, 0);
5242 Versions up from 15 have static objects.
5246 block->m_static_objects.deSerialize(is);
5250 sector->insertBlock(block);
5253 Convert old formats to new and save
5256 // Save old format blocks in new format
5257 if(version < SER_FMT_VER_HIGHEST)
5262 // We just loaded it from the disk, so it's up-to-date.
5263 block->resetChangedFlag();
5266 catch(SerializationError &e)
5268 dstream<<"WARNING: Invalid block data on disk "
5269 "(SerializationError). Ignoring. "
5270 "A new one will be generated."
5273 // TODO: Backup file; name is in fullpath.
5277 void ServerMap::PrintInfo(std::ostream &out)
5288 ClientMap::ClientMap(
5290 MapDrawControl &control,
5291 scene::ISceneNode* parent,
5292 scene::ISceneManager* mgr,
5296 scene::ISceneNode(parent, mgr, id),
5299 m_camera_position(0,0,0),
5300 m_camera_direction(0,0,1)
5302 m_camera_mutex.Init();
5303 assert(m_camera_mutex.IsInitialized());
5305 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5306 BS*1000000,BS*1000000,BS*1000000);
5309 ClientMap::~ClientMap()
5311 /*JMutexAutoLock lock(mesh_mutex);
5320 MapSector * ClientMap::emergeSector(v2s16 p2d)
5322 DSTACK(__FUNCTION_NAME);
5323 // Check that it doesn't exist already
5325 return getSectorNoGenerate(p2d);
5327 catch(InvalidPositionException &e)
5332 ClientMapSector *sector = new ClientMapSector(this, p2d);
5335 JMutexAutoLock lock(m_sector_mutex);
5336 m_sectors.insert(p2d, sector);
5342 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5344 DSTACK(__FUNCTION_NAME);
5345 ClientMapSector *sector = NULL;
5347 JMutexAutoLock lock(m_sector_mutex);
5349 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5353 sector = (ClientMapSector*)n->getValue();
5354 assert(sector->getId() == MAPSECTOR_CLIENT);
5358 sector = new ClientMapSector(this, p2d);
5360 JMutexAutoLock lock(m_sector_mutex);
5361 m_sectors.insert(p2d, sector);
5365 sector->deSerialize(is);
5368 void ClientMap::OnRegisterSceneNode()
5372 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5373 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5376 ISceneNode::OnRegisterSceneNode();
5379 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5381 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5382 DSTACK(__FUNCTION_NAME);
5384 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5387 Get time for measuring timeout.
5389 Measuring time is very useful for long delays when the
5390 machine is swapping a lot.
5392 int time1 = time(0);
5394 //u32 daynight_ratio = m_client->getDayNightRatio();
5396 m_camera_mutex.Lock();
5397 v3f camera_position = m_camera_position;
5398 v3f camera_direction = m_camera_direction;
5399 m_camera_mutex.Unlock();
5402 Get all blocks and draw all visible ones
5405 v3s16 cam_pos_nodes(
5406 camera_position.X / BS,
5407 camera_position.Y / BS,
5408 camera_position.Z / BS);
5410 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5412 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5413 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5415 // Take a fair amount as we will be dropping more out later
5417 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5418 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5419 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5421 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5422 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5423 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5425 u32 vertex_count = 0;
5427 // For limiting number of mesh updates per frame
5428 u32 mesh_update_count = 0;
5430 u32 blocks_would_have_drawn = 0;
5431 u32 blocks_drawn = 0;
5433 //NOTE: The sectors map should be locked but we're not doing it
5434 // because it'd cause too much delays
5436 int timecheck_counter = 0;
5437 core::map<v2s16, MapSector*>::Iterator si;
5438 si = m_sectors.getIterator();
5439 for(; si.atEnd() == false; si++)
5442 timecheck_counter++;
5443 if(timecheck_counter > 50)
5445 timecheck_counter = 0;
5446 int time2 = time(0);
5447 if(time2 > time1 + 4)
5449 dstream<<"ClientMap::renderMap(): "
5450 "Rendering takes ages, returning."
5457 MapSector *sector = si.getNode()->getValue();
5458 v2s16 sp = sector->getPos();
5460 if(m_control.range_all == false)
5462 if(sp.X < p_blocks_min.X
5463 || sp.X > p_blocks_max.X
5464 || sp.Y < p_blocks_min.Z
5465 || sp.Y > p_blocks_max.Z)
5469 core::list< MapBlock * > sectorblocks;
5470 sector->getBlocks(sectorblocks);
5476 core::list< MapBlock * >::Iterator i;
5477 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5479 MapBlock *block = *i;
5482 Compare block position to camera position, skip
5483 if not seen on display
5486 float range = 100000 * BS;
5487 if(m_control.range_all == false)
5488 range = m_control.wanted_range * BS;
5491 if(isBlockInSight(block->getPos(), camera_position,
5492 camera_direction, range, &d) == false)
5497 // This is ugly (spherical distance limit?)
5498 /*if(m_control.range_all == false &&
5499 d - 0.5*BS*MAP_BLOCKSIZE > range)
5504 Update expired mesh (used for day/night change)
5506 It doesn't work exactly like it should now with the
5507 tasked mesh update but whatever.
5510 bool mesh_expired = false;
5513 JMutexAutoLock lock(block->mesh_mutex);
5515 mesh_expired = block->getMeshExpired();
5517 // Mesh has not been expired and there is no mesh:
5518 // block has no content
5519 if(block->mesh == NULL && mesh_expired == false)
5523 f32 faraway = BS*50;
5524 //f32 faraway = m_control.wanted_range * BS;
5527 This has to be done with the mesh_mutex unlocked
5529 // Pretty random but this should work somewhat nicely
5530 if(mesh_expired && (
5531 (mesh_update_count < 3
5532 && (d < faraway || mesh_update_count < 2)
5535 (m_control.range_all && mesh_update_count < 20)
5538 /*if(mesh_expired && mesh_update_count < 6
5539 && (d < faraway || mesh_update_count < 3))*/
5541 mesh_update_count++;
5543 // Mesh has been expired: generate new mesh
5544 //block->updateMesh(daynight_ratio);
5545 m_client->addUpdateMeshTask(block->getPos());
5547 mesh_expired = false;
5552 Draw the faces of the block
5555 JMutexAutoLock lock(block->mesh_mutex);
5557 scene::SMesh *mesh = block->mesh;
5562 blocks_would_have_drawn++;
5563 if(blocks_drawn >= m_control.wanted_max_blocks
5564 && m_control.range_all == false
5565 && d > m_control.wanted_min_range * BS)
5569 u32 c = mesh->getMeshBufferCount();
5571 for(u32 i=0; i<c; i++)
5573 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5574 const video::SMaterial& material = buf->getMaterial();
5575 video::IMaterialRenderer* rnd =
5576 driver->getMaterialRenderer(material.MaterialType);
5577 bool transparent = (rnd && rnd->isTransparent());
5578 // Render transparent on transparent pass and likewise.
5579 if(transparent == is_transparent_pass)
5582 This *shouldn't* hurt too much because Irrlicht
5583 doesn't change opengl textures if the old
5584 material is set again.
5586 driver->setMaterial(buf->getMaterial());
5587 driver->drawMeshBuffer(buf);
5588 vertex_count += buf->getVertexCount();
5592 } // foreach sectorblocks
5595 m_control.blocks_drawn = blocks_drawn;
5596 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5598 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5599 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5602 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5603 core::map<v3s16, MapBlock*> *affected_blocks)
5605 bool changed = false;
5607 Add it to all blocks touching it
5610 v3s16(0,0,0), // this
5611 v3s16(0,0,1), // back
5612 v3s16(0,1,0), // top
5613 v3s16(1,0,0), // right
5614 v3s16(0,0,-1), // front
5615 v3s16(0,-1,0), // bottom
5616 v3s16(-1,0,0), // left
5618 for(u16 i=0; i<7; i++)
5620 v3s16 p2 = p + dirs[i];
5621 // Block position of neighbor (or requested) node
5622 v3s16 blockpos = getNodeBlockPos(p2);
5623 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5624 if(blockref == NULL)
5626 // Relative position of requested node
5627 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5628 if(blockref->setTempMod(relpos, mod))
5633 if(changed && affected_blocks!=NULL)
5635 for(u16 i=0; i<7; i++)
5637 v3s16 p2 = p + dirs[i];
5638 // Block position of neighbor (or requested) node
5639 v3s16 blockpos = getNodeBlockPos(p2);
5640 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5641 if(blockref == NULL)
5643 affected_blocks->insert(blockpos, blockref);
5649 bool ClientMap::clearTempMod(v3s16 p,
5650 core::map<v3s16, MapBlock*> *affected_blocks)
5652 bool changed = false;
5654 v3s16(0,0,0), // this
5655 v3s16(0,0,1), // back
5656 v3s16(0,1,0), // top
5657 v3s16(1,0,0), // right
5658 v3s16(0,0,-1), // front
5659 v3s16(0,-1,0), // bottom
5660 v3s16(-1,0,0), // left
5662 for(u16 i=0; i<7; i++)
5664 v3s16 p2 = p + dirs[i];
5665 // Block position of neighbor (or requested) node
5666 v3s16 blockpos = getNodeBlockPos(p2);
5667 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5668 if(blockref == NULL)
5670 // Relative position of requested node
5671 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5672 if(blockref->clearTempMod(relpos))
5677 if(changed && affected_blocks!=NULL)
5679 for(u16 i=0; i<7; i++)
5681 v3s16 p2 = p + dirs[i];
5682 // Block position of neighbor (or requested) node
5683 v3s16 blockpos = getNodeBlockPos(p2);
5684 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5685 if(blockref == NULL)
5687 affected_blocks->insert(blockpos, blockref);
5693 void ClientMap::expireMeshes(bool only_daynight_diffed)
5695 TimeTaker timer("expireMeshes()");
5697 core::map<v2s16, MapSector*>::Iterator si;
5698 si = m_sectors.getIterator();
5699 for(; si.atEnd() == false; si++)
5701 MapSector *sector = si.getNode()->getValue();
5703 core::list< MapBlock * > sectorblocks;
5704 sector->getBlocks(sectorblocks);
5706 core::list< MapBlock * >::Iterator i;
5707 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5709 MapBlock *block = *i;
5711 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5717 JMutexAutoLock lock(block->mesh_mutex);
5718 if(block->mesh != NULL)
5720 /*block->mesh->drop();
5721 block->mesh = NULL;*/
5722 block->setMeshExpired(true);
5729 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5731 assert(mapType() == MAPTYPE_CLIENT);
5734 v3s16 p = blockpos + v3s16(0,0,0);
5735 MapBlock *b = getBlockNoCreate(p);
5736 b->updateMesh(daynight_ratio);
5737 //b->setMeshExpired(true);
5739 catch(InvalidPositionException &e){}
5742 v3s16 p = blockpos + v3s16(-1,0,0);
5743 MapBlock *b = getBlockNoCreate(p);
5744 b->updateMesh(daynight_ratio);
5745 //b->setMeshExpired(true);
5747 catch(InvalidPositionException &e){}
5749 v3s16 p = blockpos + v3s16(0,-1,0);
5750 MapBlock *b = getBlockNoCreate(p);
5751 b->updateMesh(daynight_ratio);
5752 //b->setMeshExpired(true);
5754 catch(InvalidPositionException &e){}
5756 v3s16 p = blockpos + v3s16(0,0,-1);
5757 MapBlock *b = getBlockNoCreate(p);
5758 b->updateMesh(daynight_ratio);
5759 //b->setMeshExpired(true);
5761 catch(InvalidPositionException &e){}
5766 Update mesh of block in which the node is, and if the node is at the
5767 leading edge, update the appropriate leading blocks too.
5769 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5777 v3s16 blockposes[4];
5778 for(u32 i=0; i<4; i++)
5780 v3s16 np = nodepos + dirs[i];
5781 blockposes[i] = getNodeBlockPos(np);
5782 // Don't update mesh of block if it has been done already
5783 bool already_updated = false;
5784 for(u32 j=0; j<i; j++)
5786 if(blockposes[j] == blockposes[i])
5788 already_updated = true;
5795 MapBlock *b = getBlockNoCreate(blockposes[i]);
5796 b->updateMesh(daynight_ratio);
5801 void ClientMap::PrintInfo(std::ostream &out)
5812 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5817 MapVoxelManipulator::~MapVoxelManipulator()
5819 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5823 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5825 TimeTaker timer1("emerge", &emerge_time);
5827 // Units of these are MapBlocks
5828 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5829 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5831 VoxelArea block_area_nodes
5832 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5834 addArea(block_area_nodes);
5836 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5837 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5838 for(s32 x=p_min.X; x<=p_max.X; x++)
5841 core::map<v3s16, bool>::Node *n;
5842 n = m_loaded_blocks.find(p);
5846 bool block_data_inexistent = false;
5849 TimeTaker timer1("emerge load", &emerge_load_time);
5851 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5852 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5855 dstream<<std::endl;*/
5857 MapBlock *block = m_map->getBlockNoCreate(p);
5858 if(block->isDummy())
5859 block_data_inexistent = true;
5861 block->copyTo(*this);
5863 catch(InvalidPositionException &e)
5865 block_data_inexistent = true;
5868 if(block_data_inexistent)
5870 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5871 // Fill with VOXELFLAG_INEXISTENT
5872 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5873 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5875 s32 i = m_area.index(a.MinEdge.X,y,z);
5876 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5880 m_loaded_blocks.insert(p, !block_data_inexistent);
5883 //dstream<<"emerge done"<<std::endl;
5887 SUGG: Add an option to only update eg. water and air nodes.
5888 This will make it interfere less with important stuff if
5891 void MapVoxelManipulator::blitBack
5892 (core::map<v3s16, MapBlock*> & modified_blocks)
5894 if(m_area.getExtent() == v3s16(0,0,0))
5897 //TimeTaker timer1("blitBack");
5899 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5900 <<m_loaded_blocks.size()<<std::endl;*/
5903 Initialize block cache
5905 v3s16 blockpos_last;
5906 MapBlock *block = NULL;
5907 bool block_checked_in_modified = false;
5909 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5910 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5911 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5915 u8 f = m_flags[m_area.index(p)];
5916 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5919 MapNode &n = m_data[m_area.index(p)];
5921 v3s16 blockpos = getNodeBlockPos(p);
5926 if(block == NULL || blockpos != blockpos_last){
5927 block = m_map->getBlockNoCreate(blockpos);
5928 blockpos_last = blockpos;
5929 block_checked_in_modified = false;
5932 // Calculate relative position in block
5933 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5935 // Don't continue if nothing has changed here
5936 if(block->getNode(relpos) == n)
5939 //m_map->setNode(m_area.MinEdge + p, n);
5940 block->setNode(relpos, n);
5943 Make sure block is in modified_blocks
5945 if(block_checked_in_modified == false)
5947 modified_blocks[blockpos] = block;
5948 block_checked_in_modified = true;
5951 catch(InvalidPositionException &e)
5957 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5958 MapVoxelManipulator(map)
5962 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5966 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5968 // Just create the area so that it can be pointed to
5969 VoxelManipulator::emerge(a, caller_id);
5972 void ManualMapVoxelManipulator::initialEmerge(
5973 v3s16 blockpos_min, v3s16 blockpos_max)
5975 TimeTaker timer1("initialEmerge", &emerge_time);
5977 // Units of these are MapBlocks
5978 v3s16 p_min = blockpos_min;
5979 v3s16 p_max = blockpos_max;
5981 VoxelArea block_area_nodes
5982 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5984 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5987 dstream<<"initialEmerge: area: ";
5988 block_area_nodes.print(dstream);
5989 dstream<<" ("<<size_MB<<"MB)";
5993 addArea(block_area_nodes);
5995 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5996 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5997 for(s32 x=p_min.X; x<=p_max.X; x++)
6000 core::map<v3s16, bool>::Node *n;
6001 n = m_loaded_blocks.find(p);
6005 bool block_data_inexistent = false;
6008 TimeTaker timer1("emerge load", &emerge_load_time);
6010 MapBlock *block = m_map->getBlockNoCreate(p);
6011 if(block->isDummy())
6012 block_data_inexistent = true;
6014 block->copyTo(*this);
6016 catch(InvalidPositionException &e)
6018 block_data_inexistent = true;
6021 if(block_data_inexistent)
6024 Mark area inexistent
6026 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6027 // Fill with VOXELFLAG_INEXISTENT
6028 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6029 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6031 s32 i = m_area.index(a.MinEdge.X,y,z);
6032 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6036 m_loaded_blocks.insert(p, !block_data_inexistent);
6040 void ManualMapVoxelManipulator::blitBackAll(
6041 core::map<v3s16, MapBlock*> * modified_blocks)
6043 if(m_area.getExtent() == v3s16(0,0,0))
6047 Copy data of all blocks
6049 for(core::map<v3s16, bool>::Iterator
6050 i = m_loaded_blocks.getIterator();
6051 i.atEnd() == false; i++)
6053 bool existed = i.getNode()->getValue();
6054 if(existed == false)
6056 v3s16 p = i.getNode()->getKey();
6057 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6060 dstream<<"WARNING: "<<__FUNCTION_NAME
6061 <<": got NULL block "
6062 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6067 block->copyFrom(*this);
6070 modified_blocks->insert(p, block);