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
959 If node is under sunlight and doesn't let sunlight through,
960 take all sunlighted nodes under it and clear light from them
961 and from where the light has been spread.
962 TODO: This could be optimized by mass-unlighting instead
965 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
969 //m_dout<<DTIME<<"y="<<y<<std::endl;
970 v3s16 n2pos(p.X, y, p.Z);
976 catch(InvalidPositionException &e)
981 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
983 unLightNeighbors(LIGHTBANK_DAY,
984 n2pos, n2.getLight(LIGHTBANK_DAY),
985 light_sources, modified_blocks);
986 n2.setLight(LIGHTBANK_DAY, 0);
994 for(s32 i=0; i<2; i++)
996 enum LightBank bank = banks[i];
999 Spread light from all nodes that might be capable of doing so
1001 spreadLight(bank, light_sources, modified_blocks);
1005 Update information about whether day and night light differ
1007 for(core::map<v3s16, MapBlock*>::Iterator
1008 i = modified_blocks.getIterator();
1009 i.atEnd() == false; i++)
1011 MapBlock *block = i.getNode()->getValue();
1012 block->updateDayNightDiff();
1016 Add neighboring liquid nodes and the node itself if it is
1017 liquid (=water node was added) to transform queue.
1020 v3s16(0,0,0), // self
1021 v3s16(0,0,1), // back
1022 v3s16(0,1,0), // top
1023 v3s16(1,0,0), // right
1024 v3s16(0,0,-1), // front
1025 v3s16(0,-1,0), // bottom
1026 v3s16(-1,0,0), // left
1028 for(u16 i=0; i<7; i++)
1033 v3s16 p2 = p + dirs[i];
1035 MapNode n2 = getNode(p2);
1036 if(content_liquid(n2.d))
1038 m_transforming_liquid.push_back(p2);
1041 }catch(InvalidPositionException &e)
1049 void Map::removeNodeAndUpdate(v3s16 p,
1050 core::map<v3s16, MapBlock*> &modified_blocks)
1052 /*PrintInfo(m_dout);
1053 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1054 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1056 bool node_under_sunlight = true;
1058 v3s16 toppos = p + v3s16(0,1,0);
1060 // Node will be replaced with this
1061 u8 replace_material = CONTENT_AIR;
1064 If there is a node at top and it doesn't have sunlight,
1065 there will be no sunlight going down.
1068 MapNode topnode = getNode(toppos);
1070 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1071 node_under_sunlight = false;
1073 catch(InvalidPositionException &e)
1077 core::map<v3s16, bool> light_sources;
1079 enum LightBank banks[] =
1084 for(s32 i=0; i<2; i++)
1086 enum LightBank bank = banks[i];
1089 Unlight neighbors (in case the node is a light source)
1091 unLightNeighbors(bank, p,
1092 getNode(p).getLight(bank),
1093 light_sources, modified_blocks);
1098 This also clears the lighting.
1102 n.d = replace_material;
1105 for(s32 i=0; i<2; i++)
1107 enum LightBank bank = banks[i];
1110 Recalculate lighting
1112 spreadLight(bank, light_sources, modified_blocks);
1115 // Add the block of the removed node to modified_blocks
1116 v3s16 blockpos = getNodeBlockPos(p);
1117 MapBlock * block = getBlockNoCreate(blockpos);
1118 assert(block != NULL);
1119 modified_blocks.insert(blockpos, block);
1122 If the removed node was under sunlight, propagate the
1123 sunlight down from it and then light all neighbors
1124 of the propagated blocks.
1126 if(node_under_sunlight)
1128 s16 ybottom = propagateSunlight(p, modified_blocks);
1129 /*m_dout<<DTIME<<"Node was under sunlight. "
1130 "Propagating sunlight";
1131 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1133 for(; y >= ybottom; y--)
1135 v3s16 p2(p.X, y, p.Z);
1136 /*m_dout<<DTIME<<"lighting neighbors of node ("
1137 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1139 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1144 // Set the lighting of this node to 0
1145 // TODO: Is this needed? Lighting is cleared up there already.
1147 MapNode n = getNode(p);
1148 n.setLight(LIGHTBANK_DAY, 0);
1151 catch(InvalidPositionException &e)
1157 for(s32 i=0; i<2; i++)
1159 enum LightBank bank = banks[i];
1161 // Get the brightest neighbour node and propagate light from it
1162 v3s16 n2p = getBrightestNeighbour(bank, p);
1164 MapNode n2 = getNode(n2p);
1165 lightNeighbors(bank, n2p, modified_blocks);
1167 catch(InvalidPositionException &e)
1173 Update information about whether day and night light differ
1175 for(core::map<v3s16, MapBlock*>::Iterator
1176 i = modified_blocks.getIterator();
1177 i.atEnd() == false; i++)
1179 MapBlock *block = i.getNode()->getValue();
1180 block->updateDayNightDiff();
1184 Add neighboring liquid nodes to transform queue.
1187 v3s16(0,0,1), // back
1188 v3s16(0,1,0), // top
1189 v3s16(1,0,0), // right
1190 v3s16(0,0,-1), // front
1191 v3s16(0,-1,0), // bottom
1192 v3s16(-1,0,0), // left
1194 for(u16 i=0; i<6; i++)
1199 v3s16 p2 = p + dirs[i];
1201 MapNode n2 = getNode(p2);
1202 if(content_liquid(n2.d))
1204 m_transforming_liquid.push_back(p2);
1207 }catch(InvalidPositionException &e)
1213 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1216 event.type = MEET_ADDNODE;
1220 bool succeeded = true;
1222 core::map<v3s16, MapBlock*> modified_blocks;
1223 addNodeAndUpdate(p, n, modified_blocks);
1225 // Copy modified_blocks to event
1226 for(core::map<v3s16, MapBlock*>::Iterator
1227 i = modified_blocks.getIterator();
1228 i.atEnd()==false; i++)
1230 event.modified_blocks.insert(i.getNode()->getKey(), false);
1233 catch(InvalidPositionException &e){
1237 dispatchEvent(&event);
1242 bool Map::removeNodeWithEvent(v3s16 p)
1245 event.type = MEET_REMOVENODE;
1248 bool succeeded = true;
1250 core::map<v3s16, MapBlock*> modified_blocks;
1251 removeNodeAndUpdate(p, modified_blocks);
1253 // Copy modified_blocks to event
1254 for(core::map<v3s16, MapBlock*>::Iterator
1255 i = modified_blocks.getIterator();
1256 i.atEnd()==false; i++)
1258 event.modified_blocks.insert(i.getNode()->getKey(), false);
1261 catch(InvalidPositionException &e){
1265 dispatchEvent(&event);
1270 bool Map::dayNightDiffed(v3s16 blockpos)
1273 v3s16 p = blockpos + v3s16(0,0,0);
1274 MapBlock *b = getBlockNoCreate(p);
1275 if(b->dayNightDiffed())
1278 catch(InvalidPositionException &e){}
1281 v3s16 p = blockpos + v3s16(-1,0,0);
1282 MapBlock *b = getBlockNoCreate(p);
1283 if(b->dayNightDiffed())
1286 catch(InvalidPositionException &e){}
1288 v3s16 p = blockpos + v3s16(0,-1,0);
1289 MapBlock *b = getBlockNoCreate(p);
1290 if(b->dayNightDiffed())
1293 catch(InvalidPositionException &e){}
1295 v3s16 p = blockpos + v3s16(0,0,-1);
1296 MapBlock *b = getBlockNoCreate(p);
1297 if(b->dayNightDiffed())
1300 catch(InvalidPositionException &e){}
1303 v3s16 p = blockpos + v3s16(1,0,0);
1304 MapBlock *b = getBlockNoCreate(p);
1305 if(b->dayNightDiffed())
1308 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(0,1,0);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1317 v3s16 p = blockpos + v3s16(0,0,1);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1328 Updates usage timers
1330 void Map::timerUpdate(float dtime)
1332 JMutexAutoLock lock(m_sector_mutex);
1334 core::map<v2s16, MapSector*>::Iterator si;
1336 si = m_sectors.getIterator();
1337 for(; si.atEnd() == false; si++)
1339 MapSector *sector = si.getNode()->getValue();
1340 sector->usage_timer += dtime;
1344 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1347 Wait for caches to be removed before continuing.
1349 This disables the existence of caches while locked
1351 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1353 core::list<v2s16>::Iterator j;
1354 for(j=list.begin(); j!=list.end(); j++)
1356 MapSector *sector = m_sectors[*j];
1359 sector->deleteBlocks();
1364 If sector is in sector cache, remove it from there
1366 if(m_sector_cache == sector)
1368 m_sector_cache = NULL;
1371 Remove from map and delete
1373 m_sectors.remove(*j);
1379 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1380 core::list<v3s16> *deleted_blocks)
1382 JMutexAutoLock lock(m_sector_mutex);
1384 core::list<v2s16> sector_deletion_queue;
1385 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1386 for(; i.atEnd() == false; i++)
1388 MapSector *sector = i.getNode()->getValue();
1390 Delete sector from memory if it hasn't been used in a long time
1392 if(sector->usage_timer > timeout)
1394 sector_deletion_queue.push_back(i.getNode()->getKey());
1396 if(deleted_blocks != NULL)
1398 // Collect positions of blocks of sector
1399 MapSector *sector = i.getNode()->getValue();
1400 core::list<MapBlock*> blocks;
1401 sector->getBlocks(blocks);
1402 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1403 i != blocks.end(); i++)
1405 deleted_blocks->push_back((*i)->getPos());
1410 deleteSectors(sector_deletion_queue, only_blocks);
1411 return sector_deletion_queue.getSize();
1414 void Map::PrintInfo(std::ostream &out)
1419 #define WATER_DROP_BOOST 4
1421 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1423 DSTACK(__FUNCTION_NAME);
1424 //TimeTaker timer("transformLiquids()");
1427 u32 initial_size = m_transforming_liquid.size();
1429 /*if(initial_size != 0)
1430 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1432 while(m_transforming_liquid.size() != 0)
1435 Get a queued transforming liquid node
1437 v3s16 p0 = m_transforming_liquid.pop_front();
1439 MapNode n0 = getNode(p0);
1441 // Don't deal with non-liquids
1442 if(content_liquid(n0.d) == false)
1445 bool is_source = !content_flowing_liquid(n0.d);
1447 u8 liquid_level = 8;
1448 if(is_source == false)
1449 liquid_level = n0.param2 & 0x0f;
1451 // Turn possible source into non-source
1452 u8 nonsource_c = make_liquid_flowing(n0.d);
1455 If not source, check that some node flows into this one
1456 and what is the level of liquid in this one
1458 if(is_source == false)
1460 s8 new_liquid_level_max = -1;
1462 v3s16 dirs_from[5] = {
1463 v3s16(0,1,0), // top
1464 v3s16(0,0,1), // back
1465 v3s16(1,0,0), // right
1466 v3s16(0,0,-1), // front
1467 v3s16(-1,0,0), // left
1469 for(u16 i=0; i<5; i++)
1474 bool from_top = (i==0);
1476 v3s16 p2 = p0 + dirs_from[i];
1477 MapNode n2 = getNode(p2);
1479 if(content_liquid(n2.d))
1481 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1482 // Check that the liquids are the same type
1483 if(n2_nonsource_c != nonsource_c)
1485 dstream<<"WARNING: Not handling: different liquids"
1486 " collide"<<std::endl;
1489 bool n2_is_source = !content_flowing_liquid(n2.d);
1490 s8 n2_liquid_level = 8;
1491 if(n2_is_source == false)
1492 n2_liquid_level = n2.param2 & 0x07;
1494 s8 new_liquid_level = -1;
1497 //new_liquid_level = 7;
1498 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1499 new_liquid_level = 7;
1501 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1503 else if(n2_liquid_level > 0)
1505 new_liquid_level = n2_liquid_level - 1;
1508 if(new_liquid_level > new_liquid_level_max)
1509 new_liquid_level_max = new_liquid_level;
1512 }catch(InvalidPositionException &e)
1518 If liquid level should be something else, update it and
1519 add all the neighboring water nodes to the transform queue.
1521 if(new_liquid_level_max != liquid_level)
1523 if(new_liquid_level_max == -1)
1525 // Remove water alltoghether
1532 n0.param2 = new_liquid_level_max;
1536 // Block has been modified
1538 v3s16 blockpos = getNodeBlockPos(p0);
1539 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1541 modified_blocks.insert(blockpos, block);
1545 Add neighboring non-source liquid nodes to transform queue.
1548 v3s16(0,0,1), // back
1549 v3s16(0,1,0), // top
1550 v3s16(1,0,0), // right
1551 v3s16(0,0,-1), // front
1552 v3s16(0,-1,0), // bottom
1553 v3s16(-1,0,0), // left
1555 for(u16 i=0; i<6; i++)
1560 v3s16 p2 = p0 + dirs[i];
1562 MapNode n2 = getNode(p2);
1563 if(content_flowing_liquid(n2.d))
1565 m_transforming_liquid.push_back(p2);
1568 }catch(InvalidPositionException &e)
1575 // Get a new one from queue if the node has turned into non-water
1576 if(content_liquid(n0.d) == false)
1580 Flow water from this node
1582 v3s16 dirs_to[5] = {
1583 v3s16(0,-1,0), // bottom
1584 v3s16(0,0,1), // back
1585 v3s16(1,0,0), // right
1586 v3s16(0,0,-1), // front
1587 v3s16(-1,0,0), // left
1589 for(u16 i=0; i<5; i++)
1594 bool to_bottom = (i == 0);
1596 // If liquid is at lowest possible height, it's not going
1597 // anywhere except down
1598 if(liquid_level == 0 && to_bottom == false)
1601 u8 liquid_next_level = 0;
1602 // If going to bottom
1605 //liquid_next_level = 7;
1606 if(liquid_level >= 7 - WATER_DROP_BOOST)
1607 liquid_next_level = 7;
1609 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1612 liquid_next_level = liquid_level - 1;
1614 bool n2_changed = false;
1615 bool flowed = false;
1617 v3s16 p2 = p0 + dirs_to[i];
1619 MapNode n2 = getNode(p2);
1620 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1622 if(content_liquid(n2.d))
1624 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1625 // Check that the liquids are the same type
1626 if(n2_nonsource_c != nonsource_c)
1628 dstream<<"WARNING: Not handling: different liquids"
1629 " collide"<<std::endl;
1632 bool n2_is_source = !content_flowing_liquid(n2.d);
1633 u8 n2_liquid_level = 8;
1634 if(n2_is_source == false)
1635 n2_liquid_level = n2.param2 & 0x07;
1644 // Just flow into the source, nothing changes.
1645 // n2_changed is not set because destination didn't change
1650 if(liquid_next_level > liquid_level)
1652 n2.param2 = liquid_next_level;
1660 else if(n2.d == CONTENT_AIR)
1663 n2.param2 = liquid_next_level;
1670 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1674 m_transforming_liquid.push_back(p2);
1676 v3s16 blockpos = getNodeBlockPos(p2);
1677 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1679 modified_blocks.insert(blockpos, block);
1682 // If n2_changed to bottom, don't flow anywhere else
1683 if(to_bottom && flowed && !is_source)
1686 }catch(InvalidPositionException &e)
1692 //if(loopcount >= 100000)
1693 if(loopcount >= initial_size * 1)
1696 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1699 NodeMetadata* Map::getNodeMetadataClone(v3s16 p)
1701 v3s16 blockpos = getNodeBlockPos(p);
1702 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1703 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1706 NodeMetadata *meta = block->m_node_metadata.getClone(p_rel);
1714 ServerMap::ServerMap(std::string savedir):
1720 //m_chunksize = 16; // Too slow
1721 m_chunksize = 8; // Takes a few seconds
1725 // TODO: Save to and load from a file
1726 m_seed = (((u64)(myrand()%0xffff)<<0)
1727 + ((u64)(myrand()%0xffff)<<16)
1728 + ((u64)(myrand()%0xffff)<<32)
1729 + ((u64)(myrand()%0xffff)<<48));
1732 Experimental and debug stuff
1739 Try to load map; if not found, create a new one.
1742 m_savedir = savedir;
1743 m_map_saving_enabled = false;
1747 // If directory exists, check contents and load if possible
1748 if(fs::PathExists(m_savedir))
1750 // If directory is empty, it is safe to save into it.
1751 if(fs::GetDirListing(m_savedir).size() == 0)
1753 dstream<<DTIME<<"Server: Empty save directory is valid."
1755 m_map_saving_enabled = true;
1759 // Load map metadata (seed, chunksize)
1762 // Load chunk metadata
1765 /*// Load sector (0,0) and throw and exception on fail
1766 if(loadSectorFull(v2s16(0,0)) == false)
1767 throw LoadError("Failed to load sector (0,0)");*/
1769 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1770 "metadata and sector (0,0) from "<<savedir<<
1771 ", assuming valid save directory."
1774 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1775 <<"and chunk metadata from "<<savedir
1776 <<", assuming valid save directory."
1779 m_map_saving_enabled = true;
1780 // Map loaded, not creating new one
1784 // If directory doesn't exist, it is safe to save to it
1786 m_map_saving_enabled = true;
1789 catch(std::exception &e)
1791 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1792 <<", exception: "<<e.what()<<std::endl;
1793 dstream<<"Please remove the map or fix it."<<std::endl;
1794 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1797 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1799 // Create zero sector
1800 emergeSector(v2s16(0,0));
1802 // Initially write whole map
1806 ServerMap::~ServerMap()
1810 if(m_map_saving_enabled)
1813 // Save only changed parts
1815 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1819 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1822 catch(std::exception &e)
1824 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1825 <<", exception: "<<e.what()<<std::endl;
1831 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1832 for(; i.atEnd() == false; i++)
1834 MapChunk *chunk = i.getNode()->getValue();
1840 Some helper functions for the map generator
1843 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1845 v3s16 em = vmanip.m_area.getExtent();
1846 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1847 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1848 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1850 for(y=y_nodes_max; y>=y_nodes_min; y--)
1852 MapNode &n = vmanip.m_data[i];
1853 if(content_walkable(n.d))
1856 vmanip.m_area.add_y(em, i, -1);
1858 if(y >= y_nodes_min)
1864 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1866 v3s16 em = vmanip.m_area.getExtent();
1867 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1868 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1869 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1871 for(y=y_nodes_max; y>=y_nodes_min; y--)
1873 MapNode &n = vmanip.m_data[i];
1874 if(content_walkable(n.d)
1875 && n.d != CONTENT_TREE
1876 && n.d != CONTENT_LEAVES)
1879 vmanip.m_area.add_y(em, i, -1);
1881 if(y >= y_nodes_min)
1887 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1889 MapNode treenode(CONTENT_TREE);
1890 MapNode leavesnode(CONTENT_LEAVES);
1892 s16 trunk_h = myrand_range(3, 6);
1894 for(s16 ii=0; ii<trunk_h; ii++)
1896 if(vmanip.m_area.contains(p1))
1897 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1901 // p1 is now the last piece of the trunk
1904 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1905 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1906 Buffer<u8> leaves_d(leaves_a.getVolume());
1907 for(s32 i=0; i<leaves_a.getVolume(); i++)
1910 // Force leaves at near the end of the trunk
1913 for(s16 z=-d; z<=d; z++)
1914 for(s16 y=-d; y<=d; y++)
1915 for(s16 x=-d; x<=d; x++)
1917 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1921 // Add leaves randomly
1922 for(u32 iii=0; iii<7; iii++)
1927 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1928 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1929 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1932 for(s16 z=0; z<=d; z++)
1933 for(s16 y=0; y<=d; y++)
1934 for(s16 x=0; x<=d; x++)
1936 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1940 // Blit leaves to vmanip
1941 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1942 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1943 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1947 if(vmanip.m_area.contains(p) == false)
1949 u32 vi = vmanip.m_area.index(p);
1950 if(vmanip.m_data[vi].d != CONTENT_AIR)
1952 u32 i = leaves_a.index(x,y,z);
1953 if(leaves_d[i] == 1)
1954 vmanip.m_data[vi] = leavesnode;
1959 Noise functions. Make sure seed is mangled differently in each one.
1962 // Amount of trees per area in nodes
1963 double tree_amount_2d(u64 seed, v2s16 p)
1965 double noise = noise2d_perlin(
1966 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1968 double zeroval = -0.3;
1972 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1975 #define AVERAGE_MUD_AMOUNT 4
1977 double base_rock_level_2d(u64 seed, v2s16 p)
1979 // The base ground level
1980 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
1981 + 25. * noise2d_perlin(
1982 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1983 (seed>>32)+654879876, 6, 0.6);
1985 /*// A bit hillier one
1986 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
1987 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1988 (seed>>27)+90340, 6, 0.69);
1992 // Higher ground level
1993 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
1994 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1995 seed+85039, 5, 0.69);
1996 //higher = 30; // For debugging
1998 // Limit higher to at least base
2002 // Steepness factor of cliffs
2003 double b = 1.0 + 1.0 * noise2d_perlin(
2004 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2006 b = rangelim(b, 0.0, 1000.0);
2009 b = rangelim(b, 3.0, 1000.0);
2010 //dstream<<"b="<<b<<std::endl;
2013 // Offset to more low
2014 double a_off = -0.2;
2015 // High/low selector
2016 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2017 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2018 seed-359, 6, 0.7));*/
2019 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2020 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2021 seed-359, 5, 0.60));
2023 a = rangelim(a, 0.0, 1.0);
2025 //dstream<<"a="<<a<<std::endl;
2027 double h = base*(1.0-a) + higher*a;
2034 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2037 This is the main map generation method
2040 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2041 core::map<v3s16, MapBlock*> &changed_blocks,
2044 DSTACK(__FUNCTION_NAME);
2047 Don't generate if already fully generated
2051 MapChunk *chunk = getChunk(chunkpos);
2052 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2054 dstream<<"generateChunkRaw(): Chunk "
2055 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2056 <<" already generated"<<std::endl;
2061 dstream<<"generateChunkRaw(): Generating chunk "
2062 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2065 TimeTaker timer("generateChunkRaw()");
2067 // The distance how far into the neighbors the generator is allowed to go.
2068 s16 max_spread_amount_sectors = 2;
2069 assert(max_spread_amount_sectors <= m_chunksize);
2070 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2072 // Minimum amount of space left on sides for mud to fall in
2073 //s16 min_mud_fall_space = 2;
2075 // Maximum diameter of stone obstacles in X and Z
2076 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2077 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2079 s16 y_blocks_min = -4;
2080 s16 y_blocks_max = 3;
2081 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2082 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2083 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2085 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2086 s16 sectorpos_base_size = m_chunksize;
2088 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2089 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2090 v2s16 sectorpos_bigbase =
2091 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2092 s16 sectorpos_bigbase_size =
2093 sectorpos_base_size + 2 * max_spread_amount_sectors;
2095 v3s16 bigarea_blocks_min(
2096 sectorpos_bigbase.X,
2101 v3s16 bigarea_blocks_max(
2102 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2104 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2107 // Relative values to control amount of stuff in one chunk
2108 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2109 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2110 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2111 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2112 *(u32)h_blocks*MAP_BLOCKSIZE;
2115 The limiting edges of the lighting update, inclusive.
2117 s16 lighting_min_d = 0-max_spread_amount;
2118 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2121 Create the whole area of this and the neighboring chunks
2124 TimeTaker timer("generateChunkRaw() create area");
2126 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2127 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2129 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2130 ServerMapSector *sector = createSector(sectorpos);
2133 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2135 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2136 MapBlock *block = createBlock(blockpos);
2138 // Lighting won't be calculated
2139 //block->setLightingExpired(true);
2140 // Lighting will be calculated
2141 block->setLightingExpired(false);
2144 Block gets sunlight if this is true.
2146 This should be set to true when the top side of a block
2147 is completely exposed to the sky.
2149 Actually this doesn't matter now because the
2150 initial lighting is done here.
2152 block->setIsUnderground(y != y_blocks_max);
2158 Now we have a big empty area.
2160 Make a ManualMapVoxelManipulator that contains this and the
2164 ManualMapVoxelManipulator vmanip(this);
2165 // Add the area we just generated
2167 TimeTaker timer("generateChunkRaw() initialEmerge");
2168 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2172 vmanip.clearFlag(0xff);
2174 TimeTaker timer_generate("generateChunkRaw() generate");
2176 // Maximum height of the stone surface and obstacles.
2177 // This is used to disable dungeon generation from going too high.
2178 s16 stone_surface_max_y = 0;
2181 Generate general ground level to full area
2186 //TimeTaker timer1("ground level");
2188 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2189 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2192 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2195 Skip of already generated
2198 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2199 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2203 // Ground height at this point
2204 float surface_y_f = 0.0;
2206 // Use perlin noise for ground height
2207 surface_y_f = base_rock_level_2d(m_seed, p2d);
2209 /*// Experimental stuff
2211 float a = highlands_level_2d(m_seed, p2d);
2216 // Convert to integer
2217 s16 surface_y = (s16)surface_y_f;
2220 if(surface_y > stone_surface_max_y)
2221 stone_surface_max_y = surface_y;
2224 Fill ground with stone
2227 // Use fast index incrementing
2228 v3s16 em = vmanip.m_area.getExtent();
2229 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2230 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2232 vmanip.m_data[i].d = CONTENT_STONE;
2234 vmanip.m_area.add_y(em, i, 1);
2242 Randomize some parameters
2245 s32 stone_obstacle_count = 0;
2246 /*s32 stone_obstacle_count =
2247 rangelim((1.0+noise2d(m_seed+897,
2248 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2250 s16 stone_obstacle_max_height = 0;
2251 /*s16 stone_obstacle_max_height =
2252 rangelim((1.0+noise2d(m_seed+5902,
2253 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2256 Loop this part, it will make stuff look older and newer nicely
2258 //for(u32 i_age=0; i_age<1; i_age++)
2259 for(u32 i_age=0; i_age<2; i_age++)
2264 //TimeTaker timer1("stone obstacles");
2267 Add some random stone obstacles
2270 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2272 // Randomize max height so usually stuff will be quite low
2273 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2275 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2276 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2279 myrand_range(5, stone_obstacle_max_size),
2280 myrand_range(0, maxheight_randomized),
2281 myrand_range(5, stone_obstacle_max_size)
2284 // Don't make stupid small rectangle bumps
2289 myrand_range(1+ob_size.X/2+2,
2290 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2291 myrand_range(1+ob_size.Z/2+2,
2292 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2295 // Minimum space left on top of the obstacle
2296 s16 min_head_space = 12;
2298 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2299 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2301 // Node position in 2d
2302 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2304 // Find stone ground level
2305 // (ignore everything else than mud in already generated chunks)
2306 // and mud amount over the stone level
2310 v3s16 em = vmanip.m_area.getExtent();
2311 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2313 // Go to ground level
2314 for(y=y_nodes_max; y>=y_nodes_min; y--)
2316 MapNode *n = &vmanip.m_data[i];
2317 /*if(content_walkable(n.d)
2318 && n.d != CONTENT_MUD
2319 && n.d != CONTENT_GRASS)
2321 if(n->d == CONTENT_STONE)
2324 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2328 Change to mud because otherwise we might
2329 be throwing mud on grass at the next
2335 vmanip.m_area.add_y(em, i, -1);
2337 if(y >= y_nodes_min)
2340 surface_y = y_nodes_min;
2348 v3s16 em = vmanip.m_area.getExtent();
2349 s16 y_start = surface_y+1;
2350 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2354 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2356 MapNode &n = vmanip.m_data[i];
2357 n.d = CONTENT_STONE;
2359 if(y > stone_surface_max_y)
2360 stone_surface_max_y = y;
2363 if(count >= ob_size.Y)
2366 vmanip.m_area.add_y(em, i, 1);
2370 for(; y<=y_nodes_max - min_head_space; y++)
2372 MapNode &n = vmanip.m_data[i];
2375 if(count >= mud_amount)
2378 vmanip.m_area.add_y(em, i, 1);
2388 //TimeTaker timer1("dungeons");
2393 u32 dungeons_count = relative_volume / 600000;
2394 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2395 if(stone_surface_max_y < WATER_LEVEL)
2397 /*u32 dungeons_count = 0;
2398 u32 bruises_count = 0;*/
2399 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2401 s16 min_tunnel_diameter = 2;
2402 s16 max_tunnel_diameter = 6;
2403 u16 tunnel_routepoints = 25;
2405 bool bruise_surface = (jj < bruises_count);
2409 min_tunnel_diameter = 5;
2410 max_tunnel_diameter = myrand_range(10, 20);
2411 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2412 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2414 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2415 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2417 tunnel_routepoints = 5;
2420 // Allowed route area size in nodes
2422 sectorpos_base_size*MAP_BLOCKSIZE,
2423 h_blocks*MAP_BLOCKSIZE,
2424 sectorpos_base_size*MAP_BLOCKSIZE
2427 // Area starting point in nodes
2429 sectorpos_base.X*MAP_BLOCKSIZE,
2430 y_blocks_min*MAP_BLOCKSIZE,
2431 sectorpos_base.Y*MAP_BLOCKSIZE
2435 //(this should be more than the maximum radius of the tunnel)
2436 //s16 insure = 5; // Didn't work with max_d = 20
2438 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2439 ar += v3s16(1,0,1) * more * 2;
2440 of -= v3s16(1,0,1) * more;
2442 s16 route_y_min = 0;
2443 // Allow half a diameter + 7 over stone surface
2444 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2446 /*// If dungeons, don't go through surface too often
2447 if(bruise_surface == false)
2448 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2450 // Limit maximum to area
2451 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2455 /*// Minimum is at y=0
2456 route_y_min = -of.Y - 0;*/
2457 // Minimum is at y=max_tunnel_diameter/4
2458 //route_y_min = -of.Y + max_tunnel_diameter/4;
2459 //s16 min = -of.Y + max_tunnel_diameter/4;
2460 s16 min = -of.Y + 0;
2461 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2462 route_y_min = rangelim(route_y_min, 0, route_y_max);
2465 /*dstream<<"route_y_min = "<<route_y_min
2466 <<", route_y_max = "<<route_y_max<<std::endl;*/
2468 s16 route_start_y_min = route_y_min;
2469 s16 route_start_y_max = route_y_max;
2471 // Start every 2nd dungeon from surface
2472 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2474 if(coming_from_surface)
2476 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2479 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2480 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2482 // Randomize starting position
2484 (float)(myrand()%ar.X)+0.5,
2485 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2486 (float)(myrand()%ar.Z)+0.5
2489 MapNode airnode(CONTENT_AIR);
2492 Generate some tunnel starting from orp
2495 for(u16 j=0; j<tunnel_routepoints; j++)
2498 s16 min_d = min_tunnel_diameter;
2499 s16 max_d = max_tunnel_diameter;
2500 s16 rs = myrand_range(min_d, max_d);
2505 maxlen = v3s16(rs*7,rs*7,rs*7);
2509 maxlen = v3s16(15, myrand_range(1, 20), 15);
2514 if(coming_from_surface && j < 3)
2517 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2518 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2519 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2525 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2526 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2527 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2534 else if(rp.X >= ar.X)
2536 if(rp.Y < route_y_min)
2538 else if(rp.Y >= route_y_max)
2539 rp.Y = route_y_max-1;
2542 else if(rp.Z >= ar.Z)
2546 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2548 v3f fp = orp + vec * f;
2549 v3s16 cp(fp.X, fp.Y, fp.Z);
2552 s16 d1 = d0 + rs - 1;
2553 for(s16 z0=d0; z0<=d1; z0++)
2555 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2556 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2557 for(s16 x0=-si; x0<=si-1; x0++)
2559 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2560 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2561 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2562 //s16 si2 = rs - abs(x0);
2563 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2569 /*if(isInArea(p, ar) == false)
2571 // Check only height
2572 if(y < 0 || y >= ar.Y)
2576 //assert(vmanip.m_area.contains(p));
2577 if(vmanip.m_area.contains(p) == false)
2579 dstream<<"WARNING: "<<__FUNCTION_NAME
2580 <<":"<<__LINE__<<": "
2581 <<"point not in area"
2586 // Just set it to air, it will be changed to
2588 u32 i = vmanip.m_area.index(p);
2589 vmanip.m_data[i] = airnode;
2591 if(bruise_surface == false)
2594 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2609 //TimeTaker timer1("ore veins");
2614 for(u32 jj=0; jj<relative_volume/1000; jj++)
2616 s16 max_vein_diameter = 3;
2618 // Allowed route area size in nodes
2620 sectorpos_base_size*MAP_BLOCKSIZE,
2621 h_blocks*MAP_BLOCKSIZE,
2622 sectorpos_base_size*MAP_BLOCKSIZE
2625 // Area starting point in nodes
2627 sectorpos_base.X*MAP_BLOCKSIZE,
2628 y_blocks_min*MAP_BLOCKSIZE,
2629 sectorpos_base.Y*MAP_BLOCKSIZE
2633 //(this should be more than the maximum radius of the tunnel)
2635 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2636 ar += v3s16(1,0,1) * more * 2;
2637 of -= v3s16(1,0,1) * more;
2639 // Randomize starting position
2641 (float)(myrand()%ar.X)+0.5,
2642 (float)(myrand()%ar.Y)+0.5,
2643 (float)(myrand()%ar.Z)+0.5
2646 // Randomize mineral
2649 mineral = MINERAL_COAL;
2651 mineral = MINERAL_IRON;
2654 Generate some vein starting from orp
2657 for(u16 j=0; j<2; j++)
2660 (float)(myrand()%ar.X)+0.5,
2661 (float)(myrand()%ar.Y)+0.5,
2662 (float)(myrand()%ar.Z)+0.5
2664 v3f vec = rp - orp;*/
2666 v3s16 maxlen(5, 5, 5);
2668 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2669 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2670 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2675 else if(rp.X >= ar.X)
2679 else if(rp.Y >= ar.Y)
2683 else if(rp.Z >= ar.Z)
2689 s16 max_d = max_vein_diameter;
2690 s16 rs = myrand_range(min_d, max_d);
2692 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2694 v3f fp = orp + vec * f;
2695 v3s16 cp(fp.X, fp.Y, fp.Z);
2697 s16 d1 = d0 + rs - 1;
2698 for(s16 z0=d0; z0<=d1; z0++)
2700 s16 si = rs - abs(z0);
2701 for(s16 x0=-si; x0<=si-1; x0++)
2703 s16 si2 = rs - abs(x0);
2704 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2706 // Don't put mineral to every place
2714 /*if(isInArea(p, ar) == false)
2716 // Check only height
2717 if(y < 0 || y >= ar.Y)
2721 assert(vmanip.m_area.contains(p));
2723 // Just set it to air, it will be changed to
2725 u32 i = vmanip.m_area.index(p);
2726 MapNode *n = &vmanip.m_data[i];
2727 if(n->d == CONTENT_STONE)
2742 //TimeTaker timer1("add mud");
2745 Add mud to the central chunk
2748 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2749 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2751 // Node position in 2d
2752 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2754 // Randomize mud amount
2755 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2756 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2757 m_seed+1, 3, 0.55));
2759 // Find ground level
2760 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2763 If topmost node is grass, change it to mud.
2764 It might be if it was flown to there from a neighboring
2765 chunk and then converted.
2768 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2769 MapNode *n = &vmanip.m_data[i];
2770 if(n->d == CONTENT_GRASS)
2779 v3s16 em = vmanip.m_area.getExtent();
2780 s16 y_start = surface_y+1;
2781 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2782 for(s16 y=y_start; y<=y_nodes_max; y++)
2784 if(mudcount >= mud_add_amount)
2787 MapNode &n = vmanip.m_data[i];
2791 vmanip.m_area.add_y(em, i, 1);
2800 TimeTaker timer1("flow mud");
2803 Flow mud away from steep edges
2806 // Limit area by 1 because mud is flown into neighbors.
2807 s16 mudflow_minpos = 0-max_spread_amount+1;
2808 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2810 // Iterate a few times
2811 for(s16 k=0; k<3; k++)
2814 for(s16 x=mudflow_minpos;
2817 for(s16 z=mudflow_minpos;
2821 // Invert coordinates every 2nd iteration
2824 x = mudflow_maxpos - (x-mudflow_minpos);
2825 z = mudflow_maxpos - (z-mudflow_minpos);
2828 // Node position in 2d
2829 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2831 v3s16 em = vmanip.m_area.getExtent();
2832 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2839 for(; y>=y_nodes_min; y--)
2841 n = &vmanip.m_data[i];
2842 //if(content_walkable(n->d))
2844 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2847 vmanip.m_area.add_y(em, i, -1);
2850 // Stop if out of area
2851 //if(vmanip.m_area.contains(i) == false)
2855 /*// If not mud, do nothing to it
2856 MapNode *n = &vmanip.m_data[i];
2857 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2861 Don't flow it if the stuff under it is not mud
2865 vmanip.m_area.add_y(em, i2, -1);
2866 // Cancel if out of area
2867 if(vmanip.m_area.contains(i2) == false)
2869 MapNode *n2 = &vmanip.m_data[i2];
2870 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2874 // Make it exactly mud
2877 /*s16 recurse_count = 0;
2881 v3s16(0,0,1), // back
2882 v3s16(1,0,0), // right
2883 v3s16(0,0,-1), // front
2884 v3s16(-1,0,0), // left
2887 // Theck that upper is air or doesn't exist.
2888 // Cancel dropping if upper keeps it in place
2890 vmanip.m_area.add_y(em, i3, 1);
2891 if(vmanip.m_area.contains(i3) == true
2892 && content_walkable(vmanip.m_data[i3].d) == true)
2899 for(u32 di=0; di<4; di++)
2901 v3s16 dirp = dirs4[di];
2904 vmanip.m_area.add_p(em, i2, dirp);
2905 // Fail if out of area
2906 if(vmanip.m_area.contains(i2) == false)
2908 // Check that side is air
2909 MapNode *n2 = &vmanip.m_data[i2];
2910 if(content_walkable(n2->d))
2912 // Check that under side is air
2913 vmanip.m_area.add_y(em, i2, -1);
2914 if(vmanip.m_area.contains(i2) == false)
2916 n2 = &vmanip.m_data[i2];
2917 if(content_walkable(n2->d))
2919 /*// Check that under that is air (need a drop of 2)
2920 vmanip.m_area.add_y(em, i2, -1);
2921 if(vmanip.m_area.contains(i2) == false)
2923 n2 = &vmanip.m_data[i2];
2924 if(content_walkable(n2->d))
2926 // Loop further down until not air
2928 vmanip.m_area.add_y(em, i2, -1);
2929 // Fail if out of area
2930 if(vmanip.m_area.contains(i2) == false)
2932 n2 = &vmanip.m_data[i2];
2933 }while(content_walkable(n2->d) == false);
2934 // Loop one up so that we're in air
2935 vmanip.m_area.add_y(em, i2, 1);
2936 n2 = &vmanip.m_data[i2];
2938 // Move mud to new place
2940 // Set old place to be air
2941 *n = MapNode(CONTENT_AIR);
2954 //TimeTaker timer1("add water");
2957 Add water to the central chunk (and a bit more)
2960 for(s16 x=0-max_spread_amount;
2961 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2963 for(s16 z=0-max_spread_amount;
2964 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2967 // Node position in 2d
2968 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2970 // Find ground level
2971 //s16 surface_y = find_ground_level(vmanip, p2d);
2974 If ground level is over water level, skip.
2975 NOTE: This leaves caves near water without water,
2976 which looks especially crappy when the nearby water
2977 won't start flowing either for some reason
2979 /*if(surface_y > WATER_LEVEL)
2986 v3s16 em = vmanip.m_area.getExtent();
2987 u8 light = LIGHT_MAX;
2988 // Start at global water surface level
2989 s16 y_start = WATER_LEVEL;
2990 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2991 MapNode *n = &vmanip.m_data[i];
2993 /*// Add first one to transforming liquid queue, if water
2994 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2996 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2997 m_transforming_liquid.push_back(p);
3000 for(s16 y=y_start; y>=y_nodes_min; y--)
3002 n = &vmanip.m_data[i];
3004 // Stop when there is no water and no air
3005 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3006 && n->d != CONTENT_WATER)
3008 /*// Add bottom one to transforming liquid queue
3009 vmanip.m_area.add_y(em, i, 1);
3010 n = &vmanip.m_data[i];
3011 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3013 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3014 m_transforming_liquid.push_back(p);
3020 // Make water only not in dungeons
3021 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3023 n->d = CONTENT_WATERSOURCE;
3024 //n->setLight(LIGHTBANK_DAY, light);
3026 // Add to transforming liquid queue (in case it'd
3028 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3029 m_transforming_liquid.push_back(p);
3033 vmanip.m_area.add_y(em, i, -1);
3046 //TimeTaker timer1("convert mud to sand");
3052 //s16 mud_add_amount = myrand_range(2, 4);
3053 //s16 mud_add_amount = 0;
3055 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3056 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3057 for(s16 x=0-max_spread_amount+1;
3058 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3060 for(s16 z=0-max_spread_amount+1;
3061 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3064 // Node position in 2d
3065 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3067 // Determine whether to have sand here
3068 double sandnoise = noise2d_perlin(
3069 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3070 m_seed+59420, 3, 0.50);
3072 bool have_sand = (sandnoise > -0.15);
3074 if(have_sand == false)
3077 // Find ground level
3078 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3080 if(surface_y > WATER_LEVEL + 2)
3084 v3s16 em = vmanip.m_area.getExtent();
3085 s16 y_start = surface_y;
3086 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3087 u32 not_sand_counter = 0;
3088 for(s16 y=y_start; y>=y_nodes_min; y--)
3090 MapNode *n = &vmanip.m_data[i];
3091 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3093 n->d = CONTENT_SAND;
3098 if(not_sand_counter > 3)
3102 vmanip.m_area.add_y(em, i, -1);
3111 //TimeTaker timer1("generate trees");
3117 // Divide area into parts
3119 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3120 double area = sidelen * sidelen;
3121 for(s16 x0=0; x0<div; x0++)
3122 for(s16 z0=0; z0<div; z0++)
3124 // Center position of part of division
3126 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3127 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3129 // Minimum edge of part of division
3131 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3132 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3134 // Maximum edge of part of division
3136 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3137 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3140 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3141 // Put trees in random places on part of division
3142 for(u32 i=0; i<tree_count; i++)
3144 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3145 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3146 s16 y = find_ground_level(vmanip, v2s16(x,z));
3147 // Don't make a tree under water level
3150 // Don't make a tree so high that it doesn't fit
3151 if(y > y_nodes_max - 6)
3155 Trees grow only on mud and grass
3158 u32 i = vmanip.m_area.index(v3s16(p));
3159 MapNode *n = &vmanip.m_data[i];
3160 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3165 make_tree(vmanip, p);
3168 /*u32 tree_max = relative_area / 60;
3169 //u32 count = myrand_range(0, tree_max);
3170 for(u32 i=0; i<count; i++)
3172 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3173 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3174 x += sectorpos_base.X*MAP_BLOCKSIZE;
3175 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3176 s16 y = find_ground_level(vmanip, v2s16(x,z));
3177 // Don't make a tree under water level
3182 make_tree(vmanip, p);
3190 //TimeTaker timer1("grow grass");
3196 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3197 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3198 for(s16 x=0-max_spread_amount;
3199 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3201 for(s16 z=0-max_spread_amount;
3202 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3205 // Node position in 2d
3206 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3209 Find the lowest surface to which enough light ends up
3212 Basically just wait until not air and not leaves.
3216 v3s16 em = vmanip.m_area.getExtent();
3217 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3219 // Go to ground level
3220 for(y=y_nodes_max; y>=y_nodes_min; y--)
3222 MapNode &n = vmanip.m_data[i];
3223 if(n.d != CONTENT_AIR
3224 && n.d != CONTENT_LEAVES)
3226 vmanip.m_area.add_y(em, i, -1);
3228 if(y >= y_nodes_min)
3231 surface_y = y_nodes_min;
3234 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3235 MapNode *n = &vmanip.m_data[i];
3236 if(n->d == CONTENT_MUD)
3237 n->d = CONTENT_GRASS;
3243 Initial lighting (sunlight)
3246 core::map<v3s16, bool> light_sources;
3249 // 750ms @cs=8, can't optimize more
3250 TimeTaker timer1("initial lighting");
3254 Go through the edges and add all nodes that have light to light_sources
3258 for(s16 i=0; i<4; i++)
3260 for(s16 j=lighting_min_d;
3267 if(i == 0 || i == 1)
3269 x = (i==0) ? lighting_min_d : lighting_max_d;
3278 z = (i==0) ? lighting_min_d : lighting_max_d;
3285 // Node position in 2d
3286 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3289 v3s16 em = vmanip.m_area.getExtent();
3290 s16 y_start = y_nodes_max;
3291 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3292 for(s16 y=y_start; y>=y_nodes_min; y--)
3294 MapNode *n = &vmanip.m_data[i];
3295 if(n->getLight(LIGHTBANK_DAY) != 0)
3297 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3299 //NOTE: This is broken, at least the index has to
3308 Go through the edges and apply sunlight to them, not caring
3313 for(s16 i=0; i<4; i++)
3315 for(s16 j=lighting_min_d;
3322 if(i == 0 || i == 1)
3324 x = (i==0) ? lighting_min_d : lighting_max_d;
3333 z = (i==0) ? lighting_min_d : lighting_max_d;
3340 // Node position in 2d
3341 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3343 // Loop from top to down
3345 u8 light = LIGHT_SUN;
3346 v3s16 em = vmanip.m_area.getExtent();
3347 s16 y_start = y_nodes_max;
3348 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3349 for(s16 y=y_start; y>=y_nodes_min; y--)
3351 MapNode *n = &vmanip.m_data[i];
3352 if(light_propagates_content(n->d) == false)
3356 else if(light != LIGHT_SUN
3357 || sunlight_propagates_content(n->d) == false)
3363 n->setLight(LIGHTBANK_DAY, light);
3364 n->setLight(LIGHTBANK_NIGHT, 0);
3368 // Insert light source
3369 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3372 // Increment index by y
3373 vmanip.m_area.add_y(em, i, -1);
3379 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3380 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3381 /*for(s16 x=0-max_spread_amount+1;
3382 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3384 for(s16 z=0-max_spread_amount+1;
3385 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3389 This has to be 1 smaller than the actual area, because
3390 neighboring nodes are checked.
3392 for(s16 x=lighting_min_d+1;
3393 x<=lighting_max_d-1;
3395 for(s16 z=lighting_min_d+1;
3396 z<=lighting_max_d-1;
3399 // Node position in 2d
3400 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3403 Apply initial sunlight
3406 u8 light = LIGHT_SUN;
3407 bool add_to_sources = false;
3408 v3s16 em = vmanip.m_area.getExtent();
3409 s16 y_start = y_nodes_max;
3410 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3411 for(s16 y=y_start; y>=y_nodes_min; y--)
3413 MapNode *n = &vmanip.m_data[i];
3415 if(light_propagates_content(n->d) == false)
3419 else if(light != LIGHT_SUN
3420 || sunlight_propagates_content(n->d) == false)
3426 // This doesn't take much time
3427 if(add_to_sources == false)
3430 Check sides. If side is not air or water, start
3431 adding to light_sources.
3434 v3s16(0,0,1), // back
3435 v3s16(1,0,0), // right
3436 v3s16(0,0,-1), // front
3437 v3s16(-1,0,0), // left
3439 for(u32 di=0; di<4; di++)
3441 v3s16 dirp = dirs4[di];
3443 vmanip.m_area.add_p(em, i2, dirp);
3444 MapNode *n2 = &vmanip.m_data[i2];
3446 n2->d != CONTENT_AIR
3447 && n2->d != CONTENT_WATERSOURCE
3448 && n2->d != CONTENT_WATER
3450 add_to_sources = true;
3456 n->setLight(LIGHTBANK_DAY, light);
3457 n->setLight(LIGHTBANK_NIGHT, 0);
3459 // This doesn't take much time
3460 if(light != 0 && add_to_sources)
3462 // Insert light source
3463 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3466 // Increment index by y
3467 vmanip.m_area.add_y(em, i, -1);
3474 for(s16 x=lighting_min_d+1;
3475 x<=lighting_max_d-1;
3477 for(s16 z=lighting_min_d+1;
3478 z<=lighting_max_d-1;
3481 // Node position in 2d
3482 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3485 Apply initial sunlight
3488 u8 light = LIGHT_SUN;
3489 v3s16 em = vmanip.m_area.getExtent();
3490 s16 y_start = y_nodes_max;
3491 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3492 for(s16 y=y_start; y>=y_nodes_min; y--)
3494 MapNode *n = &vmanip.m_data[i];
3496 if(light_propagates_content(n->d) == false)
3500 else if(light != LIGHT_SUN
3501 || sunlight_propagates_content(n->d) == false)
3507 n->setLight(LIGHTBANK_DAY, light);
3508 n->setLight(LIGHTBANK_NIGHT, 0);
3510 // This doesn't take much time
3513 // Insert light source
3514 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3517 // Increment index by y
3518 vmanip.m_area.add_y(em, i, -1);
3526 // Spread light around
3528 TimeTaker timer("generateChunkRaw() spreadLight");
3529 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3536 timer_generate.stop();
3539 Blit generated stuff to map
3543 //TimeTaker timer("generateChunkRaw() blitBackAll");
3544 vmanip.blitBackAll(&changed_blocks);
3548 Update day/night difference cache of the MapBlocks
3551 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3552 i.atEnd() == false; i++)
3554 MapBlock *block = i.getNode()->getValue();
3555 block->updateDayNightDiff();
3561 Create chunk metadata
3564 for(s16 x=-1; x<=1; x++)
3565 for(s16 y=-1; y<=1; y++)
3567 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3568 // Add chunk meta information
3569 MapChunk *chunk = getChunk(chunkpos0);
3572 chunk = new MapChunk();
3573 m_chunks.insert(chunkpos0, chunk);
3575 //chunk->setIsVolatile(true);
3576 if(chunk->getGenLevel() > GENERATED_PARTLY)
3577 chunk->setGenLevel(GENERATED_PARTLY);
3581 Set central chunk non-volatile
3583 MapChunk *chunk = getChunk(chunkpos);
3586 //chunk->setIsVolatile(false);
3587 chunk->setGenLevel(GENERATED_FULLY);
3590 Save changed parts of map
3595 Return central chunk (which was requested)
3600 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3601 core::map<v3s16, MapBlock*> &changed_blocks)
3603 dstream<<"generateChunk(): Generating chunk "
3604 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3607 /*for(s16 x=-1; x<=1; x++)
3608 for(s16 y=-1; y<=1; y++)*/
3609 for(s16 x=-0; x<=0; x++)
3610 for(s16 y=-0; y<=0; y++)
3612 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3613 MapChunk *chunk = getChunk(chunkpos0);
3614 // Skip if already generated
3615 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3617 generateChunkRaw(chunkpos0, changed_blocks);
3620 assert(chunkNonVolatile(chunkpos1));
3622 MapChunk *chunk = getChunk(chunkpos1);
3626 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3628 DSTACK("%s: p2d=(%d,%d)",
3633 Check if it exists already in memory
3635 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3640 Try to load it from disk (with blocks)
3642 if(loadSectorFull(p2d) == true)
3644 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3647 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3648 throw InvalidPositionException("");
3654 Do not create over-limit
3656 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3657 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3658 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3659 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3660 throw InvalidPositionException("createSector(): pos. over limit");
3663 Generate blank sector
3666 sector = new ServerMapSector(this, p2d);
3668 // Sector position on map in nodes
3669 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3674 m_sectors.insert(p2d, sector);
3679 MapSector * ServerMap::emergeSector(v2s16 p2d,
3680 core::map<v3s16, MapBlock*> &changed_blocks)
3682 DSTACK("%s: p2d=(%d,%d)",
3689 v2s16 chunkpos = sector_to_chunk(p2d);
3690 /*bool chunk_nonvolatile = false;
3691 MapChunk *chunk = getChunk(chunkpos);
3692 if(chunk && chunk->getIsVolatile() == false)
3693 chunk_nonvolatile = true;*/
3694 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3697 If chunk is not fully generated, generate chunk
3699 if(chunk_nonvolatile == false)
3701 // Generate chunk and neighbors
3702 generateChunk(chunkpos, changed_blocks);
3706 Return sector if it exists now
3708 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3713 Try to load it from disk
3715 if(loadSectorFull(p2d) == true)
3717 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3720 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3721 throw InvalidPositionException("");
3727 generateChunk should have generated the sector
3731 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3732 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3736 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3738 return createSector(p2d);
3743 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3746 generateChunkRaw(chunkpos, changed_blocks, true);
3749 Return sector if it exists now
3751 sector = getSectorNoGenerateNoEx(p2d);
3755 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3763 //return generateSector();
3767 NOTE: This is not used for main map generation, only for blocks
3768 that are very high or low
3770 MapBlock * ServerMap::generateBlock(
3772 MapBlock *original_dummy,
3773 ServerMapSector *sector,
3774 core::map<v3s16, MapBlock*> &changed_blocks,
3775 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3778 DSTACK("%s: p=(%d,%d,%d)",
3782 /*dstream<<"generateBlock(): "
3783 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3786 MapBlock *block = original_dummy;
3788 v2s16 p2d(p.X, p.Z);
3790 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3793 Do not generate over-limit
3795 if(blockpos_over_limit(p))
3797 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3798 throw InvalidPositionException("generateBlock(): pos. over limit");
3802 If block doesn't exist, create one.
3803 If it exists, it is a dummy. In that case unDummify() it.
3805 NOTE: This already sets the map as the parent of the block
3809 block = sector->createBlankBlockNoInsert(block_y);
3813 // Remove the block so that nobody can get a half-generated one.
3814 sector->removeBlock(block);
3815 // Allocate the block to contain the generated data
3819 u8 water_material = CONTENT_WATERSOURCE;
3821 s32 lowest_ground_y = 32767;
3822 s32 highest_ground_y = -32768;
3824 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3825 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3827 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3829 //s16 surface_y = 0;
3831 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3832 + AVERAGE_MUD_AMOUNT;
3834 if(surface_y < lowest_ground_y)
3835 lowest_ground_y = surface_y;
3836 if(surface_y > highest_ground_y)
3837 highest_ground_y = surface_y;
3839 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3841 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3843 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3848 NOTE: If there are some man-made structures above the
3849 newly created block, they won't be taken into account.
3851 if(real_y > surface_y)
3852 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3858 // If node is over heightmap y, it's air or water
3859 if(real_y > surface_y)
3861 // If under water level, it's water
3862 if(real_y < WATER_LEVEL)
3864 n.d = water_material;
3865 n.setLight(LIGHTBANK_DAY,
3866 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3868 Add to transforming liquid queue (in case it'd
3871 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3872 m_transforming_liquid.push_back(real_pos);
3878 // Else it's ground or dungeons (air)
3881 // If it's surface_depth under ground, it's stone
3882 if(real_y <= surface_y - surface_depth)
3884 n.d = CONTENT_STONE;
3888 // It is mud if it is under the first ground
3889 // level or under water
3890 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3896 n.d = CONTENT_GRASS;
3899 //n.d = CONTENT_MUD;
3901 /*// If under water level, it's mud
3902 if(real_y < WATER_LEVEL)
3904 // Only the topmost node is grass
3905 else if(real_y <= surface_y - 1)
3908 n.d = CONTENT_GRASS;*/
3912 block->setNode(v3s16(x0,y0,z0), n);
3917 Calculate some helper variables
3920 // Completely underground if the highest part of block is under lowest
3922 // This has to be very sure; it's probably one too strict now but
3923 // that's just better.
3924 bool completely_underground =
3925 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3927 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3929 bool mostly_underwater_surface = false;
3930 if(highest_ground_y < WATER_LEVEL
3931 && some_part_underground && !completely_underground)
3932 mostly_underwater_surface = true;
3935 Get local attributes
3938 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3940 float caves_amount = 0.5;
3945 NOTE: BEWARE: Too big amount of attribute points slows verything
3947 1 interpolation from 5000 points takes 2-3ms.
3949 //TimeTaker timer("generateBlock() local attribute retrieval");
3950 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3951 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3952 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3956 //dstream<<"generateBlock(): Done"<<std::endl;
3962 // Initialize temporary table
3963 const s32 ued = MAP_BLOCKSIZE;
3964 bool underground_emptiness[ued*ued*ued];
3965 for(s32 i=0; i<ued*ued*ued; i++)
3967 underground_emptiness[i] = 0;
3974 Initialize orp and ors. Try to find if some neighboring
3975 MapBlock has a tunnel ended in its side
3979 (float)(myrand()%ued)+0.5,
3980 (float)(myrand()%ued)+0.5,
3981 (float)(myrand()%ued)+0.5
3984 bool found_existing = false;
3990 for(s16 y=0; y<ued; y++)
3991 for(s16 x=0; x<ued; x++)
3993 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3994 if(getNode(ap).d == CONTENT_AIR)
3996 orp = v3f(x+1,y+1,0);
3997 found_existing = true;
3998 goto continue_generating;
4002 catch(InvalidPositionException &e){}
4008 for(s16 y=0; y<ued; y++)
4009 for(s16 x=0; x<ued; x++)
4011 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4012 if(getNode(ap).d == CONTENT_AIR)
4014 orp = v3f(x+1,y+1,ued-1);
4015 found_existing = true;
4016 goto continue_generating;
4020 catch(InvalidPositionException &e){}
4026 for(s16 y=0; y<ued; y++)
4027 for(s16 z=0; z<ued; z++)
4029 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4030 if(getNode(ap).d == CONTENT_AIR)
4032 orp = v3f(0,y+1,z+1);
4033 found_existing = true;
4034 goto continue_generating;
4038 catch(InvalidPositionException &e){}
4044 for(s16 y=0; y<ued; y++)
4045 for(s16 z=0; z<ued; z++)
4047 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4048 if(getNode(ap).d == CONTENT_AIR)
4050 orp = v3f(ued-1,y+1,z+1);
4051 found_existing = true;
4052 goto continue_generating;
4056 catch(InvalidPositionException &e){}
4062 for(s16 x=0; x<ued; x++)
4063 for(s16 z=0; z<ued; z++)
4065 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4066 if(getNode(ap).d == CONTENT_AIR)
4068 orp = v3f(x+1,0,z+1);
4069 found_existing = true;
4070 goto continue_generating;
4074 catch(InvalidPositionException &e){}
4080 for(s16 x=0; x<ued; x++)
4081 for(s16 z=0; z<ued; z++)
4083 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4084 if(getNode(ap).d == CONTENT_AIR)
4086 orp = v3f(x+1,ued-1,z+1);
4087 found_existing = true;
4088 goto continue_generating;
4092 catch(InvalidPositionException &e){}
4094 continue_generating:
4097 Choose whether to actually generate dungeon
4099 bool do_generate_dungeons = true;
4100 // Don't generate if no part is underground
4101 if(!some_part_underground)
4103 do_generate_dungeons = false;
4105 // Don't generate if mostly underwater surface
4106 /*else if(mostly_underwater_surface)
4108 do_generate_dungeons = false;
4110 // Partly underground = cave
4111 else if(!completely_underground)
4113 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4115 // Found existing dungeon underground
4116 else if(found_existing && completely_underground)
4118 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4120 // Underground and no dungeons found
4123 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4126 if(do_generate_dungeons)
4129 Generate some tunnel starting from orp and ors
4131 for(u16 i=0; i<3; i++)
4134 (float)(myrand()%ued)+0.5,
4135 (float)(myrand()%ued)+0.5,
4136 (float)(myrand()%ued)+0.5
4140 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4144 for(float f=0; f<1.0; f+=0.04)
4146 v3f fp = orp + vec * f;
4147 v3s16 cp(fp.X, fp.Y, fp.Z);
4149 s16 d1 = d0 + rs - 1;
4150 for(s16 z0=d0; z0<=d1; z0++)
4152 s16 si = rs - abs(z0);
4153 for(s16 x0=-si; x0<=si-1; x0++)
4155 s16 si2 = rs - abs(x0);
4156 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4162 if(isInArea(p, ued) == false)
4164 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4176 // Set to true if has caves.
4177 // Set when some non-air is changed to air when making caves.
4178 bool has_dungeons = false;
4181 Apply temporary cave data to block
4184 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4185 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4187 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4189 MapNode n = block->getNode(v3s16(x0,y0,z0));
4192 if(underground_emptiness[
4193 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4194 +ued*(y0*ued/MAP_BLOCKSIZE)
4195 +(x0*ued/MAP_BLOCKSIZE)])
4197 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4200 has_dungeons = true;
4206 block->setNode(v3s16(x0,y0,z0), n);
4211 This is used for guessing whether or not the block should
4212 receive sunlight from the top if the block above doesn't exist
4214 block->setIsUnderground(completely_underground);
4217 Force lighting update if some part of block is partly
4218 underground and has caves.
4220 /*if(some_part_underground && !completely_underground && has_dungeons)
4222 //dstream<<"Half-ground caves"<<std::endl;
4223 lighting_invalidated_blocks[block->getPos()] = block;
4226 // DEBUG: Always update lighting
4227 //lighting_invalidated_blocks[block->getPos()] = block;
4233 if(some_part_underground)
4235 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4240 for(s16 i=0; i<underground_level/4 + 1; i++)
4242 if(myrand()%50 == 0)
4245 (myrand()%(MAP_BLOCKSIZE-2))+1,
4246 (myrand()%(MAP_BLOCKSIZE-2))+1,
4247 (myrand()%(MAP_BLOCKSIZE-2))+1
4253 for(u16 i=0; i<27; i++)
4255 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4257 block->setNode(cp+g_27dirs[i], n);
4265 u16 coal_amount = 30;
4266 u16 coal_rareness = 60 / coal_amount;
4267 if(coal_rareness == 0)
4269 if(myrand()%coal_rareness == 0)
4271 u16 a = myrand() % 16;
4272 u16 amount = coal_amount * a*a*a / 1000;
4273 for(s16 i=0; i<amount; i++)
4276 (myrand()%(MAP_BLOCKSIZE-2))+1,
4277 (myrand()%(MAP_BLOCKSIZE-2))+1,
4278 (myrand()%(MAP_BLOCKSIZE-2))+1
4282 n.d = CONTENT_STONE;
4283 n.param = MINERAL_COAL;
4285 for(u16 i=0; i<27; i++)
4287 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4289 block->setNode(cp+g_27dirs[i], n);
4297 //TODO: change to iron_amount or whatever
4298 u16 iron_amount = 15;
4299 u16 iron_rareness = 60 / iron_amount;
4300 if(iron_rareness == 0)
4302 if(myrand()%iron_rareness == 0)
4304 u16 a = myrand() % 16;
4305 u16 amount = iron_amount * a*a*a / 1000;
4306 for(s16 i=0; i<amount; i++)
4309 (myrand()%(MAP_BLOCKSIZE-2))+1,
4310 (myrand()%(MAP_BLOCKSIZE-2))+1,
4311 (myrand()%(MAP_BLOCKSIZE-2))+1
4315 n.d = CONTENT_STONE;
4316 n.param = MINERAL_IRON;
4318 for(u16 i=0; i<27; i++)
4320 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4322 block->setNode(cp+g_27dirs[i], n);
4329 Create a few rats in empty blocks underground
4331 if(completely_underground)
4333 //for(u16 i=0; i<2; i++)
4336 (myrand()%(MAP_BLOCKSIZE-2))+1,
4337 (myrand()%(MAP_BLOCKSIZE-2))+1,
4338 (myrand()%(MAP_BLOCKSIZE-2))+1
4341 // Check that the place is empty
4342 //if(!is_ground_content(block->getNode(cp).d))
4345 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4346 block->addObject(obj);
4352 Add block to sector.
4354 sector->insertBlock(block);
4356 // Lighting is invalid after generation.
4357 block->setLightingExpired(true);
4364 <<"lighting_invalidated_blocks.size()"
4368 <<" "<<lighting_invalidated_blocks.size()
4369 <<", "<<has_dungeons
4370 <<", "<<completely_underground
4371 <<", "<<some_part_underground
4378 MapBlock * ServerMap::createBlock(v3s16 p)
4380 DSTACK("%s: p=(%d,%d,%d)",
4381 __FUNCTION_NAME, p.X, p.Y, p.Z);
4384 Do not create over-limit
4386 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4387 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4388 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4389 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4390 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4391 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4392 throw InvalidPositionException("createBlock(): pos. over limit");
4394 v2s16 p2d(p.X, p.Z);
4397 This will create or load a sector if not found in memory.
4398 If block exists on disk, it will be loaded.
4400 NOTE: On old save formats, this will be slow, as it generates
4401 lighting on blocks for them.
4403 ServerMapSector *sector;
4405 sector = (ServerMapSector*)createSector(p2d);
4406 assert(sector->getId() == MAPSECTOR_SERVER);
4408 catch(InvalidPositionException &e)
4410 dstream<<"createBlock: createSector() failed"<<std::endl;
4414 NOTE: This should not be done, or at least the exception
4415 should not be passed on as std::exception, because it
4416 won't be catched at all.
4418 /*catch(std::exception &e)
4420 dstream<<"createBlock: createSector() failed: "
4421 <<e.what()<<std::endl;
4426 Try to get a block from the sector
4429 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4433 block = sector->createBlankBlock(block_y);
4437 MapBlock * ServerMap::emergeBlock(
4439 bool only_from_disk,
4440 core::map<v3s16, MapBlock*> &changed_blocks,
4441 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4444 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4446 p.X, p.Y, p.Z, only_from_disk);
4449 Do not generate over-limit
4451 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4452 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4453 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4454 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4455 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4456 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4457 throw InvalidPositionException("emergeBlock(): pos. over limit");
4459 v2s16 p2d(p.X, p.Z);
4462 This will create or load a sector if not found in memory.
4463 If block exists on disk, it will be loaded.
4465 ServerMapSector *sector;
4467 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4468 assert(sector->getId() == MAPSECTOR_SERVER);
4470 catch(InvalidPositionException &e)
4472 dstream<<"emergeBlock: emergeSector() failed: "
4473 <<e.what()<<std::endl;
4474 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4476 <<"You could try to delete it."<<std::endl;
4479 catch(VersionMismatchException &e)
4481 dstream<<"emergeBlock: emergeSector() failed: "
4482 <<e.what()<<std::endl;
4483 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4485 <<"You could try to delete it."<<std::endl;
4489 NOTE: This should not be done, or at least the exception
4490 should not be passed on as std::exception, because it
4491 won't be catched at all.
4493 /*catch(std::exception &e)
4495 dstream<<"emergeBlock: emergeSector() failed: "
4496 <<e.what()<<std::endl;
4497 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4499 <<"You could try to delete it."<<std::endl;
4504 Try to get a block from the sector
4507 bool does_not_exist = false;
4508 bool lighting_expired = false;
4509 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4513 does_not_exist = true;
4515 else if(block->isDummy() == true)
4517 does_not_exist = true;
4519 else if(block->getLightingExpired())
4521 lighting_expired = true;
4526 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4531 If block was not found on disk and not going to generate a
4532 new one, make sure there is a dummy block in place.
4534 if(only_from_disk && (does_not_exist || lighting_expired))
4536 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4540 // Create dummy block
4541 block = new MapBlock(this, p, true);
4543 // Add block to sector
4544 sector->insertBlock(block);
4550 //dstream<<"Not found on disk, generating."<<std::endl;
4552 //TimeTaker("emergeBlock() generate");
4554 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4557 If the block doesn't exist, generate the block.
4561 block = generateBlock(p, block, sector, changed_blocks,
4562 lighting_invalidated_blocks);
4565 if(lighting_expired)
4567 lighting_invalidated_blocks.insert(p, block);
4571 Initially update sunlight
4575 core::map<v3s16, bool> light_sources;
4576 bool black_air_left = false;
4577 bool bottom_invalid =
4578 block->propagateSunlight(light_sources, true,
4579 &black_air_left, true);
4581 // If sunlight didn't reach everywhere and part of block is
4582 // above ground, lighting has to be properly updated
4583 //if(black_air_left && some_part_underground)
4586 lighting_invalidated_blocks[block->getPos()] = block;
4591 lighting_invalidated_blocks[block->getPos()] = block;
4598 s16 ServerMap::findGroundLevel(v2s16 p2d)
4601 Uh, just do something random...
4603 // Find existing map from top to down
4606 v3s16 p(p2d.X, max, p2d.Y);
4607 for(; p.Y>min; p.Y--)
4609 MapNode n = getNodeNoEx(p);
4610 if(n.d != CONTENT_IGNORE)
4615 // If this node is not air, go to plan b
4616 if(getNodeNoEx(p).d != CONTENT_AIR)
4618 // Search existing walkable and return it
4619 for(; p.Y>min; p.Y--)
4621 MapNode n = getNodeNoEx(p);
4622 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4628 Plan B: Get from map generator perlin noise function
4630 double level = base_rock_level_2d(m_seed, p2d);
4634 void ServerMap::createDir(std::string path)
4636 if(fs::CreateDir(path) == false)
4638 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4639 <<"\""<<path<<"\""<<std::endl;
4640 throw BaseException("ServerMap failed to create directory");
4644 std::string ServerMap::getSectorSubDir(v2s16 pos)
4647 snprintf(cc, 9, "%.4x%.4x",
4648 (unsigned int)pos.X&0xffff,
4649 (unsigned int)pos.Y&0xffff);
4651 return std::string(cc);
4654 std::string ServerMap::getSectorDir(v2s16 pos)
4656 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4659 v2s16 ServerMap::getSectorPos(std::string dirname)
4661 if(dirname.size() != 8)
4662 throw InvalidFilenameException("Invalid sector directory name");
4664 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4666 throw InvalidFilenameException("Invalid sector directory name");
4667 v2s16 pos((s16)x, (s16)y);
4671 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4673 v2s16 p2d = getSectorPos(sectordir);
4675 if(blockfile.size() != 4){
4676 throw InvalidFilenameException("Invalid block filename");
4679 int r = sscanf(blockfile.c_str(), "%4x", &y);
4681 throw InvalidFilenameException("Invalid block filename");
4682 return v3s16(p2d.X, y, p2d.Y);
4685 void ServerMap::save(bool only_changed)
4687 DSTACK(__FUNCTION_NAME);
4688 if(m_map_saving_enabled == false)
4690 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4694 if(only_changed == false)
4695 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4701 u32 sector_meta_count = 0;
4702 u32 block_count = 0;
4705 JMutexAutoLock lock(m_sector_mutex);
4707 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4708 for(; i.atEnd() == false; i++)
4710 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4711 assert(sector->getId() == MAPSECTOR_SERVER);
4713 if(sector->differs_from_disk || only_changed == false)
4715 saveSectorMeta(sector);
4716 sector_meta_count++;
4718 core::list<MapBlock*> blocks;
4719 sector->getBlocks(blocks);
4720 core::list<MapBlock*>::Iterator j;
4721 for(j=blocks.begin(); j!=blocks.end(); j++)
4723 MapBlock *block = *j;
4724 if(block->getChangedFlag() || only_changed == false)
4729 /*dstream<<"ServerMap: Written block ("
4730 <<block->getPos().X<<","
4731 <<block->getPos().Y<<","
4732 <<block->getPos().Z<<")"
4741 Only print if something happened or saved whole map
4743 if(only_changed == false || sector_meta_count != 0
4744 || block_count != 0)
4746 dstream<<DTIME<<"ServerMap: Written: "
4747 <<sector_meta_count<<" sector metadata files, "
4748 <<block_count<<" block files"
4753 void ServerMap::loadAll()
4755 DSTACK(__FUNCTION_NAME);
4756 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4761 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4763 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4765 JMutexAutoLock lock(m_sector_mutex);
4768 s32 printed_counter = -100000;
4769 s32 count = list.size();
4771 std::vector<fs::DirListNode>::iterator i;
4772 for(i=list.begin(); i!=list.end(); i++)
4774 if(counter > printed_counter + 10)
4776 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4777 printed_counter = counter;
4781 MapSector *sector = NULL;
4783 // We want directories
4787 sector = loadSectorMeta(i->name);
4789 catch(InvalidFilenameException &e)
4791 // This catches unknown crap in directory
4794 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4795 (m_savedir+"/sectors/"+i->name);
4796 std::vector<fs::DirListNode>::iterator i2;
4797 for(i2=list2.begin(); i2!=list2.end(); i2++)
4803 loadBlock(i->name, i2->name, sector);
4805 catch(InvalidFilenameException &e)
4807 // This catches unknown crap in directory
4811 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4815 void ServerMap::saveMasterHeightmap()
4817 DSTACK(__FUNCTION_NAME);
4819 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4821 createDir(m_savedir);
4823 /*std::string fullpath = m_savedir + "/master_heightmap";
4824 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4825 if(o.good() == false)
4826 throw FileNotGoodException("Cannot open master heightmap");*/
4828 // Format used for writing
4829 //u8 version = SER_FMT_VER_HIGHEST;
4832 void ServerMap::loadMasterHeightmap()
4834 DSTACK(__FUNCTION_NAME);
4836 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4838 /*std::string fullpath = m_savedir + "/master_heightmap";
4839 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4840 if(is.good() == false)
4841 throw FileNotGoodException("Cannot open master heightmap");*/
4845 void ServerMap::saveMapMeta()
4847 DSTACK(__FUNCTION_NAME);
4849 dstream<<"INFO: ServerMap::saveMapMeta(): "
4850 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4853 createDir(m_savedir);
4855 std::string fullpath = m_savedir + "/map_meta.txt";
4856 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4857 if(os.good() == false)
4859 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4860 <<"could not open"<<fullpath<<std::endl;
4861 throw FileNotGoodException("Cannot open chunk metadata");
4865 params.setU64("seed", m_seed);
4866 params.setS32("chunksize", m_chunksize);
4868 params.writeLines(os);
4870 os<<"[end_of_params]\n";
4874 void ServerMap::loadMapMeta()
4876 DSTACK(__FUNCTION_NAME);
4878 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4881 std::string fullpath = m_savedir + "/map_meta.txt";
4882 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4883 if(is.good() == false)
4885 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4886 <<"could not open"<<fullpath<<std::endl;
4887 throw FileNotGoodException("Cannot open chunk metadata");
4895 throw SerializationError
4896 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4898 std::getline(is, line);
4899 std::string trimmedline = trim(line);
4900 if(trimmedline == "[end_of_params]")
4902 params.parseConfigLine(line);
4905 m_seed = params.getU64("seed");
4906 m_chunksize = params.getS32("chunksize");
4908 dstream<<"INFO: ServerMap::loadMapMeta(): "
4909 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4913 void ServerMap::saveChunkMeta()
4915 DSTACK(__FUNCTION_NAME);
4917 u32 count = m_chunks.size();
4919 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4920 <<count<<" chunks"<<std::endl;
4922 createDir(m_savedir);
4924 std::string fullpath = m_savedir + "/chunk_meta";
4925 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4926 if(os.good() == false)
4928 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4929 <<"could not open"<<fullpath<<std::endl;
4930 throw FileNotGoodException("Cannot open chunk metadata");
4936 os.write((char*)&version, 1);
4941 writeU32(buf, count);
4942 os.write((char*)buf, 4);
4944 for(core::map<v2s16, MapChunk*>::Iterator
4945 i = m_chunks.getIterator();
4946 i.atEnd()==false; i++)
4948 v2s16 p = i.getNode()->getKey();
4949 MapChunk *chunk = i.getNode()->getValue();
4952 os.write((char*)buf, 4);
4954 chunk->serialize(os, version);
4958 void ServerMap::loadChunkMeta()
4960 DSTACK(__FUNCTION_NAME);
4962 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
4965 std::string fullpath = m_savedir + "/chunk_meta";
4966 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4967 if(is.good() == false)
4969 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
4970 <<"could not open"<<fullpath<<std::endl;
4971 throw FileNotGoodException("Cannot open chunk metadata");
4977 is.read((char*)&version, 1);
4982 is.read((char*)buf, 4);
4983 u32 count = readU32(buf);
4985 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
4986 <<count<<" chunks"<<std::endl;
4988 for(u32 i=0; i<count; i++)
4991 MapChunk *chunk = new MapChunk();
4993 is.read((char*)buf, 4);
4996 chunk->deSerialize(is, version);
4997 m_chunks.insert(p, chunk);
5001 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5003 DSTACK(__FUNCTION_NAME);
5004 // Format used for writing
5005 u8 version = SER_FMT_VER_HIGHEST;
5007 v2s16 pos = sector->getPos();
5008 createDir(m_savedir);
5009 createDir(m_savedir+"/sectors");
5010 std::string dir = getSectorDir(pos);
5013 std::string fullpath = dir + "/meta";
5014 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5015 if(o.good() == false)
5016 throw FileNotGoodException("Cannot open sector metafile");
5018 sector->serialize(o, version);
5020 sector->differs_from_disk = false;
5023 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5025 DSTACK(__FUNCTION_NAME);
5027 v2s16 p2d = getSectorPos(dirname);
5028 std::string dir = m_savedir + "/sectors/" + dirname;
5030 std::string fullpath = dir + "/meta";
5031 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5032 if(is.good() == false)
5033 throw FileNotGoodException("Cannot open sector metafile");
5035 ServerMapSector *sector = ServerMapSector::deSerialize
5036 (is, this, p2d, m_sectors);
5038 sector->differs_from_disk = false;
5043 bool ServerMap::loadSectorFull(v2s16 p2d)
5045 DSTACK(__FUNCTION_NAME);
5046 std::string sectorsubdir = getSectorSubDir(p2d);
5048 MapSector *sector = NULL;
5050 JMutexAutoLock lock(m_sector_mutex);
5053 sector = loadSectorMeta(sectorsubdir);
5055 catch(InvalidFilenameException &e)
5059 catch(FileNotGoodException &e)
5063 catch(std::exception &e)
5071 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5072 (m_savedir+"/sectors/"+sectorsubdir);
5073 std::vector<fs::DirListNode>::iterator i2;
5074 for(i2=list2.begin(); i2!=list2.end(); i2++)
5080 loadBlock(sectorsubdir, i2->name, sector);
5082 catch(InvalidFilenameException &e)
5084 // This catches unknown crap in directory
5090 void ServerMap::saveBlock(MapBlock *block)
5092 DSTACK(__FUNCTION_NAME);
5094 Dummy blocks are not written
5096 if(block->isDummy())
5098 /*v3s16 p = block->getPos();
5099 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5100 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5104 // Format used for writing
5105 u8 version = SER_FMT_VER_HIGHEST;
5107 v3s16 p3d = block->getPos();
5108 v2s16 p2d(p3d.X, p3d.Z);
5109 createDir(m_savedir);
5110 createDir(m_savedir+"/sectors");
5111 std::string dir = getSectorDir(p2d);
5114 // Block file is map/sectors/xxxxxxxx/xxxx
5116 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5117 std::string fullpath = dir + "/" + cc;
5118 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5119 if(o.good() == false)
5120 throw FileNotGoodException("Cannot open block data");
5123 [0] u8 serialization version
5126 o.write((char*)&version, 1);
5128 block->serialize(o, version);
5131 Versions up from 9 have block objects.
5135 block->serializeObjects(o, version);
5138 // We just wrote it to the disk
5139 block->resetChangedFlag();
5142 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5144 DSTACK(__FUNCTION_NAME);
5148 // Block file is map/sectors/xxxxxxxx/xxxx
5149 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5150 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5151 if(is.good() == false)
5152 throw FileNotGoodException("Cannot open block file");
5154 v3s16 p3d = getBlockPos(sectordir, blockfile);
5155 v2s16 p2d(p3d.X, p3d.Z);
5157 assert(sector->getPos() == p2d);
5159 u8 version = SER_FMT_VER_INVALID;
5160 is.read((char*)&version, 1);
5163 throw SerializationError("ServerMap::loadBlock(): Failed"
5164 " to read MapBlock version");
5166 /*u32 block_size = MapBlock::serializedLength(version);
5167 SharedBuffer<u8> data(block_size);
5168 is.read((char*)*data, block_size);*/
5170 // This will always return a sector because we're the server
5171 //MapSector *sector = emergeSector(p2d);
5173 MapBlock *block = NULL;
5174 bool created_new = false;
5176 block = sector->getBlockNoCreate(p3d.Y);
5178 catch(InvalidPositionException &e)
5180 block = sector->createBlankBlockNoInsert(p3d.Y);
5184 // deserialize block data
5185 block->deSerialize(is, version);
5188 Versions up from 9 have block objects.
5192 block->updateObjects(is, version, NULL, 0);
5196 sector->insertBlock(block);
5199 Convert old formats to new and save
5202 // Save old format blocks in new format
5203 if(version < SER_FMT_VER_HIGHEST)
5208 // We just loaded it from the disk, so it's up-to-date.
5209 block->resetChangedFlag();
5212 catch(SerializationError &e)
5214 dstream<<"WARNING: Invalid block data on disk "
5215 "(SerializationError). Ignoring. "
5216 "A new one will be generated."
5221 void ServerMap::PrintInfo(std::ostream &out)
5232 ClientMap::ClientMap(
5234 MapDrawControl &control,
5235 scene::ISceneNode* parent,
5236 scene::ISceneManager* mgr,
5240 scene::ISceneNode(parent, mgr, id),
5243 m_camera_position(0,0,0),
5244 m_camera_direction(0,0,1)
5246 m_camera_mutex.Init();
5247 assert(m_camera_mutex.IsInitialized());
5249 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5250 BS*1000000,BS*1000000,BS*1000000);
5253 ClientMap::~ClientMap()
5255 /*JMutexAutoLock lock(mesh_mutex);
5264 MapSector * ClientMap::emergeSector(v2s16 p2d)
5266 DSTACK(__FUNCTION_NAME);
5267 // Check that it doesn't exist already
5269 return getSectorNoGenerate(p2d);
5271 catch(InvalidPositionException &e)
5276 ClientMapSector *sector = new ClientMapSector(this, p2d);
5279 JMutexAutoLock lock(m_sector_mutex);
5280 m_sectors.insert(p2d, sector);
5286 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5288 DSTACK(__FUNCTION_NAME);
5289 ClientMapSector *sector = NULL;
5291 JMutexAutoLock lock(m_sector_mutex);
5293 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5297 sector = (ClientMapSector*)n->getValue();
5298 assert(sector->getId() == MAPSECTOR_CLIENT);
5302 sector = new ClientMapSector(this, p2d);
5304 JMutexAutoLock lock(m_sector_mutex);
5305 m_sectors.insert(p2d, sector);
5309 sector->deSerialize(is);
5312 void ClientMap::OnRegisterSceneNode()
5316 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5317 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5320 ISceneNode::OnRegisterSceneNode();
5323 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5325 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5326 DSTACK(__FUNCTION_NAME);
5328 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5331 Get time for measuring timeout.
5333 Measuring time is very useful for long delays when the
5334 machine is swapping a lot.
5336 int time1 = time(0);
5338 u32 daynight_ratio = m_client->getDayNightRatio();
5340 m_camera_mutex.Lock();
5341 v3f camera_position = m_camera_position;
5342 v3f camera_direction = m_camera_direction;
5343 m_camera_mutex.Unlock();
5346 Get all blocks and draw all visible ones
5349 v3s16 cam_pos_nodes(
5350 camera_position.X / BS,
5351 camera_position.Y / BS,
5352 camera_position.Z / BS);
5354 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5356 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5357 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5359 // Take a fair amount as we will be dropping more out later
5361 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5362 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5363 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5365 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5366 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5367 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5369 u32 vertex_count = 0;
5371 // For limiting number of mesh updates per frame
5372 u32 mesh_update_count = 0;
5374 u32 blocks_would_have_drawn = 0;
5375 u32 blocks_drawn = 0;
5377 //NOTE: The sectors map should be locked but we're not doing it
5378 // because it'd cause too much delays
5380 int timecheck_counter = 0;
5381 core::map<v2s16, MapSector*>::Iterator si;
5382 si = m_sectors.getIterator();
5383 for(; si.atEnd() == false; si++)
5386 timecheck_counter++;
5387 if(timecheck_counter > 50)
5389 timecheck_counter = 0;
5390 int time2 = time(0);
5391 if(time2 > time1 + 4)
5393 dstream<<"ClientMap::renderMap(): "
5394 "Rendering takes ages, returning."
5401 MapSector *sector = si.getNode()->getValue();
5402 v2s16 sp = sector->getPos();
5404 if(m_control.range_all == false)
5406 if(sp.X < p_blocks_min.X
5407 || sp.X > p_blocks_max.X
5408 || sp.Y < p_blocks_min.Z
5409 || sp.Y > p_blocks_max.Z)
5413 core::list< MapBlock * > sectorblocks;
5414 sector->getBlocks(sectorblocks);
5420 core::list< MapBlock * >::Iterator i;
5421 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5423 MapBlock *block = *i;
5426 Compare block position to camera position, skip
5427 if not seen on display
5430 float range = 100000 * BS;
5431 if(m_control.range_all == false)
5432 range = m_control.wanted_range * BS;
5435 if(isBlockInSight(block->getPos(), camera_position,
5436 camera_direction, range, &d) == false)
5442 /*if(m_control.range_all == false &&
5443 d - 0.5*BS*MAP_BLOCKSIZE > range)
5448 Update expired mesh (used for day/night change)
5451 bool mesh_expired = false;
5454 JMutexAutoLock lock(block->mesh_mutex);
5456 mesh_expired = block->getMeshExpired();
5458 // Mesh has not been expired and there is no mesh:
5459 // block has no content
5460 if(block->mesh == NULL && mesh_expired == false)
5464 f32 faraway = BS*50;
5465 //f32 faraway = m_control.wanted_range * BS;
5468 This has to be done with the mesh_mutex unlocked
5470 // Pretty random but this should work somewhat nicely
5471 if(mesh_expired && (
5472 (mesh_update_count < 3
5473 && (d < faraway || mesh_update_count < 2)
5476 (m_control.range_all && mesh_update_count < 20)
5479 /*if(mesh_expired && mesh_update_count < 6
5480 && (d < faraway || mesh_update_count < 3))*/
5482 mesh_update_count++;
5484 // Mesh has been expired: generate new mesh
5485 block->updateMesh(daynight_ratio);
5486 //m_client->addUpdateMeshTask(block);
5488 mesh_expired = false;
5492 Don't draw an expired mesh that is far away
5494 /*if(mesh_expired && d >= faraway)
5497 // Instead, delete it
5498 JMutexAutoLock lock(block->mesh_mutex);
5501 block->mesh->drop();
5504 // And continue to next block
5509 Draw the faces of the block
5512 JMutexAutoLock lock(block->mesh_mutex);
5514 scene::SMesh *mesh = block->mesh;
5519 blocks_would_have_drawn++;
5520 if(blocks_drawn >= m_control.wanted_max_blocks
5521 && m_control.range_all == false
5522 && d > m_control.wanted_min_range * BS)
5526 u32 c = mesh->getMeshBufferCount();
5528 for(u32 i=0; i<c; i++)
5530 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5531 const video::SMaterial& material = buf->getMaterial();
5532 video::IMaterialRenderer* rnd =
5533 driver->getMaterialRenderer(material.MaterialType);
5534 bool transparent = (rnd && rnd->isTransparent());
5535 // Render transparent on transparent pass and likewise.
5536 if(transparent == is_transparent_pass)
5539 This *shouldn't* hurt too much because Irrlicht
5540 doesn't change opengl textures if the old
5541 material is set again.
5543 driver->setMaterial(buf->getMaterial());
5544 driver->drawMeshBuffer(buf);
5545 vertex_count += buf->getVertexCount();
5549 } // foreach sectorblocks
5552 m_control.blocks_drawn = blocks_drawn;
5553 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5555 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5556 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5559 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5560 core::map<v3s16, MapBlock*> *affected_blocks)
5562 bool changed = false;
5564 Add it to all blocks touching it
5567 v3s16(0,0,0), // this
5568 v3s16(0,0,1), // back
5569 v3s16(0,1,0), // top
5570 v3s16(1,0,0), // right
5571 v3s16(0,0,-1), // front
5572 v3s16(0,-1,0), // bottom
5573 v3s16(-1,0,0), // left
5575 for(u16 i=0; i<7; i++)
5577 v3s16 p2 = p + dirs[i];
5578 // Block position of neighbor (or requested) node
5579 v3s16 blockpos = getNodeBlockPos(p2);
5580 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5581 if(blockref == NULL)
5583 // Relative position of requested node
5584 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5585 if(blockref->setTempMod(relpos, mod))
5590 if(changed && affected_blocks!=NULL)
5592 for(u16 i=0; i<7; i++)
5594 v3s16 p2 = p + dirs[i];
5595 // Block position of neighbor (or requested) node
5596 v3s16 blockpos = getNodeBlockPos(p2);
5597 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5598 if(blockref == NULL)
5600 affected_blocks->insert(blockpos, blockref);
5606 bool ClientMap::clearTempMod(v3s16 p,
5607 core::map<v3s16, MapBlock*> *affected_blocks)
5609 bool changed = false;
5611 v3s16(0,0,0), // this
5612 v3s16(0,0,1), // back
5613 v3s16(0,1,0), // top
5614 v3s16(1,0,0), // right
5615 v3s16(0,0,-1), // front
5616 v3s16(0,-1,0), // bottom
5617 v3s16(-1,0,0), // left
5619 for(u16 i=0; i<7; i++)
5621 v3s16 p2 = p + dirs[i];
5622 // Block position of neighbor (or requested) node
5623 v3s16 blockpos = getNodeBlockPos(p2);
5624 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5625 if(blockref == NULL)
5627 // Relative position of requested node
5628 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5629 if(blockref->clearTempMod(relpos))
5634 if(changed && affected_blocks!=NULL)
5636 for(u16 i=0; i<7; i++)
5638 v3s16 p2 = p + dirs[i];
5639 // Block position of neighbor (or requested) node
5640 v3s16 blockpos = getNodeBlockPos(p2);
5641 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5642 if(blockref == NULL)
5644 affected_blocks->insert(blockpos, blockref);
5650 void ClientMap::expireMeshes(bool only_daynight_diffed)
5652 TimeTaker timer("expireMeshes()");
5654 core::map<v2s16, MapSector*>::Iterator si;
5655 si = m_sectors.getIterator();
5656 for(; si.atEnd() == false; si++)
5658 MapSector *sector = si.getNode()->getValue();
5660 core::list< MapBlock * > sectorblocks;
5661 sector->getBlocks(sectorblocks);
5663 core::list< MapBlock * >::Iterator i;
5664 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5666 MapBlock *block = *i;
5668 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5674 JMutexAutoLock lock(block->mesh_mutex);
5675 if(block->mesh != NULL)
5677 /*block->mesh->drop();
5678 block->mesh = NULL;*/
5679 block->setMeshExpired(true);
5686 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5688 assert(mapType() == MAPTYPE_CLIENT);
5691 v3s16 p = blockpos + v3s16(0,0,0);
5692 MapBlock *b = getBlockNoCreate(p);
5693 b->updateMesh(daynight_ratio);
5694 //b->setMeshExpired(true);
5696 catch(InvalidPositionException &e){}
5699 v3s16 p = blockpos + v3s16(-1,0,0);
5700 MapBlock *b = getBlockNoCreate(p);
5701 b->updateMesh(daynight_ratio);
5702 //b->setMeshExpired(true);
5704 catch(InvalidPositionException &e){}
5706 v3s16 p = blockpos + v3s16(0,-1,0);
5707 MapBlock *b = getBlockNoCreate(p);
5708 b->updateMesh(daynight_ratio);
5709 //b->setMeshExpired(true);
5711 catch(InvalidPositionException &e){}
5713 v3s16 p = blockpos + v3s16(0,0,-1);
5714 MapBlock *b = getBlockNoCreate(p);
5715 b->updateMesh(daynight_ratio);
5716 //b->setMeshExpired(true);
5718 catch(InvalidPositionException &e){}
5723 Update mesh of block in which the node is, and if the node is at the
5724 leading edge, update the appropriate leading blocks too.
5726 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5734 v3s16 blockposes[4];
5735 for(u32 i=0; i<4; i++)
5737 v3s16 np = nodepos + dirs[i];
5738 blockposes[i] = getNodeBlockPos(np);
5739 // Don't update mesh of block if it has been done already
5740 bool already_updated = false;
5741 for(u32 j=0; j<i; j++)
5743 if(blockposes[j] == blockposes[i])
5745 already_updated = true;
5752 MapBlock *b = getBlockNoCreate(blockposes[i]);
5753 b->updateMesh(daynight_ratio);
5758 void ClientMap::PrintInfo(std::ostream &out)
5769 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5774 MapVoxelManipulator::~MapVoxelManipulator()
5776 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5780 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5782 TimeTaker timer1("emerge", &emerge_time);
5784 // Units of these are MapBlocks
5785 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5786 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5788 VoxelArea block_area_nodes
5789 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5791 addArea(block_area_nodes);
5793 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5794 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5795 for(s32 x=p_min.X; x<=p_max.X; x++)
5798 core::map<v3s16, bool>::Node *n;
5799 n = m_loaded_blocks.find(p);
5803 bool block_data_inexistent = false;
5806 TimeTaker timer1("emerge load", &emerge_load_time);
5808 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5809 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5812 dstream<<std::endl;*/
5814 MapBlock *block = m_map->getBlockNoCreate(p);
5815 if(block->isDummy())
5816 block_data_inexistent = true;
5818 block->copyTo(*this);
5820 catch(InvalidPositionException &e)
5822 block_data_inexistent = true;
5825 if(block_data_inexistent)
5827 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5828 // Fill with VOXELFLAG_INEXISTENT
5829 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5830 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5832 s32 i = m_area.index(a.MinEdge.X,y,z);
5833 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5837 m_loaded_blocks.insert(p, !block_data_inexistent);
5840 //dstream<<"emerge done"<<std::endl;
5844 SUGG: Add an option to only update eg. water and air nodes.
5845 This will make it interfere less with important stuff if
5848 void MapVoxelManipulator::blitBack
5849 (core::map<v3s16, MapBlock*> & modified_blocks)
5851 if(m_area.getExtent() == v3s16(0,0,0))
5854 //TimeTaker timer1("blitBack");
5856 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5857 <<m_loaded_blocks.size()<<std::endl;*/
5860 Initialize block cache
5862 v3s16 blockpos_last;
5863 MapBlock *block = NULL;
5864 bool block_checked_in_modified = false;
5866 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5867 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5868 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5872 u8 f = m_flags[m_area.index(p)];
5873 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5876 MapNode &n = m_data[m_area.index(p)];
5878 v3s16 blockpos = getNodeBlockPos(p);
5883 if(block == NULL || blockpos != blockpos_last){
5884 block = m_map->getBlockNoCreate(blockpos);
5885 blockpos_last = blockpos;
5886 block_checked_in_modified = false;
5889 // Calculate relative position in block
5890 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5892 // Don't continue if nothing has changed here
5893 if(block->getNode(relpos) == n)
5896 //m_map->setNode(m_area.MinEdge + p, n);
5897 block->setNode(relpos, n);
5900 Make sure block is in modified_blocks
5902 if(block_checked_in_modified == false)
5904 modified_blocks[blockpos] = block;
5905 block_checked_in_modified = true;
5908 catch(InvalidPositionException &e)
5914 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5915 MapVoxelManipulator(map)
5919 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5923 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5925 // Just create the area so that it can be pointed to
5926 VoxelManipulator::emerge(a, caller_id);
5929 void ManualMapVoxelManipulator::initialEmerge(
5930 v3s16 blockpos_min, v3s16 blockpos_max)
5932 TimeTaker timer1("initialEmerge", &emerge_time);
5934 // Units of these are MapBlocks
5935 v3s16 p_min = blockpos_min;
5936 v3s16 p_max = blockpos_max;
5938 VoxelArea block_area_nodes
5939 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5941 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5944 dstream<<"initialEmerge: area: ";
5945 block_area_nodes.print(dstream);
5946 dstream<<" ("<<size_MB<<"MB)";
5950 addArea(block_area_nodes);
5952 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5953 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5954 for(s32 x=p_min.X; x<=p_max.X; x++)
5957 core::map<v3s16, bool>::Node *n;
5958 n = m_loaded_blocks.find(p);
5962 bool block_data_inexistent = false;
5965 TimeTaker timer1("emerge load", &emerge_load_time);
5967 MapBlock *block = m_map->getBlockNoCreate(p);
5968 if(block->isDummy())
5969 block_data_inexistent = true;
5971 block->copyTo(*this);
5973 catch(InvalidPositionException &e)
5975 block_data_inexistent = true;
5978 if(block_data_inexistent)
5981 Mark area inexistent
5983 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5984 // Fill with VOXELFLAG_INEXISTENT
5985 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5986 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5988 s32 i = m_area.index(a.MinEdge.X,y,z);
5989 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5993 m_loaded_blocks.insert(p, !block_data_inexistent);
5997 void ManualMapVoxelManipulator::blitBackAll(
5998 core::map<v3s16, MapBlock*> * modified_blocks)
6000 if(m_area.getExtent() == v3s16(0,0,0))
6004 Copy data of all blocks
6006 for(core::map<v3s16, bool>::Iterator
6007 i = m_loaded_blocks.getIterator();
6008 i.atEnd() == false; i++)
6010 bool existed = i.getNode()->getValue();
6011 if(existed == false)
6013 v3s16 p = i.getNode()->getKey();
6014 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6017 dstream<<"WARNING: "<<__FUNCTION_NAME
6018 <<": got NULL block "
6019 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6024 block->copyFrom(*this);
6027 modified_blocks->insert(p, block);