3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
35 Map::Map(std::ostream &dout):
39 m_sector_mutex.Init();
40 assert(m_sector_mutex.IsInitialized());
48 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
49 for(; i.atEnd() == false; i++)
51 MapSector *sector = i.getNode()->getValue();
56 void Map::addEventReceiver(MapEventReceiver *event_receiver)
58 m_event_receivers.insert(event_receiver, false);
61 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
63 if(m_event_receivers.find(event_receiver) == NULL)
65 m_event_receivers.remove(event_receiver);
68 void Map::dispatchEvent(MapEditEvent *event)
70 for(core::map<MapEventReceiver*, bool>::Iterator
71 i = m_event_receivers.getIterator();
72 i.atEnd()==false; i++)
74 MapEventReceiver* event_receiver = i.getNode()->getKey();
75 event_receiver->onMapEditEvent(event);
79 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
81 if(m_sector_cache != NULL && p == m_sector_cache_p){
82 MapSector * sector = m_sector_cache;
83 // Reset inactivity timer
84 sector->usage_timer = 0.0;
88 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
93 MapSector *sector = n->getValue();
95 // Cache the last result
97 m_sector_cache = sector;
99 // Reset inactivity timer
100 sector->usage_timer = 0.0;
104 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
106 JMutexAutoLock lock(m_sector_mutex);
108 return getSectorNoGenerateNoExNoLock(p);
111 MapSector * Map::getSectorNoGenerate(v2s16 p)
113 MapSector *sector = getSectorNoGenerateNoEx(p);
115 throw InvalidPositionException();
120 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
122 v2s16 p2d(p3d.X, p3d.Z);
123 MapSector * sector = getSectorNoGenerate(p2d);
125 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
134 v2s16 p2d(p3d.X, p3d.Z);
135 MapSector * sector = getSectorNoGenerate(p2d);
136 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
139 catch(InvalidPositionException &e)
145 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorCreate(p2d);
150 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
153 block = sector->createBlankBlock(p3d.Y);
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
171 Goes recursively through the neighbours of the node.
173 Alters only transparent nodes.
175 If the lighting of the neighbour is lower than the lighting of
176 the node was (before changing it to 0 at the step before), the
177 lighting of the neighbour is set to 0 and then the same stuff
178 repeats for the neighbour.
180 The ending nodes of the routine are stored in light_sources.
181 This is useful when a light is removed. In such case, this
182 routine can be called for the light node and then again for
183 light_sources to re-light the area without the removed light.
185 values of from_nodes are lighting values.
187 void Map::unspreadLight(enum LightBank bank,
188 core::map<v3s16, u8> & from_nodes,
189 core::map<v3s16, bool> & light_sources,
190 core::map<v3s16, MapBlock*> & modified_blocks)
193 v3s16(0,0,1), // back
195 v3s16(1,0,0), // right
196 v3s16(0,0,-1), // front
197 v3s16(0,-1,0), // bottom
198 v3s16(-1,0,0), // left
201 if(from_nodes.size() == 0)
204 u32 blockchangecount = 0;
206 core::map<v3s16, u8> unlighted_nodes;
207 core::map<v3s16, u8>::Iterator j;
208 j = from_nodes.getIterator();
211 Initialize block cache
214 MapBlock *block = NULL;
215 // Cache this a bit, too
216 bool block_checked_in_modified = false;
218 for(; j.atEnd() == false; j++)
220 v3s16 pos = j.getNode()->getKey();
221 v3s16 blockpos = getNodeBlockPos(pos);
223 // Only fetch a new block if the block position has changed
225 if(block == NULL || blockpos != blockpos_last){
226 block = getBlockNoCreate(blockpos);
227 blockpos_last = blockpos;
229 block_checked_in_modified = false;
233 catch(InvalidPositionException &e)
241 // Calculate relative position in block
242 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
244 // Get node straight from the block
245 MapNode n = block->getNode(relpos);
247 u8 oldlight = j.getNode()->getValue();
249 // Loop through 6 neighbors
250 for(u16 i=0; i<6; i++)
252 // Get the position of the neighbor node
253 v3s16 n2pos = pos + dirs[i];
255 // Get the block where the node is located
256 v3s16 blockpos = getNodeBlockPos(n2pos);
260 // Only fetch a new block if the block position has changed
262 if(block == NULL || blockpos != blockpos_last){
263 block = getBlockNoCreate(blockpos);
264 blockpos_last = blockpos;
266 block_checked_in_modified = false;
270 catch(InvalidPositionException &e)
275 // Calculate relative position in block
276 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
277 // Get node straight from the block
278 MapNode n2 = block->getNode(relpos);
280 bool changed = false;
282 //TODO: Optimize output by optimizing light_sources?
285 If the neighbor is dimmer than what was specified
286 as oldlight (the light of the previous node)
288 if(n2.getLight(bank) < oldlight)
291 And the neighbor is transparent and it has some light
293 if(n2.light_propagates() && n2.getLight(bank) != 0)
296 Set light to 0 and add to queue
299 u8 current_light = n2.getLight(bank);
300 n2.setLight(bank, 0);
301 block->setNode(relpos, n2);
303 unlighted_nodes.insert(n2pos, current_light);
307 Remove from light_sources if it is there
308 NOTE: This doesn't happen nearly at all
310 /*if(light_sources.find(n2pos))
312 std::cout<<"Removed from light_sources"<<std::endl;
313 light_sources.remove(n2pos);
318 if(light_sources.find(n2pos) != NULL)
319 light_sources.remove(n2pos);*/
322 light_sources.insert(n2pos, true);
325 // Add to modified_blocks
326 if(changed == true && block_checked_in_modified == false)
328 // If the block is not found in modified_blocks, add.
329 if(modified_blocks.find(blockpos) == NULL)
331 modified_blocks.insert(blockpos, block);
333 block_checked_in_modified = true;
336 catch(InvalidPositionException &e)
343 /*dstream<<"unspreadLight(): Changed block "
344 <<blockchangecount<<" times"
345 <<" for "<<from_nodes.size()<<" nodes"
348 if(unlighted_nodes.size() > 0)
349 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
353 A single-node wrapper of the above
355 void Map::unLightNeighbors(enum LightBank bank,
356 v3s16 pos, u8 lightwas,
357 core::map<v3s16, bool> & light_sources,
358 core::map<v3s16, MapBlock*> & modified_blocks)
360 core::map<v3s16, u8> from_nodes;
361 from_nodes.insert(pos, lightwas);
363 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
367 Lights neighbors of from_nodes, collects all them and then
370 void Map::spreadLight(enum LightBank bank,
371 core::map<v3s16, bool> & from_nodes,
372 core::map<v3s16, MapBlock*> & modified_blocks)
374 const v3s16 dirs[6] = {
375 v3s16(0,0,1), // back
377 v3s16(1,0,0), // right
378 v3s16(0,0,-1), // front
379 v3s16(0,-1,0), // bottom
380 v3s16(-1,0,0), // left
383 if(from_nodes.size() == 0)
386 u32 blockchangecount = 0;
388 core::map<v3s16, bool> lighted_nodes;
389 core::map<v3s16, bool>::Iterator j;
390 j = from_nodes.getIterator();
393 Initialize block cache
396 MapBlock *block = NULL;
397 // Cache this a bit, too
398 bool block_checked_in_modified = false;
400 for(; j.atEnd() == false; j++)
401 //for(; j != from_nodes.end(); j++)
403 v3s16 pos = j.getNode()->getKey();
405 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
406 v3s16 blockpos = getNodeBlockPos(pos);
408 // Only fetch a new block if the block position has changed
410 if(block == NULL || blockpos != blockpos_last){
411 block = getBlockNoCreate(blockpos);
412 blockpos_last = blockpos;
414 block_checked_in_modified = false;
418 catch(InvalidPositionException &e)
426 // Calculate relative position in block
427 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
429 // Get node straight from the block
430 MapNode n = block->getNode(relpos);
432 u8 oldlight = n.getLight(bank);
433 u8 newlight = diminish_light(oldlight);
435 // Loop through 6 neighbors
436 for(u16 i=0; i<6; i++){
437 // Get the position of the neighbor node
438 v3s16 n2pos = pos + dirs[i];
440 // Get the block where the node is located
441 v3s16 blockpos = getNodeBlockPos(n2pos);
445 // Only fetch a new block if the block position has changed
447 if(block == NULL || blockpos != blockpos_last){
448 block = getBlockNoCreate(blockpos);
449 blockpos_last = blockpos;
451 block_checked_in_modified = false;
455 catch(InvalidPositionException &e)
460 // Calculate relative position in block
461 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
462 // Get node straight from the block
463 MapNode n2 = block->getNode(relpos);
465 bool changed = false;
467 If the neighbor is brighter than the current node,
468 add to list (it will light up this node on its turn)
470 if(n2.getLight(bank) > undiminish_light(oldlight))
472 lighted_nodes.insert(n2pos, true);
473 //lighted_nodes.push_back(n2pos);
477 If the neighbor is dimmer than how much light this node
478 would spread on it, add to list
480 if(n2.getLight(bank) < newlight)
482 if(n2.light_propagates())
484 n2.setLight(bank, newlight);
485 block->setNode(relpos, n2);
486 lighted_nodes.insert(n2pos, true);
487 //lighted_nodes.push_back(n2pos);
492 // Add to modified_blocks
493 if(changed == true && block_checked_in_modified == false)
495 // If the block is not found in modified_blocks, add.
496 if(modified_blocks.find(blockpos) == NULL)
498 modified_blocks.insert(blockpos, block);
500 block_checked_in_modified = true;
503 catch(InvalidPositionException &e)
510 /*dstream<<"spreadLight(): Changed block "
511 <<blockchangecount<<" times"
512 <<" for "<<from_nodes.size()<<" nodes"
515 if(lighted_nodes.size() > 0)
516 spreadLight(bank, lighted_nodes, modified_blocks);
520 A single-node source variation of the above.
522 void Map::lightNeighbors(enum LightBank bank,
524 core::map<v3s16, MapBlock*> & modified_blocks)
526 core::map<v3s16, bool> from_nodes;
527 from_nodes.insert(pos, true);
528 spreadLight(bank, from_nodes, modified_blocks);
531 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
534 v3s16(0,0,1), // back
536 v3s16(1,0,0), // right
537 v3s16(0,0,-1), // front
538 v3s16(0,-1,0), // bottom
539 v3s16(-1,0,0), // left
542 u8 brightest_light = 0;
543 v3s16 brightest_pos(0,0,0);
544 bool found_something = false;
546 // Loop through 6 neighbors
547 for(u16 i=0; i<6; i++){
548 // Get the position of the neighbor node
549 v3s16 n2pos = p + dirs[i];
554 catch(InvalidPositionException &e)
558 if(n2.getLight(bank) > brightest_light || found_something == false){
559 brightest_light = n2.getLight(bank);
560 brightest_pos = n2pos;
561 found_something = true;
565 if(found_something == false)
566 throw InvalidPositionException();
568 return brightest_pos;
572 Propagates sunlight down from a node.
573 Starting point gets sunlight.
575 Returns the lowest y value of where the sunlight went.
577 Mud is turned into grass in where the sunlight stops.
579 s16 Map::propagateSunlight(v3s16 start,
580 core::map<v3s16, MapBlock*> & modified_blocks)
585 v3s16 pos(start.X, y, start.Z);
587 v3s16 blockpos = getNodeBlockPos(pos);
590 block = getBlockNoCreate(blockpos);
592 catch(InvalidPositionException &e)
597 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
598 MapNode n = block->getNode(relpos);
600 if(n.sunlight_propagates())
602 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
603 block->setNode(relpos, n);
605 modified_blocks.insert(blockpos, block);
609 // Turn mud into grass
610 if(n.d == CONTENT_MUD)
613 block->setNode(relpos, n);
614 modified_blocks.insert(blockpos, block);
617 // Sunlight goes no further
624 void Map::updateLighting(enum LightBank bank,
625 core::map<v3s16, MapBlock*> & a_blocks,
626 core::map<v3s16, MapBlock*> & modified_blocks)
628 /*m_dout<<DTIME<<"Map::updateLighting(): "
629 <<a_blocks.size()<<" blocks."<<std::endl;*/
631 //TimeTaker timer("updateLighting");
635 //u32 count_was = modified_blocks.size();
637 core::map<v3s16, MapBlock*> blocks_to_update;
639 core::map<v3s16, bool> light_sources;
641 core::map<v3s16, u8> unlight_from;
643 core::map<v3s16, MapBlock*>::Iterator i;
644 i = a_blocks.getIterator();
645 for(; i.atEnd() == false; i++)
647 MapBlock *block = i.getNode()->getValue();
651 // Don't bother with dummy blocks.
655 v3s16 pos = block->getPos();
656 modified_blocks.insert(pos, block);
658 blocks_to_update.insert(pos, block);
661 Clear all light from block
663 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
664 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
665 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
670 MapNode n = block->getNode(v3s16(x,y,z));
671 u8 oldlight = n.getLight(bank);
673 block->setNode(v3s16(x,y,z), n);
675 // Collect borders for unlighting
676 if(x==0 || x == MAP_BLOCKSIZE-1
677 || y==0 || y == MAP_BLOCKSIZE-1
678 || z==0 || z == MAP_BLOCKSIZE-1)
680 v3s16 p_map = p + v3s16(
683 MAP_BLOCKSIZE*pos.Z);
684 unlight_from.insert(p_map, oldlight);
687 catch(InvalidPositionException &e)
690 This would happen when dealing with a
694 dstream<<"updateLighting(): InvalidPositionException"
699 if(bank == LIGHTBANK_DAY)
701 bool bottom_valid = block->propagateSunlight(light_sources);
703 // If bottom is valid, we're done.
707 else if(bank == LIGHTBANK_NIGHT)
709 // For night lighting, sunlight is not propagated
714 // Invalid lighting bank
718 /*dstream<<"Bottom for sunlight-propagated block ("
719 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
722 // Bottom sunlight is not valid; get the block and loop to it
726 block = getBlockNoCreate(pos);
728 catch(InvalidPositionException &e)
738 TimeTaker timer("unspreadLight");
739 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
744 u32 diff = modified_blocks.size() - count_was;
745 count_was = modified_blocks.size();
746 dstream<<"unspreadLight modified "<<diff<<std::endl;
750 TimeTaker timer("spreadLight");
751 spreadLight(bank, light_sources, modified_blocks);
756 u32 diff = modified_blocks.size() - count_was;
757 count_was = modified_blocks.size();
758 dstream<<"spreadLight modified "<<diff<<std::endl;
763 //MapVoxelManipulator vmanip(this);
765 // Make a manual voxel manipulator and load all the blocks
766 // that touch the requested blocks
767 ManualMapVoxelManipulator vmanip(this);
768 core::map<v3s16, MapBlock*>::Iterator i;
769 i = blocks_to_update.getIterator();
770 for(; i.atEnd() == false; i++)
772 MapBlock *block = i.getNode()->getValue();
773 v3s16 p = block->getPos();
775 // Add all surrounding blocks
776 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
779 Add all surrounding blocks that have up-to-date lighting
780 NOTE: This doesn't quite do the job (not everything
781 appropriate is lighted)
783 /*for(s16 z=-1; z<=1; z++)
784 for(s16 y=-1; y<=1; y++)
785 for(s16 x=-1; x<=1; x++)
788 MapBlock *block = getBlockNoCreateNoEx(p);
793 if(block->getLightingExpired())
795 vmanip.initialEmerge(p, p);
798 // Lighting of block will be updated completely
799 block->setLightingExpired(false);
803 //TimeTaker timer("unSpreadLight");
804 vmanip.unspreadLight(bank, unlight_from, light_sources);
807 //TimeTaker timer("spreadLight");
808 vmanip.spreadLight(bank, light_sources);
811 //TimeTaker timer("blitBack");
812 vmanip.blitBack(modified_blocks);
814 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
818 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
821 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
822 core::map<v3s16, MapBlock*> & modified_blocks)
824 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
825 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
828 Update information about whether day and night light differ
830 for(core::map<v3s16, MapBlock*>::Iterator
831 i = modified_blocks.getIterator();
832 i.atEnd() == false; i++)
834 MapBlock *block = i.getNode()->getValue();
835 block->updateDayNightDiff();
840 This is called after changing a node from transparent to opaque.
841 The lighting value of the node should be left as-is after changing
842 other values. This sets the lighting value to 0.
844 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
845 core::map<v3s16, MapBlock*> &modified_blocks)
848 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
849 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
852 From this node to nodes underneath:
853 If lighting is sunlight (1.0), unlight neighbours and
858 v3s16 toppos = p + v3s16(0,1,0);
859 v3s16 bottompos = p + v3s16(0,-1,0);
861 bool node_under_sunlight = true;
862 core::map<v3s16, bool> light_sources;
865 If there is a node at top and it doesn't have sunlight,
866 there has not been any sunlight going down.
868 Otherwise there probably is.
871 MapNode topnode = getNode(toppos);
873 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
874 node_under_sunlight = false;
876 catch(InvalidPositionException &e)
881 If the new node doesn't propagate sunlight and there is
882 grass below, change it to mud
884 if(content_features(n.d).sunlight_propagates == false)
887 MapNode bottomnode = getNode(bottompos);
889 if(bottomnode.d == CONTENT_GRASS
890 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
892 bottomnode.d = CONTENT_MUD;
893 setNode(bottompos, bottomnode);
896 catch(InvalidPositionException &e)
902 If the new node is mud and it is under sunlight, change it
905 if(n.d == CONTENT_MUD && node_under_sunlight)
911 Remove all light that has come out of this node
914 enum LightBank banks[] =
919 for(s32 i=0; i<2; i++)
921 enum LightBank bank = banks[i];
923 u8 lightwas = getNode(p).getLight(bank);
925 // Add the block of the added node to modified_blocks
926 v3s16 blockpos = getNodeBlockPos(p);
927 MapBlock * block = getBlockNoCreate(blockpos);
928 assert(block != NULL);
929 modified_blocks.insert(blockpos, block);
931 assert(isValidPosition(p));
933 // Unlight neighbours of node.
934 // This means setting light of all consequent dimmer nodes
936 // This also collects the nodes at the border which will spread
937 // light again into this.
938 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
944 Set the node on the map
950 If node is under sunlight, take all sunlighted nodes under
951 it and clear light from them and from where the light has
953 TODO: This could be optimized by mass-unlighting instead
956 if(node_under_sunlight)
960 //m_dout<<DTIME<<"y="<<y<<std::endl;
961 v3s16 n2pos(p.X, y, p.Z);
967 catch(InvalidPositionException &e)
972 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
974 unLightNeighbors(LIGHTBANK_DAY,
975 n2pos, n2.getLight(LIGHTBANK_DAY),
976 light_sources, modified_blocks);
977 n2.setLight(LIGHTBANK_DAY, 0);
985 for(s32 i=0; i<2; i++)
987 enum LightBank bank = banks[i];
990 Spread light from all nodes that might be capable of doing so
992 spreadLight(bank, light_sources, modified_blocks);
996 Update information about whether day and night light differ
998 for(core::map<v3s16, MapBlock*>::Iterator
999 i = modified_blocks.getIterator();
1000 i.atEnd() == false; i++)
1002 MapBlock *block = i.getNode()->getValue();
1003 block->updateDayNightDiff();
1007 Add neighboring liquid nodes and the node itself if it is
1008 liquid (=water node was added) to transform queue.
1011 v3s16(0,0,0), // self
1012 v3s16(0,0,1), // back
1013 v3s16(0,1,0), // top
1014 v3s16(1,0,0), // right
1015 v3s16(0,0,-1), // front
1016 v3s16(0,-1,0), // bottom
1017 v3s16(-1,0,0), // left
1019 for(u16 i=0; i<7; i++)
1024 v3s16 p2 = p + dirs[i];
1026 MapNode n2 = getNode(p2);
1027 if(content_liquid(n2.d))
1029 m_transforming_liquid.push_back(p2);
1032 }catch(InvalidPositionException &e)
1040 void Map::removeNodeAndUpdate(v3s16 p,
1041 core::map<v3s16, MapBlock*> &modified_blocks)
1043 /*PrintInfo(m_dout);
1044 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1045 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1047 bool node_under_sunlight = true;
1049 v3s16 toppos = p + v3s16(0,1,0);
1051 // Node will be replaced with this
1052 u8 replace_material = CONTENT_AIR;
1055 If there is a node at top and it doesn't have sunlight,
1056 there will be no sunlight going down.
1059 MapNode topnode = getNode(toppos);
1061 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1062 node_under_sunlight = false;
1064 catch(InvalidPositionException &e)
1068 core::map<v3s16, bool> light_sources;
1070 enum LightBank banks[] =
1075 for(s32 i=0; i<2; i++)
1077 enum LightBank bank = banks[i];
1080 Unlight neighbors (in case the node is a light source)
1082 unLightNeighbors(bank, p,
1083 getNode(p).getLight(bank),
1084 light_sources, modified_blocks);
1089 This also clears the lighting.
1093 n.d = replace_material;
1096 for(s32 i=0; i<2; i++)
1098 enum LightBank bank = banks[i];
1101 Recalculate lighting
1103 spreadLight(bank, light_sources, modified_blocks);
1106 // Add the block of the removed node to modified_blocks
1107 v3s16 blockpos = getNodeBlockPos(p);
1108 MapBlock * block = getBlockNoCreate(blockpos);
1109 assert(block != NULL);
1110 modified_blocks.insert(blockpos, block);
1113 If the removed node was under sunlight, propagate the
1114 sunlight down from it and then light all neighbors
1115 of the propagated blocks.
1117 if(node_under_sunlight)
1119 s16 ybottom = propagateSunlight(p, modified_blocks);
1120 /*m_dout<<DTIME<<"Node was under sunlight. "
1121 "Propagating sunlight";
1122 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1124 for(; y >= ybottom; y--)
1126 v3s16 p2(p.X, y, p.Z);
1127 /*m_dout<<DTIME<<"lighting neighbors of node ("
1128 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1130 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1135 // Set the lighting of this node to 0
1136 // TODO: Is this needed? Lighting is cleared up there already.
1138 MapNode n = getNode(p);
1139 n.setLight(LIGHTBANK_DAY, 0);
1142 catch(InvalidPositionException &e)
1148 for(s32 i=0; i<2; i++)
1150 enum LightBank bank = banks[i];
1152 // Get the brightest neighbour node and propagate light from it
1153 v3s16 n2p = getBrightestNeighbour(bank, p);
1155 MapNode n2 = getNode(n2p);
1156 lightNeighbors(bank, n2p, modified_blocks);
1158 catch(InvalidPositionException &e)
1164 Update information about whether day and night light differ
1166 for(core::map<v3s16, MapBlock*>::Iterator
1167 i = modified_blocks.getIterator();
1168 i.atEnd() == false; i++)
1170 MapBlock *block = i.getNode()->getValue();
1171 block->updateDayNightDiff();
1175 Add neighboring liquid nodes to transform queue.
1178 v3s16(0,0,1), // back
1179 v3s16(0,1,0), // top
1180 v3s16(1,0,0), // right
1181 v3s16(0,0,-1), // front
1182 v3s16(0,-1,0), // bottom
1183 v3s16(-1,0,0), // left
1185 for(u16 i=0; i<6; i++)
1190 v3s16 p2 = p + dirs[i];
1192 MapNode n2 = getNode(p2);
1193 if(content_liquid(n2.d))
1195 m_transforming_liquid.push_back(p2);
1198 }catch(InvalidPositionException &e)
1204 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1207 event.type = MEET_ADDNODE;
1211 bool succeeded = true;
1213 core::map<v3s16, MapBlock*> modified_blocks;
1214 addNodeAndUpdate(p, n, modified_blocks);
1216 // Copy modified_blocks to event
1217 for(core::map<v3s16, MapBlock*>::Iterator
1218 i = modified_blocks.getIterator();
1219 i.atEnd()==false; i++)
1221 event.modified_blocks.insert(i.getNode()->getKey(), false);
1224 catch(InvalidPositionException &e){
1228 dispatchEvent(&event);
1233 bool Map::removeNodeWithEvent(v3s16 p)
1236 event.type = MEET_REMOVENODE;
1239 bool succeeded = true;
1241 core::map<v3s16, MapBlock*> modified_blocks;
1242 removeNodeAndUpdate(p, modified_blocks);
1244 // Copy modified_blocks to event
1245 for(core::map<v3s16, MapBlock*>::Iterator
1246 i = modified_blocks.getIterator();
1247 i.atEnd()==false; i++)
1249 event.modified_blocks.insert(i.getNode()->getKey(), false);
1252 catch(InvalidPositionException &e){
1256 dispatchEvent(&event);
1261 bool Map::dayNightDiffed(v3s16 blockpos)
1264 v3s16 p = blockpos + v3s16(0,0,0);
1265 MapBlock *b = getBlockNoCreate(p);
1266 if(b->dayNightDiffed())
1269 catch(InvalidPositionException &e){}
1272 v3s16 p = blockpos + v3s16(-1,0,0);
1273 MapBlock *b = getBlockNoCreate(p);
1274 if(b->dayNightDiffed())
1277 catch(InvalidPositionException &e){}
1279 v3s16 p = blockpos + v3s16(0,-1,0);
1280 MapBlock *b = getBlockNoCreate(p);
1281 if(b->dayNightDiffed())
1284 catch(InvalidPositionException &e){}
1286 v3s16 p = blockpos + v3s16(0,0,-1);
1287 MapBlock *b = getBlockNoCreate(p);
1288 if(b->dayNightDiffed())
1291 catch(InvalidPositionException &e){}
1294 v3s16 p = blockpos + v3s16(1,0,0);
1295 MapBlock *b = getBlockNoCreate(p);
1296 if(b->dayNightDiffed())
1299 catch(InvalidPositionException &e){}
1301 v3s16 p = blockpos + v3s16(0,1,0);
1302 MapBlock *b = getBlockNoCreate(p);
1303 if(b->dayNightDiffed())
1306 catch(InvalidPositionException &e){}
1308 v3s16 p = blockpos + v3s16(0,0,1);
1309 MapBlock *b = getBlockNoCreate(p);
1310 if(b->dayNightDiffed())
1313 catch(InvalidPositionException &e){}
1319 Updates usage timers
1321 void Map::timerUpdate(float dtime)
1323 JMutexAutoLock lock(m_sector_mutex);
1325 core::map<v2s16, MapSector*>::Iterator si;
1327 si = m_sectors.getIterator();
1328 for(; si.atEnd() == false; si++)
1330 MapSector *sector = si.getNode()->getValue();
1331 sector->usage_timer += dtime;
1335 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1338 Wait for caches to be removed before continuing.
1340 This disables the existence of caches while locked
1342 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1344 core::list<v2s16>::Iterator j;
1345 for(j=list.begin(); j!=list.end(); j++)
1347 MapSector *sector = m_sectors[*j];
1350 sector->deleteBlocks();
1355 If sector is in sector cache, remove it from there
1357 if(m_sector_cache == sector)
1359 m_sector_cache = NULL;
1362 Remove from map and delete
1364 m_sectors.remove(*j);
1370 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1371 core::list<v3s16> *deleted_blocks)
1373 JMutexAutoLock lock(m_sector_mutex);
1375 core::list<v2s16> sector_deletion_queue;
1376 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1377 for(; i.atEnd() == false; i++)
1379 MapSector *sector = i.getNode()->getValue();
1381 Delete sector from memory if it hasn't been used in a long time
1383 if(sector->usage_timer > timeout)
1385 sector_deletion_queue.push_back(i.getNode()->getKey());
1387 if(deleted_blocks != NULL)
1389 // Collect positions of blocks of sector
1390 MapSector *sector = i.getNode()->getValue();
1391 core::list<MapBlock*> blocks;
1392 sector->getBlocks(blocks);
1393 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1394 i != blocks.end(); i++)
1396 deleted_blocks->push_back((*i)->getPos());
1401 deleteSectors(sector_deletion_queue, only_blocks);
1402 return sector_deletion_queue.getSize();
1405 void Map::PrintInfo(std::ostream &out)
1410 #define WATER_DROP_BOOST 4
1412 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1414 DSTACK(__FUNCTION_NAME);
1415 //TimeTaker timer("transformLiquids()");
1418 u32 initial_size = m_transforming_liquid.size();
1420 /*if(initial_size != 0)
1421 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1423 while(m_transforming_liquid.size() != 0)
1426 Get a queued transforming liquid node
1428 v3s16 p0 = m_transforming_liquid.pop_front();
1430 MapNode n0 = getNode(p0);
1432 // Don't deal with non-liquids
1433 if(content_liquid(n0.d) == false)
1436 bool is_source = !content_flowing_liquid(n0.d);
1438 u8 liquid_level = 8;
1439 if(is_source == false)
1440 liquid_level = n0.param2 & 0x0f;
1442 // Turn possible source into non-source
1443 u8 nonsource_c = make_liquid_flowing(n0.d);
1446 If not source, check that some node flows into this one
1447 and what is the level of liquid in this one
1449 if(is_source == false)
1451 s8 new_liquid_level_max = -1;
1453 v3s16 dirs_from[5] = {
1454 v3s16(0,1,0), // top
1455 v3s16(0,0,1), // back
1456 v3s16(1,0,0), // right
1457 v3s16(0,0,-1), // front
1458 v3s16(-1,0,0), // left
1460 for(u16 i=0; i<5; i++)
1465 bool from_top = (i==0);
1467 v3s16 p2 = p0 + dirs_from[i];
1468 MapNode n2 = getNode(p2);
1470 if(content_liquid(n2.d))
1472 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1473 // Check that the liquids are the same type
1474 if(n2_nonsource_c != nonsource_c)
1476 dstream<<"WARNING: Not handling: different liquids"
1477 " collide"<<std::endl;
1480 bool n2_is_source = !content_flowing_liquid(n2.d);
1481 s8 n2_liquid_level = 8;
1482 if(n2_is_source == false)
1483 n2_liquid_level = n2.param2 & 0x07;
1485 s8 new_liquid_level = -1;
1488 //new_liquid_level = 7;
1489 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1490 new_liquid_level = 7;
1492 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1494 else if(n2_liquid_level > 0)
1496 new_liquid_level = n2_liquid_level - 1;
1499 if(new_liquid_level > new_liquid_level_max)
1500 new_liquid_level_max = new_liquid_level;
1503 }catch(InvalidPositionException &e)
1509 If liquid level should be something else, update it and
1510 add all the neighboring water nodes to the transform queue.
1512 if(new_liquid_level_max != liquid_level)
1514 if(new_liquid_level_max == -1)
1516 // Remove water alltoghether
1523 n0.param2 = new_liquid_level_max;
1527 // Block has been modified
1529 v3s16 blockpos = getNodeBlockPos(p0);
1530 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1532 modified_blocks.insert(blockpos, block);
1536 Add neighboring non-source liquid nodes to transform queue.
1539 v3s16(0,0,1), // back
1540 v3s16(0,1,0), // top
1541 v3s16(1,0,0), // right
1542 v3s16(0,0,-1), // front
1543 v3s16(0,-1,0), // bottom
1544 v3s16(-1,0,0), // left
1546 for(u16 i=0; i<6; i++)
1551 v3s16 p2 = p0 + dirs[i];
1553 MapNode n2 = getNode(p2);
1554 if(content_flowing_liquid(n2.d))
1556 m_transforming_liquid.push_back(p2);
1559 }catch(InvalidPositionException &e)
1566 // Get a new one from queue if the node has turned into non-water
1567 if(content_liquid(n0.d) == false)
1571 Flow water from this node
1573 v3s16 dirs_to[5] = {
1574 v3s16(0,-1,0), // bottom
1575 v3s16(0,0,1), // back
1576 v3s16(1,0,0), // right
1577 v3s16(0,0,-1), // front
1578 v3s16(-1,0,0), // left
1580 for(u16 i=0; i<5; i++)
1585 bool to_bottom = (i == 0);
1587 // If liquid is at lowest possible height, it's not going
1588 // anywhere except down
1589 if(liquid_level == 0 && to_bottom == false)
1592 u8 liquid_next_level = 0;
1593 // If going to bottom
1596 //liquid_next_level = 7;
1597 if(liquid_level >= 7 - WATER_DROP_BOOST)
1598 liquid_next_level = 7;
1600 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1603 liquid_next_level = liquid_level - 1;
1605 bool n2_changed = false;
1606 bool flowed = false;
1608 v3s16 p2 = p0 + dirs_to[i];
1610 MapNode n2 = getNode(p2);
1611 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1613 if(content_liquid(n2.d))
1615 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1616 // Check that the liquids are the same type
1617 if(n2_nonsource_c != nonsource_c)
1619 dstream<<"WARNING: Not handling: different liquids"
1620 " collide"<<std::endl;
1623 bool n2_is_source = !content_flowing_liquid(n2.d);
1624 u8 n2_liquid_level = 8;
1625 if(n2_is_source == false)
1626 n2_liquid_level = n2.param2 & 0x07;
1635 // Just flow into the source, nothing changes.
1636 // n2_changed is not set because destination didn't change
1641 if(liquid_next_level > liquid_level)
1643 n2.param2 = liquid_next_level;
1651 else if(n2.d == CONTENT_AIR)
1654 n2.param2 = liquid_next_level;
1661 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1665 m_transforming_liquid.push_back(p2);
1667 v3s16 blockpos = getNodeBlockPos(p2);
1668 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1670 modified_blocks.insert(blockpos, block);
1673 // If n2_changed to bottom, don't flow anywhere else
1674 if(to_bottom && flowed && !is_source)
1677 }catch(InvalidPositionException &e)
1683 //if(loopcount >= 100000)
1684 if(loopcount >= initial_size * 1)
1687 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1694 ServerMap::ServerMap(std::string savedir):
1700 //m_chunksize = 16; // Too slow
1701 m_chunksize = 8; // Takes a few seconds
1705 // TODO: Save to and load from a file
1706 m_seed = (((u64)(myrand()%0xffff)<<0)
1707 + ((u64)(myrand()%0xffff)<<16)
1708 + ((u64)(myrand()%0xffff)<<32)
1709 + ((u64)(myrand()%0xffff)<<48));
1712 Experimental and debug stuff
1719 Try to load map; if not found, create a new one.
1722 m_savedir = savedir;
1723 m_map_saving_enabled = false;
1727 // If directory exists, check contents and load if possible
1728 if(fs::PathExists(m_savedir))
1730 // If directory is empty, it is safe to save into it.
1731 if(fs::GetDirListing(m_savedir).size() == 0)
1733 dstream<<DTIME<<"Server: Empty save directory is valid."
1735 m_map_saving_enabled = true;
1739 // Load map metadata (seed, chunksize)
1742 // Load chunk metadata
1745 /*// Load sector (0,0) and throw and exception on fail
1746 if(loadSectorFull(v2s16(0,0)) == false)
1747 throw LoadError("Failed to load sector (0,0)");*/
1749 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1750 "metadata and sector (0,0) from "<<savedir<<
1751 ", assuming valid save directory."
1754 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1755 <<"and chunk metadata from "<<savedir
1756 <<", assuming valid save directory."
1759 m_map_saving_enabled = true;
1760 // Map loaded, not creating new one
1764 // If directory doesn't exist, it is safe to save to it
1766 m_map_saving_enabled = true;
1769 catch(std::exception &e)
1771 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1772 <<", exception: "<<e.what()<<std::endl;
1773 dstream<<"Please remove the map or fix it."<<std::endl;
1774 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1777 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1779 // Create zero sector
1780 emergeSector(v2s16(0,0));
1782 // Initially write whole map
1786 ServerMap::~ServerMap()
1790 if(m_map_saving_enabled)
1793 // Save only changed parts
1795 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1799 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1802 catch(std::exception &e)
1804 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1805 <<", exception: "<<e.what()<<std::endl;
1811 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1812 for(; i.atEnd() == false; i++)
1814 MapChunk *chunk = i.getNode()->getValue();
1820 Some helper functions for the map generator
1823 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1825 v3s16 em = vmanip.m_area.getExtent();
1826 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1827 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1828 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1830 for(y=y_nodes_max; y>=y_nodes_min; y--)
1832 MapNode &n = vmanip.m_data[i];
1833 if(content_walkable(n.d))
1836 vmanip.m_area.add_y(em, i, -1);
1838 if(y >= y_nodes_min)
1844 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1846 v3s16 em = vmanip.m_area.getExtent();
1847 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1848 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1849 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1851 for(y=y_nodes_max; y>=y_nodes_min; y--)
1853 MapNode &n = vmanip.m_data[i];
1854 if(content_walkable(n.d)
1855 && n.d != CONTENT_TREE
1856 && n.d != CONTENT_LEAVES)
1859 vmanip.m_area.add_y(em, i, -1);
1861 if(y >= y_nodes_min)
1867 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1869 MapNode treenode(CONTENT_TREE);
1870 MapNode leavesnode(CONTENT_LEAVES);
1872 s16 trunk_h = myrand_range(3, 6);
1874 for(s16 ii=0; ii<trunk_h; ii++)
1876 if(vmanip.m_area.contains(p1))
1877 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1881 // p1 is now the last piece of the trunk
1884 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1885 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1886 Buffer<u8> leaves_d(leaves_a.getVolume());
1887 for(s32 i=0; i<leaves_a.getVolume(); i++)
1890 // Force leaves at near the end of the trunk
1893 for(s16 z=-d; z<=d; z++)
1894 for(s16 y=-d; y<=d; y++)
1895 for(s16 x=-d; x<=d; x++)
1897 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1901 // Add leaves randomly
1902 for(u32 iii=0; iii<7; iii++)
1907 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1908 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1909 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1912 for(s16 z=0; z<=d; z++)
1913 for(s16 y=0; y<=d; y++)
1914 for(s16 x=0; x<=d; x++)
1916 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1920 // Blit leaves to vmanip
1921 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1922 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1923 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1927 if(vmanip.m_area.contains(p) == false)
1929 u32 vi = vmanip.m_area.index(p);
1930 if(vmanip.m_data[vi].d != CONTENT_AIR)
1932 u32 i = leaves_a.index(x,y,z);
1933 if(leaves_d[i] == 1)
1934 vmanip.m_data[vi] = leavesnode;
1939 Noise functions. Make sure seed is mangled differently in each one.
1942 // Amount of trees per area in nodes
1943 double tree_amount_2d(u64 seed, v2s16 p)
1945 double noise = noise2d_perlin(
1946 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1948 double zeroval = -0.3;
1952 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1955 #define AVERAGE_MUD_AMOUNT 4
1957 double base_rock_level_2d(u64 seed, v2s16 p)
1959 // The base ground level
1960 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
1961 + 25. * noise2d_perlin(
1962 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1963 (seed>>32)+654879876, 6, 0.6);
1965 /*// A bit hillier one
1966 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
1967 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1968 (seed>>27)+90340, 6, 0.69);
1972 // Higher ground level
1973 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
1974 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1975 seed+85039, 5, 0.69);
1976 //higher = 30; // For debugging
1978 // Limit higher to at least base
1982 // Steepness factor of cliffs
1983 double b = 1.0 + 1.0 * noise2d_perlin(
1984 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1986 b = rangelim(b, 0.0, 1000.0);
1989 b = rangelim(b, 3.0, 1000.0);
1990 //dstream<<"b="<<b<<std::endl;
1993 // Offset to more low
1994 double a_off = -0.2;
1995 // High/low selector
1996 /*double a = 0.5 + b * (a_off + noise2d_perlin(
1997 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1998 seed-359, 6, 0.7));*/
1999 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2000 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2001 seed-359, 5, 0.60));
2003 a = rangelim(a, 0.0, 1.0);
2005 //dstream<<"a="<<a<<std::endl;
2007 double h = base*(1.0-a) + higher*a;
2014 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2017 This is the main map generation method
2020 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2021 core::map<v3s16, MapBlock*> &changed_blocks,
2024 DSTACK(__FUNCTION_NAME);
2027 Don't generate if already fully generated
2031 MapChunk *chunk = getChunk(chunkpos);
2032 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2034 dstream<<"generateChunkRaw(): Chunk "
2035 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2036 <<" already generated"<<std::endl;
2041 dstream<<"generateChunkRaw(): Generating chunk "
2042 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2045 TimeTaker timer("generateChunkRaw()");
2047 // The distance how far into the neighbors the generator is allowed to go.
2048 s16 max_spread_amount_sectors = 2;
2049 assert(max_spread_amount_sectors <= m_chunksize);
2050 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2052 // Minimum amount of space left on sides for mud to fall in
2053 //s16 min_mud_fall_space = 2;
2055 // Maximum diameter of stone obstacles in X and Z
2056 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2057 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2059 s16 y_blocks_min = -4;
2060 s16 y_blocks_max = 3;
2061 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2062 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2063 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2065 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2066 s16 sectorpos_base_size = m_chunksize;
2068 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2069 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2070 v2s16 sectorpos_bigbase =
2071 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2072 s16 sectorpos_bigbase_size =
2073 sectorpos_base_size + 2 * max_spread_amount_sectors;
2075 v3s16 bigarea_blocks_min(
2076 sectorpos_bigbase.X,
2081 v3s16 bigarea_blocks_max(
2082 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2084 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2087 // Relative values to control amount of stuff in one chunk
2088 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2089 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2090 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2091 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2092 *(u32)h_blocks*MAP_BLOCKSIZE;
2095 The limiting edges of the lighting update, inclusive.
2097 s16 lighting_min_d = 0-max_spread_amount;
2098 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2101 Create the whole area of this and the neighboring chunks
2104 TimeTaker timer("generateChunkRaw() create area");
2106 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2107 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2109 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2110 ServerMapSector *sector = createSector(sectorpos);
2113 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2115 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2116 MapBlock *block = createBlock(blockpos);
2118 // Lighting won't be calculated
2119 //block->setLightingExpired(true);
2120 // Lighting will be calculated
2121 block->setLightingExpired(false);
2124 Block gets sunlight if this is true.
2126 This should be set to true when the top side of a block
2127 is completely exposed to the sky.
2129 Actually this doesn't matter now because the
2130 initial lighting is done here.
2132 block->setIsUnderground(y != y_blocks_max);
2138 Now we have a big empty area.
2140 Make a ManualMapVoxelManipulator that contains this and the
2144 ManualMapVoxelManipulator vmanip(this);
2145 // Add the area we just generated
2147 TimeTaker timer("generateChunkRaw() initialEmerge");
2148 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2152 vmanip.clearFlag(0xff);
2154 TimeTaker timer_generate("generateChunkRaw() generate");
2156 // Maximum height of the stone surface and obstacles.
2157 // This is used to disable dungeon generation from going too high.
2158 s16 stone_surface_max_y = 0;
2161 Generate general ground level to full area
2166 //TimeTaker timer1("ground level");
2168 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2169 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2172 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2175 Skip of already generated
2178 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2179 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2183 // Ground height at this point
2184 float surface_y_f = 0.0;
2186 // Use perlin noise for ground height
2187 surface_y_f = base_rock_level_2d(m_seed, p2d);
2189 /*// Experimental stuff
2191 float a = highlands_level_2d(m_seed, p2d);
2196 // Convert to integer
2197 s16 surface_y = (s16)surface_y_f;
2200 if(surface_y > stone_surface_max_y)
2201 stone_surface_max_y = surface_y;
2204 Fill ground with stone
2207 // Use fast index incrementing
2208 v3s16 em = vmanip.m_area.getExtent();
2209 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2210 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2212 vmanip.m_data[i].d = CONTENT_STONE;
2214 vmanip.m_area.add_y(em, i, 1);
2222 Randomize some parameters
2225 s32 stone_obstacle_count = 0;
2226 /*s32 stone_obstacle_count =
2227 rangelim((1.0+noise2d(m_seed+897,
2228 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2230 s16 stone_obstacle_max_height = 0;
2231 /*s16 stone_obstacle_max_height =
2232 rangelim((1.0+noise2d(m_seed+5902,
2233 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2236 Loop this part, it will make stuff look older and newer nicely
2238 //for(u32 i_age=0; i_age<1; i_age++)
2239 for(u32 i_age=0; i_age<2; i_age++)
2244 //TimeTaker timer1("stone obstacles");
2247 Add some random stone obstacles
2250 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2252 // Randomize max height so usually stuff will be quite low
2253 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2255 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2256 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2259 myrand_range(5, stone_obstacle_max_size),
2260 myrand_range(0, maxheight_randomized),
2261 myrand_range(5, stone_obstacle_max_size)
2264 // Don't make stupid small rectangle bumps
2269 myrand_range(1+ob_size.X/2+2,
2270 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2271 myrand_range(1+ob_size.Z/2+2,
2272 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2275 // Minimum space left on top of the obstacle
2276 s16 min_head_space = 12;
2278 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2279 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2281 // Node position in 2d
2282 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2284 // Find stone ground level
2285 // (ignore everything else than mud in already generated chunks)
2286 // and mud amount over the stone level
2290 v3s16 em = vmanip.m_area.getExtent();
2291 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2293 // Go to ground level
2294 for(y=y_nodes_max; y>=y_nodes_min; y--)
2296 MapNode *n = &vmanip.m_data[i];
2297 /*if(content_walkable(n.d)
2298 && n.d != CONTENT_MUD
2299 && n.d != CONTENT_GRASS)
2301 if(n->d == CONTENT_STONE)
2304 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2308 Change to mud because otherwise we might
2309 be throwing mud on grass at the next
2315 vmanip.m_area.add_y(em, i, -1);
2317 if(y >= y_nodes_min)
2320 surface_y = y_nodes_min;
2328 v3s16 em = vmanip.m_area.getExtent();
2329 s16 y_start = surface_y+1;
2330 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2334 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2336 MapNode &n = vmanip.m_data[i];
2337 n.d = CONTENT_STONE;
2339 if(y > stone_surface_max_y)
2340 stone_surface_max_y = y;
2343 if(count >= ob_size.Y)
2346 vmanip.m_area.add_y(em, i, 1);
2350 for(; y<=y_nodes_max - min_head_space; y++)
2352 MapNode &n = vmanip.m_data[i];
2355 if(count >= mud_amount)
2358 vmanip.m_area.add_y(em, i, 1);
2368 //TimeTaker timer1("dungeons");
2373 u32 dungeons_count = relative_volume / 600000;
2374 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2375 if(stone_surface_max_y < WATER_LEVEL)
2377 /*u32 dungeons_count = 0;
2378 u32 bruises_count = 0;*/
2379 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2381 s16 min_tunnel_diameter = 2;
2382 s16 max_tunnel_diameter = 6;
2383 u16 tunnel_routepoints = 25;
2385 bool bruise_surface = (jj < bruises_count);
2389 min_tunnel_diameter = 5;
2390 max_tunnel_diameter = myrand_range(10, 20);
2391 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2392 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2394 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2395 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2397 tunnel_routepoints = 5;
2400 // Allowed route area size in nodes
2402 sectorpos_base_size*MAP_BLOCKSIZE,
2403 h_blocks*MAP_BLOCKSIZE,
2404 sectorpos_base_size*MAP_BLOCKSIZE
2407 // Area starting point in nodes
2409 sectorpos_base.X*MAP_BLOCKSIZE,
2410 y_blocks_min*MAP_BLOCKSIZE,
2411 sectorpos_base.Y*MAP_BLOCKSIZE
2415 //(this should be more than the maximum radius of the tunnel)
2416 //s16 insure = 5; // Didn't work with max_d = 20
2418 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2419 ar += v3s16(1,0,1) * more * 2;
2420 of -= v3s16(1,0,1) * more;
2422 s16 route_y_min = 0;
2423 // Allow half a diameter + 7 over stone surface
2424 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2426 /*// If dungeons, don't go through surface too often
2427 if(bruise_surface == false)
2428 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2430 // Limit maximum to area
2431 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2435 /*// Minimum is at y=0
2436 route_y_min = -of.Y - 0;*/
2437 // Minimum is at y=max_tunnel_diameter/4
2438 //route_y_min = -of.Y + max_tunnel_diameter/4;
2439 //s16 min = -of.Y + max_tunnel_diameter/4;
2440 s16 min = -of.Y + 0;
2441 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2442 route_y_min = rangelim(route_y_min, 0, route_y_max);
2445 /*dstream<<"route_y_min = "<<route_y_min
2446 <<", route_y_max = "<<route_y_max<<std::endl;*/
2448 s16 route_start_y_min = route_y_min;
2449 s16 route_start_y_max = route_y_max;
2451 // Start every 2nd dungeon from surface
2452 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2454 if(coming_from_surface)
2456 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2459 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2460 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2462 // Randomize starting position
2464 (float)(myrand()%ar.X)+0.5,
2465 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2466 (float)(myrand()%ar.Z)+0.5
2469 MapNode airnode(CONTENT_AIR);
2472 Generate some tunnel starting from orp
2475 for(u16 j=0; j<tunnel_routepoints; j++)
2478 s16 min_d = min_tunnel_diameter;
2479 s16 max_d = max_tunnel_diameter;
2480 s16 rs = myrand_range(min_d, max_d);
2485 maxlen = v3s16(rs*7,rs*7,rs*7);
2489 maxlen = v3s16(15, myrand_range(1, 20), 15);
2494 if(coming_from_surface && j < 3)
2497 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2498 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2499 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2505 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2506 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2507 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2514 else if(rp.X >= ar.X)
2516 if(rp.Y < route_y_min)
2518 else if(rp.Y >= route_y_max)
2519 rp.Y = route_y_max-1;
2522 else if(rp.Z >= ar.Z)
2526 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2528 v3f fp = orp + vec * f;
2529 v3s16 cp(fp.X, fp.Y, fp.Z);
2532 s16 d1 = d0 + rs - 1;
2533 for(s16 z0=d0; z0<=d1; z0++)
2535 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2536 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2537 for(s16 x0=-si; x0<=si-1; x0++)
2539 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2540 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2541 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2542 //s16 si2 = rs - abs(x0);
2543 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2549 /*if(isInArea(p, ar) == false)
2551 // Check only height
2552 if(y < 0 || y >= ar.Y)
2556 //assert(vmanip.m_area.contains(p));
2557 if(vmanip.m_area.contains(p) == false)
2559 dstream<<"WARNING: "<<__FUNCTION_NAME
2560 <<":"<<__LINE__<<": "
2561 <<"point not in area"
2566 // Just set it to air, it will be changed to
2568 u32 i = vmanip.m_area.index(p);
2569 vmanip.m_data[i] = airnode;
2571 if(bruise_surface == false)
2574 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2589 //TimeTaker timer1("ore veins");
2594 for(u32 jj=0; jj<relative_volume/1000; jj++)
2596 s16 max_vein_diameter = 3;
2598 // Allowed route area size in nodes
2600 sectorpos_base_size*MAP_BLOCKSIZE,
2601 h_blocks*MAP_BLOCKSIZE,
2602 sectorpos_base_size*MAP_BLOCKSIZE
2605 // Area starting point in nodes
2607 sectorpos_base.X*MAP_BLOCKSIZE,
2608 y_blocks_min*MAP_BLOCKSIZE,
2609 sectorpos_base.Y*MAP_BLOCKSIZE
2613 //(this should be more than the maximum radius of the tunnel)
2615 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2616 ar += v3s16(1,0,1) * more * 2;
2617 of -= v3s16(1,0,1) * more;
2619 // Randomize starting position
2621 (float)(myrand()%ar.X)+0.5,
2622 (float)(myrand()%ar.Y)+0.5,
2623 (float)(myrand()%ar.Z)+0.5
2626 // Randomize mineral
2629 mineral = MINERAL_COAL;
2631 mineral = MINERAL_IRON;
2634 Generate some vein starting from orp
2637 for(u16 j=0; j<2; j++)
2640 (float)(myrand()%ar.X)+0.5,
2641 (float)(myrand()%ar.Y)+0.5,
2642 (float)(myrand()%ar.Z)+0.5
2644 v3f vec = rp - orp;*/
2646 v3s16 maxlen(5, 5, 5);
2648 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2649 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2650 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2655 else if(rp.X >= ar.X)
2659 else if(rp.Y >= ar.Y)
2663 else if(rp.Z >= ar.Z)
2669 s16 max_d = max_vein_diameter;
2670 s16 rs = myrand_range(min_d, max_d);
2672 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2674 v3f fp = orp + vec * f;
2675 v3s16 cp(fp.X, fp.Y, fp.Z);
2677 s16 d1 = d0 + rs - 1;
2678 for(s16 z0=d0; z0<=d1; z0++)
2680 s16 si = rs - abs(z0);
2681 for(s16 x0=-si; x0<=si-1; x0++)
2683 s16 si2 = rs - abs(x0);
2684 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2686 // Don't put mineral to every place
2694 /*if(isInArea(p, ar) == false)
2696 // Check only height
2697 if(y < 0 || y >= ar.Y)
2701 assert(vmanip.m_area.contains(p));
2703 // Just set it to air, it will be changed to
2705 u32 i = vmanip.m_area.index(p);
2706 MapNode *n = &vmanip.m_data[i];
2707 if(n->d == CONTENT_STONE)
2722 //TimeTaker timer1("add mud");
2725 Add mud to the central chunk
2728 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2729 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2731 // Node position in 2d
2732 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2734 // Randomize mud amount
2735 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2736 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2737 m_seed+1, 3, 0.55));
2739 // Find ground level
2740 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2743 If topmost node is grass, change it to mud.
2744 It might be if it was flown to there from a neighboring
2745 chunk and then converted.
2748 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2749 MapNode *n = &vmanip.m_data[i];
2750 if(n->d == CONTENT_GRASS)
2759 v3s16 em = vmanip.m_area.getExtent();
2760 s16 y_start = surface_y+1;
2761 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2762 for(s16 y=y_start; y<=y_nodes_max; y++)
2764 if(mudcount >= mud_add_amount)
2767 MapNode &n = vmanip.m_data[i];
2771 vmanip.m_area.add_y(em, i, 1);
2780 TimeTaker timer1("flow mud");
2783 Flow mud away from steep edges
2786 // Limit area by 1 because mud is flown into neighbors.
2787 s16 mudflow_minpos = 0-max_spread_amount+1;
2788 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2790 // Iterate a few times
2791 for(s16 k=0; k<3; k++)
2794 for(s16 x=mudflow_minpos;
2797 for(s16 z=mudflow_minpos;
2801 // Invert coordinates every 2nd iteration
2804 x = mudflow_maxpos - (x-mudflow_minpos);
2805 z = mudflow_maxpos - (z-mudflow_minpos);
2808 // Node position in 2d
2809 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2811 v3s16 em = vmanip.m_area.getExtent();
2812 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2819 for(; y>=y_nodes_min; y--)
2821 n = &vmanip.m_data[i];
2822 //if(content_walkable(n->d))
2824 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2827 vmanip.m_area.add_y(em, i, -1);
2830 // Stop if out of area
2831 //if(vmanip.m_area.contains(i) == false)
2835 /*// If not mud, do nothing to it
2836 MapNode *n = &vmanip.m_data[i];
2837 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2841 Don't flow it if the stuff under it is not mud
2845 vmanip.m_area.add_y(em, i2, -1);
2846 // Cancel if out of area
2847 if(vmanip.m_area.contains(i2) == false)
2849 MapNode *n2 = &vmanip.m_data[i2];
2850 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2854 // Make it exactly mud
2857 /*s16 recurse_count = 0;
2861 v3s16(0,0,1), // back
2862 v3s16(1,0,0), // right
2863 v3s16(0,0,-1), // front
2864 v3s16(-1,0,0), // left
2867 // Theck that upper is air or doesn't exist.
2868 // Cancel dropping if upper keeps it in place
2870 vmanip.m_area.add_y(em, i3, 1);
2871 if(vmanip.m_area.contains(i3) == true
2872 && content_walkable(vmanip.m_data[i3].d) == true)
2879 for(u32 di=0; di<4; di++)
2881 v3s16 dirp = dirs4[di];
2884 vmanip.m_area.add_p(em, i2, dirp);
2885 // Fail if out of area
2886 if(vmanip.m_area.contains(i2) == false)
2888 // Check that side is air
2889 MapNode *n2 = &vmanip.m_data[i2];
2890 if(content_walkable(n2->d))
2892 // Check that under side is air
2893 vmanip.m_area.add_y(em, i2, -1);
2894 if(vmanip.m_area.contains(i2) == false)
2896 n2 = &vmanip.m_data[i2];
2897 if(content_walkable(n2->d))
2899 /*// Check that under that is air (need a drop of 2)
2900 vmanip.m_area.add_y(em, i2, -1);
2901 if(vmanip.m_area.contains(i2) == false)
2903 n2 = &vmanip.m_data[i2];
2904 if(content_walkable(n2->d))
2906 // Loop further down until not air
2908 vmanip.m_area.add_y(em, i2, -1);
2909 // Fail if out of area
2910 if(vmanip.m_area.contains(i2) == false)
2912 n2 = &vmanip.m_data[i2];
2913 }while(content_walkable(n2->d) == false);
2914 // Loop one up so that we're in air
2915 vmanip.m_area.add_y(em, i2, 1);
2916 n2 = &vmanip.m_data[i2];
2918 // Move mud to new place
2920 // Set old place to be air
2921 *n = MapNode(CONTENT_AIR);
2934 //TimeTaker timer1("add water");
2937 Add water to the central chunk (and a bit more)
2940 for(s16 x=0-max_spread_amount;
2941 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2943 for(s16 z=0-max_spread_amount;
2944 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2947 // Node position in 2d
2948 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2950 // Find ground level
2951 //s16 surface_y = find_ground_level(vmanip, p2d);
2954 If ground level is over water level, skip.
2955 NOTE: This leaves caves near water without water,
2956 which looks especially crappy when the nearby water
2957 won't start flowing either for some reason
2959 /*if(surface_y > WATER_LEVEL)
2966 v3s16 em = vmanip.m_area.getExtent();
2967 u8 light = LIGHT_MAX;
2968 // Start at global water surface level
2969 s16 y_start = WATER_LEVEL;
2970 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2971 MapNode *n = &vmanip.m_data[i];
2973 /*// Add first one to transforming liquid queue, if water
2974 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2976 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2977 m_transforming_liquid.push_back(p);
2980 for(s16 y=y_start; y>=y_nodes_min; y--)
2982 n = &vmanip.m_data[i];
2984 // Stop when there is no water and no air
2985 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2986 && n->d != CONTENT_WATER)
2988 /*// Add bottom one to transforming liquid queue
2989 vmanip.m_area.add_y(em, i, 1);
2990 n = &vmanip.m_data[i];
2991 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2993 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2994 m_transforming_liquid.push_back(p);
3000 // Make water only not in dungeons
3001 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3003 n->d = CONTENT_WATERSOURCE;
3004 //n->setLight(LIGHTBANK_DAY, light);
3006 // Add to transforming liquid queue (in case it'd
3008 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3009 m_transforming_liquid.push_back(p);
3013 vmanip.m_area.add_y(em, i, -1);
3026 //TimeTaker timer1("convert mud to sand");
3032 //s16 mud_add_amount = myrand_range(2, 4);
3033 //s16 mud_add_amount = 0;
3035 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3036 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3037 for(s16 x=0-max_spread_amount+1;
3038 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3040 for(s16 z=0-max_spread_amount+1;
3041 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3044 // Node position in 2d
3045 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3047 // Determine whether to have sand here
3048 double sandnoise = noise2d_perlin(
3049 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3050 m_seed+59420, 3, 0.50);
3052 bool have_sand = (sandnoise > -0.15);
3054 if(have_sand == false)
3057 // Find ground level
3058 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3060 if(surface_y > WATER_LEVEL + 2)
3064 v3s16 em = vmanip.m_area.getExtent();
3065 s16 y_start = surface_y;
3066 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3067 u32 not_sand_counter = 0;
3068 for(s16 y=y_start; y>=y_nodes_min; y--)
3070 MapNode *n = &vmanip.m_data[i];
3071 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3073 n->d = CONTENT_SAND;
3078 if(not_sand_counter > 3)
3082 vmanip.m_area.add_y(em, i, -1);
3091 //TimeTaker timer1("generate trees");
3097 // Divide area into parts
3099 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3100 double area = sidelen * sidelen;
3101 for(s16 x0=0; x0<div; x0++)
3102 for(s16 z0=0; z0<div; z0++)
3104 // Center position of part of division
3106 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3107 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3109 // Minimum edge of part of division
3111 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3112 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3114 // Maximum edge of part of division
3116 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3117 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3120 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3121 // Put trees in random places on part of division
3122 for(u32 i=0; i<tree_count; i++)
3124 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3125 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3126 s16 y = find_ground_level(vmanip, v2s16(x,z));
3127 // Don't make a tree under water level
3132 Trees grow only on mud and grass
3135 u32 i = vmanip.m_area.index(v3s16(p));
3136 MapNode *n = &vmanip.m_data[i];
3137 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3142 make_tree(vmanip, p);
3145 /*u32 tree_max = relative_area / 60;
3146 //u32 count = myrand_range(0, tree_max);
3147 for(u32 i=0; i<count; i++)
3149 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3150 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3151 x += sectorpos_base.X*MAP_BLOCKSIZE;
3152 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3153 s16 y = find_ground_level(vmanip, v2s16(x,z));
3154 // Don't make a tree under water level
3159 make_tree(vmanip, p);
3167 //TimeTaker timer1("grow grass");
3173 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3174 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3175 for(s16 x=0-max_spread_amount;
3176 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3178 for(s16 z=0-max_spread_amount;
3179 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3182 // Node position in 2d
3183 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3186 Find the lowest surface to which enough light ends up
3189 Basically just wait until not air and not leaves.
3193 v3s16 em = vmanip.m_area.getExtent();
3194 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3196 // Go to ground level
3197 for(y=y_nodes_max; y>=y_nodes_min; y--)
3199 MapNode &n = vmanip.m_data[i];
3200 if(n.d != CONTENT_AIR
3201 && n.d != CONTENT_LEAVES)
3203 vmanip.m_area.add_y(em, i, -1);
3205 if(y >= y_nodes_min)
3208 surface_y = y_nodes_min;
3211 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3212 MapNode *n = &vmanip.m_data[i];
3213 if(n->d == CONTENT_MUD)
3214 n->d = CONTENT_GRASS;
3220 Initial lighting (sunlight)
3223 core::map<v3s16, bool> light_sources;
3226 // 750ms @cs=8, can't optimize more
3227 TimeTaker timer1("initial lighting");
3231 Go through the edges and add all nodes that have light to light_sources
3235 for(s16 i=0; i<4; i++)
3237 for(s16 j=lighting_min_d;
3244 if(i == 0 || i == 1)
3246 x = (i==0) ? lighting_min_d : lighting_max_d;
3255 z = (i==0) ? lighting_min_d : lighting_max_d;
3262 // Node position in 2d
3263 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3266 v3s16 em = vmanip.m_area.getExtent();
3267 s16 y_start = y_nodes_max;
3268 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3269 for(s16 y=y_start; y>=y_nodes_min; y--)
3271 MapNode *n = &vmanip.m_data[i];
3272 if(n->getLight(LIGHTBANK_DAY) != 0)
3274 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3276 //NOTE: This is broken, at least the index has to
3285 Go through the edges and apply sunlight to them, not caring
3290 for(s16 i=0; i<4; i++)
3292 for(s16 j=lighting_min_d;
3299 if(i == 0 || i == 1)
3301 x = (i==0) ? lighting_min_d : lighting_max_d;
3310 z = (i==0) ? lighting_min_d : lighting_max_d;
3317 // Node position in 2d
3318 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3320 // Loop from top to down
3322 u8 light = LIGHT_SUN;
3323 v3s16 em = vmanip.m_area.getExtent();
3324 s16 y_start = y_nodes_max;
3325 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3326 for(s16 y=y_start; y>=y_nodes_min; y--)
3328 MapNode *n = &vmanip.m_data[i];
3329 if(light_propagates_content(n->d) == false)
3333 else if(light != LIGHT_SUN
3334 || sunlight_propagates_content(n->d) == false)
3340 n->setLight(LIGHTBANK_DAY, light);
3341 n->setLight(LIGHTBANK_NIGHT, 0);
3345 // Insert light source
3346 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3349 // Increment index by y
3350 vmanip.m_area.add_y(em, i, -1);
3356 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3357 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3358 /*for(s16 x=0-max_spread_amount+1;
3359 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3361 for(s16 z=0-max_spread_amount+1;
3362 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3366 This has to be 1 smaller than the actual area, because
3367 neighboring nodes are checked.
3369 for(s16 x=lighting_min_d+1;
3370 x<=lighting_max_d-1;
3372 for(s16 z=lighting_min_d+1;
3373 z<=lighting_max_d-1;
3376 // Node position in 2d
3377 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3380 Apply initial sunlight
3383 u8 light = LIGHT_SUN;
3384 bool add_to_sources = false;
3385 v3s16 em = vmanip.m_area.getExtent();
3386 s16 y_start = y_nodes_max;
3387 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3388 for(s16 y=y_start; y>=y_nodes_min; y--)
3390 MapNode *n = &vmanip.m_data[i];
3392 if(light_propagates_content(n->d) == false)
3396 else if(light != LIGHT_SUN
3397 || sunlight_propagates_content(n->d) == false)
3403 // This doesn't take much time
3404 if(add_to_sources == false)
3407 Check sides. If side is not air or water, start
3408 adding to light_sources.
3411 v3s16(0,0,1), // back
3412 v3s16(1,0,0), // right
3413 v3s16(0,0,-1), // front
3414 v3s16(-1,0,0), // left
3416 for(u32 di=0; di<4; di++)
3418 v3s16 dirp = dirs4[di];
3420 vmanip.m_area.add_p(em, i2, dirp);
3421 MapNode *n2 = &vmanip.m_data[i2];
3423 n2->d != CONTENT_AIR
3424 && n2->d != CONTENT_WATERSOURCE
3425 && n2->d != CONTENT_WATER
3427 add_to_sources = true;
3433 n->setLight(LIGHTBANK_DAY, light);
3434 n->setLight(LIGHTBANK_NIGHT, 0);
3436 // This doesn't take much time
3437 if(light != 0 && add_to_sources)
3439 // Insert light source
3440 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3443 // Increment index by y
3444 vmanip.m_area.add_y(em, i, -1);
3451 for(s16 x=lighting_min_d+1;
3452 x<=lighting_max_d-1;
3454 for(s16 z=lighting_min_d+1;
3455 z<=lighting_max_d-1;
3458 // Node position in 2d
3459 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3462 Apply initial sunlight
3465 u8 light = LIGHT_SUN;
3466 v3s16 em = vmanip.m_area.getExtent();
3467 s16 y_start = y_nodes_max;
3468 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3469 for(s16 y=y_start; y>=y_nodes_min; y--)
3471 MapNode *n = &vmanip.m_data[i];
3473 if(light_propagates_content(n->d) == false)
3477 else if(light != LIGHT_SUN
3478 || sunlight_propagates_content(n->d) == false)
3484 n->setLight(LIGHTBANK_DAY, light);
3485 n->setLight(LIGHTBANK_NIGHT, 0);
3487 // This doesn't take much time
3490 // Insert light source
3491 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3494 // Increment index by y
3495 vmanip.m_area.add_y(em, i, -1);
3503 // Spread light around
3505 TimeTaker timer("generateChunkRaw() spreadLight");
3506 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3513 timer_generate.stop();
3516 Blit generated stuff to map
3520 //TimeTaker timer("generateChunkRaw() blitBackAll");
3521 vmanip.blitBackAll(&changed_blocks);
3525 Update day/night difference cache of the MapBlocks
3528 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3529 i.atEnd() == false; i++)
3531 MapBlock *block = i.getNode()->getValue();
3532 block->updateDayNightDiff();
3538 Create chunk metadata
3541 for(s16 x=-1; x<=1; x++)
3542 for(s16 y=-1; y<=1; y++)
3544 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3545 // Add chunk meta information
3546 MapChunk *chunk = getChunk(chunkpos0);
3549 chunk = new MapChunk();
3550 m_chunks.insert(chunkpos0, chunk);
3552 //chunk->setIsVolatile(true);
3553 if(chunk->getGenLevel() > GENERATED_PARTLY)
3554 chunk->setGenLevel(GENERATED_PARTLY);
3558 Set central chunk non-volatile
3560 MapChunk *chunk = getChunk(chunkpos);
3563 //chunk->setIsVolatile(false);
3564 chunk->setGenLevel(GENERATED_FULLY);
3567 Save changed parts of map
3572 Return central chunk (which was requested)
3577 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3578 core::map<v3s16, MapBlock*> &changed_blocks)
3580 dstream<<"generateChunk(): Generating chunk "
3581 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3584 /*for(s16 x=-1; x<=1; x++)
3585 for(s16 y=-1; y<=1; y++)*/
3586 for(s16 x=-0; x<=0; x++)
3587 for(s16 y=-0; y<=0; y++)
3589 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3590 MapChunk *chunk = getChunk(chunkpos0);
3591 // Skip if already generated
3592 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3594 generateChunkRaw(chunkpos0, changed_blocks);
3597 assert(chunkNonVolatile(chunkpos1));
3599 MapChunk *chunk = getChunk(chunkpos1);
3603 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3605 DSTACK("%s: p2d=(%d,%d)",
3610 Check if it exists already in memory
3612 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3617 Try to load it from disk (with blocks)
3619 if(loadSectorFull(p2d) == true)
3621 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3624 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3625 throw InvalidPositionException("");
3631 Do not create over-limit
3633 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3634 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3635 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3636 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3637 throw InvalidPositionException("createSector(): pos. over limit");
3640 Generate blank sector
3643 sector = new ServerMapSector(this, p2d);
3645 // Sector position on map in nodes
3646 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3651 m_sectors.insert(p2d, sector);
3656 MapSector * ServerMap::emergeSector(v2s16 p2d,
3657 core::map<v3s16, MapBlock*> &changed_blocks)
3659 DSTACK("%s: p2d=(%d,%d)",
3666 v2s16 chunkpos = sector_to_chunk(p2d);
3667 /*bool chunk_nonvolatile = false;
3668 MapChunk *chunk = getChunk(chunkpos);
3669 if(chunk && chunk->getIsVolatile() == false)
3670 chunk_nonvolatile = true;*/
3671 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3674 If chunk is not fully generated, generate chunk
3676 if(chunk_nonvolatile == false)
3678 // Generate chunk and neighbors
3679 generateChunk(chunkpos, changed_blocks);
3683 Return sector if it exists now
3685 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3690 Try to load it from disk
3692 if(loadSectorFull(p2d) == true)
3694 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3697 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3698 throw InvalidPositionException("");
3704 generateChunk should have generated the sector
3708 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3709 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3713 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3715 return createSector(p2d);
3720 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3723 generateChunkRaw(chunkpos, changed_blocks, true);
3726 Return sector if it exists now
3728 sector = getSectorNoGenerateNoEx(p2d);
3732 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3740 //return generateSector();
3744 NOTE: This is not used for main map generation, only for blocks
3745 that are very high or low
3747 MapBlock * ServerMap::generateBlock(
3749 MapBlock *original_dummy,
3750 ServerMapSector *sector,
3751 core::map<v3s16, MapBlock*> &changed_blocks,
3752 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3755 DSTACK("%s: p=(%d,%d,%d)",
3759 /*dstream<<"generateBlock(): "
3760 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3763 MapBlock *block = original_dummy;
3765 v2s16 p2d(p.X, p.Z);
3767 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3770 Do not generate over-limit
3772 if(blockpos_over_limit(p))
3774 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3775 throw InvalidPositionException("generateBlock(): pos. over limit");
3779 If block doesn't exist, create one.
3780 If it exists, it is a dummy. In that case unDummify() it.
3782 NOTE: This already sets the map as the parent of the block
3786 block = sector->createBlankBlockNoInsert(block_y);
3790 // Remove the block so that nobody can get a half-generated one.
3791 sector->removeBlock(block);
3792 // Allocate the block to contain the generated data
3796 u8 water_material = CONTENT_WATERSOURCE;
3798 s32 lowest_ground_y = 32767;
3799 s32 highest_ground_y = -32768;
3801 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3802 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3804 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3806 //s16 surface_y = 0;
3808 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3809 + AVERAGE_MUD_AMOUNT;
3811 if(surface_y < lowest_ground_y)
3812 lowest_ground_y = surface_y;
3813 if(surface_y > highest_ground_y)
3814 highest_ground_y = surface_y;
3816 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3818 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3820 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3825 NOTE: If there are some man-made structures above the
3826 newly created block, they won't be taken into account.
3828 if(real_y > surface_y)
3829 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3835 // If node is over heightmap y, it's air or water
3836 if(real_y > surface_y)
3838 // If under water level, it's water
3839 if(real_y < WATER_LEVEL)
3841 n.d = water_material;
3842 n.setLight(LIGHTBANK_DAY,
3843 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3845 Add to transforming liquid queue (in case it'd
3848 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3849 m_transforming_liquid.push_back(real_pos);
3855 // Else it's ground or dungeons (air)
3858 // If it's surface_depth under ground, it's stone
3859 if(real_y <= surface_y - surface_depth)
3861 n.d = CONTENT_STONE;
3865 // It is mud if it is under the first ground
3866 // level or under water
3867 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3873 n.d = CONTENT_GRASS;
3876 //n.d = CONTENT_MUD;
3878 /*// If under water level, it's mud
3879 if(real_y < WATER_LEVEL)
3881 // Only the topmost node is grass
3882 else if(real_y <= surface_y - 1)
3885 n.d = CONTENT_GRASS;*/
3889 block->setNode(v3s16(x0,y0,z0), n);
3894 Calculate some helper variables
3897 // Completely underground if the highest part of block is under lowest
3899 // This has to be very sure; it's probably one too strict now but
3900 // that's just better.
3901 bool completely_underground =
3902 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3904 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3906 bool mostly_underwater_surface = false;
3907 if(highest_ground_y < WATER_LEVEL
3908 && some_part_underground && !completely_underground)
3909 mostly_underwater_surface = true;
3912 Get local attributes
3915 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3917 float caves_amount = 0.5;
3922 NOTE: BEWARE: Too big amount of attribute points slows verything
3924 1 interpolation from 5000 points takes 2-3ms.
3926 //TimeTaker timer("generateBlock() local attribute retrieval");
3927 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3928 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3929 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3933 //dstream<<"generateBlock(): Done"<<std::endl;
3939 // Initialize temporary table
3940 const s32 ued = MAP_BLOCKSIZE;
3941 bool underground_emptiness[ued*ued*ued];
3942 for(s32 i=0; i<ued*ued*ued; i++)
3944 underground_emptiness[i] = 0;
3951 Initialize orp and ors. Try to find if some neighboring
3952 MapBlock has a tunnel ended in its side
3956 (float)(myrand()%ued)+0.5,
3957 (float)(myrand()%ued)+0.5,
3958 (float)(myrand()%ued)+0.5
3961 bool found_existing = false;
3967 for(s16 y=0; y<ued; y++)
3968 for(s16 x=0; x<ued; x++)
3970 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3971 if(getNode(ap).d == CONTENT_AIR)
3973 orp = v3f(x+1,y+1,0);
3974 found_existing = true;
3975 goto continue_generating;
3979 catch(InvalidPositionException &e){}
3985 for(s16 y=0; y<ued; y++)
3986 for(s16 x=0; x<ued; x++)
3988 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3989 if(getNode(ap).d == CONTENT_AIR)
3991 orp = v3f(x+1,y+1,ued-1);
3992 found_existing = true;
3993 goto continue_generating;
3997 catch(InvalidPositionException &e){}
4003 for(s16 y=0; y<ued; y++)
4004 for(s16 z=0; z<ued; z++)
4006 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4007 if(getNode(ap).d == CONTENT_AIR)
4009 orp = v3f(0,y+1,z+1);
4010 found_existing = true;
4011 goto continue_generating;
4015 catch(InvalidPositionException &e){}
4021 for(s16 y=0; y<ued; y++)
4022 for(s16 z=0; z<ued; z++)
4024 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4025 if(getNode(ap).d == CONTENT_AIR)
4027 orp = v3f(ued-1,y+1,z+1);
4028 found_existing = true;
4029 goto continue_generating;
4033 catch(InvalidPositionException &e){}
4039 for(s16 x=0; x<ued; x++)
4040 for(s16 z=0; z<ued; z++)
4042 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4043 if(getNode(ap).d == CONTENT_AIR)
4045 orp = v3f(x+1,0,z+1);
4046 found_existing = true;
4047 goto continue_generating;
4051 catch(InvalidPositionException &e){}
4057 for(s16 x=0; x<ued; x++)
4058 for(s16 z=0; z<ued; z++)
4060 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4061 if(getNode(ap).d == CONTENT_AIR)
4063 orp = v3f(x+1,ued-1,z+1);
4064 found_existing = true;
4065 goto continue_generating;
4069 catch(InvalidPositionException &e){}
4071 continue_generating:
4074 Choose whether to actually generate dungeon
4076 bool do_generate_dungeons = true;
4077 // Don't generate if no part is underground
4078 if(!some_part_underground)
4080 do_generate_dungeons = false;
4082 // Don't generate if mostly underwater surface
4083 /*else if(mostly_underwater_surface)
4085 do_generate_dungeons = false;
4087 // Partly underground = cave
4088 else if(!completely_underground)
4090 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4092 // Found existing dungeon underground
4093 else if(found_existing && completely_underground)
4095 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4097 // Underground and no dungeons found
4100 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4103 if(do_generate_dungeons)
4106 Generate some tunnel starting from orp and ors
4108 for(u16 i=0; i<3; i++)
4111 (float)(myrand()%ued)+0.5,
4112 (float)(myrand()%ued)+0.5,
4113 (float)(myrand()%ued)+0.5
4117 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4121 for(float f=0; f<1.0; f+=0.04)
4123 v3f fp = orp + vec * f;
4124 v3s16 cp(fp.X, fp.Y, fp.Z);
4126 s16 d1 = d0 + rs - 1;
4127 for(s16 z0=d0; z0<=d1; z0++)
4129 s16 si = rs - abs(z0);
4130 for(s16 x0=-si; x0<=si-1; x0++)
4132 s16 si2 = rs - abs(x0);
4133 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4139 if(isInArea(p, ued) == false)
4141 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4153 // Set to true if has caves.
4154 // Set when some non-air is changed to air when making caves.
4155 bool has_dungeons = false;
4158 Apply temporary cave data to block
4161 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4162 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4164 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4166 MapNode n = block->getNode(v3s16(x0,y0,z0));
4169 if(underground_emptiness[
4170 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4171 +ued*(y0*ued/MAP_BLOCKSIZE)
4172 +(x0*ued/MAP_BLOCKSIZE)])
4174 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4177 has_dungeons = true;
4183 block->setNode(v3s16(x0,y0,z0), n);
4188 This is used for guessing whether or not the block should
4189 receive sunlight from the top if the block above doesn't exist
4191 block->setIsUnderground(completely_underground);
4194 Force lighting update if some part of block is partly
4195 underground and has caves.
4197 /*if(some_part_underground && !completely_underground && has_dungeons)
4199 //dstream<<"Half-ground caves"<<std::endl;
4200 lighting_invalidated_blocks[block->getPos()] = block;
4203 // DEBUG: Always update lighting
4204 //lighting_invalidated_blocks[block->getPos()] = block;
4210 if(some_part_underground)
4212 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4217 for(s16 i=0; i<underground_level/4 + 1; i++)
4219 if(myrand()%50 == 0)
4222 (myrand()%(MAP_BLOCKSIZE-2))+1,
4223 (myrand()%(MAP_BLOCKSIZE-2))+1,
4224 (myrand()%(MAP_BLOCKSIZE-2))+1
4230 for(u16 i=0; i<27; i++)
4232 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4234 block->setNode(cp+g_27dirs[i], n);
4242 u16 coal_amount = 30;
4243 u16 coal_rareness = 60 / coal_amount;
4244 if(coal_rareness == 0)
4246 if(myrand()%coal_rareness == 0)
4248 u16 a = myrand() % 16;
4249 u16 amount = coal_amount * a*a*a / 1000;
4250 for(s16 i=0; i<amount; i++)
4253 (myrand()%(MAP_BLOCKSIZE-2))+1,
4254 (myrand()%(MAP_BLOCKSIZE-2))+1,
4255 (myrand()%(MAP_BLOCKSIZE-2))+1
4259 n.d = CONTENT_STONE;
4260 n.param = MINERAL_COAL;
4262 for(u16 i=0; i<27; i++)
4264 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4266 block->setNode(cp+g_27dirs[i], n);
4274 //TODO: change to iron_amount or whatever
4275 u16 iron_amount = 15;
4276 u16 iron_rareness = 60 / iron_amount;
4277 if(iron_rareness == 0)
4279 if(myrand()%iron_rareness == 0)
4281 u16 a = myrand() % 16;
4282 u16 amount = iron_amount * a*a*a / 1000;
4283 for(s16 i=0; i<amount; i++)
4286 (myrand()%(MAP_BLOCKSIZE-2))+1,
4287 (myrand()%(MAP_BLOCKSIZE-2))+1,
4288 (myrand()%(MAP_BLOCKSIZE-2))+1
4292 n.d = CONTENT_STONE;
4293 n.param = MINERAL_IRON;
4295 for(u16 i=0; i<27; i++)
4297 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4299 block->setNode(cp+g_27dirs[i], n);
4306 Create a few rats in empty blocks underground
4308 if(completely_underground)
4310 //for(u16 i=0; i<2; i++)
4313 (myrand()%(MAP_BLOCKSIZE-2))+1,
4314 (myrand()%(MAP_BLOCKSIZE-2))+1,
4315 (myrand()%(MAP_BLOCKSIZE-2))+1
4318 // Check that the place is empty
4319 //if(!is_ground_content(block->getNode(cp).d))
4322 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4323 block->addObject(obj);
4329 Add block to sector.
4331 sector->insertBlock(block);
4333 // Lighting is invalid after generation.
4334 block->setLightingExpired(true);
4341 <<"lighting_invalidated_blocks.size()"
4345 <<" "<<lighting_invalidated_blocks.size()
4346 <<", "<<has_dungeons
4347 <<", "<<completely_underground
4348 <<", "<<some_part_underground
4355 MapBlock * ServerMap::createBlock(v3s16 p)
4357 DSTACK("%s: p=(%d,%d,%d)",
4358 __FUNCTION_NAME, p.X, p.Y, p.Z);
4361 Do not create over-limit
4363 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4364 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4365 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4366 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4367 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4368 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4369 throw InvalidPositionException("createBlock(): pos. over limit");
4371 v2s16 p2d(p.X, p.Z);
4374 This will create or load a sector if not found in memory.
4375 If block exists on disk, it will be loaded.
4377 NOTE: On old save formats, this will be slow, as it generates
4378 lighting on blocks for them.
4380 ServerMapSector *sector;
4382 sector = (ServerMapSector*)createSector(p2d);
4383 assert(sector->getId() == MAPSECTOR_SERVER);
4385 catch(InvalidPositionException &e)
4387 dstream<<"createBlock: createSector() failed"<<std::endl;
4391 NOTE: This should not be done, or at least the exception
4392 should not be passed on as std::exception, because it
4393 won't be catched at all.
4395 /*catch(std::exception &e)
4397 dstream<<"createBlock: createSector() failed: "
4398 <<e.what()<<std::endl;
4403 Try to get a block from the sector
4406 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4410 block = sector->createBlankBlock(block_y);
4414 MapBlock * ServerMap::emergeBlock(
4416 bool only_from_disk,
4417 core::map<v3s16, MapBlock*> &changed_blocks,
4418 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4421 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4423 p.X, p.Y, p.Z, only_from_disk);
4426 Do not generate over-limit
4428 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4429 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4430 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4431 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4432 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4433 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4434 throw InvalidPositionException("emergeBlock(): pos. over limit");
4436 v2s16 p2d(p.X, p.Z);
4439 This will create or load a sector if not found in memory.
4440 If block exists on disk, it will be loaded.
4442 ServerMapSector *sector;
4444 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4445 assert(sector->getId() == MAPSECTOR_SERVER);
4447 catch(InvalidPositionException &e)
4449 dstream<<"emergeBlock: emergeSector() failed: "
4450 <<e.what()<<std::endl;
4451 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4453 <<"You could try to delete it."<<std::endl;
4456 catch(VersionMismatchException &e)
4458 dstream<<"emergeBlock: emergeSector() failed: "
4459 <<e.what()<<std::endl;
4460 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4462 <<"You could try to delete it."<<std::endl;
4466 NOTE: This should not be done, or at least the exception
4467 should not be passed on as std::exception, because it
4468 won't be catched at all.
4470 /*catch(std::exception &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;
4481 Try to get a block from the sector
4484 bool does_not_exist = false;
4485 bool lighting_expired = false;
4486 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4490 does_not_exist = true;
4492 else if(block->isDummy() == true)
4494 does_not_exist = true;
4496 else if(block->getLightingExpired())
4498 lighting_expired = true;
4503 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4508 If block was not found on disk and not going to generate a
4509 new one, make sure there is a dummy block in place.
4511 if(only_from_disk && (does_not_exist || lighting_expired))
4513 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4517 // Create dummy block
4518 block = new MapBlock(this, p, true);
4520 // Add block to sector
4521 sector->insertBlock(block);
4527 //dstream<<"Not found on disk, generating."<<std::endl;
4529 //TimeTaker("emergeBlock() generate");
4531 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4534 If the block doesn't exist, generate the block.
4538 block = generateBlock(p, block, sector, changed_blocks,
4539 lighting_invalidated_blocks);
4542 if(lighting_expired)
4544 lighting_invalidated_blocks.insert(p, block);
4548 Initially update sunlight
4552 core::map<v3s16, bool> light_sources;
4553 bool black_air_left = false;
4554 bool bottom_invalid =
4555 block->propagateSunlight(light_sources, true,
4556 &black_air_left, true);
4558 // If sunlight didn't reach everywhere and part of block is
4559 // above ground, lighting has to be properly updated
4560 //if(black_air_left && some_part_underground)
4563 lighting_invalidated_blocks[block->getPos()] = block;
4568 lighting_invalidated_blocks[block->getPos()] = block;
4575 s16 ServerMap::findGroundLevel(v2s16 p2d)
4578 Uh, just do something random...
4580 // Find existing map from top to down
4583 v3s16 p(p2d.X, max, p2d.Y);
4584 for(; p.Y>min; p.Y--)
4586 MapNode n = getNodeNoEx(p);
4587 if(n.d != CONTENT_IGNORE)
4592 // If this node is not air, go to plan b
4593 if(getNodeNoEx(p).d != CONTENT_AIR)
4595 // Search existing walkable and return it
4596 for(; p.Y>min; p.Y--)
4598 MapNode n = getNodeNoEx(p);
4599 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4605 Plan B: Get from map generator perlin noise function
4607 double level = base_rock_level_2d(m_seed, p2d);
4611 void ServerMap::createDir(std::string path)
4613 if(fs::CreateDir(path) == false)
4615 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4616 <<"\""<<path<<"\""<<std::endl;
4617 throw BaseException("ServerMap failed to create directory");
4621 std::string ServerMap::getSectorSubDir(v2s16 pos)
4624 snprintf(cc, 9, "%.4x%.4x",
4625 (unsigned int)pos.X&0xffff,
4626 (unsigned int)pos.Y&0xffff);
4628 return std::string(cc);
4631 std::string ServerMap::getSectorDir(v2s16 pos)
4633 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4636 v2s16 ServerMap::getSectorPos(std::string dirname)
4638 if(dirname.size() != 8)
4639 throw InvalidFilenameException("Invalid sector directory name");
4641 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4643 throw InvalidFilenameException("Invalid sector directory name");
4644 v2s16 pos((s16)x, (s16)y);
4648 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4650 v2s16 p2d = getSectorPos(sectordir);
4652 if(blockfile.size() != 4){
4653 throw InvalidFilenameException("Invalid block filename");
4656 int r = sscanf(blockfile.c_str(), "%4x", &y);
4658 throw InvalidFilenameException("Invalid block filename");
4659 return v3s16(p2d.X, y, p2d.Y);
4662 void ServerMap::save(bool only_changed)
4664 DSTACK(__FUNCTION_NAME);
4665 if(m_map_saving_enabled == false)
4667 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4671 if(only_changed == false)
4672 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4678 u32 sector_meta_count = 0;
4679 u32 block_count = 0;
4682 JMutexAutoLock lock(m_sector_mutex);
4684 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4685 for(; i.atEnd() == false; i++)
4687 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4688 assert(sector->getId() == MAPSECTOR_SERVER);
4690 if(sector->differs_from_disk || only_changed == false)
4692 saveSectorMeta(sector);
4693 sector_meta_count++;
4695 core::list<MapBlock*> blocks;
4696 sector->getBlocks(blocks);
4697 core::list<MapBlock*>::Iterator j;
4698 for(j=blocks.begin(); j!=blocks.end(); j++)
4700 MapBlock *block = *j;
4701 if(block->getChangedFlag() || only_changed == false)
4706 /*dstream<<"ServerMap: Written block ("
4707 <<block->getPos().X<<","
4708 <<block->getPos().Y<<","
4709 <<block->getPos().Z<<")"
4718 Only print if something happened or saved whole map
4720 if(only_changed == false || sector_meta_count != 0
4721 || block_count != 0)
4723 dstream<<DTIME<<"ServerMap: Written: "
4724 <<sector_meta_count<<" sector metadata files, "
4725 <<block_count<<" block files"
4730 void ServerMap::loadAll()
4732 DSTACK(__FUNCTION_NAME);
4733 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4738 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4740 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4742 JMutexAutoLock lock(m_sector_mutex);
4745 s32 printed_counter = -100000;
4746 s32 count = list.size();
4748 std::vector<fs::DirListNode>::iterator i;
4749 for(i=list.begin(); i!=list.end(); i++)
4751 if(counter > printed_counter + 10)
4753 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4754 printed_counter = counter;
4758 MapSector *sector = NULL;
4760 // We want directories
4764 sector = loadSectorMeta(i->name);
4766 catch(InvalidFilenameException &e)
4768 // This catches unknown crap in directory
4771 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4772 (m_savedir+"/sectors/"+i->name);
4773 std::vector<fs::DirListNode>::iterator i2;
4774 for(i2=list2.begin(); i2!=list2.end(); i2++)
4780 loadBlock(i->name, i2->name, sector);
4782 catch(InvalidFilenameException &e)
4784 // This catches unknown crap in directory
4788 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4792 void ServerMap::saveMasterHeightmap()
4794 DSTACK(__FUNCTION_NAME);
4796 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4798 createDir(m_savedir);
4800 /*std::string fullpath = m_savedir + "/master_heightmap";
4801 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4802 if(o.good() == false)
4803 throw FileNotGoodException("Cannot open master heightmap");*/
4805 // Format used for writing
4806 //u8 version = SER_FMT_VER_HIGHEST;
4809 void ServerMap::loadMasterHeightmap()
4811 DSTACK(__FUNCTION_NAME);
4813 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4815 /*std::string fullpath = m_savedir + "/master_heightmap";
4816 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4817 if(is.good() == false)
4818 throw FileNotGoodException("Cannot open master heightmap");*/
4822 void ServerMap::saveMapMeta()
4824 DSTACK(__FUNCTION_NAME);
4826 dstream<<"INFO: ServerMap::saveMapMeta(): "
4827 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4830 createDir(m_savedir);
4832 std::string fullpath = m_savedir + "/map_meta.txt";
4833 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4834 if(os.good() == false)
4836 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4837 <<"could not open"<<fullpath<<std::endl;
4838 throw FileNotGoodException("Cannot open chunk metadata");
4842 params.setU64("seed", m_seed);
4843 params.setS32("chunksize", m_chunksize);
4845 params.writeLines(os);
4847 os<<"[end_of_params]\n";
4851 void ServerMap::loadMapMeta()
4853 DSTACK(__FUNCTION_NAME);
4855 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4858 std::string fullpath = m_savedir + "/map_meta.txt";
4859 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4860 if(is.good() == false)
4862 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4863 <<"could not open"<<fullpath<<std::endl;
4864 throw FileNotGoodException("Cannot open chunk metadata");
4872 throw SerializationError
4873 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4875 std::getline(is, line);
4876 std::string trimmedline = trim(line);
4877 if(trimmedline == "[end_of_params]")
4879 params.parseConfigLine(line);
4882 m_seed = params.getU64("seed");
4883 m_chunksize = params.getS32("chunksize");
4885 dstream<<"INFO: ServerMap::loadMapMeta(): "
4886 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4890 void ServerMap::saveChunkMeta()
4892 DSTACK(__FUNCTION_NAME);
4894 u32 count = m_chunks.size();
4896 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4897 <<count<<" chunks"<<std::endl;
4899 createDir(m_savedir);
4901 std::string fullpath = m_savedir + "/chunk_meta";
4902 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4903 if(os.good() == false)
4905 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4906 <<"could not open"<<fullpath<<std::endl;
4907 throw FileNotGoodException("Cannot open chunk metadata");
4913 os.write((char*)&version, 1);
4918 writeU32(buf, count);
4919 os.write((char*)buf, 4);
4921 for(core::map<v2s16, MapChunk*>::Iterator
4922 i = m_chunks.getIterator();
4923 i.atEnd()==false; i++)
4925 v2s16 p = i.getNode()->getKey();
4926 MapChunk *chunk = i.getNode()->getValue();
4929 os.write((char*)buf, 4);
4931 chunk->serialize(os, version);
4935 void ServerMap::loadChunkMeta()
4937 DSTACK(__FUNCTION_NAME);
4939 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
4942 std::string fullpath = m_savedir + "/chunk_meta";
4943 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4944 if(is.good() == false)
4946 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
4947 <<"could not open"<<fullpath<<std::endl;
4948 throw FileNotGoodException("Cannot open chunk metadata");
4954 is.read((char*)&version, 1);
4959 is.read((char*)buf, 4);
4960 u32 count = readU32(buf);
4962 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
4963 <<count<<" chunks"<<std::endl;
4965 for(u32 i=0; i<count; i++)
4968 MapChunk *chunk = new MapChunk();
4970 is.read((char*)buf, 4);
4973 chunk->deSerialize(is, version);
4974 m_chunks.insert(p, chunk);
4978 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4980 DSTACK(__FUNCTION_NAME);
4981 // Format used for writing
4982 u8 version = SER_FMT_VER_HIGHEST;
4984 v2s16 pos = sector->getPos();
4985 createDir(m_savedir);
4986 createDir(m_savedir+"/sectors");
4987 std::string dir = getSectorDir(pos);
4990 std::string fullpath = dir + "/meta";
4991 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4992 if(o.good() == false)
4993 throw FileNotGoodException("Cannot open sector metafile");
4995 sector->serialize(o, version);
4997 sector->differs_from_disk = false;
5000 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5002 DSTACK(__FUNCTION_NAME);
5004 v2s16 p2d = getSectorPos(dirname);
5005 std::string dir = m_savedir + "/sectors/" + dirname;
5007 std::string fullpath = dir + "/meta";
5008 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5009 if(is.good() == false)
5010 throw FileNotGoodException("Cannot open sector metafile");
5012 ServerMapSector *sector = ServerMapSector::deSerialize
5013 (is, this, p2d, m_sectors);
5015 sector->differs_from_disk = false;
5020 bool ServerMap::loadSectorFull(v2s16 p2d)
5022 DSTACK(__FUNCTION_NAME);
5023 std::string sectorsubdir = getSectorSubDir(p2d);
5025 MapSector *sector = NULL;
5027 JMutexAutoLock lock(m_sector_mutex);
5030 sector = loadSectorMeta(sectorsubdir);
5032 catch(InvalidFilenameException &e)
5036 catch(FileNotGoodException &e)
5040 catch(std::exception &e)
5048 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5049 (m_savedir+"/sectors/"+sectorsubdir);
5050 std::vector<fs::DirListNode>::iterator i2;
5051 for(i2=list2.begin(); i2!=list2.end(); i2++)
5057 loadBlock(sectorsubdir, i2->name, sector);
5059 catch(InvalidFilenameException &e)
5061 // This catches unknown crap in directory
5067 void ServerMap::saveBlock(MapBlock *block)
5069 DSTACK(__FUNCTION_NAME);
5071 Dummy blocks are not written
5073 if(block->isDummy())
5075 /*v3s16 p = block->getPos();
5076 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5077 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5081 // Format used for writing
5082 u8 version = SER_FMT_VER_HIGHEST;
5084 v3s16 p3d = block->getPos();
5085 v2s16 p2d(p3d.X, p3d.Z);
5086 createDir(m_savedir);
5087 createDir(m_savedir+"/sectors");
5088 std::string dir = getSectorDir(p2d);
5091 // Block file is map/sectors/xxxxxxxx/xxxx
5093 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5094 std::string fullpath = dir + "/" + cc;
5095 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5096 if(o.good() == false)
5097 throw FileNotGoodException("Cannot open block data");
5100 [0] u8 serialization version
5103 o.write((char*)&version, 1);
5105 block->serialize(o, version);
5108 Versions up from 9 have block objects.
5112 block->serializeObjects(o, version);
5115 // We just wrote it to the disk
5116 block->resetChangedFlag();
5119 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5121 DSTACK(__FUNCTION_NAME);
5125 // Block file is map/sectors/xxxxxxxx/xxxx
5126 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5127 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5128 if(is.good() == false)
5129 throw FileNotGoodException("Cannot open block file");
5131 v3s16 p3d = getBlockPos(sectordir, blockfile);
5132 v2s16 p2d(p3d.X, p3d.Z);
5134 assert(sector->getPos() == p2d);
5136 u8 version = SER_FMT_VER_INVALID;
5137 is.read((char*)&version, 1);
5140 throw SerializationError("ServerMap::loadBlock(): Failed"
5141 " to read MapBlock version");
5143 /*u32 block_size = MapBlock::serializedLength(version);
5144 SharedBuffer<u8> data(block_size);
5145 is.read((char*)*data, block_size);*/
5147 // This will always return a sector because we're the server
5148 //MapSector *sector = emergeSector(p2d);
5150 MapBlock *block = NULL;
5151 bool created_new = false;
5153 block = sector->getBlockNoCreate(p3d.Y);
5155 catch(InvalidPositionException &e)
5157 block = sector->createBlankBlockNoInsert(p3d.Y);
5161 // deserialize block data
5162 block->deSerialize(is, version);
5165 Versions up from 9 have block objects.
5169 block->updateObjects(is, version, NULL, 0);
5173 sector->insertBlock(block);
5176 Convert old formats to new and save
5179 // Save old format blocks in new format
5180 if(version < SER_FMT_VER_HIGHEST)
5185 // We just loaded it from the disk, so it's up-to-date.
5186 block->resetChangedFlag();
5189 catch(SerializationError &e)
5191 dstream<<"WARNING: Invalid block data on disk "
5192 "(SerializationError). Ignoring. "
5193 "A new one will be generated."
5198 void ServerMap::PrintInfo(std::ostream &out)
5209 ClientMap::ClientMap(
5211 MapDrawControl &control,
5212 scene::ISceneNode* parent,
5213 scene::ISceneManager* mgr,
5217 scene::ISceneNode(parent, mgr, id),
5220 m_camera_position(0,0,0),
5221 m_camera_direction(0,0,1)
5223 m_camera_mutex.Init();
5224 assert(m_camera_mutex.IsInitialized());
5226 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5227 BS*1000000,BS*1000000,BS*1000000);
5230 ClientMap::~ClientMap()
5232 /*JMutexAutoLock lock(mesh_mutex);
5241 MapSector * ClientMap::emergeSector(v2s16 p2d)
5243 DSTACK(__FUNCTION_NAME);
5244 // Check that it doesn't exist already
5246 return getSectorNoGenerate(p2d);
5248 catch(InvalidPositionException &e)
5253 ClientMapSector *sector = new ClientMapSector(this, p2d);
5256 JMutexAutoLock lock(m_sector_mutex);
5257 m_sectors.insert(p2d, sector);
5263 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5265 DSTACK(__FUNCTION_NAME);
5266 ClientMapSector *sector = NULL;
5268 JMutexAutoLock lock(m_sector_mutex);
5270 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5274 sector = (ClientMapSector*)n->getValue();
5275 assert(sector->getId() == MAPSECTOR_CLIENT);
5279 sector = new ClientMapSector(this, p2d);
5281 JMutexAutoLock lock(m_sector_mutex);
5282 m_sectors.insert(p2d, sector);
5286 sector->deSerialize(is);
5289 void ClientMap::OnRegisterSceneNode()
5293 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5294 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5297 ISceneNode::OnRegisterSceneNode();
5300 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5302 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5303 DSTACK(__FUNCTION_NAME);
5305 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5308 Get time for measuring timeout.
5310 Measuring time is very useful for long delays when the
5311 machine is swapping a lot.
5313 int time1 = time(0);
5315 u32 daynight_ratio = m_client->getDayNightRatio();
5317 m_camera_mutex.Lock();
5318 v3f camera_position = m_camera_position;
5319 v3f camera_direction = m_camera_direction;
5320 m_camera_mutex.Unlock();
5323 Get all blocks and draw all visible ones
5326 v3s16 cam_pos_nodes(
5327 camera_position.X / BS,
5328 camera_position.Y / BS,
5329 camera_position.Z / BS);
5331 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5333 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5334 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5336 // Take a fair amount as we will be dropping more out later
5338 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5339 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5340 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5342 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5343 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5344 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5346 u32 vertex_count = 0;
5348 // For limiting number of mesh updates per frame
5349 u32 mesh_update_count = 0;
5351 u32 blocks_would_have_drawn = 0;
5352 u32 blocks_drawn = 0;
5354 //NOTE: The sectors map should be locked but we're not doing it
5355 // because it'd cause too much delays
5357 int timecheck_counter = 0;
5358 core::map<v2s16, MapSector*>::Iterator si;
5359 si = m_sectors.getIterator();
5360 for(; si.atEnd() == false; si++)
5363 timecheck_counter++;
5364 if(timecheck_counter > 50)
5366 timecheck_counter = 0;
5367 int time2 = time(0);
5368 if(time2 > time1 + 4)
5370 dstream<<"ClientMap::renderMap(): "
5371 "Rendering takes ages, returning."
5378 MapSector *sector = si.getNode()->getValue();
5379 v2s16 sp = sector->getPos();
5381 if(m_control.range_all == false)
5383 if(sp.X < p_blocks_min.X
5384 || sp.X > p_blocks_max.X
5385 || sp.Y < p_blocks_min.Z
5386 || sp.Y > p_blocks_max.Z)
5390 core::list< MapBlock * > sectorblocks;
5391 sector->getBlocks(sectorblocks);
5397 core::list< MapBlock * >::Iterator i;
5398 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5400 MapBlock *block = *i;
5403 Compare block position to camera position, skip
5404 if not seen on display
5407 float range = 100000 * BS;
5408 if(m_control.range_all == false)
5409 range = m_control.wanted_range * BS;
5412 if(isBlockInSight(block->getPos(), camera_position,
5413 camera_direction, range, &d) == false)
5419 /*if(m_control.range_all == false &&
5420 d - 0.5*BS*MAP_BLOCKSIZE > range)
5425 Update expired mesh (used for day/night change)
5428 bool mesh_expired = false;
5431 JMutexAutoLock lock(block->mesh_mutex);
5433 mesh_expired = block->getMeshExpired();
5435 // Mesh has not been expired and there is no mesh:
5436 // block has no content
5437 if(block->mesh == NULL && mesh_expired == false)
5441 f32 faraway = BS*50;
5442 //f32 faraway = m_control.wanted_range * BS;
5445 This has to be done with the mesh_mutex unlocked
5447 // Pretty random but this should work somewhat nicely
5448 if(mesh_expired && (
5449 (mesh_update_count < 3
5450 && (d < faraway || mesh_update_count < 2)
5453 (m_control.range_all && mesh_update_count < 20)
5456 /*if(mesh_expired && mesh_update_count < 6
5457 && (d < faraway || mesh_update_count < 3))*/
5459 mesh_update_count++;
5461 // Mesh has been expired: generate new mesh
5462 //block->updateMeshes(daynight_i);
5463 block->updateMesh(daynight_ratio);
5465 mesh_expired = false;
5469 Don't draw an expired mesh that is far away
5471 /*if(mesh_expired && d >= faraway)
5474 // Instead, delete it
5475 JMutexAutoLock lock(block->mesh_mutex);
5478 block->mesh->drop();
5481 // And continue to next block
5486 Draw the faces of the block
5489 JMutexAutoLock lock(block->mesh_mutex);
5491 scene::SMesh *mesh = block->mesh;
5496 blocks_would_have_drawn++;
5497 if(blocks_drawn >= m_control.wanted_max_blocks
5498 && m_control.range_all == false
5499 && d > m_control.wanted_min_range * BS)
5503 u32 c = mesh->getMeshBufferCount();
5505 for(u32 i=0; i<c; i++)
5507 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5508 const video::SMaterial& material = buf->getMaterial();
5509 video::IMaterialRenderer* rnd =
5510 driver->getMaterialRenderer(material.MaterialType);
5511 bool transparent = (rnd && rnd->isTransparent());
5512 // Render transparent on transparent pass and likewise.
5513 if(transparent == is_transparent_pass)
5516 This *shouldn't* hurt too much because Irrlicht
5517 doesn't change opengl textures if the old
5518 material is set again.
5520 driver->setMaterial(buf->getMaterial());
5521 driver->drawMeshBuffer(buf);
5522 vertex_count += buf->getVertexCount();
5526 } // foreach sectorblocks
5529 m_control.blocks_drawn = blocks_drawn;
5530 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5532 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5533 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5536 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5537 core::map<v3s16, MapBlock*> *affected_blocks)
5539 bool changed = false;
5541 Add it to all blocks touching it
5544 v3s16(0,0,0), // this
5545 v3s16(0,0,1), // back
5546 v3s16(0,1,0), // top
5547 v3s16(1,0,0), // right
5548 v3s16(0,0,-1), // front
5549 v3s16(0,-1,0), // bottom
5550 v3s16(-1,0,0), // left
5552 for(u16 i=0; i<7; i++)
5554 v3s16 p2 = p + dirs[i];
5555 // Block position of neighbor (or requested) node
5556 v3s16 blockpos = getNodeBlockPos(p2);
5557 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5558 if(blockref == NULL)
5560 // Relative position of requested node
5561 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5562 if(blockref->setTempMod(relpos, mod))
5567 if(changed && affected_blocks!=NULL)
5569 for(u16 i=0; i<7; i++)
5571 v3s16 p2 = p + dirs[i];
5572 // Block position of neighbor (or requested) node
5573 v3s16 blockpos = getNodeBlockPos(p2);
5574 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5575 if(blockref == NULL)
5577 affected_blocks->insert(blockpos, blockref);
5583 bool ClientMap::clearTempMod(v3s16 p,
5584 core::map<v3s16, MapBlock*> *affected_blocks)
5586 bool changed = false;
5588 v3s16(0,0,0), // this
5589 v3s16(0,0,1), // back
5590 v3s16(0,1,0), // top
5591 v3s16(1,0,0), // right
5592 v3s16(0,0,-1), // front
5593 v3s16(0,-1,0), // bottom
5594 v3s16(-1,0,0), // left
5596 for(u16 i=0; i<7; i++)
5598 v3s16 p2 = p + dirs[i];
5599 // Block position of neighbor (or requested) node
5600 v3s16 blockpos = getNodeBlockPos(p2);
5601 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5602 if(blockref == NULL)
5604 // Relative position of requested node
5605 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5606 if(blockref->clearTempMod(relpos))
5611 if(changed && affected_blocks!=NULL)
5613 for(u16 i=0; i<7; i++)
5615 v3s16 p2 = p + dirs[i];
5616 // Block position of neighbor (or requested) node
5617 v3s16 blockpos = getNodeBlockPos(p2);
5618 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5619 if(blockref == NULL)
5621 affected_blocks->insert(blockpos, blockref);
5627 void ClientMap::expireMeshes(bool only_daynight_diffed)
5629 TimeTaker timer("expireMeshes()");
5631 core::map<v2s16, MapSector*>::Iterator si;
5632 si = m_sectors.getIterator();
5633 for(; si.atEnd() == false; si++)
5635 MapSector *sector = si.getNode()->getValue();
5637 core::list< MapBlock * > sectorblocks;
5638 sector->getBlocks(sectorblocks);
5640 core::list< MapBlock * >::Iterator i;
5641 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5643 MapBlock *block = *i;
5645 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5651 JMutexAutoLock lock(block->mesh_mutex);
5652 if(block->mesh != NULL)
5654 /*block->mesh->drop();
5655 block->mesh = NULL;*/
5656 block->setMeshExpired(true);
5663 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5665 assert(mapType() == MAPTYPE_CLIENT);
5668 v3s16 p = blockpos + v3s16(0,0,0);
5669 MapBlock *b = getBlockNoCreate(p);
5670 b->updateMesh(daynight_ratio);
5672 catch(InvalidPositionException &e){}
5675 v3s16 p = blockpos + v3s16(-1,0,0);
5676 MapBlock *b = getBlockNoCreate(p);
5677 b->updateMesh(daynight_ratio);
5679 catch(InvalidPositionException &e){}
5681 v3s16 p = blockpos + v3s16(0,-1,0);
5682 MapBlock *b = getBlockNoCreate(p);
5683 b->updateMesh(daynight_ratio);
5685 catch(InvalidPositionException &e){}
5687 v3s16 p = blockpos + v3s16(0,0,-1);
5688 MapBlock *b = getBlockNoCreate(p);
5689 b->updateMesh(daynight_ratio);
5691 catch(InvalidPositionException &e){}
5694 v3s16 p = blockpos + v3s16(1,0,0);
5695 MapBlock *b = getBlockNoCreate(p);
5696 b->updateMesh(daynight_ratio);
5698 catch(InvalidPositionException &e){}
5700 v3s16 p = blockpos + v3s16(0,1,0);
5701 MapBlock *b = getBlockNoCreate(p);
5702 b->updateMesh(daynight_ratio);
5704 catch(InvalidPositionException &e){}
5706 v3s16 p = blockpos + v3s16(0,0,1);
5707 MapBlock *b = getBlockNoCreate(p);
5708 b->updateMesh(daynight_ratio);
5710 catch(InvalidPositionException &e){}*/
5713 void ClientMap::PrintInfo(std::ostream &out)
5724 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5729 MapVoxelManipulator::~MapVoxelManipulator()
5731 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5735 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5737 TimeTaker timer1("emerge", &emerge_time);
5739 // Units of these are MapBlocks
5740 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5741 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5743 VoxelArea block_area_nodes
5744 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5746 addArea(block_area_nodes);
5748 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5749 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5750 for(s32 x=p_min.X; x<=p_max.X; x++)
5753 core::map<v3s16, bool>::Node *n;
5754 n = m_loaded_blocks.find(p);
5758 bool block_data_inexistent = false;
5761 TimeTaker timer1("emerge load", &emerge_load_time);
5763 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5764 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5767 dstream<<std::endl;*/
5769 MapBlock *block = m_map->getBlockNoCreate(p);
5770 if(block->isDummy())
5771 block_data_inexistent = true;
5773 block->copyTo(*this);
5775 catch(InvalidPositionException &e)
5777 block_data_inexistent = true;
5780 if(block_data_inexistent)
5782 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5783 // Fill with VOXELFLAG_INEXISTENT
5784 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5785 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5787 s32 i = m_area.index(a.MinEdge.X,y,z);
5788 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5792 m_loaded_blocks.insert(p, !block_data_inexistent);
5795 //dstream<<"emerge done"<<std::endl;
5799 SUGG: Add an option to only update eg. water and air nodes.
5800 This will make it interfere less with important stuff if
5803 void MapVoxelManipulator::blitBack
5804 (core::map<v3s16, MapBlock*> & modified_blocks)
5806 if(m_area.getExtent() == v3s16(0,0,0))
5809 //TimeTaker timer1("blitBack");
5811 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5812 <<m_loaded_blocks.size()<<std::endl;*/
5815 Initialize block cache
5817 v3s16 blockpos_last;
5818 MapBlock *block = NULL;
5819 bool block_checked_in_modified = false;
5821 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5822 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5823 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5827 u8 f = m_flags[m_area.index(p)];
5828 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5831 MapNode &n = m_data[m_area.index(p)];
5833 v3s16 blockpos = getNodeBlockPos(p);
5838 if(block == NULL || blockpos != blockpos_last){
5839 block = m_map->getBlockNoCreate(blockpos);
5840 blockpos_last = blockpos;
5841 block_checked_in_modified = false;
5844 // Calculate relative position in block
5845 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5847 // Don't continue if nothing has changed here
5848 if(block->getNode(relpos) == n)
5851 //m_map->setNode(m_area.MinEdge + p, n);
5852 block->setNode(relpos, n);
5855 Make sure block is in modified_blocks
5857 if(block_checked_in_modified == false)
5859 modified_blocks[blockpos] = block;
5860 block_checked_in_modified = true;
5863 catch(InvalidPositionException &e)
5869 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5870 MapVoxelManipulator(map)
5874 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5878 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5880 // Just create the area so that it can be pointed to
5881 VoxelManipulator::emerge(a, caller_id);
5884 void ManualMapVoxelManipulator::initialEmerge(
5885 v3s16 blockpos_min, v3s16 blockpos_max)
5887 TimeTaker timer1("initialEmerge", &emerge_time);
5889 // Units of these are MapBlocks
5890 v3s16 p_min = blockpos_min;
5891 v3s16 p_max = blockpos_max;
5893 VoxelArea block_area_nodes
5894 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5896 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5899 dstream<<"initialEmerge: area: ";
5900 block_area_nodes.print(dstream);
5901 dstream<<" ("<<size_MB<<"MB)";
5905 addArea(block_area_nodes);
5907 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5908 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5909 for(s32 x=p_min.X; x<=p_max.X; x++)
5912 core::map<v3s16, bool>::Node *n;
5913 n = m_loaded_blocks.find(p);
5917 bool block_data_inexistent = false;
5920 TimeTaker timer1("emerge load", &emerge_load_time);
5922 MapBlock *block = m_map->getBlockNoCreate(p);
5923 if(block->isDummy())
5924 block_data_inexistent = true;
5926 block->copyTo(*this);
5928 catch(InvalidPositionException &e)
5930 block_data_inexistent = true;
5933 if(block_data_inexistent)
5936 Mark area inexistent
5938 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5939 // Fill with VOXELFLAG_INEXISTENT
5940 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5941 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5943 s32 i = m_area.index(a.MinEdge.X,y,z);
5944 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5948 m_loaded_blocks.insert(p, !block_data_inexistent);
5952 void ManualMapVoxelManipulator::blitBackAll(
5953 core::map<v3s16, MapBlock*> * modified_blocks)
5955 if(m_area.getExtent() == v3s16(0,0,0))
5959 Copy data of all blocks
5961 for(core::map<v3s16, bool>::Iterator
5962 i = m_loaded_blocks.getIterator();
5963 i.atEnd() == false; i++)
5965 bool existed = i.getNode()->getValue();
5966 if(existed == false)
5968 v3s16 p = i.getNode()->getKey();
5969 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5972 dstream<<"WARNING: "<<__FUNCTION_NAME
5973 <<": got NULL block "
5974 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5979 block->copyFrom(*this);
5982 modified_blocks->insert(p, block);