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;
2112 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2115 This is the main map generation method
2118 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2119 core::map<v3s16, MapBlock*> &changed_blocks,
2122 DSTACK(__FUNCTION_NAME);
2125 Don't generate if already fully generated
2129 MapChunk *chunk = getChunk(chunkpos);
2130 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2132 dstream<<"generateChunkRaw(): Chunk "
2133 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2134 <<" already generated"<<std::endl;
2139 dstream<<"generateChunkRaw(): Generating chunk "
2140 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2143 TimeTaker timer("generateChunkRaw()");
2145 // The distance how far into the neighbors the generator is allowed to go.
2146 s16 max_spread_amount_sectors = 2;
2147 assert(max_spread_amount_sectors <= m_chunksize);
2148 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2150 // Minimum amount of space left on sides for mud to fall in
2151 //s16 min_mud_fall_space = 2;
2153 // Maximum diameter of stone obstacles in X and Z
2154 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2155 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2157 s16 y_blocks_min = -4;
2158 s16 y_blocks_max = 3;
2159 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2160 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2161 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2163 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2164 s16 sectorpos_base_size = m_chunksize;
2166 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2167 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2168 v2s16 sectorpos_bigbase =
2169 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2170 s16 sectorpos_bigbase_size =
2171 sectorpos_base_size + 2 * max_spread_amount_sectors;
2173 v3s16 bigarea_blocks_min(
2174 sectorpos_bigbase.X,
2179 v3s16 bigarea_blocks_max(
2180 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2182 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2185 // Relative values to control amount of stuff in one chunk
2186 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2187 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2188 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2189 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2190 *(u32)h_blocks*MAP_BLOCKSIZE;
2193 The limiting edges of the lighting update, inclusive.
2195 s16 lighting_min_d = 0-max_spread_amount;
2196 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2199 Create the whole area of this and the neighboring chunks
2202 TimeTaker timer("generateChunkRaw() create area");
2204 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2205 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2207 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2208 ServerMapSector *sector = createSector(sectorpos);
2211 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2213 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2214 MapBlock *block = createBlock(blockpos);
2216 // Lighting won't be calculated
2217 //block->setLightingExpired(true);
2218 // Lighting will be calculated
2219 block->setLightingExpired(false);
2222 Block gets sunlight if this is true.
2224 This should be set to true when the top side of a block
2225 is completely exposed to the sky.
2227 Actually this doesn't matter now because the
2228 initial lighting is done here.
2230 block->setIsUnderground(y != y_blocks_max);
2236 Now we have a big empty area.
2238 Make a ManualMapVoxelManipulator that contains this and the
2242 ManualMapVoxelManipulator vmanip(this);
2243 // Add the area we just generated
2245 TimeTaker timer("generateChunkRaw() initialEmerge");
2246 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2250 vmanip.clearFlag(0xff);
2252 TimeTaker timer_generate("generateChunkRaw() generate");
2254 // Maximum height of the stone surface and obstacles.
2255 // This is used to disable dungeon generation from going too high.
2256 s16 stone_surface_max_y = 0;
2259 Generate general ground level to full area
2264 //TimeTaker timer1("ground level");
2266 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2267 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2270 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2273 Skip of already generated
2276 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2277 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2281 // Ground height at this point
2282 float surface_y_f = 0.0;
2284 // Use perlin noise for ground height
2285 surface_y_f = base_rock_level_2d(m_seed, p2d);
2287 /*// Experimental stuff
2289 float a = highlands_level_2d(m_seed, p2d);
2294 // Convert to integer
2295 s16 surface_y = (s16)surface_y_f;
2298 if(surface_y > stone_surface_max_y)
2299 stone_surface_max_y = surface_y;
2302 Fill ground with stone
2305 // Use fast index incrementing
2306 v3s16 em = vmanip.m_area.getExtent();
2307 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2308 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2310 vmanip.m_data[i].d = CONTENT_STONE;
2312 vmanip.m_area.add_y(em, i, 1);
2320 Randomize some parameters
2323 s32 stone_obstacle_count = 0;
2324 /*s32 stone_obstacle_count =
2325 rangelim((1.0+noise2d(m_seed+897,
2326 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2328 s16 stone_obstacle_max_height = 0;
2329 /*s16 stone_obstacle_max_height =
2330 rangelim((1.0+noise2d(m_seed+5902,
2331 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2334 Loop this part, it will make stuff look older and newer nicely
2336 //for(u32 i_age=0; i_age<1; i_age++)
2337 for(u32 i_age=0; i_age<2; i_age++)
2342 //TimeTaker timer1("stone obstacles");
2345 Add some random stone obstacles
2348 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2350 // Randomize max height so usually stuff will be quite low
2351 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2353 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2354 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2357 myrand_range(5, stone_obstacle_max_size),
2358 myrand_range(0, maxheight_randomized),
2359 myrand_range(5, stone_obstacle_max_size)
2362 // Don't make stupid small rectangle bumps
2367 myrand_range(1+ob_size.X/2+2,
2368 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2369 myrand_range(1+ob_size.Z/2+2,
2370 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2373 // Minimum space left on top of the obstacle
2374 s16 min_head_space = 12;
2376 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2377 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2379 // Node position in 2d
2380 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2382 // Find stone ground level
2383 // (ignore everything else than mud in already generated chunks)
2384 // and mud amount over the stone level
2388 v3s16 em = vmanip.m_area.getExtent();
2389 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2391 // Go to ground level
2392 for(y=y_nodes_max; y>=y_nodes_min; y--)
2394 MapNode *n = &vmanip.m_data[i];
2395 /*if(content_walkable(n.d)
2396 && n.d != CONTENT_MUD
2397 && n.d != CONTENT_GRASS)
2399 if(n->d == CONTENT_STONE)
2402 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2406 Change to mud because otherwise we might
2407 be throwing mud on grass at the next
2413 vmanip.m_area.add_y(em, i, -1);
2415 if(y >= y_nodes_min)
2418 surface_y = y_nodes_min;
2426 v3s16 em = vmanip.m_area.getExtent();
2427 s16 y_start = surface_y+1;
2428 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2432 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2434 MapNode &n = vmanip.m_data[i];
2435 n.d = CONTENT_STONE;
2437 if(y > stone_surface_max_y)
2438 stone_surface_max_y = y;
2441 if(count >= ob_size.Y)
2444 vmanip.m_area.add_y(em, i, 1);
2448 for(; y<=y_nodes_max - min_head_space; y++)
2450 MapNode &n = vmanip.m_data[i];
2453 if(count >= mud_amount)
2456 vmanip.m_area.add_y(em, i, 1);
2466 //TimeTaker timer1("dungeons");
2471 u32 dungeons_count = relative_volume / 600000;
2472 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2473 if(stone_surface_max_y < WATER_LEVEL)
2475 /*u32 dungeons_count = 0;
2476 u32 bruises_count = 0;*/
2477 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2479 s16 min_tunnel_diameter = 2;
2480 s16 max_tunnel_diameter = 6;
2481 u16 tunnel_routepoints = 25;
2483 bool bruise_surface = (jj < bruises_count);
2487 min_tunnel_diameter = 5;
2488 max_tunnel_diameter = myrand_range(10, 20);
2489 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2490 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2492 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2493 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2495 tunnel_routepoints = 5;
2498 // Allowed route area size in nodes
2500 sectorpos_base_size*MAP_BLOCKSIZE,
2501 h_blocks*MAP_BLOCKSIZE,
2502 sectorpos_base_size*MAP_BLOCKSIZE
2505 // Area starting point in nodes
2507 sectorpos_base.X*MAP_BLOCKSIZE,
2508 y_blocks_min*MAP_BLOCKSIZE,
2509 sectorpos_base.Y*MAP_BLOCKSIZE
2513 //(this should be more than the maximum radius of the tunnel)
2514 //s16 insure = 5; // Didn't work with max_d = 20
2516 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2517 ar += v3s16(1,0,1) * more * 2;
2518 of -= v3s16(1,0,1) * more;
2520 s16 route_y_min = 0;
2521 // Allow half a diameter + 7 over stone surface
2522 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2524 /*// If dungeons, don't go through surface too often
2525 if(bruise_surface == false)
2526 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2528 // Limit maximum to area
2529 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2533 /*// Minimum is at y=0
2534 route_y_min = -of.Y - 0;*/
2535 // Minimum is at y=max_tunnel_diameter/4
2536 //route_y_min = -of.Y + max_tunnel_diameter/4;
2537 //s16 min = -of.Y + max_tunnel_diameter/4;
2538 s16 min = -of.Y + 0;
2539 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2540 route_y_min = rangelim(route_y_min, 0, route_y_max);
2543 /*dstream<<"route_y_min = "<<route_y_min
2544 <<", route_y_max = "<<route_y_max<<std::endl;*/
2546 s16 route_start_y_min = route_y_min;
2547 s16 route_start_y_max = route_y_max;
2549 // Start every 2nd dungeon from surface
2550 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2552 if(coming_from_surface)
2554 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2557 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2558 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2560 // Randomize starting position
2562 (float)(myrand()%ar.X)+0.5,
2563 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2564 (float)(myrand()%ar.Z)+0.5
2567 MapNode airnode(CONTENT_AIR);
2570 Generate some tunnel starting from orp
2573 for(u16 j=0; j<tunnel_routepoints; j++)
2576 s16 min_d = min_tunnel_diameter;
2577 s16 max_d = max_tunnel_diameter;
2578 s16 rs = myrand_range(min_d, max_d);
2583 maxlen = v3s16(rs*7,rs*7,rs*7);
2587 maxlen = v3s16(15, myrand_range(1, 20), 15);
2592 if(coming_from_surface && j < 3)
2595 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2596 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2597 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2603 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2604 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2605 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2612 else if(rp.X >= ar.X)
2614 if(rp.Y < route_y_min)
2616 else if(rp.Y >= route_y_max)
2617 rp.Y = route_y_max-1;
2620 else if(rp.Z >= ar.Z)
2624 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2626 v3f fp = orp + vec * f;
2627 v3s16 cp(fp.X, fp.Y, fp.Z);
2630 s16 d1 = d0 + rs - 1;
2631 for(s16 z0=d0; z0<=d1; z0++)
2633 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2634 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2635 for(s16 x0=-si; x0<=si-1; x0++)
2637 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2638 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2639 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2640 //s16 si2 = rs - abs(x0);
2641 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2647 /*if(isInArea(p, ar) == false)
2649 // Check only height
2650 if(y < 0 || y >= ar.Y)
2654 //assert(vmanip.m_area.contains(p));
2655 if(vmanip.m_area.contains(p) == false)
2657 dstream<<"WARNING: "<<__FUNCTION_NAME
2658 <<":"<<__LINE__<<": "
2659 <<"point not in area"
2664 // Just set it to air, it will be changed to
2666 u32 i = vmanip.m_area.index(p);
2667 vmanip.m_data[i] = airnode;
2669 if(bruise_surface == false)
2672 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2687 //TimeTaker timer1("ore veins");
2692 for(u32 jj=0; jj<relative_volume/1000; jj++)
2694 s16 max_vein_diameter = 3;
2696 // Allowed route area size in nodes
2698 sectorpos_base_size*MAP_BLOCKSIZE,
2699 h_blocks*MAP_BLOCKSIZE,
2700 sectorpos_base_size*MAP_BLOCKSIZE
2703 // Area starting point in nodes
2705 sectorpos_base.X*MAP_BLOCKSIZE,
2706 y_blocks_min*MAP_BLOCKSIZE,
2707 sectorpos_base.Y*MAP_BLOCKSIZE
2711 //(this should be more than the maximum radius of the tunnel)
2713 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2714 ar += v3s16(1,0,1) * more * 2;
2715 of -= v3s16(1,0,1) * more;
2717 // Randomize starting position
2719 (float)(myrand()%ar.X)+0.5,
2720 (float)(myrand()%ar.Y)+0.5,
2721 (float)(myrand()%ar.Z)+0.5
2724 // Randomize mineral
2727 mineral = MINERAL_COAL;
2729 mineral = MINERAL_IRON;
2732 Generate some vein starting from orp
2735 for(u16 j=0; j<2; j++)
2738 (float)(myrand()%ar.X)+0.5,
2739 (float)(myrand()%ar.Y)+0.5,
2740 (float)(myrand()%ar.Z)+0.5
2742 v3f vec = rp - orp;*/
2744 v3s16 maxlen(5, 5, 5);
2746 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2747 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2748 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2753 else if(rp.X >= ar.X)
2757 else if(rp.Y >= ar.Y)
2761 else if(rp.Z >= ar.Z)
2767 s16 max_d = max_vein_diameter;
2768 s16 rs = myrand_range(min_d, max_d);
2770 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2772 v3f fp = orp + vec * f;
2773 v3s16 cp(fp.X, fp.Y, fp.Z);
2775 s16 d1 = d0 + rs - 1;
2776 for(s16 z0=d0; z0<=d1; z0++)
2778 s16 si = rs - abs(z0);
2779 for(s16 x0=-si; x0<=si-1; x0++)
2781 s16 si2 = rs - abs(x0);
2782 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2784 // Don't put mineral to every place
2792 /*if(isInArea(p, ar) == false)
2794 // Check only height
2795 if(y < 0 || y >= ar.Y)
2799 assert(vmanip.m_area.contains(p));
2801 // Just set it to air, it will be changed to
2803 u32 i = vmanip.m_area.index(p);
2804 MapNode *n = &vmanip.m_data[i];
2805 if(n->d == CONTENT_STONE)
2820 //TimeTaker timer1("add mud");
2823 Add mud to the central chunk
2826 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2827 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2829 // Node position in 2d
2830 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2832 // Randomize mud amount
2833 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2834 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2835 m_seed+1, 3, 0.55));
2837 // Find ground level
2838 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2841 If topmost node is grass, change it to mud.
2842 It might be if it was flown to there from a neighboring
2843 chunk and then converted.
2846 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2847 MapNode *n = &vmanip.m_data[i];
2848 if(n->d == CONTENT_GRASS)
2857 v3s16 em = vmanip.m_area.getExtent();
2858 s16 y_start = surface_y+1;
2859 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2860 for(s16 y=y_start; y<=y_nodes_max; y++)
2862 if(mudcount >= mud_add_amount)
2865 MapNode &n = vmanip.m_data[i];
2869 vmanip.m_area.add_y(em, i, 1);
2878 TimeTaker timer1("flow mud");
2881 Flow mud away from steep edges
2884 // Limit area by 1 because mud is flown into neighbors.
2885 s16 mudflow_minpos = 0-max_spread_amount+1;
2886 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2888 // Iterate a few times
2889 for(s16 k=0; k<3; k++)
2892 for(s16 x=mudflow_minpos;
2895 for(s16 z=mudflow_minpos;
2899 // Invert coordinates every 2nd iteration
2902 x = mudflow_maxpos - (x-mudflow_minpos);
2903 z = mudflow_maxpos - (z-mudflow_minpos);
2906 // Node position in 2d
2907 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2909 v3s16 em = vmanip.m_area.getExtent();
2910 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2917 for(; y>=y_nodes_min; y--)
2919 n = &vmanip.m_data[i];
2920 //if(content_walkable(n->d))
2922 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2925 vmanip.m_area.add_y(em, i, -1);
2928 // Stop if out of area
2929 //if(vmanip.m_area.contains(i) == false)
2933 /*// If not mud, do nothing to it
2934 MapNode *n = &vmanip.m_data[i];
2935 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2939 Don't flow it if the stuff under it is not mud
2943 vmanip.m_area.add_y(em, i2, -1);
2944 // Cancel if out of area
2945 if(vmanip.m_area.contains(i2) == false)
2947 MapNode *n2 = &vmanip.m_data[i2];
2948 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2952 // Make it exactly mud
2955 /*s16 recurse_count = 0;
2959 v3s16(0,0,1), // back
2960 v3s16(1,0,0), // right
2961 v3s16(0,0,-1), // front
2962 v3s16(-1,0,0), // left
2965 // Theck that upper is air or doesn't exist.
2966 // Cancel dropping if upper keeps it in place
2968 vmanip.m_area.add_y(em, i3, 1);
2969 if(vmanip.m_area.contains(i3) == true
2970 && content_walkable(vmanip.m_data[i3].d) == true)
2977 for(u32 di=0; di<4; di++)
2979 v3s16 dirp = dirs4[di];
2982 vmanip.m_area.add_p(em, i2, dirp);
2983 // Fail if out of area
2984 if(vmanip.m_area.contains(i2) == false)
2986 // Check that side is air
2987 MapNode *n2 = &vmanip.m_data[i2];
2988 if(content_walkable(n2->d))
2990 // Check that under side is air
2991 vmanip.m_area.add_y(em, i2, -1);
2992 if(vmanip.m_area.contains(i2) == false)
2994 n2 = &vmanip.m_data[i2];
2995 if(content_walkable(n2->d))
2997 /*// Check that under that is air (need a drop of 2)
2998 vmanip.m_area.add_y(em, i2, -1);
2999 if(vmanip.m_area.contains(i2) == false)
3001 n2 = &vmanip.m_data[i2];
3002 if(content_walkable(n2->d))
3004 // Loop further down until not air
3006 vmanip.m_area.add_y(em, i2, -1);
3007 // Fail if out of area
3008 if(vmanip.m_area.contains(i2) == false)
3010 n2 = &vmanip.m_data[i2];
3011 }while(content_walkable(n2->d) == false);
3012 // Loop one up so that we're in air
3013 vmanip.m_area.add_y(em, i2, 1);
3014 n2 = &vmanip.m_data[i2];
3016 // Move mud to new place
3018 // Set old place to be air
3019 *n = MapNode(CONTENT_AIR);
3032 //TimeTaker timer1("add water");
3035 Add water to the central chunk (and a bit more)
3038 for(s16 x=0-max_spread_amount;
3039 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3041 for(s16 z=0-max_spread_amount;
3042 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3045 // Node position in 2d
3046 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3048 // Find ground level
3049 //s16 surface_y = find_ground_level(vmanip, p2d);
3052 If ground level is over water level, skip.
3053 NOTE: This leaves caves near water without water,
3054 which looks especially crappy when the nearby water
3055 won't start flowing either for some reason
3057 /*if(surface_y > WATER_LEVEL)
3064 v3s16 em = vmanip.m_area.getExtent();
3065 u8 light = LIGHT_MAX;
3066 // Start at global water surface level
3067 s16 y_start = WATER_LEVEL;
3068 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3069 MapNode *n = &vmanip.m_data[i];
3071 /*// Add first one to transforming liquid queue, if water
3072 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3074 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3075 m_transforming_liquid.push_back(p);
3078 for(s16 y=y_start; y>=y_nodes_min; y--)
3080 n = &vmanip.m_data[i];
3082 // Stop when there is no water and no air
3083 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3084 && n->d != CONTENT_WATER)
3086 /*// Add bottom one to transforming liquid queue
3087 vmanip.m_area.add_y(em, i, 1);
3088 n = &vmanip.m_data[i];
3089 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3091 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3092 m_transforming_liquid.push_back(p);
3098 // Make water only not in dungeons
3099 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3101 n->d = CONTENT_WATERSOURCE;
3102 //n->setLight(LIGHTBANK_DAY, light);
3104 // Add to transforming liquid queue (in case it'd
3106 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3107 m_transforming_liquid.push_back(p);
3111 vmanip.m_area.add_y(em, i, -1);
3124 //TimeTaker timer1("convert mud to sand");
3130 //s16 mud_add_amount = myrand_range(2, 4);
3131 //s16 mud_add_amount = 0;
3133 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3134 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3135 for(s16 x=0-max_spread_amount+1;
3136 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3138 for(s16 z=0-max_spread_amount+1;
3139 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3142 // Node position in 2d
3143 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3145 // Determine whether to have sand here
3146 double sandnoise = noise2d_perlin(
3147 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3148 m_seed+59420, 3, 0.50);
3150 bool have_sand = (sandnoise > -0.15);
3152 if(have_sand == false)
3155 // Find ground level
3156 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3158 if(surface_y > WATER_LEVEL + 2)
3162 v3s16 em = vmanip.m_area.getExtent();
3163 s16 y_start = surface_y;
3164 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3165 u32 not_sand_counter = 0;
3166 for(s16 y=y_start; y>=y_nodes_min; y--)
3168 MapNode *n = &vmanip.m_data[i];
3169 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3171 n->d = CONTENT_SAND;
3176 if(not_sand_counter > 3)
3180 vmanip.m_area.add_y(em, i, -1);
3189 //TimeTaker timer1("generate trees");
3195 // Divide area into parts
3197 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3198 double area = sidelen * sidelen;
3199 for(s16 x0=0; x0<div; x0++)
3200 for(s16 z0=0; z0<div; z0++)
3202 // Center position of part of division
3204 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3205 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3207 // Minimum edge of part of division
3209 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3210 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3212 // Maximum edge of part of division
3214 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3215 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3218 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3219 // Put trees in random places on part of division
3220 for(u32 i=0; i<tree_count; i++)
3222 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3223 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3224 s16 y = find_ground_level(vmanip, v2s16(x,z));
3225 // Don't make a tree under water level
3228 // Don't make a tree so high that it doesn't fit
3229 if(y > y_nodes_max - 6)
3233 Trees grow only on mud and grass
3236 u32 i = vmanip.m_area.index(v3s16(p));
3237 MapNode *n = &vmanip.m_data[i];
3238 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3243 make_tree(vmanip, p);
3246 /*u32 tree_max = relative_area / 60;
3247 //u32 count = myrand_range(0, tree_max);
3248 for(u32 i=0; i<count; i++)
3250 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3251 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3252 x += sectorpos_base.X*MAP_BLOCKSIZE;
3253 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3254 s16 y = find_ground_level(vmanip, v2s16(x,z));
3255 // Don't make a tree under water level
3260 make_tree(vmanip, p);
3268 //TimeTaker timer1("grow grass");
3274 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3275 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3276 for(s16 x=0-max_spread_amount;
3277 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3279 for(s16 z=0-max_spread_amount;
3280 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3283 // Node position in 2d
3284 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3287 Find the lowest surface to which enough light ends up
3290 Basically just wait until not air and not leaves.
3294 v3s16 em = vmanip.m_area.getExtent();
3295 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3297 // Go to ground level
3298 for(y=y_nodes_max; y>=y_nodes_min; y--)
3300 MapNode &n = vmanip.m_data[i];
3301 if(n.d != CONTENT_AIR
3302 && n.d != CONTENT_LEAVES)
3304 vmanip.m_area.add_y(em, i, -1);
3306 if(y >= y_nodes_min)
3309 surface_y = y_nodes_min;
3312 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3313 MapNode *n = &vmanip.m_data[i];
3314 if(n->d == CONTENT_MUD)
3315 n->d = CONTENT_GRASS;
3321 Initial lighting (sunlight)
3324 core::map<v3s16, bool> light_sources;
3327 // 750ms @cs=8, can't optimize more
3328 TimeTaker timer1("initial lighting");
3332 Go through the edges and add all nodes that have light to light_sources
3336 for(s16 i=0; i<4; i++)
3338 for(s16 j=lighting_min_d;
3345 if(i == 0 || i == 1)
3347 x = (i==0) ? lighting_min_d : lighting_max_d;
3356 z = (i==0) ? lighting_min_d : lighting_max_d;
3363 // Node position in 2d
3364 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3367 v3s16 em = vmanip.m_area.getExtent();
3368 s16 y_start = y_nodes_max;
3369 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3370 for(s16 y=y_start; y>=y_nodes_min; y--)
3372 MapNode *n = &vmanip.m_data[i];
3373 if(n->getLight(LIGHTBANK_DAY) != 0)
3375 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3377 //NOTE: This is broken, at least the index has to
3386 Go through the edges and apply sunlight to them, not caring
3391 for(s16 i=0; i<4; i++)
3393 for(s16 j=lighting_min_d;
3400 if(i == 0 || i == 1)
3402 x = (i==0) ? lighting_min_d : lighting_max_d;
3411 z = (i==0) ? lighting_min_d : lighting_max_d;
3418 // Node position in 2d
3419 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3421 // Loop from top to down
3423 u8 light = LIGHT_SUN;
3424 v3s16 em = vmanip.m_area.getExtent();
3425 s16 y_start = y_nodes_max;
3426 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3427 for(s16 y=y_start; y>=y_nodes_min; y--)
3429 MapNode *n = &vmanip.m_data[i];
3430 if(light_propagates_content(n->d) == false)
3434 else if(light != LIGHT_SUN
3435 || sunlight_propagates_content(n->d) == false)
3441 n->setLight(LIGHTBANK_DAY, light);
3442 n->setLight(LIGHTBANK_NIGHT, 0);
3446 // Insert light source
3447 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3450 // Increment index by y
3451 vmanip.m_area.add_y(em, i, -1);
3457 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3458 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3459 /*for(s16 x=0-max_spread_amount+1;
3460 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3462 for(s16 z=0-max_spread_amount+1;
3463 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3467 This has to be 1 smaller than the actual area, because
3468 neighboring nodes are checked.
3470 for(s16 x=lighting_min_d+1;
3471 x<=lighting_max_d-1;
3473 for(s16 z=lighting_min_d+1;
3474 z<=lighting_max_d-1;
3477 // Node position in 2d
3478 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3481 Apply initial sunlight
3484 u8 light = LIGHT_SUN;
3485 bool add_to_sources = false;
3486 v3s16 em = vmanip.m_area.getExtent();
3487 s16 y_start = y_nodes_max;
3488 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3489 for(s16 y=y_start; y>=y_nodes_min; y--)
3491 MapNode *n = &vmanip.m_data[i];
3493 if(light_propagates_content(n->d) == false)
3497 else if(light != LIGHT_SUN
3498 || sunlight_propagates_content(n->d) == false)
3504 // This doesn't take much time
3505 if(add_to_sources == false)
3508 Check sides. If side is not air or water, start
3509 adding to light_sources.
3512 v3s16(0,0,1), // back
3513 v3s16(1,0,0), // right
3514 v3s16(0,0,-1), // front
3515 v3s16(-1,0,0), // left
3517 for(u32 di=0; di<4; di++)
3519 v3s16 dirp = dirs4[di];
3521 vmanip.m_area.add_p(em, i2, dirp);
3522 MapNode *n2 = &vmanip.m_data[i2];
3524 n2->d != CONTENT_AIR
3525 && n2->d != CONTENT_WATERSOURCE
3526 && n2->d != CONTENT_WATER
3528 add_to_sources = true;
3534 n->setLight(LIGHTBANK_DAY, light);
3535 n->setLight(LIGHTBANK_NIGHT, 0);
3537 // This doesn't take much time
3538 if(light != 0 && add_to_sources)
3540 // Insert light source
3541 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3544 // Increment index by y
3545 vmanip.m_area.add_y(em, i, -1);
3552 for(s16 x=lighting_min_d+1;
3553 x<=lighting_max_d-1;
3555 for(s16 z=lighting_min_d+1;
3556 z<=lighting_max_d-1;
3559 // Node position in 2d
3560 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3563 Apply initial sunlight
3566 u8 light = LIGHT_SUN;
3567 v3s16 em = vmanip.m_area.getExtent();
3568 s16 y_start = y_nodes_max;
3569 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3570 for(s16 y=y_start; y>=y_nodes_min; y--)
3572 MapNode *n = &vmanip.m_data[i];
3574 if(light_propagates_content(n->d) == false)
3578 else if(light != LIGHT_SUN
3579 || sunlight_propagates_content(n->d) == false)
3585 n->setLight(LIGHTBANK_DAY, light);
3586 n->setLight(LIGHTBANK_NIGHT, 0);
3588 // This doesn't take much time
3591 // Insert light source
3592 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3595 // Increment index by y
3596 vmanip.m_area.add_y(em, i, -1);
3604 // Spread light around
3606 TimeTaker timer("generateChunkRaw() spreadLight");
3607 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3614 timer_generate.stop();
3617 Blit generated stuff to map
3621 //TimeTaker timer("generateChunkRaw() blitBackAll");
3622 vmanip.blitBackAll(&changed_blocks);
3626 Update day/night difference cache of the MapBlocks
3629 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3630 i.atEnd() == false; i++)
3632 MapBlock *block = i.getNode()->getValue();
3633 block->updateDayNightDiff();
3639 Create chunk metadata
3642 for(s16 x=-1; x<=1; x++)
3643 for(s16 y=-1; y<=1; y++)
3645 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3646 // Add chunk meta information
3647 MapChunk *chunk = getChunk(chunkpos0);
3650 chunk = new MapChunk();
3651 m_chunks.insert(chunkpos0, chunk);
3653 //chunk->setIsVolatile(true);
3654 if(chunk->getGenLevel() > GENERATED_PARTLY)
3655 chunk->setGenLevel(GENERATED_PARTLY);
3659 Set central chunk non-volatile
3661 MapChunk *chunk = getChunk(chunkpos);
3664 //chunk->setIsVolatile(false);
3665 chunk->setGenLevel(GENERATED_FULLY);
3668 Save changed parts of map
3673 Return central chunk (which was requested)
3678 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3679 core::map<v3s16, MapBlock*> &changed_blocks)
3681 dstream<<"generateChunk(): Generating chunk "
3682 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3685 /*for(s16 x=-1; x<=1; x++)
3686 for(s16 y=-1; y<=1; y++)*/
3687 for(s16 x=-0; x<=0; x++)
3688 for(s16 y=-0; y<=0; y++)
3690 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3691 MapChunk *chunk = getChunk(chunkpos0);
3692 // Skip if already generated
3693 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3695 generateChunkRaw(chunkpos0, changed_blocks);
3698 assert(chunkNonVolatile(chunkpos1));
3700 MapChunk *chunk = getChunk(chunkpos1);
3704 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3706 DSTACK("%s: p2d=(%d,%d)",
3711 Check if it exists already in memory
3713 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3718 Try to load it from disk (with blocks)
3720 if(loadSectorFull(p2d) == true)
3722 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3725 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3726 throw InvalidPositionException("");
3732 Do not create over-limit
3734 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3735 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3736 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3737 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3738 throw InvalidPositionException("createSector(): pos. over limit");
3741 Generate blank sector
3744 sector = new ServerMapSector(this, p2d);
3746 // Sector position on map in nodes
3747 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3752 m_sectors.insert(p2d, sector);
3757 MapSector * ServerMap::emergeSector(v2s16 p2d,
3758 core::map<v3s16, MapBlock*> &changed_blocks)
3760 DSTACK("%s: p2d=(%d,%d)",
3767 v2s16 chunkpos = sector_to_chunk(p2d);
3768 /*bool chunk_nonvolatile = false;
3769 MapChunk *chunk = getChunk(chunkpos);
3770 if(chunk && chunk->getIsVolatile() == false)
3771 chunk_nonvolatile = true;*/
3772 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3775 If chunk is not fully generated, generate chunk
3777 if(chunk_nonvolatile == false)
3779 // Generate chunk and neighbors
3780 generateChunk(chunkpos, changed_blocks);
3784 Return sector if it exists now
3786 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3791 Try to load it from disk
3793 if(loadSectorFull(p2d) == true)
3795 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3798 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3799 throw InvalidPositionException("");
3805 generateChunk should have generated the sector
3809 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3810 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3814 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3816 return createSector(p2d);
3821 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3824 generateChunkRaw(chunkpos, changed_blocks, true);
3827 Return sector if it exists now
3829 sector = getSectorNoGenerateNoEx(p2d);
3833 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3841 //return generateSector();
3845 NOTE: This is not used for main map generation, only for blocks
3846 that are very high or low
3848 MapBlock * ServerMap::generateBlock(
3850 MapBlock *original_dummy,
3851 ServerMapSector *sector,
3852 core::map<v3s16, MapBlock*> &changed_blocks,
3853 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3856 DSTACK("%s: p=(%d,%d,%d)",
3860 /*dstream<<"generateBlock(): "
3861 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3864 MapBlock *block = original_dummy;
3866 v2s16 p2d(p.X, p.Z);
3868 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3871 Do not generate over-limit
3873 if(blockpos_over_limit(p))
3875 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3876 throw InvalidPositionException("generateBlock(): pos. over limit");
3880 If block doesn't exist, create one.
3881 If it exists, it is a dummy. In that case unDummify() it.
3883 NOTE: This already sets the map as the parent of the block
3887 block = sector->createBlankBlockNoInsert(block_y);
3891 // Remove the block so that nobody can get a half-generated one.
3892 sector->removeBlock(block);
3893 // Allocate the block to contain the generated data
3897 u8 water_material = CONTENT_WATERSOURCE;
3899 s32 lowest_ground_y = 32767;
3900 s32 highest_ground_y = -32768;
3902 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3903 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3905 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3907 //s16 surface_y = 0;
3909 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3910 + AVERAGE_MUD_AMOUNT;
3912 if(surface_y < lowest_ground_y)
3913 lowest_ground_y = surface_y;
3914 if(surface_y > highest_ground_y)
3915 highest_ground_y = surface_y;
3917 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3919 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3921 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3926 NOTE: If there are some man-made structures above the
3927 newly created block, they won't be taken into account.
3929 if(real_y > surface_y)
3930 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3936 // If node is over heightmap y, it's air or water
3937 if(real_y > surface_y)
3939 // If under water level, it's water
3940 if(real_y < WATER_LEVEL)
3942 n.d = water_material;
3943 n.setLight(LIGHTBANK_DAY,
3944 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3946 Add to transforming liquid queue (in case it'd
3949 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3950 m_transforming_liquid.push_back(real_pos);
3956 // Else it's ground or dungeons (air)
3959 // If it's surface_depth under ground, it's stone
3960 if(real_y <= surface_y - surface_depth)
3962 n.d = CONTENT_STONE;
3966 // It is mud if it is under the first ground
3967 // level or under water
3968 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3974 n.d = CONTENT_GRASS;
3977 //n.d = CONTENT_MUD;
3979 /*// If under water level, it's mud
3980 if(real_y < WATER_LEVEL)
3982 // Only the topmost node is grass
3983 else if(real_y <= surface_y - 1)
3986 n.d = CONTENT_GRASS;*/
3990 block->setNode(v3s16(x0,y0,z0), n);
3995 Calculate some helper variables
3998 // Completely underground if the highest part of block is under lowest
4000 // This has to be very sure; it's probably one too strict now but
4001 // that's just better.
4002 bool completely_underground =
4003 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4005 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4007 bool mostly_underwater_surface = false;
4008 if(highest_ground_y < WATER_LEVEL
4009 && some_part_underground && !completely_underground)
4010 mostly_underwater_surface = true;
4013 Get local attributes
4016 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4018 float caves_amount = 0.5;
4023 NOTE: BEWARE: Too big amount of attribute points slows verything
4025 1 interpolation from 5000 points takes 2-3ms.
4027 //TimeTaker timer("generateBlock() local attribute retrieval");
4028 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4029 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4030 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4034 //dstream<<"generateBlock(): Done"<<std::endl;
4040 // Initialize temporary table
4041 const s32 ued = MAP_BLOCKSIZE;
4042 bool underground_emptiness[ued*ued*ued];
4043 for(s32 i=0; i<ued*ued*ued; i++)
4045 underground_emptiness[i] = 0;
4052 Initialize orp and ors. Try to find if some neighboring
4053 MapBlock has a tunnel ended in its side
4057 (float)(myrand()%ued)+0.5,
4058 (float)(myrand()%ued)+0.5,
4059 (float)(myrand()%ued)+0.5
4062 bool found_existing = false;
4068 for(s16 y=0; y<ued; y++)
4069 for(s16 x=0; x<ued; x++)
4071 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4072 if(getNode(ap).d == CONTENT_AIR)
4074 orp = v3f(x+1,y+1,0);
4075 found_existing = true;
4076 goto continue_generating;
4080 catch(InvalidPositionException &e){}
4086 for(s16 y=0; y<ued; y++)
4087 for(s16 x=0; x<ued; x++)
4089 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4090 if(getNode(ap).d == CONTENT_AIR)
4092 orp = v3f(x+1,y+1,ued-1);
4093 found_existing = true;
4094 goto continue_generating;
4098 catch(InvalidPositionException &e){}
4104 for(s16 y=0; y<ued; y++)
4105 for(s16 z=0; z<ued; z++)
4107 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4108 if(getNode(ap).d == CONTENT_AIR)
4110 orp = v3f(0,y+1,z+1);
4111 found_existing = true;
4112 goto continue_generating;
4116 catch(InvalidPositionException &e){}
4122 for(s16 y=0; y<ued; y++)
4123 for(s16 z=0; z<ued; z++)
4125 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4126 if(getNode(ap).d == CONTENT_AIR)
4128 orp = v3f(ued-1,y+1,z+1);
4129 found_existing = true;
4130 goto continue_generating;
4134 catch(InvalidPositionException &e){}
4140 for(s16 x=0; x<ued; x++)
4141 for(s16 z=0; z<ued; z++)
4143 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4144 if(getNode(ap).d == CONTENT_AIR)
4146 orp = v3f(x+1,0,z+1);
4147 found_existing = true;
4148 goto continue_generating;
4152 catch(InvalidPositionException &e){}
4158 for(s16 x=0; x<ued; x++)
4159 for(s16 z=0; z<ued; z++)
4161 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4162 if(getNode(ap).d == CONTENT_AIR)
4164 orp = v3f(x+1,ued-1,z+1);
4165 found_existing = true;
4166 goto continue_generating;
4170 catch(InvalidPositionException &e){}
4172 continue_generating:
4175 Choose whether to actually generate dungeon
4177 bool do_generate_dungeons = true;
4178 // Don't generate if no part is underground
4179 if(!some_part_underground)
4181 do_generate_dungeons = false;
4183 // Don't generate if mostly underwater surface
4184 /*else if(mostly_underwater_surface)
4186 do_generate_dungeons = false;
4188 // Partly underground = cave
4189 else if(!completely_underground)
4191 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4193 // Found existing dungeon underground
4194 else if(found_existing && completely_underground)
4196 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4198 // Underground and no dungeons found
4201 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4204 if(do_generate_dungeons)
4207 Generate some tunnel starting from orp and ors
4209 for(u16 i=0; i<3; i++)
4212 (float)(myrand()%ued)+0.5,
4213 (float)(myrand()%ued)+0.5,
4214 (float)(myrand()%ued)+0.5
4218 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4222 for(float f=0; f<1.0; f+=0.04)
4224 v3f fp = orp + vec * f;
4225 v3s16 cp(fp.X, fp.Y, fp.Z);
4227 s16 d1 = d0 + rs - 1;
4228 for(s16 z0=d0; z0<=d1; z0++)
4230 s16 si = rs - abs(z0);
4231 for(s16 x0=-si; x0<=si-1; x0++)
4233 s16 si2 = rs - abs(x0);
4234 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4240 if(isInArea(p, ued) == false)
4242 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4254 // Set to true if has caves.
4255 // Set when some non-air is changed to air when making caves.
4256 bool has_dungeons = false;
4259 Apply temporary cave data to block
4262 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4263 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4265 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4267 MapNode n = block->getNode(v3s16(x0,y0,z0));
4270 if(underground_emptiness[
4271 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4272 +ued*(y0*ued/MAP_BLOCKSIZE)
4273 +(x0*ued/MAP_BLOCKSIZE)])
4275 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4278 has_dungeons = true;
4284 block->setNode(v3s16(x0,y0,z0), n);
4289 This is used for guessing whether or not the block should
4290 receive sunlight from the top if the block above doesn't exist
4292 block->setIsUnderground(completely_underground);
4295 Force lighting update if some part of block is partly
4296 underground and has caves.
4298 /*if(some_part_underground && !completely_underground && has_dungeons)
4300 //dstream<<"Half-ground caves"<<std::endl;
4301 lighting_invalidated_blocks[block->getPos()] = block;
4304 // DEBUG: Always update lighting
4305 //lighting_invalidated_blocks[block->getPos()] = block;
4311 if(some_part_underground)
4313 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4318 for(s16 i=0; i<underground_level/4 + 1; i++)
4320 if(myrand()%50 == 0)
4323 (myrand()%(MAP_BLOCKSIZE-2))+1,
4324 (myrand()%(MAP_BLOCKSIZE-2))+1,
4325 (myrand()%(MAP_BLOCKSIZE-2))+1
4331 for(u16 i=0; i<27; i++)
4333 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4335 block->setNode(cp+g_27dirs[i], n);
4343 u16 coal_amount = 30;
4344 u16 coal_rareness = 60 / coal_amount;
4345 if(coal_rareness == 0)
4347 if(myrand()%coal_rareness == 0)
4349 u16 a = myrand() % 16;
4350 u16 amount = coal_amount * a*a*a / 1000;
4351 for(s16 i=0; i<amount; i++)
4354 (myrand()%(MAP_BLOCKSIZE-2))+1,
4355 (myrand()%(MAP_BLOCKSIZE-2))+1,
4356 (myrand()%(MAP_BLOCKSIZE-2))+1
4360 n.d = CONTENT_STONE;
4361 n.param = MINERAL_COAL;
4363 for(u16 i=0; i<27; i++)
4365 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4367 block->setNode(cp+g_27dirs[i], n);
4375 //TODO: change to iron_amount or whatever
4376 u16 iron_amount = 15;
4377 u16 iron_rareness = 60 / iron_amount;
4378 if(iron_rareness == 0)
4380 if(myrand()%iron_rareness == 0)
4382 u16 a = myrand() % 16;
4383 u16 amount = iron_amount * a*a*a / 1000;
4384 for(s16 i=0; i<amount; i++)
4387 (myrand()%(MAP_BLOCKSIZE-2))+1,
4388 (myrand()%(MAP_BLOCKSIZE-2))+1,
4389 (myrand()%(MAP_BLOCKSIZE-2))+1
4393 n.d = CONTENT_STONE;
4394 n.param = MINERAL_IRON;
4396 for(u16 i=0; i<27; i++)
4398 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4400 block->setNode(cp+g_27dirs[i], n);
4407 Create a few rats in empty blocks underground
4409 if(completely_underground)
4411 //for(u16 i=0; i<2; i++)
4414 (myrand()%(MAP_BLOCKSIZE-2))+1,
4415 (myrand()%(MAP_BLOCKSIZE-2))+1,
4416 (myrand()%(MAP_BLOCKSIZE-2))+1
4419 // Check that the place is empty
4420 //if(!is_ground_content(block->getNode(cp).d))
4423 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4424 block->addObject(obj);
4430 Add block to sector.
4432 sector->insertBlock(block);
4434 // Lighting is invalid after generation.
4435 block->setLightingExpired(true);
4442 <<"lighting_invalidated_blocks.size()"
4446 <<" "<<lighting_invalidated_blocks.size()
4447 <<", "<<has_dungeons
4448 <<", "<<completely_underground
4449 <<", "<<some_part_underground
4456 MapBlock * ServerMap::createBlock(v3s16 p)
4458 DSTACK("%s: p=(%d,%d,%d)",
4459 __FUNCTION_NAME, p.X, p.Y, p.Z);
4462 Do not create over-limit
4464 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4465 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4466 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4467 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4468 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4469 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4470 throw InvalidPositionException("createBlock(): pos. over limit");
4472 v2s16 p2d(p.X, p.Z);
4475 This will create or load a sector if not found in memory.
4476 If block exists on disk, it will be loaded.
4478 NOTE: On old save formats, this will be slow, as it generates
4479 lighting on blocks for them.
4481 ServerMapSector *sector;
4483 sector = (ServerMapSector*)createSector(p2d);
4484 assert(sector->getId() == MAPSECTOR_SERVER);
4486 catch(InvalidPositionException &e)
4488 dstream<<"createBlock: createSector() failed"<<std::endl;
4492 NOTE: This should not be done, or at least the exception
4493 should not be passed on as std::exception, because it
4494 won't be catched at all.
4496 /*catch(std::exception &e)
4498 dstream<<"createBlock: createSector() failed: "
4499 <<e.what()<<std::endl;
4504 Try to get a block from the sector
4507 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4511 block = sector->createBlankBlock(block_y);
4515 MapBlock * ServerMap::emergeBlock(
4517 bool only_from_disk,
4518 core::map<v3s16, MapBlock*> &changed_blocks,
4519 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4522 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4524 p.X, p.Y, p.Z, only_from_disk);
4527 Do not generate over-limit
4529 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4530 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4531 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4532 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4533 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4534 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4535 throw InvalidPositionException("emergeBlock(): pos. over limit");
4537 v2s16 p2d(p.X, p.Z);
4540 This will create or load a sector if not found in memory.
4541 If block exists on disk, it will be loaded.
4543 ServerMapSector *sector;
4545 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4546 assert(sector->getId() == MAPSECTOR_SERVER);
4548 catch(InvalidPositionException &e)
4550 dstream<<"emergeBlock: emergeSector() failed: "
4551 <<e.what()<<std::endl;
4552 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4554 <<"You could try to delete it."<<std::endl;
4557 catch(VersionMismatchException &e)
4559 dstream<<"emergeBlock: emergeSector() failed: "
4560 <<e.what()<<std::endl;
4561 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4563 <<"You could try to delete it."<<std::endl;
4567 NOTE: This should not be done, or at least the exception
4568 should not be passed on as std::exception, because it
4569 won't be catched at all.
4571 /*catch(std::exception &e)
4573 dstream<<"emergeBlock: emergeSector() failed: "
4574 <<e.what()<<std::endl;
4575 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4577 <<"You could try to delete it."<<std::endl;
4582 Try to get a block from the sector
4585 bool does_not_exist = false;
4586 bool lighting_expired = false;
4587 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4591 does_not_exist = true;
4593 else if(block->isDummy() == true)
4595 does_not_exist = true;
4597 else if(block->getLightingExpired())
4599 lighting_expired = true;
4604 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4609 If block was not found on disk and not going to generate a
4610 new one, make sure there is a dummy block in place.
4612 if(only_from_disk && (does_not_exist || lighting_expired))
4614 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4618 // Create dummy block
4619 block = new MapBlock(this, p, true);
4621 // Add block to sector
4622 sector->insertBlock(block);
4628 //dstream<<"Not found on disk, generating."<<std::endl;
4630 //TimeTaker("emergeBlock() generate");
4632 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4635 If the block doesn't exist, generate the block.
4639 block = generateBlock(p, block, sector, changed_blocks,
4640 lighting_invalidated_blocks);
4643 if(lighting_expired)
4645 lighting_invalidated_blocks.insert(p, block);
4649 Initially update sunlight
4653 core::map<v3s16, bool> light_sources;
4654 bool black_air_left = false;
4655 bool bottom_invalid =
4656 block->propagateSunlight(light_sources, true,
4657 &black_air_left, true);
4659 // If sunlight didn't reach everywhere and part of block is
4660 // above ground, lighting has to be properly updated
4661 //if(black_air_left && some_part_underground)
4664 lighting_invalidated_blocks[block->getPos()] = block;
4669 lighting_invalidated_blocks[block->getPos()] = block;
4676 s16 ServerMap::findGroundLevel(v2s16 p2d)
4679 Uh, just do something random...
4681 // Find existing map from top to down
4684 v3s16 p(p2d.X, max, p2d.Y);
4685 for(; p.Y>min; p.Y--)
4687 MapNode n = getNodeNoEx(p);
4688 if(n.d != CONTENT_IGNORE)
4693 // If this node is not air, go to plan b
4694 if(getNodeNoEx(p).d != CONTENT_AIR)
4696 // Search existing walkable and return it
4697 for(; p.Y>min; p.Y--)
4699 MapNode n = getNodeNoEx(p);
4700 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4706 Plan B: Get from map generator perlin noise function
4708 double level = base_rock_level_2d(m_seed, p2d);
4712 void ServerMap::createDir(std::string path)
4714 if(fs::CreateDir(path) == false)
4716 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4717 <<"\""<<path<<"\""<<std::endl;
4718 throw BaseException("ServerMap failed to create directory");
4722 std::string ServerMap::getSectorSubDir(v2s16 pos)
4725 snprintf(cc, 9, "%.4x%.4x",
4726 (unsigned int)pos.X&0xffff,
4727 (unsigned int)pos.Y&0xffff);
4729 return std::string(cc);
4732 std::string ServerMap::getSectorDir(v2s16 pos)
4734 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4737 v2s16 ServerMap::getSectorPos(std::string dirname)
4739 if(dirname.size() != 8)
4740 throw InvalidFilenameException("Invalid sector directory name");
4742 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4744 throw InvalidFilenameException("Invalid sector directory name");
4745 v2s16 pos((s16)x, (s16)y);
4749 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4751 v2s16 p2d = getSectorPos(sectordir);
4753 if(blockfile.size() != 4){
4754 throw InvalidFilenameException("Invalid block filename");
4757 int r = sscanf(blockfile.c_str(), "%4x", &y);
4759 throw InvalidFilenameException("Invalid block filename");
4760 return v3s16(p2d.X, y, p2d.Y);
4763 void ServerMap::save(bool only_changed)
4765 DSTACK(__FUNCTION_NAME);
4766 if(m_map_saving_enabled == false)
4768 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4772 if(only_changed == false)
4773 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4779 u32 sector_meta_count = 0;
4780 u32 block_count = 0;
4783 JMutexAutoLock lock(m_sector_mutex);
4785 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4786 for(; i.atEnd() == false; i++)
4788 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4789 assert(sector->getId() == MAPSECTOR_SERVER);
4791 if(sector->differs_from_disk || only_changed == false)
4793 saveSectorMeta(sector);
4794 sector_meta_count++;
4796 core::list<MapBlock*> blocks;
4797 sector->getBlocks(blocks);
4798 core::list<MapBlock*>::Iterator j;
4799 for(j=blocks.begin(); j!=blocks.end(); j++)
4801 MapBlock *block = *j;
4802 if(block->getChangedFlag() || only_changed == false)
4807 /*dstream<<"ServerMap: Written block ("
4808 <<block->getPos().X<<","
4809 <<block->getPos().Y<<","
4810 <<block->getPos().Z<<")"
4819 Only print if something happened or saved whole map
4821 if(only_changed == false || sector_meta_count != 0
4822 || block_count != 0)
4824 dstream<<DTIME<<"ServerMap: Written: "
4825 <<sector_meta_count<<" sector metadata files, "
4826 <<block_count<<" block files"
4831 void ServerMap::loadAll()
4833 DSTACK(__FUNCTION_NAME);
4834 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4839 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4841 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4843 JMutexAutoLock lock(m_sector_mutex);
4846 s32 printed_counter = -100000;
4847 s32 count = list.size();
4849 std::vector<fs::DirListNode>::iterator i;
4850 for(i=list.begin(); i!=list.end(); i++)
4852 if(counter > printed_counter + 10)
4854 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4855 printed_counter = counter;
4859 MapSector *sector = NULL;
4861 // We want directories
4865 sector = loadSectorMeta(i->name);
4867 catch(InvalidFilenameException &e)
4869 // This catches unknown crap in directory
4872 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4873 (m_savedir+"/sectors/"+i->name);
4874 std::vector<fs::DirListNode>::iterator i2;
4875 for(i2=list2.begin(); i2!=list2.end(); i2++)
4881 loadBlock(i->name, i2->name, sector);
4883 catch(InvalidFilenameException &e)
4885 // This catches unknown crap in directory
4889 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4893 void ServerMap::saveMasterHeightmap()
4895 DSTACK(__FUNCTION_NAME);
4897 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4899 createDir(m_savedir);
4901 /*std::string fullpath = m_savedir + "/master_heightmap";
4902 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4903 if(o.good() == false)
4904 throw FileNotGoodException("Cannot open master heightmap");*/
4906 // Format used for writing
4907 //u8 version = SER_FMT_VER_HIGHEST;
4910 void ServerMap::loadMasterHeightmap()
4912 DSTACK(__FUNCTION_NAME);
4914 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4916 /*std::string fullpath = m_savedir + "/master_heightmap";
4917 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4918 if(is.good() == false)
4919 throw FileNotGoodException("Cannot open master heightmap");*/
4923 void ServerMap::saveMapMeta()
4925 DSTACK(__FUNCTION_NAME);
4927 dstream<<"INFO: ServerMap::saveMapMeta(): "
4928 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4931 createDir(m_savedir);
4933 std::string fullpath = m_savedir + "/map_meta.txt";
4934 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4935 if(os.good() == false)
4937 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4938 <<"could not open"<<fullpath<<std::endl;
4939 throw FileNotGoodException("Cannot open chunk metadata");
4943 params.setU64("seed", m_seed);
4944 params.setS32("chunksize", m_chunksize);
4946 params.writeLines(os);
4948 os<<"[end_of_params]\n";
4952 void ServerMap::loadMapMeta()
4954 DSTACK(__FUNCTION_NAME);
4956 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4959 std::string fullpath = m_savedir + "/map_meta.txt";
4960 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4961 if(is.good() == false)
4963 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4964 <<"could not open"<<fullpath<<std::endl;
4965 throw FileNotGoodException("Cannot open chunk metadata");
4973 throw SerializationError
4974 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4976 std::getline(is, line);
4977 std::string trimmedline = trim(line);
4978 if(trimmedline == "[end_of_params]")
4980 params.parseConfigLine(line);
4983 m_seed = params.getU64("seed");
4984 m_chunksize = params.getS32("chunksize");
4986 dstream<<"INFO: ServerMap::loadMapMeta(): "
4987 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4991 void ServerMap::saveChunkMeta()
4993 DSTACK(__FUNCTION_NAME);
4995 u32 count = m_chunks.size();
4997 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4998 <<count<<" chunks"<<std::endl;
5000 createDir(m_savedir);
5002 std::string fullpath = m_savedir + "/chunk_meta";
5003 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5004 if(os.good() == false)
5006 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5007 <<"could not open"<<fullpath<<std::endl;
5008 throw FileNotGoodException("Cannot open chunk metadata");
5014 os.write((char*)&version, 1);
5019 writeU32(buf, count);
5020 os.write((char*)buf, 4);
5022 for(core::map<v2s16, MapChunk*>::Iterator
5023 i = m_chunks.getIterator();
5024 i.atEnd()==false; i++)
5026 v2s16 p = i.getNode()->getKey();
5027 MapChunk *chunk = i.getNode()->getValue();
5030 os.write((char*)buf, 4);
5032 chunk->serialize(os, version);
5036 void ServerMap::loadChunkMeta()
5038 DSTACK(__FUNCTION_NAME);
5040 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5043 std::string fullpath = m_savedir + "/chunk_meta";
5044 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5045 if(is.good() == false)
5047 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5048 <<"could not open"<<fullpath<<std::endl;
5049 throw FileNotGoodException("Cannot open chunk metadata");
5055 is.read((char*)&version, 1);
5060 is.read((char*)buf, 4);
5061 u32 count = readU32(buf);
5063 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5064 <<count<<" chunks"<<std::endl;
5066 for(u32 i=0; i<count; i++)
5069 MapChunk *chunk = new MapChunk();
5071 is.read((char*)buf, 4);
5074 chunk->deSerialize(is, version);
5075 m_chunks.insert(p, chunk);
5079 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5081 DSTACK(__FUNCTION_NAME);
5082 // Format used for writing
5083 u8 version = SER_FMT_VER_HIGHEST;
5085 v2s16 pos = sector->getPos();
5086 createDir(m_savedir);
5087 createDir(m_savedir+"/sectors");
5088 std::string dir = getSectorDir(pos);
5091 std::string fullpath = dir + "/meta";
5092 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5093 if(o.good() == false)
5094 throw FileNotGoodException("Cannot open sector metafile");
5096 sector->serialize(o, version);
5098 sector->differs_from_disk = false;
5101 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5103 DSTACK(__FUNCTION_NAME);
5105 v2s16 p2d = getSectorPos(dirname);
5106 std::string dir = m_savedir + "/sectors/" + dirname;
5108 std::string fullpath = dir + "/meta";
5109 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5110 if(is.good() == false)
5111 throw FileNotGoodException("Cannot open sector metafile");
5113 ServerMapSector *sector = ServerMapSector::deSerialize
5114 (is, this, p2d, m_sectors);
5116 sector->differs_from_disk = false;
5121 bool ServerMap::loadSectorFull(v2s16 p2d)
5123 DSTACK(__FUNCTION_NAME);
5124 std::string sectorsubdir = getSectorSubDir(p2d);
5126 MapSector *sector = NULL;
5128 JMutexAutoLock lock(m_sector_mutex);
5131 sector = loadSectorMeta(sectorsubdir);
5133 catch(InvalidFilenameException &e)
5137 catch(FileNotGoodException &e)
5141 catch(std::exception &e)
5149 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5150 (m_savedir+"/sectors/"+sectorsubdir);
5151 std::vector<fs::DirListNode>::iterator i2;
5152 for(i2=list2.begin(); i2!=list2.end(); i2++)
5158 loadBlock(sectorsubdir, i2->name, sector);
5160 catch(InvalidFilenameException &e)
5162 // This catches unknown crap in directory
5168 void ServerMap::saveBlock(MapBlock *block)
5170 DSTACK(__FUNCTION_NAME);
5172 Dummy blocks are not written
5174 if(block->isDummy())
5176 /*v3s16 p = block->getPos();
5177 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5178 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5182 // Format used for writing
5183 u8 version = SER_FMT_VER_HIGHEST;
5185 v3s16 p3d = block->getPos();
5186 v2s16 p2d(p3d.X, p3d.Z);
5187 createDir(m_savedir);
5188 createDir(m_savedir+"/sectors");
5189 std::string dir = getSectorDir(p2d);
5192 // Block file is map/sectors/xxxxxxxx/xxxx
5194 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5195 std::string fullpath = dir + "/" + cc;
5196 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5197 if(o.good() == false)
5198 throw FileNotGoodException("Cannot open block data");
5201 [0] u8 serialization version
5204 o.write((char*)&version, 1);
5206 block->serialize(o, version);
5209 Versions up from 9 have block objects.
5213 block->serializeObjects(o, version);
5216 // We just wrote it to the disk
5217 block->resetChangedFlag();
5220 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5222 DSTACK(__FUNCTION_NAME);
5224 // Block file is map/sectors/xxxxxxxx/xxxx
5225 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5228 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5229 if(is.good() == false)
5230 throw FileNotGoodException("Cannot open block file");
5232 v3s16 p3d = getBlockPos(sectordir, blockfile);
5233 v2s16 p2d(p3d.X, p3d.Z);
5235 assert(sector->getPos() == p2d);
5237 u8 version = SER_FMT_VER_INVALID;
5238 is.read((char*)&version, 1);
5241 throw SerializationError("ServerMap::loadBlock(): Failed"
5242 " to read MapBlock version");
5244 /*u32 block_size = MapBlock::serializedLength(version);
5245 SharedBuffer<u8> data(block_size);
5246 is.read((char*)*data, block_size);*/
5248 // This will always return a sector because we're the server
5249 //MapSector *sector = emergeSector(p2d);
5251 MapBlock *block = NULL;
5252 bool created_new = false;
5254 block = sector->getBlockNoCreate(p3d.Y);
5256 catch(InvalidPositionException &e)
5258 block = sector->createBlankBlockNoInsert(p3d.Y);
5262 // deserialize block data
5263 block->deSerialize(is, version);
5266 Versions up from 9 have block objects.
5270 block->updateObjects(is, version, NULL, 0);
5274 sector->insertBlock(block);
5277 Convert old formats to new and save
5280 // Save old format blocks in new format
5281 if(version < SER_FMT_VER_HIGHEST)
5286 // We just loaded it from the disk, so it's up-to-date.
5287 block->resetChangedFlag();
5290 catch(SerializationError &e)
5292 dstream<<"WARNING: Invalid block data on disk "
5293 "(SerializationError). Ignoring. "
5294 "A new one will be generated."
5297 // TODO: Backup file; name is in fullpath.
5301 void ServerMap::PrintInfo(std::ostream &out)
5312 ClientMap::ClientMap(
5314 MapDrawControl &control,
5315 scene::ISceneNode* parent,
5316 scene::ISceneManager* mgr,
5320 scene::ISceneNode(parent, mgr, id),
5323 m_camera_position(0,0,0),
5324 m_camera_direction(0,0,1)
5326 m_camera_mutex.Init();
5327 assert(m_camera_mutex.IsInitialized());
5329 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5330 BS*1000000,BS*1000000,BS*1000000);
5333 ClientMap::~ClientMap()
5335 /*JMutexAutoLock lock(mesh_mutex);
5344 MapSector * ClientMap::emergeSector(v2s16 p2d)
5346 DSTACK(__FUNCTION_NAME);
5347 // Check that it doesn't exist already
5349 return getSectorNoGenerate(p2d);
5351 catch(InvalidPositionException &e)
5356 ClientMapSector *sector = new ClientMapSector(this, p2d);
5359 JMutexAutoLock lock(m_sector_mutex);
5360 m_sectors.insert(p2d, sector);
5366 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5368 DSTACK(__FUNCTION_NAME);
5369 ClientMapSector *sector = NULL;
5371 JMutexAutoLock lock(m_sector_mutex);
5373 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5377 sector = (ClientMapSector*)n->getValue();
5378 assert(sector->getId() == MAPSECTOR_CLIENT);
5382 sector = new ClientMapSector(this, p2d);
5384 JMutexAutoLock lock(m_sector_mutex);
5385 m_sectors.insert(p2d, sector);
5389 sector->deSerialize(is);
5392 void ClientMap::OnRegisterSceneNode()
5396 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5397 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5400 ISceneNode::OnRegisterSceneNode();
5403 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5405 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5406 DSTACK(__FUNCTION_NAME);
5408 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5411 Get time for measuring timeout.
5413 Measuring time is very useful for long delays when the
5414 machine is swapping a lot.
5416 int time1 = time(0);
5418 //u32 daynight_ratio = m_client->getDayNightRatio();
5420 m_camera_mutex.Lock();
5421 v3f camera_position = m_camera_position;
5422 v3f camera_direction = m_camera_direction;
5423 m_camera_mutex.Unlock();
5426 Get all blocks and draw all visible ones
5429 v3s16 cam_pos_nodes(
5430 camera_position.X / BS,
5431 camera_position.Y / BS,
5432 camera_position.Z / BS);
5434 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5436 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5437 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5439 // Take a fair amount as we will be dropping more out later
5441 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5442 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5443 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5445 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5446 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5447 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5449 u32 vertex_count = 0;
5451 // For limiting number of mesh updates per frame
5452 u32 mesh_update_count = 0;
5454 u32 blocks_would_have_drawn = 0;
5455 u32 blocks_drawn = 0;
5457 //NOTE: The sectors map should be locked but we're not doing it
5458 // because it'd cause too much delays
5460 int timecheck_counter = 0;
5461 core::map<v2s16, MapSector*>::Iterator si;
5462 si = m_sectors.getIterator();
5463 for(; si.atEnd() == false; si++)
5466 timecheck_counter++;
5467 if(timecheck_counter > 50)
5469 timecheck_counter = 0;
5470 int time2 = time(0);
5471 if(time2 > time1 + 4)
5473 dstream<<"ClientMap::renderMap(): "
5474 "Rendering takes ages, returning."
5481 MapSector *sector = si.getNode()->getValue();
5482 v2s16 sp = sector->getPos();
5484 if(m_control.range_all == false)
5486 if(sp.X < p_blocks_min.X
5487 || sp.X > p_blocks_max.X
5488 || sp.Y < p_blocks_min.Z
5489 || sp.Y > p_blocks_max.Z)
5493 core::list< MapBlock * > sectorblocks;
5494 sector->getBlocks(sectorblocks);
5500 core::list< MapBlock * >::Iterator i;
5501 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5503 MapBlock *block = *i;
5506 Compare block position to camera position, skip
5507 if not seen on display
5510 float range = 100000 * BS;
5511 if(m_control.range_all == false)
5512 range = m_control.wanted_range * BS;
5515 if(isBlockInSight(block->getPos(), camera_position,
5516 camera_direction, range, &d) == false)
5521 // This is ugly (spherical distance limit?)
5522 /*if(m_control.range_all == false &&
5523 d - 0.5*BS*MAP_BLOCKSIZE > range)
5528 Update expired mesh (used for day/night change)
5530 It doesn't work exactly like it should now with the
5531 tasked mesh update but whatever.
5534 bool mesh_expired = false;
5537 JMutexAutoLock lock(block->mesh_mutex);
5539 mesh_expired = block->getMeshExpired();
5541 // Mesh has not been expired and there is no mesh:
5542 // block has no content
5543 if(block->mesh == NULL && mesh_expired == false)
5547 f32 faraway = BS*50;
5548 //f32 faraway = m_control.wanted_range * BS;
5551 This has to be done with the mesh_mutex unlocked
5553 // Pretty random but this should work somewhat nicely
5554 if(mesh_expired && (
5555 (mesh_update_count < 3
5556 && (d < faraway || mesh_update_count < 2)
5559 (m_control.range_all && mesh_update_count < 20)
5562 /*if(mesh_expired && mesh_update_count < 6
5563 && (d < faraway || mesh_update_count < 3))*/
5565 mesh_update_count++;
5567 // Mesh has been expired: generate new mesh
5568 //block->updateMesh(daynight_ratio);
5569 m_client->addUpdateMeshTask(block->getPos());
5571 mesh_expired = false;
5576 Draw the faces of the block
5579 JMutexAutoLock lock(block->mesh_mutex);
5581 scene::SMesh *mesh = block->mesh;
5586 blocks_would_have_drawn++;
5587 if(blocks_drawn >= m_control.wanted_max_blocks
5588 && m_control.range_all == false
5589 && d > m_control.wanted_min_range * BS)
5593 u32 c = mesh->getMeshBufferCount();
5595 for(u32 i=0; i<c; i++)
5597 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5598 const video::SMaterial& material = buf->getMaterial();
5599 video::IMaterialRenderer* rnd =
5600 driver->getMaterialRenderer(material.MaterialType);
5601 bool transparent = (rnd && rnd->isTransparent());
5602 // Render transparent on transparent pass and likewise.
5603 if(transparent == is_transparent_pass)
5606 This *shouldn't* hurt too much because Irrlicht
5607 doesn't change opengl textures if the old
5608 material is set again.
5610 driver->setMaterial(buf->getMaterial());
5611 driver->drawMeshBuffer(buf);
5612 vertex_count += buf->getVertexCount();
5616 } // foreach sectorblocks
5619 m_control.blocks_drawn = blocks_drawn;
5620 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5622 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5623 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5626 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5627 core::map<v3s16, MapBlock*> *affected_blocks)
5629 bool changed = false;
5631 Add it to all blocks touching it
5634 v3s16(0,0,0), // this
5635 v3s16(0,0,1), // back
5636 v3s16(0,1,0), // top
5637 v3s16(1,0,0), // right
5638 v3s16(0,0,-1), // front
5639 v3s16(0,-1,0), // bottom
5640 v3s16(-1,0,0), // left
5642 for(u16 i=0; i<7; i++)
5644 v3s16 p2 = p + dirs[i];
5645 // Block position of neighbor (or requested) node
5646 v3s16 blockpos = getNodeBlockPos(p2);
5647 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5648 if(blockref == NULL)
5650 // Relative position of requested node
5651 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5652 if(blockref->setTempMod(relpos, mod))
5657 if(changed && affected_blocks!=NULL)
5659 for(u16 i=0; i<7; i++)
5661 v3s16 p2 = p + dirs[i];
5662 // Block position of neighbor (or requested) node
5663 v3s16 blockpos = getNodeBlockPos(p2);
5664 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5665 if(blockref == NULL)
5667 affected_blocks->insert(blockpos, blockref);
5673 bool ClientMap::clearTempMod(v3s16 p,
5674 core::map<v3s16, MapBlock*> *affected_blocks)
5676 bool changed = false;
5678 v3s16(0,0,0), // this
5679 v3s16(0,0,1), // back
5680 v3s16(0,1,0), // top
5681 v3s16(1,0,0), // right
5682 v3s16(0,0,-1), // front
5683 v3s16(0,-1,0), // bottom
5684 v3s16(-1,0,0), // left
5686 for(u16 i=0; i<7; i++)
5688 v3s16 p2 = p + dirs[i];
5689 // Block position of neighbor (or requested) node
5690 v3s16 blockpos = getNodeBlockPos(p2);
5691 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5692 if(blockref == NULL)
5694 // Relative position of requested node
5695 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5696 if(blockref->clearTempMod(relpos))
5701 if(changed && affected_blocks!=NULL)
5703 for(u16 i=0; i<7; i++)
5705 v3s16 p2 = p + dirs[i];
5706 // Block position of neighbor (or requested) node
5707 v3s16 blockpos = getNodeBlockPos(p2);
5708 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5709 if(blockref == NULL)
5711 affected_blocks->insert(blockpos, blockref);
5717 void ClientMap::expireMeshes(bool only_daynight_diffed)
5719 TimeTaker timer("expireMeshes()");
5721 core::map<v2s16, MapSector*>::Iterator si;
5722 si = m_sectors.getIterator();
5723 for(; si.atEnd() == false; si++)
5725 MapSector *sector = si.getNode()->getValue();
5727 core::list< MapBlock * > sectorblocks;
5728 sector->getBlocks(sectorblocks);
5730 core::list< MapBlock * >::Iterator i;
5731 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5733 MapBlock *block = *i;
5735 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5741 JMutexAutoLock lock(block->mesh_mutex);
5742 if(block->mesh != NULL)
5744 /*block->mesh->drop();
5745 block->mesh = NULL;*/
5746 block->setMeshExpired(true);
5753 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5755 assert(mapType() == MAPTYPE_CLIENT);
5758 v3s16 p = blockpos + v3s16(0,0,0);
5759 MapBlock *b = getBlockNoCreate(p);
5760 b->updateMesh(daynight_ratio);
5761 //b->setMeshExpired(true);
5763 catch(InvalidPositionException &e){}
5766 v3s16 p = blockpos + v3s16(-1,0,0);
5767 MapBlock *b = getBlockNoCreate(p);
5768 b->updateMesh(daynight_ratio);
5769 //b->setMeshExpired(true);
5771 catch(InvalidPositionException &e){}
5773 v3s16 p = blockpos + v3s16(0,-1,0);
5774 MapBlock *b = getBlockNoCreate(p);
5775 b->updateMesh(daynight_ratio);
5776 //b->setMeshExpired(true);
5778 catch(InvalidPositionException &e){}
5780 v3s16 p = blockpos + v3s16(0,0,-1);
5781 MapBlock *b = getBlockNoCreate(p);
5782 b->updateMesh(daynight_ratio);
5783 //b->setMeshExpired(true);
5785 catch(InvalidPositionException &e){}
5790 Update mesh of block in which the node is, and if the node is at the
5791 leading edge, update the appropriate leading blocks too.
5793 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5801 v3s16 blockposes[4];
5802 for(u32 i=0; i<4; i++)
5804 v3s16 np = nodepos + dirs[i];
5805 blockposes[i] = getNodeBlockPos(np);
5806 // Don't update mesh of block if it has been done already
5807 bool already_updated = false;
5808 for(u32 j=0; j<i; j++)
5810 if(blockposes[j] == blockposes[i])
5812 already_updated = true;
5819 MapBlock *b = getBlockNoCreate(blockposes[i]);
5820 b->updateMesh(daynight_ratio);
5825 void ClientMap::PrintInfo(std::ostream &out)
5836 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5841 MapVoxelManipulator::~MapVoxelManipulator()
5843 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5847 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5849 TimeTaker timer1("emerge", &emerge_time);
5851 // Units of these are MapBlocks
5852 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5853 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5855 VoxelArea block_area_nodes
5856 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5858 addArea(block_area_nodes);
5860 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5861 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5862 for(s32 x=p_min.X; x<=p_max.X; x++)
5865 core::map<v3s16, bool>::Node *n;
5866 n = m_loaded_blocks.find(p);
5870 bool block_data_inexistent = false;
5873 TimeTaker timer1("emerge load", &emerge_load_time);
5875 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5876 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5879 dstream<<std::endl;*/
5881 MapBlock *block = m_map->getBlockNoCreate(p);
5882 if(block->isDummy())
5883 block_data_inexistent = true;
5885 block->copyTo(*this);
5887 catch(InvalidPositionException &e)
5889 block_data_inexistent = true;
5892 if(block_data_inexistent)
5894 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5895 // Fill with VOXELFLAG_INEXISTENT
5896 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5897 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5899 s32 i = m_area.index(a.MinEdge.X,y,z);
5900 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5904 m_loaded_blocks.insert(p, !block_data_inexistent);
5907 //dstream<<"emerge done"<<std::endl;
5911 SUGG: Add an option to only update eg. water and air nodes.
5912 This will make it interfere less with important stuff if
5915 void MapVoxelManipulator::blitBack
5916 (core::map<v3s16, MapBlock*> & modified_blocks)
5918 if(m_area.getExtent() == v3s16(0,0,0))
5921 //TimeTaker timer1("blitBack");
5923 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5924 <<m_loaded_blocks.size()<<std::endl;*/
5927 Initialize block cache
5929 v3s16 blockpos_last;
5930 MapBlock *block = NULL;
5931 bool block_checked_in_modified = false;
5933 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5934 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5935 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5939 u8 f = m_flags[m_area.index(p)];
5940 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5943 MapNode &n = m_data[m_area.index(p)];
5945 v3s16 blockpos = getNodeBlockPos(p);
5950 if(block == NULL || blockpos != blockpos_last){
5951 block = m_map->getBlockNoCreate(blockpos);
5952 blockpos_last = blockpos;
5953 block_checked_in_modified = false;
5956 // Calculate relative position in block
5957 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5959 // Don't continue if nothing has changed here
5960 if(block->getNode(relpos) == n)
5963 //m_map->setNode(m_area.MinEdge + p, n);
5964 block->setNode(relpos, n);
5967 Make sure block is in modified_blocks
5969 if(block_checked_in_modified == false)
5971 modified_blocks[blockpos] = block;
5972 block_checked_in_modified = true;
5975 catch(InvalidPositionException &e)
5981 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5982 MapVoxelManipulator(map)
5986 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5990 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5992 // Just create the area so that it can be pointed to
5993 VoxelManipulator::emerge(a, caller_id);
5996 void ManualMapVoxelManipulator::initialEmerge(
5997 v3s16 blockpos_min, v3s16 blockpos_max)
5999 TimeTaker timer1("initialEmerge", &emerge_time);
6001 // Units of these are MapBlocks
6002 v3s16 p_min = blockpos_min;
6003 v3s16 p_max = blockpos_max;
6005 VoxelArea block_area_nodes
6006 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6008 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6011 dstream<<"initialEmerge: area: ";
6012 block_area_nodes.print(dstream);
6013 dstream<<" ("<<size_MB<<"MB)";
6017 addArea(block_area_nodes);
6019 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6020 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6021 for(s32 x=p_min.X; x<=p_max.X; x++)
6024 core::map<v3s16, bool>::Node *n;
6025 n = m_loaded_blocks.find(p);
6029 bool block_data_inexistent = false;
6032 TimeTaker timer1("emerge load", &emerge_load_time);
6034 MapBlock *block = m_map->getBlockNoCreate(p);
6035 if(block->isDummy())
6036 block_data_inexistent = true;
6038 block->copyTo(*this);
6040 catch(InvalidPositionException &e)
6042 block_data_inexistent = true;
6045 if(block_data_inexistent)
6048 Mark area inexistent
6050 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6051 // Fill with VOXELFLAG_INEXISTENT
6052 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6053 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6055 s32 i = m_area.index(a.MinEdge.X,y,z);
6056 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6060 m_loaded_blocks.insert(p, !block_data_inexistent);
6064 void ManualMapVoxelManipulator::blitBackAll(
6065 core::map<v3s16, MapBlock*> * modified_blocks)
6067 if(m_area.getExtent() == v3s16(0,0,0))
6071 Copy data of all blocks
6073 for(core::map<v3s16, bool>::Iterator
6074 i = m_loaded_blocks.getIterator();
6075 i.atEnd() == false; i++)
6077 bool existed = i.getNode()->getValue();
6078 if(existed == false)
6080 v3s16 p = i.getNode()->getKey();
6081 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6084 dstream<<"WARNING: "<<__FUNCTION_NAME
6085 <<": got NULL block "
6086 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6091 block->copyFrom(*this);
6094 modified_blocks->insert(p, block);