3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
35 Map::Map(std::ostream &dout):
39 m_sector_mutex.Init();
40 assert(m_sector_mutex.IsInitialized());
48 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
49 for(; i.atEnd() == false; i++)
51 MapSector *sector = i.getNode()->getValue();
56 void Map::addEventReceiver(MapEventReceiver *event_receiver)
58 m_event_receivers.insert(event_receiver, false);
61 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
63 if(m_event_receivers.find(event_receiver) == NULL)
65 m_event_receivers.remove(event_receiver);
68 void Map::dispatchEvent(MapEditEvent *event)
70 for(core::map<MapEventReceiver*, bool>::Iterator
71 i = m_event_receivers.getIterator();
72 i.atEnd()==false; i++)
74 MapEventReceiver* event_receiver = i.getNode()->getKey();
75 event_receiver->onMapEditEvent(event);
79 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
81 if(m_sector_cache != NULL && p == m_sector_cache_p){
82 MapSector * sector = m_sector_cache;
83 // Reset inactivity timer
84 sector->usage_timer = 0.0;
88 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
93 MapSector *sector = n->getValue();
95 // Cache the last result
97 m_sector_cache = sector;
99 // Reset inactivity timer
100 sector->usage_timer = 0.0;
104 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
106 JMutexAutoLock lock(m_sector_mutex);
108 return getSectorNoGenerateNoExNoLock(p);
111 MapSector * Map::getSectorNoGenerate(v2s16 p)
113 MapSector *sector = getSectorNoGenerateNoEx(p);
115 throw InvalidPositionException();
120 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
122 v2s16 p2d(p3d.X, p3d.Z);
123 MapSector * sector = getSectorNoGenerate(p2d);
125 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
134 v2s16 p2d(p3d.X, p3d.Z);
135 MapSector * sector = getSectorNoGenerate(p2d);
136 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
139 catch(InvalidPositionException &e)
145 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorCreate(p2d);
150 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
153 block = sector->createBlankBlock(p3d.Y);
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
171 Goes recursively through the neighbours of the node.
173 Alters only transparent nodes.
175 If the lighting of the neighbour is lower than the lighting of
176 the node was (before changing it to 0 at the step before), the
177 lighting of the neighbour is set to 0 and then the same stuff
178 repeats for the neighbour.
180 The ending nodes of the routine are stored in light_sources.
181 This is useful when a light is removed. In such case, this
182 routine can be called for the light node and then again for
183 light_sources to re-light the area without the removed light.
185 values of from_nodes are lighting values.
187 void Map::unspreadLight(enum LightBank bank,
188 core::map<v3s16, u8> & from_nodes,
189 core::map<v3s16, bool> & light_sources,
190 core::map<v3s16, MapBlock*> & modified_blocks)
193 v3s16(0,0,1), // back
195 v3s16(1,0,0), // right
196 v3s16(0,0,-1), // front
197 v3s16(0,-1,0), // bottom
198 v3s16(-1,0,0), // left
201 if(from_nodes.size() == 0)
204 u32 blockchangecount = 0;
206 core::map<v3s16, u8> unlighted_nodes;
207 core::map<v3s16, u8>::Iterator j;
208 j = from_nodes.getIterator();
211 Initialize block cache
214 MapBlock *block = NULL;
215 // Cache this a bit, too
216 bool block_checked_in_modified = false;
218 for(; j.atEnd() == false; j++)
220 v3s16 pos = j.getNode()->getKey();
221 v3s16 blockpos = getNodeBlockPos(pos);
223 // Only fetch a new block if the block position has changed
225 if(block == NULL || blockpos != blockpos_last){
226 block = getBlockNoCreate(blockpos);
227 blockpos_last = blockpos;
229 block_checked_in_modified = false;
233 catch(InvalidPositionException &e)
241 // Calculate relative position in block
242 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
244 // Get node straight from the block
245 MapNode n = block->getNode(relpos);
247 u8 oldlight = j.getNode()->getValue();
249 // Loop through 6 neighbors
250 for(u16 i=0; i<6; i++)
252 // Get the position of the neighbor node
253 v3s16 n2pos = pos + dirs[i];
255 // Get the block where the node is located
256 v3s16 blockpos = getNodeBlockPos(n2pos);
260 // Only fetch a new block if the block position has changed
262 if(block == NULL || blockpos != blockpos_last){
263 block = getBlockNoCreate(blockpos);
264 blockpos_last = blockpos;
266 block_checked_in_modified = false;
270 catch(InvalidPositionException &e)
275 // Calculate relative position in block
276 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
277 // Get node straight from the block
278 MapNode n2 = block->getNode(relpos);
280 bool changed = false;
282 //TODO: Optimize output by optimizing light_sources?
285 If the neighbor is dimmer than what was specified
286 as oldlight (the light of the previous node)
288 if(n2.getLight(bank) < oldlight)
291 And the neighbor is transparent and it has some light
293 if(n2.light_propagates() && n2.getLight(bank) != 0)
296 Set light to 0 and add to queue
299 u8 current_light = n2.getLight(bank);
300 n2.setLight(bank, 0);
301 block->setNode(relpos, n2);
303 unlighted_nodes.insert(n2pos, current_light);
307 Remove from light_sources if it is there
308 NOTE: This doesn't happen nearly at all
310 /*if(light_sources.find(n2pos))
312 std::cout<<"Removed from light_sources"<<std::endl;
313 light_sources.remove(n2pos);
318 if(light_sources.find(n2pos) != NULL)
319 light_sources.remove(n2pos);*/
322 light_sources.insert(n2pos, true);
325 // Add to modified_blocks
326 if(changed == true && block_checked_in_modified == false)
328 // If the block is not found in modified_blocks, add.
329 if(modified_blocks.find(blockpos) == NULL)
331 modified_blocks.insert(blockpos, block);
333 block_checked_in_modified = true;
336 catch(InvalidPositionException &e)
343 /*dstream<<"unspreadLight(): Changed block "
344 <<blockchangecount<<" times"
345 <<" for "<<from_nodes.size()<<" nodes"
348 if(unlighted_nodes.size() > 0)
349 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
353 A single-node wrapper of the above
355 void Map::unLightNeighbors(enum LightBank bank,
356 v3s16 pos, u8 lightwas,
357 core::map<v3s16, bool> & light_sources,
358 core::map<v3s16, MapBlock*> & modified_blocks)
360 core::map<v3s16, u8> from_nodes;
361 from_nodes.insert(pos, lightwas);
363 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
367 Lights neighbors of from_nodes, collects all them and then
370 void Map::spreadLight(enum LightBank bank,
371 core::map<v3s16, bool> & from_nodes,
372 core::map<v3s16, MapBlock*> & modified_blocks)
374 const v3s16 dirs[6] = {
375 v3s16(0,0,1), // back
377 v3s16(1,0,0), // right
378 v3s16(0,0,-1), // front
379 v3s16(0,-1,0), // bottom
380 v3s16(-1,0,0), // left
383 if(from_nodes.size() == 0)
386 u32 blockchangecount = 0;
388 core::map<v3s16, bool> lighted_nodes;
389 core::map<v3s16, bool>::Iterator j;
390 j = from_nodes.getIterator();
393 Initialize block cache
396 MapBlock *block = NULL;
397 // Cache this a bit, too
398 bool block_checked_in_modified = false;
400 for(; j.atEnd() == false; j++)
401 //for(; j != from_nodes.end(); j++)
403 v3s16 pos = j.getNode()->getKey();
405 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
406 v3s16 blockpos = getNodeBlockPos(pos);
408 // Only fetch a new block if the block position has changed
410 if(block == NULL || blockpos != blockpos_last){
411 block = getBlockNoCreate(blockpos);
412 blockpos_last = blockpos;
414 block_checked_in_modified = false;
418 catch(InvalidPositionException &e)
426 // Calculate relative position in block
427 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
429 // Get node straight from the block
430 MapNode n = block->getNode(relpos);
432 u8 oldlight = n.getLight(bank);
433 u8 newlight = diminish_light(oldlight);
435 // Loop through 6 neighbors
436 for(u16 i=0; i<6; i++){
437 // Get the position of the neighbor node
438 v3s16 n2pos = pos + dirs[i];
440 // Get the block where the node is located
441 v3s16 blockpos = getNodeBlockPos(n2pos);
445 // Only fetch a new block if the block position has changed
447 if(block == NULL || blockpos != blockpos_last){
448 block = getBlockNoCreate(blockpos);
449 blockpos_last = blockpos;
451 block_checked_in_modified = false;
455 catch(InvalidPositionException &e)
460 // Calculate relative position in block
461 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
462 // Get node straight from the block
463 MapNode n2 = block->getNode(relpos);
465 bool changed = false;
467 If the neighbor is brighter than the current node,
468 add to list (it will light up this node on its turn)
470 if(n2.getLight(bank) > undiminish_light(oldlight))
472 lighted_nodes.insert(n2pos, true);
473 //lighted_nodes.push_back(n2pos);
477 If the neighbor is dimmer than how much light this node
478 would spread on it, add to list
480 if(n2.getLight(bank) < newlight)
482 if(n2.light_propagates())
484 n2.setLight(bank, newlight);
485 block->setNode(relpos, n2);
486 lighted_nodes.insert(n2pos, true);
487 //lighted_nodes.push_back(n2pos);
492 // Add to modified_blocks
493 if(changed == true && block_checked_in_modified == false)
495 // If the block is not found in modified_blocks, add.
496 if(modified_blocks.find(blockpos) == NULL)
498 modified_blocks.insert(blockpos, block);
500 block_checked_in_modified = true;
503 catch(InvalidPositionException &e)
510 /*dstream<<"spreadLight(): Changed block "
511 <<blockchangecount<<" times"
512 <<" for "<<from_nodes.size()<<" nodes"
515 if(lighted_nodes.size() > 0)
516 spreadLight(bank, lighted_nodes, modified_blocks);
520 A single-node source variation of the above.
522 void Map::lightNeighbors(enum LightBank bank,
524 core::map<v3s16, MapBlock*> & modified_blocks)
526 core::map<v3s16, bool> from_nodes;
527 from_nodes.insert(pos, true);
528 spreadLight(bank, from_nodes, modified_blocks);
531 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
534 v3s16(0,0,1), // back
536 v3s16(1,0,0), // right
537 v3s16(0,0,-1), // front
538 v3s16(0,-1,0), // bottom
539 v3s16(-1,0,0), // left
542 u8 brightest_light = 0;
543 v3s16 brightest_pos(0,0,0);
544 bool found_something = false;
546 // Loop through 6 neighbors
547 for(u16 i=0; i<6; i++){
548 // Get the position of the neighbor node
549 v3s16 n2pos = p + dirs[i];
554 catch(InvalidPositionException &e)
558 if(n2.getLight(bank) > brightest_light || found_something == false){
559 brightest_light = n2.getLight(bank);
560 brightest_pos = n2pos;
561 found_something = true;
565 if(found_something == false)
566 throw InvalidPositionException();
568 return brightest_pos;
572 Propagates sunlight down from a node.
573 Starting point gets sunlight.
575 Returns the lowest y value of where the sunlight went.
577 Mud is turned into grass in where the sunlight stops.
579 s16 Map::propagateSunlight(v3s16 start,
580 core::map<v3s16, MapBlock*> & modified_blocks)
585 v3s16 pos(start.X, y, start.Z);
587 v3s16 blockpos = getNodeBlockPos(pos);
590 block = getBlockNoCreate(blockpos);
592 catch(InvalidPositionException &e)
597 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
598 MapNode n = block->getNode(relpos);
600 if(n.sunlight_propagates())
602 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
603 block->setNode(relpos, n);
605 modified_blocks.insert(blockpos, block);
609 // Turn mud into grass
610 if(n.d == CONTENT_MUD)
613 block->setNode(relpos, n);
614 modified_blocks.insert(blockpos, block);
617 // Sunlight goes no further
624 void Map::updateLighting(enum LightBank bank,
625 core::map<v3s16, MapBlock*> & a_blocks,
626 core::map<v3s16, MapBlock*> & modified_blocks)
628 /*m_dout<<DTIME<<"Map::updateLighting(): "
629 <<a_blocks.size()<<" blocks."<<std::endl;*/
631 //TimeTaker timer("updateLighting");
635 //u32 count_was = modified_blocks.size();
637 core::map<v3s16, MapBlock*> blocks_to_update;
639 core::map<v3s16, bool> light_sources;
641 core::map<v3s16, u8> unlight_from;
643 core::map<v3s16, MapBlock*>::Iterator i;
644 i = a_blocks.getIterator();
645 for(; i.atEnd() == false; i++)
647 MapBlock *block = i.getNode()->getValue();
651 // Don't bother with dummy blocks.
655 v3s16 pos = block->getPos();
656 modified_blocks.insert(pos, block);
658 blocks_to_update.insert(pos, block);
661 Clear all light from block
663 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
664 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
665 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
670 MapNode n = block->getNode(v3s16(x,y,z));
671 u8 oldlight = n.getLight(bank);
673 block->setNode(v3s16(x,y,z), n);
675 // Collect borders for unlighting
676 if(x==0 || x == MAP_BLOCKSIZE-1
677 || y==0 || y == MAP_BLOCKSIZE-1
678 || z==0 || z == MAP_BLOCKSIZE-1)
680 v3s16 p_map = p + v3s16(
683 MAP_BLOCKSIZE*pos.Z);
684 unlight_from.insert(p_map, oldlight);
687 catch(InvalidPositionException &e)
690 This would happen when dealing with a
694 dstream<<"updateLighting(): InvalidPositionException"
699 if(bank == LIGHTBANK_DAY)
701 bool bottom_valid = block->propagateSunlight(light_sources);
703 // If bottom is valid, we're done.
707 else if(bank == LIGHTBANK_NIGHT)
709 // For night lighting, sunlight is not propagated
714 // Invalid lighting bank
718 /*dstream<<"Bottom for sunlight-propagated block ("
719 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
722 // Bottom sunlight is not valid; get the block and loop to it
726 block = getBlockNoCreate(pos);
728 catch(InvalidPositionException &e)
738 TimeTaker timer("unspreadLight");
739 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
744 u32 diff = modified_blocks.size() - count_was;
745 count_was = modified_blocks.size();
746 dstream<<"unspreadLight modified "<<diff<<std::endl;
750 TimeTaker timer("spreadLight");
751 spreadLight(bank, light_sources, modified_blocks);
756 u32 diff = modified_blocks.size() - count_was;
757 count_was = modified_blocks.size();
758 dstream<<"spreadLight modified "<<diff<<std::endl;
763 //MapVoxelManipulator vmanip(this);
765 // Make a manual voxel manipulator and load all the blocks
766 // that touch the requested blocks
767 ManualMapVoxelManipulator vmanip(this);
768 core::map<v3s16, MapBlock*>::Iterator i;
769 i = blocks_to_update.getIterator();
770 for(; i.atEnd() == false; i++)
772 MapBlock *block = i.getNode()->getValue();
773 v3s16 p = block->getPos();
775 // Add all surrounding blocks
776 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
779 Add all surrounding blocks that have up-to-date lighting
780 NOTE: This doesn't quite do the job (not everything
781 appropriate is lighted)
783 /*for(s16 z=-1; z<=1; z++)
784 for(s16 y=-1; y<=1; y++)
785 for(s16 x=-1; x<=1; x++)
788 MapBlock *block = getBlockNoCreateNoEx(p);
793 if(block->getLightingExpired())
795 vmanip.initialEmerge(p, p);
798 // Lighting of block will be updated completely
799 block->setLightingExpired(false);
803 //TimeTaker timer("unSpreadLight");
804 vmanip.unspreadLight(bank, unlight_from, light_sources);
807 //TimeTaker timer("spreadLight");
808 vmanip.spreadLight(bank, light_sources);
811 //TimeTaker timer("blitBack");
812 vmanip.blitBack(modified_blocks);
814 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
818 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
821 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
822 core::map<v3s16, MapBlock*> & modified_blocks)
824 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
825 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
828 Update information about whether day and night light differ
830 for(core::map<v3s16, MapBlock*>::Iterator
831 i = modified_blocks.getIterator();
832 i.atEnd() == false; i++)
834 MapBlock *block = i.getNode()->getValue();
835 block->updateDayNightDiff();
840 This is called after changing a node from transparent to opaque.
841 The lighting value of the node should be left as-is after changing
842 other values. This sets the lighting value to 0.
844 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
845 core::map<v3s16, MapBlock*> &modified_blocks)
848 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
849 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
852 From this node to nodes underneath:
853 If lighting is sunlight (1.0), unlight neighbours and
858 v3s16 toppos = p + v3s16(0,1,0);
859 v3s16 bottompos = p + v3s16(0,-1,0);
861 bool node_under_sunlight = true;
862 core::map<v3s16, bool> light_sources;
865 If there is a node at top and it doesn't have sunlight,
866 there has not been any sunlight going down.
868 Otherwise there probably is.
871 MapNode topnode = getNode(toppos);
873 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
874 node_under_sunlight = false;
876 catch(InvalidPositionException &e)
881 If the new node doesn't propagate sunlight and there is
882 grass below, change it to mud
884 if(content_features(n.d).sunlight_propagates == false)
887 MapNode bottomnode = getNode(bottompos);
889 if(bottomnode.d == CONTENT_GRASS
890 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
892 bottomnode.d = CONTENT_MUD;
893 setNode(bottompos, bottomnode);
896 catch(InvalidPositionException &e)
902 If the new node is mud and it is under sunlight, change it
905 if(n.d == CONTENT_MUD && node_under_sunlight)
911 Remove all light that has come out of this node
914 enum LightBank banks[] =
919 for(s32 i=0; i<2; i++)
921 enum LightBank bank = banks[i];
923 u8 lightwas = getNode(p).getLight(bank);
925 // Add the block of the added node to modified_blocks
926 v3s16 blockpos = getNodeBlockPos(p);
927 MapBlock * block = getBlockNoCreate(blockpos);
928 assert(block != NULL);
929 modified_blocks.insert(blockpos, block);
931 assert(isValidPosition(p));
933 // Unlight neighbours of node.
934 // This means setting light of all consequent dimmer nodes
936 // This also collects the nodes at the border which will spread
937 // light again into this.
938 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
944 If node lets sunlight through and is under sunlight, it has
947 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
949 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
953 Set the node on the map
962 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
965 NodeMetadata *meta = meta_proto->clone();
966 setNodeMetadata(p, meta);
970 If node is under sunlight and doesn't let sunlight through,
971 take all sunlighted nodes under it and clear light from them
972 and from where the light has been spread.
973 TODO: This could be optimized by mass-unlighting instead
976 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
980 //m_dout<<DTIME<<"y="<<y<<std::endl;
981 v3s16 n2pos(p.X, y, p.Z);
987 catch(InvalidPositionException &e)
992 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
994 unLightNeighbors(LIGHTBANK_DAY,
995 n2pos, n2.getLight(LIGHTBANK_DAY),
996 light_sources, modified_blocks);
997 n2.setLight(LIGHTBANK_DAY, 0);
1005 for(s32 i=0; i<2; i++)
1007 enum LightBank bank = banks[i];
1010 Spread light from all nodes that might be capable of doing so
1012 spreadLight(bank, light_sources, modified_blocks);
1016 Update information about whether day and night light differ
1018 for(core::map<v3s16, MapBlock*>::Iterator
1019 i = modified_blocks.getIterator();
1020 i.atEnd() == false; i++)
1022 MapBlock *block = i.getNode()->getValue();
1023 block->updateDayNightDiff();
1027 Add neighboring liquid nodes and the node itself if it is
1028 liquid (=water node was added) to transform queue.
1031 v3s16(0,0,0), // self
1032 v3s16(0,0,1), // back
1033 v3s16(0,1,0), // top
1034 v3s16(1,0,0), // right
1035 v3s16(0,0,-1), // front
1036 v3s16(0,-1,0), // bottom
1037 v3s16(-1,0,0), // left
1039 for(u16 i=0; i<7; i++)
1044 v3s16 p2 = p + dirs[i];
1046 MapNode n2 = getNode(p2);
1047 if(content_liquid(n2.d))
1049 m_transforming_liquid.push_back(p2);
1052 }catch(InvalidPositionException &e)
1060 void Map::removeNodeAndUpdate(v3s16 p,
1061 core::map<v3s16, MapBlock*> &modified_blocks)
1063 /*PrintInfo(m_dout);
1064 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1065 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1067 bool node_under_sunlight = true;
1069 v3s16 toppos = p + v3s16(0,1,0);
1071 // Node will be replaced with this
1072 u8 replace_material = CONTENT_AIR;
1075 If there is a node at top and it doesn't have sunlight,
1076 there will be no sunlight going down.
1079 MapNode topnode = getNode(toppos);
1081 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1082 node_under_sunlight = false;
1084 catch(InvalidPositionException &e)
1088 core::map<v3s16, bool> light_sources;
1090 enum LightBank banks[] =
1095 for(s32 i=0; i<2; i++)
1097 enum LightBank bank = banks[i];
1100 Unlight neighbors (in case the node is a light source)
1102 unLightNeighbors(bank, p,
1103 getNode(p).getLight(bank),
1104 light_sources, modified_blocks);
1108 Remove node metadata
1111 removeNodeMetadata(p);
1115 This also clears the lighting.
1119 n.d = replace_material;
1122 for(s32 i=0; i<2; i++)
1124 enum LightBank bank = banks[i];
1127 Recalculate lighting
1129 spreadLight(bank, light_sources, modified_blocks);
1132 // Add the block of the removed node to modified_blocks
1133 v3s16 blockpos = getNodeBlockPos(p);
1134 MapBlock * block = getBlockNoCreate(blockpos);
1135 assert(block != NULL);
1136 modified_blocks.insert(blockpos, block);
1139 If the removed node was under sunlight, propagate the
1140 sunlight down from it and then light all neighbors
1141 of the propagated blocks.
1143 if(node_under_sunlight)
1145 s16 ybottom = propagateSunlight(p, modified_blocks);
1146 /*m_dout<<DTIME<<"Node was under sunlight. "
1147 "Propagating sunlight";
1148 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1150 for(; y >= ybottom; y--)
1152 v3s16 p2(p.X, y, p.Z);
1153 /*m_dout<<DTIME<<"lighting neighbors of node ("
1154 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1156 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1161 // Set the lighting of this node to 0
1162 // TODO: Is this needed? Lighting is cleared up there already.
1164 MapNode n = getNode(p);
1165 n.setLight(LIGHTBANK_DAY, 0);
1168 catch(InvalidPositionException &e)
1174 for(s32 i=0; i<2; i++)
1176 enum LightBank bank = banks[i];
1178 // Get the brightest neighbour node and propagate light from it
1179 v3s16 n2p = getBrightestNeighbour(bank, p);
1181 MapNode n2 = getNode(n2p);
1182 lightNeighbors(bank, n2p, modified_blocks);
1184 catch(InvalidPositionException &e)
1190 Update information about whether day and night light differ
1192 for(core::map<v3s16, MapBlock*>::Iterator
1193 i = modified_blocks.getIterator();
1194 i.atEnd() == false; i++)
1196 MapBlock *block = i.getNode()->getValue();
1197 block->updateDayNightDiff();
1201 Add neighboring liquid nodes to transform queue.
1204 v3s16(0,0,1), // back
1205 v3s16(0,1,0), // top
1206 v3s16(1,0,0), // right
1207 v3s16(0,0,-1), // front
1208 v3s16(0,-1,0), // bottom
1209 v3s16(-1,0,0), // left
1211 for(u16 i=0; i<6; i++)
1216 v3s16 p2 = p + dirs[i];
1218 MapNode n2 = getNode(p2);
1219 if(content_liquid(n2.d))
1221 m_transforming_liquid.push_back(p2);
1224 }catch(InvalidPositionException &e)
1230 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1233 event.type = MEET_ADDNODE;
1237 bool succeeded = true;
1239 core::map<v3s16, MapBlock*> modified_blocks;
1240 addNodeAndUpdate(p, n, modified_blocks);
1242 // Copy modified_blocks to event
1243 for(core::map<v3s16, MapBlock*>::Iterator
1244 i = modified_blocks.getIterator();
1245 i.atEnd()==false; i++)
1247 event.modified_blocks.insert(i.getNode()->getKey(), false);
1250 catch(InvalidPositionException &e){
1254 dispatchEvent(&event);
1259 bool Map::removeNodeWithEvent(v3s16 p)
1262 event.type = MEET_REMOVENODE;
1265 bool succeeded = true;
1267 core::map<v3s16, MapBlock*> modified_blocks;
1268 removeNodeAndUpdate(p, modified_blocks);
1270 // Copy modified_blocks to event
1271 for(core::map<v3s16, MapBlock*>::Iterator
1272 i = modified_blocks.getIterator();
1273 i.atEnd()==false; i++)
1275 event.modified_blocks.insert(i.getNode()->getKey(), false);
1278 catch(InvalidPositionException &e){
1282 dispatchEvent(&event);
1287 bool Map::dayNightDiffed(v3s16 blockpos)
1290 v3s16 p = blockpos + v3s16(0,0,0);
1291 MapBlock *b = getBlockNoCreate(p);
1292 if(b->dayNightDiffed())
1295 catch(InvalidPositionException &e){}
1298 v3s16 p = blockpos + v3s16(-1,0,0);
1299 MapBlock *b = getBlockNoCreate(p);
1300 if(b->dayNightDiffed())
1303 catch(InvalidPositionException &e){}
1305 v3s16 p = blockpos + v3s16(0,-1,0);
1306 MapBlock *b = getBlockNoCreate(p);
1307 if(b->dayNightDiffed())
1310 catch(InvalidPositionException &e){}
1312 v3s16 p = blockpos + v3s16(0,0,-1);
1313 MapBlock *b = getBlockNoCreate(p);
1314 if(b->dayNightDiffed())
1317 catch(InvalidPositionException &e){}
1320 v3s16 p = blockpos + v3s16(1,0,0);
1321 MapBlock *b = getBlockNoCreate(p);
1322 if(b->dayNightDiffed())
1325 catch(InvalidPositionException &e){}
1327 v3s16 p = blockpos + v3s16(0,1,0);
1328 MapBlock *b = getBlockNoCreate(p);
1329 if(b->dayNightDiffed())
1332 catch(InvalidPositionException &e){}
1334 v3s16 p = blockpos + v3s16(0,0,1);
1335 MapBlock *b = getBlockNoCreate(p);
1336 if(b->dayNightDiffed())
1339 catch(InvalidPositionException &e){}
1345 Updates usage timers
1347 void Map::timerUpdate(float dtime)
1349 JMutexAutoLock lock(m_sector_mutex);
1351 core::map<v2s16, MapSector*>::Iterator si;
1353 si = m_sectors.getIterator();
1354 for(; si.atEnd() == false; si++)
1356 MapSector *sector = si.getNode()->getValue();
1357 sector->usage_timer += dtime;
1361 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1364 Wait for caches to be removed before continuing.
1366 This disables the existence of caches while locked
1368 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1370 core::list<v2s16>::Iterator j;
1371 for(j=list.begin(); j!=list.end(); j++)
1373 MapSector *sector = m_sectors[*j];
1376 sector->deleteBlocks();
1381 If sector is in sector cache, remove it from there
1383 if(m_sector_cache == sector)
1385 m_sector_cache = NULL;
1388 Remove from map and delete
1390 m_sectors.remove(*j);
1396 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1397 core::list<v3s16> *deleted_blocks)
1399 JMutexAutoLock lock(m_sector_mutex);
1401 core::list<v2s16> sector_deletion_queue;
1402 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1403 for(; i.atEnd() == false; i++)
1405 MapSector *sector = i.getNode()->getValue();
1407 Delete sector from memory if it hasn't been used in a long time
1409 if(sector->usage_timer > timeout)
1411 sector_deletion_queue.push_back(i.getNode()->getKey());
1413 if(deleted_blocks != NULL)
1415 // Collect positions of blocks of sector
1416 MapSector *sector = i.getNode()->getValue();
1417 core::list<MapBlock*> blocks;
1418 sector->getBlocks(blocks);
1419 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1420 i != blocks.end(); i++)
1422 deleted_blocks->push_back((*i)->getPos());
1427 deleteSectors(sector_deletion_queue, only_blocks);
1428 return sector_deletion_queue.getSize();
1431 void Map::PrintInfo(std::ostream &out)
1436 #define WATER_DROP_BOOST 4
1438 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1440 DSTACK(__FUNCTION_NAME);
1441 //TimeTaker timer("transformLiquids()");
1444 u32 initial_size = m_transforming_liquid.size();
1446 /*if(initial_size != 0)
1447 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1449 while(m_transforming_liquid.size() != 0)
1452 Get a queued transforming liquid node
1454 v3s16 p0 = m_transforming_liquid.pop_front();
1456 MapNode n0 = getNode(p0);
1458 // Don't deal with non-liquids
1459 if(content_liquid(n0.d) == false)
1462 bool is_source = !content_flowing_liquid(n0.d);
1464 u8 liquid_level = 8;
1465 if(is_source == false)
1466 liquid_level = n0.param2 & 0x0f;
1468 // Turn possible source into non-source
1469 u8 nonsource_c = make_liquid_flowing(n0.d);
1472 If not source, check that some node flows into this one
1473 and what is the level of liquid in this one
1475 if(is_source == false)
1477 s8 new_liquid_level_max = -1;
1479 v3s16 dirs_from[5] = {
1480 v3s16(0,1,0), // top
1481 v3s16(0,0,1), // back
1482 v3s16(1,0,0), // right
1483 v3s16(0,0,-1), // front
1484 v3s16(-1,0,0), // left
1486 for(u16 i=0; i<5; i++)
1491 bool from_top = (i==0);
1493 v3s16 p2 = p0 + dirs_from[i];
1494 MapNode n2 = getNode(p2);
1496 if(content_liquid(n2.d))
1498 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1499 // Check that the liquids are the same type
1500 if(n2_nonsource_c != nonsource_c)
1502 dstream<<"WARNING: Not handling: different liquids"
1503 " collide"<<std::endl;
1506 bool n2_is_source = !content_flowing_liquid(n2.d);
1507 s8 n2_liquid_level = 8;
1508 if(n2_is_source == false)
1509 n2_liquid_level = n2.param2 & 0x07;
1511 s8 new_liquid_level = -1;
1514 //new_liquid_level = 7;
1515 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1516 new_liquid_level = 7;
1518 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1520 else if(n2_liquid_level > 0)
1522 new_liquid_level = n2_liquid_level - 1;
1525 if(new_liquid_level > new_liquid_level_max)
1526 new_liquid_level_max = new_liquid_level;
1529 }catch(InvalidPositionException &e)
1535 If liquid level should be something else, update it and
1536 add all the neighboring water nodes to the transform queue.
1538 if(new_liquid_level_max != liquid_level)
1540 if(new_liquid_level_max == -1)
1542 // Remove water alltoghether
1549 n0.param2 = new_liquid_level_max;
1553 // Block has been modified
1555 v3s16 blockpos = getNodeBlockPos(p0);
1556 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1558 modified_blocks.insert(blockpos, block);
1562 Add neighboring non-source liquid nodes to transform queue.
1565 v3s16(0,0,1), // back
1566 v3s16(0,1,0), // top
1567 v3s16(1,0,0), // right
1568 v3s16(0,0,-1), // front
1569 v3s16(0,-1,0), // bottom
1570 v3s16(-1,0,0), // left
1572 for(u16 i=0; i<6; i++)
1577 v3s16 p2 = p0 + dirs[i];
1579 MapNode n2 = getNode(p2);
1580 if(content_flowing_liquid(n2.d))
1582 m_transforming_liquid.push_back(p2);
1585 }catch(InvalidPositionException &e)
1592 // Get a new one from queue if the node has turned into non-water
1593 if(content_liquid(n0.d) == false)
1597 Flow water from this node
1599 v3s16 dirs_to[5] = {
1600 v3s16(0,-1,0), // bottom
1601 v3s16(0,0,1), // back
1602 v3s16(1,0,0), // right
1603 v3s16(0,0,-1), // front
1604 v3s16(-1,0,0), // left
1606 for(u16 i=0; i<5; i++)
1611 bool to_bottom = (i == 0);
1613 // If liquid is at lowest possible height, it's not going
1614 // anywhere except down
1615 if(liquid_level == 0 && to_bottom == false)
1618 u8 liquid_next_level = 0;
1619 // If going to bottom
1622 //liquid_next_level = 7;
1623 if(liquid_level >= 7 - WATER_DROP_BOOST)
1624 liquid_next_level = 7;
1626 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1629 liquid_next_level = liquid_level - 1;
1631 bool n2_changed = false;
1632 bool flowed = false;
1634 v3s16 p2 = p0 + dirs_to[i];
1636 MapNode n2 = getNode(p2);
1637 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1639 if(content_liquid(n2.d))
1641 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1642 // Check that the liquids are the same type
1643 if(n2_nonsource_c != nonsource_c)
1645 dstream<<"WARNING: Not handling: different liquids"
1646 " collide"<<std::endl;
1649 bool n2_is_source = !content_flowing_liquid(n2.d);
1650 u8 n2_liquid_level = 8;
1651 if(n2_is_source == false)
1652 n2_liquid_level = n2.param2 & 0x07;
1661 // Just flow into the source, nothing changes.
1662 // n2_changed is not set because destination didn't change
1667 if(liquid_next_level > liquid_level)
1669 n2.param2 = liquid_next_level;
1677 else if(n2.d == CONTENT_AIR)
1680 n2.param2 = liquid_next_level;
1687 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1691 m_transforming_liquid.push_back(p2);
1693 v3s16 blockpos = getNodeBlockPos(p2);
1694 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1696 modified_blocks.insert(blockpos, block);
1699 // If n2_changed to bottom, don't flow anywhere else
1700 if(to_bottom && flowed && !is_source)
1703 }catch(InvalidPositionException &e)
1709 //if(loopcount >= 100000)
1710 if(loopcount >= initial_size * 1)
1713 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1716 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1718 v3s16 blockpos = getNodeBlockPos(p);
1719 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1720 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1723 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1727 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1731 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1733 v3s16 blockpos = getNodeBlockPos(p);
1734 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1735 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1738 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1742 block->m_node_metadata.set(p_rel, meta);
1745 void Map::removeNodeMetadata(v3s16 p)
1747 v3s16 blockpos = getNodeBlockPos(p);
1748 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1749 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1752 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1756 block->m_node_metadata.remove(p_rel);
1763 ServerMap::ServerMap(std::string savedir):
1769 //m_chunksize = 16; // Too slow
1770 m_chunksize = 8; // Takes a few seconds
1774 // TODO: Save to and load from a file
1775 m_seed = (((u64)(myrand()%0xffff)<<0)
1776 + ((u64)(myrand()%0xffff)<<16)
1777 + ((u64)(myrand()%0xffff)<<32)
1778 + ((u64)(myrand()%0xffff)<<48));
1781 Experimental and debug stuff
1788 Try to load map; if not found, create a new one.
1791 m_savedir = savedir;
1792 m_map_saving_enabled = false;
1796 // If directory exists, check contents and load if possible
1797 if(fs::PathExists(m_savedir))
1799 // If directory is empty, it is safe to save into it.
1800 if(fs::GetDirListing(m_savedir).size() == 0)
1802 dstream<<DTIME<<"Server: Empty save directory is valid."
1804 m_map_saving_enabled = true;
1808 // Load map metadata (seed, chunksize)
1811 // Load chunk metadata
1814 /*// Load sector (0,0) and throw and exception on fail
1815 if(loadSectorFull(v2s16(0,0)) == false)
1816 throw LoadError("Failed to load sector (0,0)");*/
1818 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1819 "metadata and sector (0,0) from "<<savedir<<
1820 ", assuming valid save directory."
1823 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1824 <<"and chunk metadata from "<<savedir
1825 <<", assuming valid save directory."
1828 m_map_saving_enabled = true;
1829 // Map loaded, not creating new one
1833 // If directory doesn't exist, it is safe to save to it
1835 m_map_saving_enabled = true;
1838 catch(std::exception &e)
1840 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1841 <<", exception: "<<e.what()<<std::endl;
1842 dstream<<"Please remove the map or fix it."<<std::endl;
1843 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1846 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1848 // Create zero sector
1849 emergeSector(v2s16(0,0));
1851 // Initially write whole map
1855 ServerMap::~ServerMap()
1859 if(m_map_saving_enabled)
1862 // Save only changed parts
1864 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1868 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1871 catch(std::exception &e)
1873 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1874 <<", exception: "<<e.what()<<std::endl;
1880 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1881 for(; i.atEnd() == false; i++)
1883 MapChunk *chunk = i.getNode()->getValue();
1889 Some helper functions for the map generator
1892 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1894 v3s16 em = vmanip.m_area.getExtent();
1895 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1896 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1897 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1899 for(y=y_nodes_max; y>=y_nodes_min; y--)
1901 MapNode &n = vmanip.m_data[i];
1902 if(content_walkable(n.d))
1905 vmanip.m_area.add_y(em, i, -1);
1907 if(y >= y_nodes_min)
1913 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1915 v3s16 em = vmanip.m_area.getExtent();
1916 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1917 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1918 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1920 for(y=y_nodes_max; y>=y_nodes_min; y--)
1922 MapNode &n = vmanip.m_data[i];
1923 if(content_walkable(n.d)
1924 && n.d != CONTENT_TREE
1925 && n.d != CONTENT_LEAVES)
1928 vmanip.m_area.add_y(em, i, -1);
1930 if(y >= y_nodes_min)
1936 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1938 MapNode treenode(CONTENT_TREE);
1939 MapNode leavesnode(CONTENT_LEAVES);
1941 s16 trunk_h = myrand_range(3, 6);
1943 for(s16 ii=0; ii<trunk_h; ii++)
1945 if(vmanip.m_area.contains(p1))
1946 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1950 // p1 is now the last piece of the trunk
1953 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1954 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1955 Buffer<u8> leaves_d(leaves_a.getVolume());
1956 for(s32 i=0; i<leaves_a.getVolume(); i++)
1959 // Force leaves at near the end of the trunk
1962 for(s16 z=-d; z<=d; z++)
1963 for(s16 y=-d; y<=d; y++)
1964 for(s16 x=-d; x<=d; x++)
1966 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1970 // Add leaves randomly
1971 for(u32 iii=0; iii<7; iii++)
1976 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1977 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1978 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1981 for(s16 z=0; z<=d; z++)
1982 for(s16 y=0; y<=d; y++)
1983 for(s16 x=0; x<=d; x++)
1985 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1989 // Blit leaves to vmanip
1990 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1991 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1992 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1996 if(vmanip.m_area.contains(p) == false)
1998 u32 vi = vmanip.m_area.index(p);
1999 if(vmanip.m_data[vi].d != CONTENT_AIR)
2001 u32 i = leaves_a.index(x,y,z);
2002 if(leaves_d[i] == 1)
2003 vmanip.m_data[vi] = leavesnode;
2008 Noise functions. Make sure seed is mangled differently in each one.
2011 // Amount of trees per area in nodes
2012 double tree_amount_2d(u64 seed, v2s16 p)
2014 double noise = noise2d_perlin(
2015 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2017 double zeroval = -0.3;
2021 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2024 #define AVERAGE_MUD_AMOUNT 4
2026 double base_rock_level_2d(u64 seed, v2s16 p)
2028 // The base ground level
2029 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2030 + 25. * noise2d_perlin(
2031 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2032 (seed>>32)+654879876, 6, 0.6);
2034 /*// A bit hillier one
2035 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2036 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2037 (seed>>27)+90340, 6, 0.69);
2041 // Higher ground level
2042 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2043 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2044 seed+85039, 5, 0.69);
2045 //higher = 30; // For debugging
2047 // Limit higher to at least base
2051 // Steepness factor of cliffs
2052 double b = 1.0 + 1.0 * noise2d_perlin(
2053 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2055 b = rangelim(b, 0.0, 1000.0);
2058 b = rangelim(b, 3.0, 1000.0);
2059 //dstream<<"b="<<b<<std::endl;
2062 // Offset to more low
2063 double a_off = -0.2;
2064 // High/low selector
2065 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2066 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2067 seed-359, 6, 0.7));*/
2068 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2069 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2070 seed-359, 5, 0.60));
2072 a = rangelim(a, 0.0, 1.0);
2074 //dstream<<"a="<<a<<std::endl;
2076 double h = base*(1.0-a) + higher*a;
2083 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2086 This is the main map generation method
2089 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2090 core::map<v3s16, MapBlock*> &changed_blocks,
2093 DSTACK(__FUNCTION_NAME);
2096 Don't generate if already fully generated
2100 MapChunk *chunk = getChunk(chunkpos);
2101 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2103 dstream<<"generateChunkRaw(): Chunk "
2104 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2105 <<" already generated"<<std::endl;
2110 dstream<<"generateChunkRaw(): Generating chunk "
2111 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2114 TimeTaker timer("generateChunkRaw()");
2116 // The distance how far into the neighbors the generator is allowed to go.
2117 s16 max_spread_amount_sectors = 2;
2118 assert(max_spread_amount_sectors <= m_chunksize);
2119 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2121 // Minimum amount of space left on sides for mud to fall in
2122 //s16 min_mud_fall_space = 2;
2124 // Maximum diameter of stone obstacles in X and Z
2125 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2126 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2128 s16 y_blocks_min = -4;
2129 s16 y_blocks_max = 3;
2130 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2131 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2132 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2134 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2135 s16 sectorpos_base_size = m_chunksize;
2137 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2138 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2139 v2s16 sectorpos_bigbase =
2140 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2141 s16 sectorpos_bigbase_size =
2142 sectorpos_base_size + 2 * max_spread_amount_sectors;
2144 v3s16 bigarea_blocks_min(
2145 sectorpos_bigbase.X,
2150 v3s16 bigarea_blocks_max(
2151 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2153 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2156 // Relative values to control amount of stuff in one chunk
2157 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2158 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2159 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2160 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2161 *(u32)h_blocks*MAP_BLOCKSIZE;
2164 The limiting edges of the lighting update, inclusive.
2166 s16 lighting_min_d = 0-max_spread_amount;
2167 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2170 Create the whole area of this and the neighboring chunks
2173 TimeTaker timer("generateChunkRaw() create area");
2175 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2176 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2178 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2179 ServerMapSector *sector = createSector(sectorpos);
2182 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2184 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2185 MapBlock *block = createBlock(blockpos);
2187 // Lighting won't be calculated
2188 //block->setLightingExpired(true);
2189 // Lighting will be calculated
2190 block->setLightingExpired(false);
2193 Block gets sunlight if this is true.
2195 This should be set to true when the top side of a block
2196 is completely exposed to the sky.
2198 Actually this doesn't matter now because the
2199 initial lighting is done here.
2201 block->setIsUnderground(y != y_blocks_max);
2207 Now we have a big empty area.
2209 Make a ManualMapVoxelManipulator that contains this and the
2213 ManualMapVoxelManipulator vmanip(this);
2214 // Add the area we just generated
2216 TimeTaker timer("generateChunkRaw() initialEmerge");
2217 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2221 vmanip.clearFlag(0xff);
2223 TimeTaker timer_generate("generateChunkRaw() generate");
2225 // Maximum height of the stone surface and obstacles.
2226 // This is used to disable dungeon generation from going too high.
2227 s16 stone_surface_max_y = 0;
2230 Generate general ground level to full area
2235 //TimeTaker timer1("ground level");
2237 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2238 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2241 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2244 Skip of already generated
2247 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2248 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2252 // Ground height at this point
2253 float surface_y_f = 0.0;
2255 // Use perlin noise for ground height
2256 surface_y_f = base_rock_level_2d(m_seed, p2d);
2258 /*// Experimental stuff
2260 float a = highlands_level_2d(m_seed, p2d);
2265 // Convert to integer
2266 s16 surface_y = (s16)surface_y_f;
2269 if(surface_y > stone_surface_max_y)
2270 stone_surface_max_y = surface_y;
2273 Fill ground with stone
2276 // Use fast index incrementing
2277 v3s16 em = vmanip.m_area.getExtent();
2278 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2279 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2281 vmanip.m_data[i].d = CONTENT_STONE;
2283 vmanip.m_area.add_y(em, i, 1);
2291 Randomize some parameters
2294 s32 stone_obstacle_count = 0;
2295 /*s32 stone_obstacle_count =
2296 rangelim((1.0+noise2d(m_seed+897,
2297 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2299 s16 stone_obstacle_max_height = 0;
2300 /*s16 stone_obstacle_max_height =
2301 rangelim((1.0+noise2d(m_seed+5902,
2302 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2305 Loop this part, it will make stuff look older and newer nicely
2307 //for(u32 i_age=0; i_age<1; i_age++)
2308 for(u32 i_age=0; i_age<2; i_age++)
2313 //TimeTaker timer1("stone obstacles");
2316 Add some random stone obstacles
2319 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2321 // Randomize max height so usually stuff will be quite low
2322 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2324 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2325 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2328 myrand_range(5, stone_obstacle_max_size),
2329 myrand_range(0, maxheight_randomized),
2330 myrand_range(5, stone_obstacle_max_size)
2333 // Don't make stupid small rectangle bumps
2338 myrand_range(1+ob_size.X/2+2,
2339 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2340 myrand_range(1+ob_size.Z/2+2,
2341 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2344 // Minimum space left on top of the obstacle
2345 s16 min_head_space = 12;
2347 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2348 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2350 // Node position in 2d
2351 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2353 // Find stone ground level
2354 // (ignore everything else than mud in already generated chunks)
2355 // and mud amount over the stone level
2359 v3s16 em = vmanip.m_area.getExtent();
2360 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2362 // Go to ground level
2363 for(y=y_nodes_max; y>=y_nodes_min; y--)
2365 MapNode *n = &vmanip.m_data[i];
2366 /*if(content_walkable(n.d)
2367 && n.d != CONTENT_MUD
2368 && n.d != CONTENT_GRASS)
2370 if(n->d == CONTENT_STONE)
2373 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2377 Change to mud because otherwise we might
2378 be throwing mud on grass at the next
2384 vmanip.m_area.add_y(em, i, -1);
2386 if(y >= y_nodes_min)
2389 surface_y = y_nodes_min;
2397 v3s16 em = vmanip.m_area.getExtent();
2398 s16 y_start = surface_y+1;
2399 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2403 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2405 MapNode &n = vmanip.m_data[i];
2406 n.d = CONTENT_STONE;
2408 if(y > stone_surface_max_y)
2409 stone_surface_max_y = y;
2412 if(count >= ob_size.Y)
2415 vmanip.m_area.add_y(em, i, 1);
2419 for(; y<=y_nodes_max - min_head_space; y++)
2421 MapNode &n = vmanip.m_data[i];
2424 if(count >= mud_amount)
2427 vmanip.m_area.add_y(em, i, 1);
2437 //TimeTaker timer1("dungeons");
2442 u32 dungeons_count = relative_volume / 600000;
2443 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2444 if(stone_surface_max_y < WATER_LEVEL)
2446 /*u32 dungeons_count = 0;
2447 u32 bruises_count = 0;*/
2448 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2450 s16 min_tunnel_diameter = 2;
2451 s16 max_tunnel_diameter = 6;
2452 u16 tunnel_routepoints = 25;
2454 bool bruise_surface = (jj < bruises_count);
2458 min_tunnel_diameter = 5;
2459 max_tunnel_diameter = myrand_range(10, 20);
2460 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2461 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2463 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2464 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2466 tunnel_routepoints = 5;
2469 // Allowed route area size in nodes
2471 sectorpos_base_size*MAP_BLOCKSIZE,
2472 h_blocks*MAP_BLOCKSIZE,
2473 sectorpos_base_size*MAP_BLOCKSIZE
2476 // Area starting point in nodes
2478 sectorpos_base.X*MAP_BLOCKSIZE,
2479 y_blocks_min*MAP_BLOCKSIZE,
2480 sectorpos_base.Y*MAP_BLOCKSIZE
2484 //(this should be more than the maximum radius of the tunnel)
2485 //s16 insure = 5; // Didn't work with max_d = 20
2487 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2488 ar += v3s16(1,0,1) * more * 2;
2489 of -= v3s16(1,0,1) * more;
2491 s16 route_y_min = 0;
2492 // Allow half a diameter + 7 over stone surface
2493 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2495 /*// If dungeons, don't go through surface too often
2496 if(bruise_surface == false)
2497 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2499 // Limit maximum to area
2500 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2504 /*// Minimum is at y=0
2505 route_y_min = -of.Y - 0;*/
2506 // Minimum is at y=max_tunnel_diameter/4
2507 //route_y_min = -of.Y + max_tunnel_diameter/4;
2508 //s16 min = -of.Y + max_tunnel_diameter/4;
2509 s16 min = -of.Y + 0;
2510 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2511 route_y_min = rangelim(route_y_min, 0, route_y_max);
2514 /*dstream<<"route_y_min = "<<route_y_min
2515 <<", route_y_max = "<<route_y_max<<std::endl;*/
2517 s16 route_start_y_min = route_y_min;
2518 s16 route_start_y_max = route_y_max;
2520 // Start every 2nd dungeon from surface
2521 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2523 if(coming_from_surface)
2525 route_start_y_min = -of.Y + stone_surface_max_y + 5;
2528 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2529 route_start_y_max = rangelim(route_start_y_max, 0, ar.Y-1);
2531 // Randomize starting position
2533 (float)(myrand()%ar.X)+0.5,
2534 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2535 (float)(myrand()%ar.Z)+0.5
2538 MapNode airnode(CONTENT_AIR);
2541 Generate some tunnel starting from orp
2544 for(u16 j=0; j<tunnel_routepoints; j++)
2547 s16 min_d = min_tunnel_diameter;
2548 s16 max_d = max_tunnel_diameter;
2549 s16 rs = myrand_range(min_d, max_d);
2554 maxlen = v3s16(rs*7,rs*7,rs*7);
2558 maxlen = v3s16(15, myrand_range(1, 20), 15);
2563 if(coming_from_surface && j < 3)
2566 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2567 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2568 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2574 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2575 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2576 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2583 else if(rp.X >= ar.X)
2585 if(rp.Y < route_y_min)
2587 else if(rp.Y >= route_y_max)
2588 rp.Y = route_y_max-1;
2591 else if(rp.Z >= ar.Z)
2595 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2597 v3f fp = orp + vec * f;
2598 v3s16 cp(fp.X, fp.Y, fp.Z);
2601 s16 d1 = d0 + rs - 1;
2602 for(s16 z0=d0; z0<=d1; z0++)
2604 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2605 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2606 for(s16 x0=-si; x0<=si-1; x0++)
2608 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2609 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2610 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2611 //s16 si2 = rs - abs(x0);
2612 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2618 /*if(isInArea(p, ar) == false)
2620 // Check only height
2621 if(y < 0 || y >= ar.Y)
2625 //assert(vmanip.m_area.contains(p));
2626 if(vmanip.m_area.contains(p) == false)
2628 dstream<<"WARNING: "<<__FUNCTION_NAME
2629 <<":"<<__LINE__<<": "
2630 <<"point not in area"
2635 // Just set it to air, it will be changed to
2637 u32 i = vmanip.m_area.index(p);
2638 vmanip.m_data[i] = airnode;
2640 if(bruise_surface == false)
2643 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2658 //TimeTaker timer1("ore veins");
2663 for(u32 jj=0; jj<relative_volume/1000; jj++)
2665 s16 max_vein_diameter = 3;
2667 // Allowed route area size in nodes
2669 sectorpos_base_size*MAP_BLOCKSIZE,
2670 h_blocks*MAP_BLOCKSIZE,
2671 sectorpos_base_size*MAP_BLOCKSIZE
2674 // Area starting point in nodes
2676 sectorpos_base.X*MAP_BLOCKSIZE,
2677 y_blocks_min*MAP_BLOCKSIZE,
2678 sectorpos_base.Y*MAP_BLOCKSIZE
2682 //(this should be more than the maximum radius of the tunnel)
2684 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2685 ar += v3s16(1,0,1) * more * 2;
2686 of -= v3s16(1,0,1) * more;
2688 // Randomize starting position
2690 (float)(myrand()%ar.X)+0.5,
2691 (float)(myrand()%ar.Y)+0.5,
2692 (float)(myrand()%ar.Z)+0.5
2695 // Randomize mineral
2698 mineral = MINERAL_COAL;
2700 mineral = MINERAL_IRON;
2703 Generate some vein starting from orp
2706 for(u16 j=0; j<2; j++)
2709 (float)(myrand()%ar.X)+0.5,
2710 (float)(myrand()%ar.Y)+0.5,
2711 (float)(myrand()%ar.Z)+0.5
2713 v3f vec = rp - orp;*/
2715 v3s16 maxlen(5, 5, 5);
2717 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2718 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2719 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2724 else if(rp.X >= ar.X)
2728 else if(rp.Y >= ar.Y)
2732 else if(rp.Z >= ar.Z)
2738 s16 max_d = max_vein_diameter;
2739 s16 rs = myrand_range(min_d, max_d);
2741 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2743 v3f fp = orp + vec * f;
2744 v3s16 cp(fp.X, fp.Y, fp.Z);
2746 s16 d1 = d0 + rs - 1;
2747 for(s16 z0=d0; z0<=d1; z0++)
2749 s16 si = rs - abs(z0);
2750 for(s16 x0=-si; x0<=si-1; x0++)
2752 s16 si2 = rs - abs(x0);
2753 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2755 // Don't put mineral to every place
2763 /*if(isInArea(p, ar) == false)
2765 // Check only height
2766 if(y < 0 || y >= ar.Y)
2770 assert(vmanip.m_area.contains(p));
2772 // Just set it to air, it will be changed to
2774 u32 i = vmanip.m_area.index(p);
2775 MapNode *n = &vmanip.m_data[i];
2776 if(n->d == CONTENT_STONE)
2791 //TimeTaker timer1("add mud");
2794 Add mud to the central chunk
2797 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2798 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2800 // Node position in 2d
2801 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2803 // Randomize mud amount
2804 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2805 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2806 m_seed+1, 3, 0.55));
2808 // Find ground level
2809 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2812 If topmost node is grass, change it to mud.
2813 It might be if it was flown to there from a neighboring
2814 chunk and then converted.
2817 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2818 MapNode *n = &vmanip.m_data[i];
2819 if(n->d == CONTENT_GRASS)
2828 v3s16 em = vmanip.m_area.getExtent();
2829 s16 y_start = surface_y+1;
2830 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2831 for(s16 y=y_start; y<=y_nodes_max; y++)
2833 if(mudcount >= mud_add_amount)
2836 MapNode &n = vmanip.m_data[i];
2840 vmanip.m_area.add_y(em, i, 1);
2849 TimeTaker timer1("flow mud");
2852 Flow mud away from steep edges
2855 // Limit area by 1 because mud is flown into neighbors.
2856 s16 mudflow_minpos = 0-max_spread_amount+1;
2857 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2859 // Iterate a few times
2860 for(s16 k=0; k<3; k++)
2863 for(s16 x=mudflow_minpos;
2866 for(s16 z=mudflow_minpos;
2870 // Invert coordinates every 2nd iteration
2873 x = mudflow_maxpos - (x-mudflow_minpos);
2874 z = mudflow_maxpos - (z-mudflow_minpos);
2877 // Node position in 2d
2878 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2880 v3s16 em = vmanip.m_area.getExtent();
2881 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2888 for(; y>=y_nodes_min; y--)
2890 n = &vmanip.m_data[i];
2891 //if(content_walkable(n->d))
2893 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2896 vmanip.m_area.add_y(em, i, -1);
2899 // Stop if out of area
2900 //if(vmanip.m_area.contains(i) == false)
2904 /*// If not mud, do nothing to it
2905 MapNode *n = &vmanip.m_data[i];
2906 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2910 Don't flow it if the stuff under it is not mud
2914 vmanip.m_area.add_y(em, i2, -1);
2915 // Cancel if out of area
2916 if(vmanip.m_area.contains(i2) == false)
2918 MapNode *n2 = &vmanip.m_data[i2];
2919 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2923 // Make it exactly mud
2926 /*s16 recurse_count = 0;
2930 v3s16(0,0,1), // back
2931 v3s16(1,0,0), // right
2932 v3s16(0,0,-1), // front
2933 v3s16(-1,0,0), // left
2936 // Theck that upper is air or doesn't exist.
2937 // Cancel dropping if upper keeps it in place
2939 vmanip.m_area.add_y(em, i3, 1);
2940 if(vmanip.m_area.contains(i3) == true
2941 && content_walkable(vmanip.m_data[i3].d) == true)
2948 for(u32 di=0; di<4; di++)
2950 v3s16 dirp = dirs4[di];
2953 vmanip.m_area.add_p(em, i2, dirp);
2954 // Fail if out of area
2955 if(vmanip.m_area.contains(i2) == false)
2957 // Check that side is air
2958 MapNode *n2 = &vmanip.m_data[i2];
2959 if(content_walkable(n2->d))
2961 // Check that under side is air
2962 vmanip.m_area.add_y(em, i2, -1);
2963 if(vmanip.m_area.contains(i2) == false)
2965 n2 = &vmanip.m_data[i2];
2966 if(content_walkable(n2->d))
2968 /*// Check that under that is air (need a drop of 2)
2969 vmanip.m_area.add_y(em, i2, -1);
2970 if(vmanip.m_area.contains(i2) == false)
2972 n2 = &vmanip.m_data[i2];
2973 if(content_walkable(n2->d))
2975 // Loop further down until not air
2977 vmanip.m_area.add_y(em, i2, -1);
2978 // Fail if out of area
2979 if(vmanip.m_area.contains(i2) == false)
2981 n2 = &vmanip.m_data[i2];
2982 }while(content_walkable(n2->d) == false);
2983 // Loop one up so that we're in air
2984 vmanip.m_area.add_y(em, i2, 1);
2985 n2 = &vmanip.m_data[i2];
2987 // Move mud to new place
2989 // Set old place to be air
2990 *n = MapNode(CONTENT_AIR);
3003 //TimeTaker timer1("add water");
3006 Add water to the central chunk (and a bit more)
3009 for(s16 x=0-max_spread_amount;
3010 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3012 for(s16 z=0-max_spread_amount;
3013 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3016 // Node position in 2d
3017 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3019 // Find ground level
3020 //s16 surface_y = find_ground_level(vmanip, p2d);
3023 If ground level is over water level, skip.
3024 NOTE: This leaves caves near water without water,
3025 which looks especially crappy when the nearby water
3026 won't start flowing either for some reason
3028 /*if(surface_y > WATER_LEVEL)
3035 v3s16 em = vmanip.m_area.getExtent();
3036 u8 light = LIGHT_MAX;
3037 // Start at global water surface level
3038 s16 y_start = WATER_LEVEL;
3039 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3040 MapNode *n = &vmanip.m_data[i];
3042 /*// Add first one to transforming liquid queue, if water
3043 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3045 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3046 m_transforming_liquid.push_back(p);
3049 for(s16 y=y_start; y>=y_nodes_min; y--)
3051 n = &vmanip.m_data[i];
3053 // Stop when there is no water and no air
3054 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3055 && n->d != CONTENT_WATER)
3057 /*// Add bottom one to transforming liquid queue
3058 vmanip.m_area.add_y(em, i, 1);
3059 n = &vmanip.m_data[i];
3060 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3062 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3063 m_transforming_liquid.push_back(p);
3069 // Make water only not in dungeons
3070 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3072 n->d = CONTENT_WATERSOURCE;
3073 //n->setLight(LIGHTBANK_DAY, light);
3075 // Add to transforming liquid queue (in case it'd
3077 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3078 m_transforming_liquid.push_back(p);
3082 vmanip.m_area.add_y(em, i, -1);
3095 //TimeTaker timer1("convert mud to sand");
3101 //s16 mud_add_amount = myrand_range(2, 4);
3102 //s16 mud_add_amount = 0;
3104 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3105 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3106 for(s16 x=0-max_spread_amount+1;
3107 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3109 for(s16 z=0-max_spread_amount+1;
3110 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3113 // Node position in 2d
3114 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3116 // Determine whether to have sand here
3117 double sandnoise = noise2d_perlin(
3118 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3119 m_seed+59420, 3, 0.50);
3121 bool have_sand = (sandnoise > -0.15);
3123 if(have_sand == false)
3126 // Find ground level
3127 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3129 if(surface_y > WATER_LEVEL + 2)
3133 v3s16 em = vmanip.m_area.getExtent();
3134 s16 y_start = surface_y;
3135 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3136 u32 not_sand_counter = 0;
3137 for(s16 y=y_start; y>=y_nodes_min; y--)
3139 MapNode *n = &vmanip.m_data[i];
3140 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3142 n->d = CONTENT_SAND;
3147 if(not_sand_counter > 3)
3151 vmanip.m_area.add_y(em, i, -1);
3160 //TimeTaker timer1("generate trees");
3166 // Divide area into parts
3168 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3169 double area = sidelen * sidelen;
3170 for(s16 x0=0; x0<div; x0++)
3171 for(s16 z0=0; z0<div; z0++)
3173 // Center position of part of division
3175 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3176 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3178 // Minimum edge of part of division
3180 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3181 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3183 // Maximum edge of part of division
3185 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3186 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3189 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3190 // Put trees in random places on part of division
3191 for(u32 i=0; i<tree_count; i++)
3193 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3194 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3195 s16 y = find_ground_level(vmanip, v2s16(x,z));
3196 // Don't make a tree under water level
3199 // Don't make a tree so high that it doesn't fit
3200 if(y > y_nodes_max - 6)
3204 Trees grow only on mud and grass
3207 u32 i = vmanip.m_area.index(v3s16(p));
3208 MapNode *n = &vmanip.m_data[i];
3209 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3214 make_tree(vmanip, p);
3217 /*u32 tree_max = relative_area / 60;
3218 //u32 count = myrand_range(0, tree_max);
3219 for(u32 i=0; i<count; i++)
3221 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3222 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3223 x += sectorpos_base.X*MAP_BLOCKSIZE;
3224 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3225 s16 y = find_ground_level(vmanip, v2s16(x,z));
3226 // Don't make a tree under water level
3231 make_tree(vmanip, p);
3239 //TimeTaker timer1("grow grass");
3245 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3246 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3247 for(s16 x=0-max_spread_amount;
3248 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3250 for(s16 z=0-max_spread_amount;
3251 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3254 // Node position in 2d
3255 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3258 Find the lowest surface to which enough light ends up
3261 Basically just wait until not air and not leaves.
3265 v3s16 em = vmanip.m_area.getExtent();
3266 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3268 // Go to ground level
3269 for(y=y_nodes_max; y>=y_nodes_min; y--)
3271 MapNode &n = vmanip.m_data[i];
3272 if(n.d != CONTENT_AIR
3273 && n.d != CONTENT_LEAVES)
3275 vmanip.m_area.add_y(em, i, -1);
3277 if(y >= y_nodes_min)
3280 surface_y = y_nodes_min;
3283 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3284 MapNode *n = &vmanip.m_data[i];
3285 if(n->d == CONTENT_MUD)
3286 n->d = CONTENT_GRASS;
3292 Initial lighting (sunlight)
3295 core::map<v3s16, bool> light_sources;
3298 // 750ms @cs=8, can't optimize more
3299 TimeTaker timer1("initial lighting");
3303 Go through the edges and add all nodes that have light to light_sources
3307 for(s16 i=0; i<4; i++)
3309 for(s16 j=lighting_min_d;
3316 if(i == 0 || i == 1)
3318 x = (i==0) ? lighting_min_d : lighting_max_d;
3327 z = (i==0) ? lighting_min_d : lighting_max_d;
3334 // Node position in 2d
3335 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3338 v3s16 em = vmanip.m_area.getExtent();
3339 s16 y_start = y_nodes_max;
3340 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3341 for(s16 y=y_start; y>=y_nodes_min; y--)
3343 MapNode *n = &vmanip.m_data[i];
3344 if(n->getLight(LIGHTBANK_DAY) != 0)
3346 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3348 //NOTE: This is broken, at least the index has to
3357 Go through the edges and apply sunlight to them, not caring
3362 for(s16 i=0; i<4; i++)
3364 for(s16 j=lighting_min_d;
3371 if(i == 0 || i == 1)
3373 x = (i==0) ? lighting_min_d : lighting_max_d;
3382 z = (i==0) ? lighting_min_d : lighting_max_d;
3389 // Node position in 2d
3390 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3392 // Loop from top to down
3394 u8 light = LIGHT_SUN;
3395 v3s16 em = vmanip.m_area.getExtent();
3396 s16 y_start = y_nodes_max;
3397 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3398 for(s16 y=y_start; y>=y_nodes_min; y--)
3400 MapNode *n = &vmanip.m_data[i];
3401 if(light_propagates_content(n->d) == false)
3405 else if(light != LIGHT_SUN
3406 || sunlight_propagates_content(n->d) == false)
3412 n->setLight(LIGHTBANK_DAY, light);
3413 n->setLight(LIGHTBANK_NIGHT, 0);
3417 // Insert light source
3418 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3421 // Increment index by y
3422 vmanip.m_area.add_y(em, i, -1);
3428 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3429 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3430 /*for(s16 x=0-max_spread_amount+1;
3431 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3433 for(s16 z=0-max_spread_amount+1;
3434 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3438 This has to be 1 smaller than the actual area, because
3439 neighboring nodes are checked.
3441 for(s16 x=lighting_min_d+1;
3442 x<=lighting_max_d-1;
3444 for(s16 z=lighting_min_d+1;
3445 z<=lighting_max_d-1;
3448 // Node position in 2d
3449 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3452 Apply initial sunlight
3455 u8 light = LIGHT_SUN;
3456 bool add_to_sources = false;
3457 v3s16 em = vmanip.m_area.getExtent();
3458 s16 y_start = y_nodes_max;
3459 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3460 for(s16 y=y_start; y>=y_nodes_min; y--)
3462 MapNode *n = &vmanip.m_data[i];
3464 if(light_propagates_content(n->d) == false)
3468 else if(light != LIGHT_SUN
3469 || sunlight_propagates_content(n->d) == false)
3475 // This doesn't take much time
3476 if(add_to_sources == false)
3479 Check sides. If side is not air or water, start
3480 adding to light_sources.
3483 v3s16(0,0,1), // back
3484 v3s16(1,0,0), // right
3485 v3s16(0,0,-1), // front
3486 v3s16(-1,0,0), // left
3488 for(u32 di=0; di<4; di++)
3490 v3s16 dirp = dirs4[di];
3492 vmanip.m_area.add_p(em, i2, dirp);
3493 MapNode *n2 = &vmanip.m_data[i2];
3495 n2->d != CONTENT_AIR
3496 && n2->d != CONTENT_WATERSOURCE
3497 && n2->d != CONTENT_WATER
3499 add_to_sources = true;
3505 n->setLight(LIGHTBANK_DAY, light);
3506 n->setLight(LIGHTBANK_NIGHT, 0);
3508 // This doesn't take much time
3509 if(light != 0 && add_to_sources)
3511 // Insert light source
3512 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3515 // Increment index by y
3516 vmanip.m_area.add_y(em, i, -1);
3523 for(s16 x=lighting_min_d+1;
3524 x<=lighting_max_d-1;
3526 for(s16 z=lighting_min_d+1;
3527 z<=lighting_max_d-1;
3530 // Node position in 2d
3531 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3534 Apply initial sunlight
3537 u8 light = LIGHT_SUN;
3538 v3s16 em = vmanip.m_area.getExtent();
3539 s16 y_start = y_nodes_max;
3540 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3541 for(s16 y=y_start; y>=y_nodes_min; y--)
3543 MapNode *n = &vmanip.m_data[i];
3545 if(light_propagates_content(n->d) == false)
3549 else if(light != LIGHT_SUN
3550 || sunlight_propagates_content(n->d) == false)
3556 n->setLight(LIGHTBANK_DAY, light);
3557 n->setLight(LIGHTBANK_NIGHT, 0);
3559 // This doesn't take much time
3562 // Insert light source
3563 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3566 // Increment index by y
3567 vmanip.m_area.add_y(em, i, -1);
3575 // Spread light around
3577 TimeTaker timer("generateChunkRaw() spreadLight");
3578 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3585 timer_generate.stop();
3588 Blit generated stuff to map
3592 //TimeTaker timer("generateChunkRaw() blitBackAll");
3593 vmanip.blitBackAll(&changed_blocks);
3597 Update day/night difference cache of the MapBlocks
3600 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3601 i.atEnd() == false; i++)
3603 MapBlock *block = i.getNode()->getValue();
3604 block->updateDayNightDiff();
3610 Create chunk metadata
3613 for(s16 x=-1; x<=1; x++)
3614 for(s16 y=-1; y<=1; y++)
3616 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3617 // Add chunk meta information
3618 MapChunk *chunk = getChunk(chunkpos0);
3621 chunk = new MapChunk();
3622 m_chunks.insert(chunkpos0, chunk);
3624 //chunk->setIsVolatile(true);
3625 if(chunk->getGenLevel() > GENERATED_PARTLY)
3626 chunk->setGenLevel(GENERATED_PARTLY);
3630 Set central chunk non-volatile
3632 MapChunk *chunk = getChunk(chunkpos);
3635 //chunk->setIsVolatile(false);
3636 chunk->setGenLevel(GENERATED_FULLY);
3639 Save changed parts of map
3644 Return central chunk (which was requested)
3649 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3650 core::map<v3s16, MapBlock*> &changed_blocks)
3652 dstream<<"generateChunk(): Generating chunk "
3653 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3656 /*for(s16 x=-1; x<=1; x++)
3657 for(s16 y=-1; y<=1; y++)*/
3658 for(s16 x=-0; x<=0; x++)
3659 for(s16 y=-0; y<=0; y++)
3661 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3662 MapChunk *chunk = getChunk(chunkpos0);
3663 // Skip if already generated
3664 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3666 generateChunkRaw(chunkpos0, changed_blocks);
3669 assert(chunkNonVolatile(chunkpos1));
3671 MapChunk *chunk = getChunk(chunkpos1);
3675 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3677 DSTACK("%s: p2d=(%d,%d)",
3682 Check if it exists already in memory
3684 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3689 Try to load it from disk (with blocks)
3691 if(loadSectorFull(p2d) == true)
3693 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3696 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3697 throw InvalidPositionException("");
3703 Do not create over-limit
3705 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3706 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3707 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3708 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3709 throw InvalidPositionException("createSector(): pos. over limit");
3712 Generate blank sector
3715 sector = new ServerMapSector(this, p2d);
3717 // Sector position on map in nodes
3718 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3723 m_sectors.insert(p2d, sector);
3728 MapSector * ServerMap::emergeSector(v2s16 p2d,
3729 core::map<v3s16, MapBlock*> &changed_blocks)
3731 DSTACK("%s: p2d=(%d,%d)",
3738 v2s16 chunkpos = sector_to_chunk(p2d);
3739 /*bool chunk_nonvolatile = false;
3740 MapChunk *chunk = getChunk(chunkpos);
3741 if(chunk && chunk->getIsVolatile() == false)
3742 chunk_nonvolatile = true;*/
3743 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3746 If chunk is not fully generated, generate chunk
3748 if(chunk_nonvolatile == false)
3750 // Generate chunk and neighbors
3751 generateChunk(chunkpos, changed_blocks);
3755 Return sector if it exists now
3757 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3762 Try to load it from disk
3764 if(loadSectorFull(p2d) == true)
3766 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3769 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3770 throw InvalidPositionException("");
3776 generateChunk should have generated the sector
3780 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3781 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3785 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3787 return createSector(p2d);
3792 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3795 generateChunkRaw(chunkpos, changed_blocks, true);
3798 Return sector if it exists now
3800 sector = getSectorNoGenerateNoEx(p2d);
3804 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3812 //return generateSector();
3816 NOTE: This is not used for main map generation, only for blocks
3817 that are very high or low
3819 MapBlock * ServerMap::generateBlock(
3821 MapBlock *original_dummy,
3822 ServerMapSector *sector,
3823 core::map<v3s16, MapBlock*> &changed_blocks,
3824 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3827 DSTACK("%s: p=(%d,%d,%d)",
3831 /*dstream<<"generateBlock(): "
3832 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3835 MapBlock *block = original_dummy;
3837 v2s16 p2d(p.X, p.Z);
3839 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3842 Do not generate over-limit
3844 if(blockpos_over_limit(p))
3846 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3847 throw InvalidPositionException("generateBlock(): pos. over limit");
3851 If block doesn't exist, create one.
3852 If it exists, it is a dummy. In that case unDummify() it.
3854 NOTE: This already sets the map as the parent of the block
3858 block = sector->createBlankBlockNoInsert(block_y);
3862 // Remove the block so that nobody can get a half-generated one.
3863 sector->removeBlock(block);
3864 // Allocate the block to contain the generated data
3868 u8 water_material = CONTENT_WATERSOURCE;
3870 s32 lowest_ground_y = 32767;
3871 s32 highest_ground_y = -32768;
3873 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3874 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3876 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3878 //s16 surface_y = 0;
3880 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3881 + AVERAGE_MUD_AMOUNT;
3883 if(surface_y < lowest_ground_y)
3884 lowest_ground_y = surface_y;
3885 if(surface_y > highest_ground_y)
3886 highest_ground_y = surface_y;
3888 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3890 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3892 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3897 NOTE: If there are some man-made structures above the
3898 newly created block, they won't be taken into account.
3900 if(real_y > surface_y)
3901 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3907 // If node is over heightmap y, it's air or water
3908 if(real_y > surface_y)
3910 // If under water level, it's water
3911 if(real_y < WATER_LEVEL)
3913 n.d = water_material;
3914 n.setLight(LIGHTBANK_DAY,
3915 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3917 Add to transforming liquid queue (in case it'd
3920 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3921 m_transforming_liquid.push_back(real_pos);
3927 // Else it's ground or dungeons (air)
3930 // If it's surface_depth under ground, it's stone
3931 if(real_y <= surface_y - surface_depth)
3933 n.d = CONTENT_STONE;
3937 // It is mud if it is under the first ground
3938 // level or under water
3939 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3945 n.d = CONTENT_GRASS;
3948 //n.d = CONTENT_MUD;
3950 /*// If under water level, it's mud
3951 if(real_y < WATER_LEVEL)
3953 // Only the topmost node is grass
3954 else if(real_y <= surface_y - 1)
3957 n.d = CONTENT_GRASS;*/
3961 block->setNode(v3s16(x0,y0,z0), n);
3966 Calculate some helper variables
3969 // Completely underground if the highest part of block is under lowest
3971 // This has to be very sure; it's probably one too strict now but
3972 // that's just better.
3973 bool completely_underground =
3974 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3976 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3978 bool mostly_underwater_surface = false;
3979 if(highest_ground_y < WATER_LEVEL
3980 && some_part_underground && !completely_underground)
3981 mostly_underwater_surface = true;
3984 Get local attributes
3987 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3989 float caves_amount = 0.5;
3994 NOTE: BEWARE: Too big amount of attribute points slows verything
3996 1 interpolation from 5000 points takes 2-3ms.
3998 //TimeTaker timer("generateBlock() local attribute retrieval");
3999 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4000 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4001 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4005 //dstream<<"generateBlock(): Done"<<std::endl;
4011 // Initialize temporary table
4012 const s32 ued = MAP_BLOCKSIZE;
4013 bool underground_emptiness[ued*ued*ued];
4014 for(s32 i=0; i<ued*ued*ued; i++)
4016 underground_emptiness[i] = 0;
4023 Initialize orp and ors. Try to find if some neighboring
4024 MapBlock has a tunnel ended in its side
4028 (float)(myrand()%ued)+0.5,
4029 (float)(myrand()%ued)+0.5,
4030 (float)(myrand()%ued)+0.5
4033 bool found_existing = false;
4039 for(s16 y=0; y<ued; y++)
4040 for(s16 x=0; x<ued; x++)
4042 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4043 if(getNode(ap).d == CONTENT_AIR)
4045 orp = v3f(x+1,y+1,0);
4046 found_existing = true;
4047 goto continue_generating;
4051 catch(InvalidPositionException &e){}
4057 for(s16 y=0; y<ued; y++)
4058 for(s16 x=0; x<ued; x++)
4060 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4061 if(getNode(ap).d == CONTENT_AIR)
4063 orp = v3f(x+1,y+1,ued-1);
4064 found_existing = true;
4065 goto continue_generating;
4069 catch(InvalidPositionException &e){}
4075 for(s16 y=0; y<ued; y++)
4076 for(s16 z=0; z<ued; z++)
4078 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4079 if(getNode(ap).d == CONTENT_AIR)
4081 orp = v3f(0,y+1,z+1);
4082 found_existing = true;
4083 goto continue_generating;
4087 catch(InvalidPositionException &e){}
4093 for(s16 y=0; y<ued; y++)
4094 for(s16 z=0; z<ued; z++)
4096 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4097 if(getNode(ap).d == CONTENT_AIR)
4099 orp = v3f(ued-1,y+1,z+1);
4100 found_existing = true;
4101 goto continue_generating;
4105 catch(InvalidPositionException &e){}
4111 for(s16 x=0; x<ued; x++)
4112 for(s16 z=0; z<ued; z++)
4114 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4115 if(getNode(ap).d == CONTENT_AIR)
4117 orp = v3f(x+1,0,z+1);
4118 found_existing = true;
4119 goto continue_generating;
4123 catch(InvalidPositionException &e){}
4129 for(s16 x=0; x<ued; x++)
4130 for(s16 z=0; z<ued; z++)
4132 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4133 if(getNode(ap).d == CONTENT_AIR)
4135 orp = v3f(x+1,ued-1,z+1);
4136 found_existing = true;
4137 goto continue_generating;
4141 catch(InvalidPositionException &e){}
4143 continue_generating:
4146 Choose whether to actually generate dungeon
4148 bool do_generate_dungeons = true;
4149 // Don't generate if no part is underground
4150 if(!some_part_underground)
4152 do_generate_dungeons = false;
4154 // Don't generate if mostly underwater surface
4155 /*else if(mostly_underwater_surface)
4157 do_generate_dungeons = false;
4159 // Partly underground = cave
4160 else if(!completely_underground)
4162 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4164 // Found existing dungeon underground
4165 else if(found_existing && completely_underground)
4167 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4169 // Underground and no dungeons found
4172 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4175 if(do_generate_dungeons)
4178 Generate some tunnel starting from orp and ors
4180 for(u16 i=0; i<3; i++)
4183 (float)(myrand()%ued)+0.5,
4184 (float)(myrand()%ued)+0.5,
4185 (float)(myrand()%ued)+0.5
4189 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4193 for(float f=0; f<1.0; f+=0.04)
4195 v3f fp = orp + vec * f;
4196 v3s16 cp(fp.X, fp.Y, fp.Z);
4198 s16 d1 = d0 + rs - 1;
4199 for(s16 z0=d0; z0<=d1; z0++)
4201 s16 si = rs - abs(z0);
4202 for(s16 x0=-si; x0<=si-1; x0++)
4204 s16 si2 = rs - abs(x0);
4205 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4211 if(isInArea(p, ued) == false)
4213 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4225 // Set to true if has caves.
4226 // Set when some non-air is changed to air when making caves.
4227 bool has_dungeons = false;
4230 Apply temporary cave data to block
4233 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4234 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4236 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4238 MapNode n = block->getNode(v3s16(x0,y0,z0));
4241 if(underground_emptiness[
4242 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4243 +ued*(y0*ued/MAP_BLOCKSIZE)
4244 +(x0*ued/MAP_BLOCKSIZE)])
4246 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4249 has_dungeons = true;
4255 block->setNode(v3s16(x0,y0,z0), n);
4260 This is used for guessing whether or not the block should
4261 receive sunlight from the top if the block above doesn't exist
4263 block->setIsUnderground(completely_underground);
4266 Force lighting update if some part of block is partly
4267 underground and has caves.
4269 /*if(some_part_underground && !completely_underground && has_dungeons)
4271 //dstream<<"Half-ground caves"<<std::endl;
4272 lighting_invalidated_blocks[block->getPos()] = block;
4275 // DEBUG: Always update lighting
4276 //lighting_invalidated_blocks[block->getPos()] = block;
4282 if(some_part_underground)
4284 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4289 for(s16 i=0; i<underground_level/4 + 1; i++)
4291 if(myrand()%50 == 0)
4294 (myrand()%(MAP_BLOCKSIZE-2))+1,
4295 (myrand()%(MAP_BLOCKSIZE-2))+1,
4296 (myrand()%(MAP_BLOCKSIZE-2))+1
4302 for(u16 i=0; i<27; i++)
4304 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4306 block->setNode(cp+g_27dirs[i], n);
4314 u16 coal_amount = 30;
4315 u16 coal_rareness = 60 / coal_amount;
4316 if(coal_rareness == 0)
4318 if(myrand()%coal_rareness == 0)
4320 u16 a = myrand() % 16;
4321 u16 amount = coal_amount * a*a*a / 1000;
4322 for(s16 i=0; i<amount; i++)
4325 (myrand()%(MAP_BLOCKSIZE-2))+1,
4326 (myrand()%(MAP_BLOCKSIZE-2))+1,
4327 (myrand()%(MAP_BLOCKSIZE-2))+1
4331 n.d = CONTENT_STONE;
4332 n.param = MINERAL_COAL;
4334 for(u16 i=0; i<27; i++)
4336 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4338 block->setNode(cp+g_27dirs[i], n);
4346 //TODO: change to iron_amount or whatever
4347 u16 iron_amount = 15;
4348 u16 iron_rareness = 60 / iron_amount;
4349 if(iron_rareness == 0)
4351 if(myrand()%iron_rareness == 0)
4353 u16 a = myrand() % 16;
4354 u16 amount = iron_amount * a*a*a / 1000;
4355 for(s16 i=0; i<amount; i++)
4358 (myrand()%(MAP_BLOCKSIZE-2))+1,
4359 (myrand()%(MAP_BLOCKSIZE-2))+1,
4360 (myrand()%(MAP_BLOCKSIZE-2))+1
4364 n.d = CONTENT_STONE;
4365 n.param = MINERAL_IRON;
4367 for(u16 i=0; i<27; i++)
4369 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4371 block->setNode(cp+g_27dirs[i], n);
4378 Create a few rats in empty blocks underground
4380 if(completely_underground)
4382 //for(u16 i=0; i<2; i++)
4385 (myrand()%(MAP_BLOCKSIZE-2))+1,
4386 (myrand()%(MAP_BLOCKSIZE-2))+1,
4387 (myrand()%(MAP_BLOCKSIZE-2))+1
4390 // Check that the place is empty
4391 //if(!is_ground_content(block->getNode(cp).d))
4394 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4395 block->addObject(obj);
4401 Add block to sector.
4403 sector->insertBlock(block);
4405 // Lighting is invalid after generation.
4406 block->setLightingExpired(true);
4413 <<"lighting_invalidated_blocks.size()"
4417 <<" "<<lighting_invalidated_blocks.size()
4418 <<", "<<has_dungeons
4419 <<", "<<completely_underground
4420 <<", "<<some_part_underground
4427 MapBlock * ServerMap::createBlock(v3s16 p)
4429 DSTACK("%s: p=(%d,%d,%d)",
4430 __FUNCTION_NAME, p.X, p.Y, p.Z);
4433 Do not create over-limit
4435 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4436 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4437 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4438 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4439 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4440 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4441 throw InvalidPositionException("createBlock(): pos. over limit");
4443 v2s16 p2d(p.X, p.Z);
4446 This will create or load a sector if not found in memory.
4447 If block exists on disk, it will be loaded.
4449 NOTE: On old save formats, this will be slow, as it generates
4450 lighting on blocks for them.
4452 ServerMapSector *sector;
4454 sector = (ServerMapSector*)createSector(p2d);
4455 assert(sector->getId() == MAPSECTOR_SERVER);
4457 catch(InvalidPositionException &e)
4459 dstream<<"createBlock: createSector() failed"<<std::endl;
4463 NOTE: This should not be done, or at least the exception
4464 should not be passed on as std::exception, because it
4465 won't be catched at all.
4467 /*catch(std::exception &e)
4469 dstream<<"createBlock: createSector() failed: "
4470 <<e.what()<<std::endl;
4475 Try to get a block from the sector
4478 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4482 block = sector->createBlankBlock(block_y);
4486 MapBlock * ServerMap::emergeBlock(
4488 bool only_from_disk,
4489 core::map<v3s16, MapBlock*> &changed_blocks,
4490 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4493 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4495 p.X, p.Y, p.Z, only_from_disk);
4498 Do not generate over-limit
4500 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4501 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4502 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4503 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4504 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4505 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4506 throw InvalidPositionException("emergeBlock(): pos. over limit");
4508 v2s16 p2d(p.X, p.Z);
4511 This will create or load a sector if not found in memory.
4512 If block exists on disk, it will be loaded.
4514 ServerMapSector *sector;
4516 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4517 assert(sector->getId() == MAPSECTOR_SERVER);
4519 catch(InvalidPositionException &e)
4521 dstream<<"emergeBlock: emergeSector() failed: "
4522 <<e.what()<<std::endl;
4523 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4525 <<"You could try to delete it."<<std::endl;
4528 catch(VersionMismatchException &e)
4530 dstream<<"emergeBlock: emergeSector() failed: "
4531 <<e.what()<<std::endl;
4532 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4534 <<"You could try to delete it."<<std::endl;
4538 NOTE: This should not be done, or at least the exception
4539 should not be passed on as std::exception, because it
4540 won't be catched at all.
4542 /*catch(std::exception &e)
4544 dstream<<"emergeBlock: emergeSector() failed: "
4545 <<e.what()<<std::endl;
4546 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4548 <<"You could try to delete it."<<std::endl;
4553 Try to get a block from the sector
4556 bool does_not_exist = false;
4557 bool lighting_expired = false;
4558 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4562 does_not_exist = true;
4564 else if(block->isDummy() == true)
4566 does_not_exist = true;
4568 else if(block->getLightingExpired())
4570 lighting_expired = true;
4575 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4580 If block was not found on disk and not going to generate a
4581 new one, make sure there is a dummy block in place.
4583 if(only_from_disk && (does_not_exist || lighting_expired))
4585 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4589 // Create dummy block
4590 block = new MapBlock(this, p, true);
4592 // Add block to sector
4593 sector->insertBlock(block);
4599 //dstream<<"Not found on disk, generating."<<std::endl;
4601 //TimeTaker("emergeBlock() generate");
4603 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4606 If the block doesn't exist, generate the block.
4610 block = generateBlock(p, block, sector, changed_blocks,
4611 lighting_invalidated_blocks);
4614 if(lighting_expired)
4616 lighting_invalidated_blocks.insert(p, block);
4620 Initially update sunlight
4624 core::map<v3s16, bool> light_sources;
4625 bool black_air_left = false;
4626 bool bottom_invalid =
4627 block->propagateSunlight(light_sources, true,
4628 &black_air_left, true);
4630 // If sunlight didn't reach everywhere and part of block is
4631 // above ground, lighting has to be properly updated
4632 //if(black_air_left && some_part_underground)
4635 lighting_invalidated_blocks[block->getPos()] = block;
4640 lighting_invalidated_blocks[block->getPos()] = block;
4647 s16 ServerMap::findGroundLevel(v2s16 p2d)
4650 Uh, just do something random...
4652 // Find existing map from top to down
4655 v3s16 p(p2d.X, max, p2d.Y);
4656 for(; p.Y>min; p.Y--)
4658 MapNode n = getNodeNoEx(p);
4659 if(n.d != CONTENT_IGNORE)
4664 // If this node is not air, go to plan b
4665 if(getNodeNoEx(p).d != CONTENT_AIR)
4667 // Search existing walkable and return it
4668 for(; p.Y>min; p.Y--)
4670 MapNode n = getNodeNoEx(p);
4671 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4677 Plan B: Get from map generator perlin noise function
4679 double level = base_rock_level_2d(m_seed, p2d);
4683 void ServerMap::createDir(std::string path)
4685 if(fs::CreateDir(path) == false)
4687 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4688 <<"\""<<path<<"\""<<std::endl;
4689 throw BaseException("ServerMap failed to create directory");
4693 std::string ServerMap::getSectorSubDir(v2s16 pos)
4696 snprintf(cc, 9, "%.4x%.4x",
4697 (unsigned int)pos.X&0xffff,
4698 (unsigned int)pos.Y&0xffff);
4700 return std::string(cc);
4703 std::string ServerMap::getSectorDir(v2s16 pos)
4705 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4708 v2s16 ServerMap::getSectorPos(std::string dirname)
4710 if(dirname.size() != 8)
4711 throw InvalidFilenameException("Invalid sector directory name");
4713 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4715 throw InvalidFilenameException("Invalid sector directory name");
4716 v2s16 pos((s16)x, (s16)y);
4720 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4722 v2s16 p2d = getSectorPos(sectordir);
4724 if(blockfile.size() != 4){
4725 throw InvalidFilenameException("Invalid block filename");
4728 int r = sscanf(blockfile.c_str(), "%4x", &y);
4730 throw InvalidFilenameException("Invalid block filename");
4731 return v3s16(p2d.X, y, p2d.Y);
4734 void ServerMap::save(bool only_changed)
4736 DSTACK(__FUNCTION_NAME);
4737 if(m_map_saving_enabled == false)
4739 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4743 if(only_changed == false)
4744 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4750 u32 sector_meta_count = 0;
4751 u32 block_count = 0;
4754 JMutexAutoLock lock(m_sector_mutex);
4756 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4757 for(; i.atEnd() == false; i++)
4759 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4760 assert(sector->getId() == MAPSECTOR_SERVER);
4762 if(sector->differs_from_disk || only_changed == false)
4764 saveSectorMeta(sector);
4765 sector_meta_count++;
4767 core::list<MapBlock*> blocks;
4768 sector->getBlocks(blocks);
4769 core::list<MapBlock*>::Iterator j;
4770 for(j=blocks.begin(); j!=blocks.end(); j++)
4772 MapBlock *block = *j;
4773 if(block->getChangedFlag() || only_changed == false)
4778 /*dstream<<"ServerMap: Written block ("
4779 <<block->getPos().X<<","
4780 <<block->getPos().Y<<","
4781 <<block->getPos().Z<<")"
4790 Only print if something happened or saved whole map
4792 if(only_changed == false || sector_meta_count != 0
4793 || block_count != 0)
4795 dstream<<DTIME<<"ServerMap: Written: "
4796 <<sector_meta_count<<" sector metadata files, "
4797 <<block_count<<" block files"
4802 void ServerMap::loadAll()
4804 DSTACK(__FUNCTION_NAME);
4805 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4810 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4812 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4814 JMutexAutoLock lock(m_sector_mutex);
4817 s32 printed_counter = -100000;
4818 s32 count = list.size();
4820 std::vector<fs::DirListNode>::iterator i;
4821 for(i=list.begin(); i!=list.end(); i++)
4823 if(counter > printed_counter + 10)
4825 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4826 printed_counter = counter;
4830 MapSector *sector = NULL;
4832 // We want directories
4836 sector = loadSectorMeta(i->name);
4838 catch(InvalidFilenameException &e)
4840 // This catches unknown crap in directory
4843 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4844 (m_savedir+"/sectors/"+i->name);
4845 std::vector<fs::DirListNode>::iterator i2;
4846 for(i2=list2.begin(); i2!=list2.end(); i2++)
4852 loadBlock(i->name, i2->name, sector);
4854 catch(InvalidFilenameException &e)
4856 // This catches unknown crap in directory
4860 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4864 void ServerMap::saveMasterHeightmap()
4866 DSTACK(__FUNCTION_NAME);
4868 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4870 createDir(m_savedir);
4872 /*std::string fullpath = m_savedir + "/master_heightmap";
4873 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4874 if(o.good() == false)
4875 throw FileNotGoodException("Cannot open master heightmap");*/
4877 // Format used for writing
4878 //u8 version = SER_FMT_VER_HIGHEST;
4881 void ServerMap::loadMasterHeightmap()
4883 DSTACK(__FUNCTION_NAME);
4885 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4887 /*std::string fullpath = m_savedir + "/master_heightmap";
4888 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4889 if(is.good() == false)
4890 throw FileNotGoodException("Cannot open master heightmap");*/
4894 void ServerMap::saveMapMeta()
4896 DSTACK(__FUNCTION_NAME);
4898 dstream<<"INFO: ServerMap::saveMapMeta(): "
4899 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4902 createDir(m_savedir);
4904 std::string fullpath = m_savedir + "/map_meta.txt";
4905 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4906 if(os.good() == false)
4908 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4909 <<"could not open"<<fullpath<<std::endl;
4910 throw FileNotGoodException("Cannot open chunk metadata");
4914 params.setU64("seed", m_seed);
4915 params.setS32("chunksize", m_chunksize);
4917 params.writeLines(os);
4919 os<<"[end_of_params]\n";
4923 void ServerMap::loadMapMeta()
4925 DSTACK(__FUNCTION_NAME);
4927 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4930 std::string fullpath = m_savedir + "/map_meta.txt";
4931 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4932 if(is.good() == false)
4934 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4935 <<"could not open"<<fullpath<<std::endl;
4936 throw FileNotGoodException("Cannot open chunk metadata");
4944 throw SerializationError
4945 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4947 std::getline(is, line);
4948 std::string trimmedline = trim(line);
4949 if(trimmedline == "[end_of_params]")
4951 params.parseConfigLine(line);
4954 m_seed = params.getU64("seed");
4955 m_chunksize = params.getS32("chunksize");
4957 dstream<<"INFO: ServerMap::loadMapMeta(): "
4958 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4962 void ServerMap::saveChunkMeta()
4964 DSTACK(__FUNCTION_NAME);
4966 u32 count = m_chunks.size();
4968 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
4969 <<count<<" chunks"<<std::endl;
4971 createDir(m_savedir);
4973 std::string fullpath = m_savedir + "/chunk_meta";
4974 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4975 if(os.good() == false)
4977 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
4978 <<"could not open"<<fullpath<<std::endl;
4979 throw FileNotGoodException("Cannot open chunk metadata");
4985 os.write((char*)&version, 1);
4990 writeU32(buf, count);
4991 os.write((char*)buf, 4);
4993 for(core::map<v2s16, MapChunk*>::Iterator
4994 i = m_chunks.getIterator();
4995 i.atEnd()==false; i++)
4997 v2s16 p = i.getNode()->getKey();
4998 MapChunk *chunk = i.getNode()->getValue();
5001 os.write((char*)buf, 4);
5003 chunk->serialize(os, version);
5007 void ServerMap::loadChunkMeta()
5009 DSTACK(__FUNCTION_NAME);
5011 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5014 std::string fullpath = m_savedir + "/chunk_meta";
5015 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5016 if(is.good() == false)
5018 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5019 <<"could not open"<<fullpath<<std::endl;
5020 throw FileNotGoodException("Cannot open chunk metadata");
5026 is.read((char*)&version, 1);
5031 is.read((char*)buf, 4);
5032 u32 count = readU32(buf);
5034 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5035 <<count<<" chunks"<<std::endl;
5037 for(u32 i=0; i<count; i++)
5040 MapChunk *chunk = new MapChunk();
5042 is.read((char*)buf, 4);
5045 chunk->deSerialize(is, version);
5046 m_chunks.insert(p, chunk);
5050 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5052 DSTACK(__FUNCTION_NAME);
5053 // Format used for writing
5054 u8 version = SER_FMT_VER_HIGHEST;
5056 v2s16 pos = sector->getPos();
5057 createDir(m_savedir);
5058 createDir(m_savedir+"/sectors");
5059 std::string dir = getSectorDir(pos);
5062 std::string fullpath = dir + "/meta";
5063 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5064 if(o.good() == false)
5065 throw FileNotGoodException("Cannot open sector metafile");
5067 sector->serialize(o, version);
5069 sector->differs_from_disk = false;
5072 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5074 DSTACK(__FUNCTION_NAME);
5076 v2s16 p2d = getSectorPos(dirname);
5077 std::string dir = m_savedir + "/sectors/" + dirname;
5079 std::string fullpath = dir + "/meta";
5080 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5081 if(is.good() == false)
5082 throw FileNotGoodException("Cannot open sector metafile");
5084 ServerMapSector *sector = ServerMapSector::deSerialize
5085 (is, this, p2d, m_sectors);
5087 sector->differs_from_disk = false;
5092 bool ServerMap::loadSectorFull(v2s16 p2d)
5094 DSTACK(__FUNCTION_NAME);
5095 std::string sectorsubdir = getSectorSubDir(p2d);
5097 MapSector *sector = NULL;
5099 JMutexAutoLock lock(m_sector_mutex);
5102 sector = loadSectorMeta(sectorsubdir);
5104 catch(InvalidFilenameException &e)
5108 catch(FileNotGoodException &e)
5112 catch(std::exception &e)
5120 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5121 (m_savedir+"/sectors/"+sectorsubdir);
5122 std::vector<fs::DirListNode>::iterator i2;
5123 for(i2=list2.begin(); i2!=list2.end(); i2++)
5129 loadBlock(sectorsubdir, i2->name, sector);
5131 catch(InvalidFilenameException &e)
5133 // This catches unknown crap in directory
5139 void ServerMap::saveBlock(MapBlock *block)
5141 DSTACK(__FUNCTION_NAME);
5143 Dummy blocks are not written
5145 if(block->isDummy())
5147 /*v3s16 p = block->getPos();
5148 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5149 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5153 // Format used for writing
5154 u8 version = SER_FMT_VER_HIGHEST;
5156 v3s16 p3d = block->getPos();
5157 v2s16 p2d(p3d.X, p3d.Z);
5158 createDir(m_savedir);
5159 createDir(m_savedir+"/sectors");
5160 std::string dir = getSectorDir(p2d);
5163 // Block file is map/sectors/xxxxxxxx/xxxx
5165 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5166 std::string fullpath = dir + "/" + cc;
5167 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5168 if(o.good() == false)
5169 throw FileNotGoodException("Cannot open block data");
5172 [0] u8 serialization version
5175 o.write((char*)&version, 1);
5177 block->serialize(o, version);
5180 Versions up from 9 have block objects.
5184 block->serializeObjects(o, version);
5187 // We just wrote it to the disk
5188 block->resetChangedFlag();
5191 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5193 DSTACK(__FUNCTION_NAME);
5195 // Block file is map/sectors/xxxxxxxx/xxxx
5196 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5199 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5200 if(is.good() == false)
5201 throw FileNotGoodException("Cannot open block file");
5203 v3s16 p3d = getBlockPos(sectordir, blockfile);
5204 v2s16 p2d(p3d.X, p3d.Z);
5206 assert(sector->getPos() == p2d);
5208 u8 version = SER_FMT_VER_INVALID;
5209 is.read((char*)&version, 1);
5212 throw SerializationError("ServerMap::loadBlock(): Failed"
5213 " to read MapBlock version");
5215 /*u32 block_size = MapBlock::serializedLength(version);
5216 SharedBuffer<u8> data(block_size);
5217 is.read((char*)*data, block_size);*/
5219 // This will always return a sector because we're the server
5220 //MapSector *sector = emergeSector(p2d);
5222 MapBlock *block = NULL;
5223 bool created_new = false;
5225 block = sector->getBlockNoCreate(p3d.Y);
5227 catch(InvalidPositionException &e)
5229 block = sector->createBlankBlockNoInsert(p3d.Y);
5233 // deserialize block data
5234 block->deSerialize(is, version);
5237 Versions up from 9 have block objects.
5241 block->updateObjects(is, version, NULL, 0);
5245 sector->insertBlock(block);
5248 Convert old formats to new and save
5251 // Save old format blocks in new format
5252 if(version < SER_FMT_VER_HIGHEST)
5257 // We just loaded it from the disk, so it's up-to-date.
5258 block->resetChangedFlag();
5261 catch(SerializationError &e)
5263 dstream<<"WARNING: Invalid block data on disk "
5264 "(SerializationError). Ignoring. "
5265 "A new one will be generated."
5268 // TODO: Backup file; name is in fullpath.
5272 void ServerMap::PrintInfo(std::ostream &out)
5283 ClientMap::ClientMap(
5285 MapDrawControl &control,
5286 scene::ISceneNode* parent,
5287 scene::ISceneManager* mgr,
5291 scene::ISceneNode(parent, mgr, id),
5294 m_camera_position(0,0,0),
5295 m_camera_direction(0,0,1)
5297 m_camera_mutex.Init();
5298 assert(m_camera_mutex.IsInitialized());
5300 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5301 BS*1000000,BS*1000000,BS*1000000);
5304 ClientMap::~ClientMap()
5306 /*JMutexAutoLock lock(mesh_mutex);
5315 MapSector * ClientMap::emergeSector(v2s16 p2d)
5317 DSTACK(__FUNCTION_NAME);
5318 // Check that it doesn't exist already
5320 return getSectorNoGenerate(p2d);
5322 catch(InvalidPositionException &e)
5327 ClientMapSector *sector = new ClientMapSector(this, p2d);
5330 JMutexAutoLock lock(m_sector_mutex);
5331 m_sectors.insert(p2d, sector);
5337 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5339 DSTACK(__FUNCTION_NAME);
5340 ClientMapSector *sector = NULL;
5342 JMutexAutoLock lock(m_sector_mutex);
5344 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5348 sector = (ClientMapSector*)n->getValue();
5349 assert(sector->getId() == MAPSECTOR_CLIENT);
5353 sector = new ClientMapSector(this, p2d);
5355 JMutexAutoLock lock(m_sector_mutex);
5356 m_sectors.insert(p2d, sector);
5360 sector->deSerialize(is);
5363 void ClientMap::OnRegisterSceneNode()
5367 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5368 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5371 ISceneNode::OnRegisterSceneNode();
5374 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5376 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5377 DSTACK(__FUNCTION_NAME);
5379 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5382 Get time for measuring timeout.
5384 Measuring time is very useful for long delays when the
5385 machine is swapping a lot.
5387 int time1 = time(0);
5389 //u32 daynight_ratio = m_client->getDayNightRatio();
5391 m_camera_mutex.Lock();
5392 v3f camera_position = m_camera_position;
5393 v3f camera_direction = m_camera_direction;
5394 m_camera_mutex.Unlock();
5397 Get all blocks and draw all visible ones
5400 v3s16 cam_pos_nodes(
5401 camera_position.X / BS,
5402 camera_position.Y / BS,
5403 camera_position.Z / BS);
5405 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5407 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5408 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5410 // Take a fair amount as we will be dropping more out later
5412 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5413 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5414 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5416 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5417 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5418 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5420 u32 vertex_count = 0;
5422 // For limiting number of mesh updates per frame
5423 u32 mesh_update_count = 0;
5425 u32 blocks_would_have_drawn = 0;
5426 u32 blocks_drawn = 0;
5428 //NOTE: The sectors map should be locked but we're not doing it
5429 // because it'd cause too much delays
5431 int timecheck_counter = 0;
5432 core::map<v2s16, MapSector*>::Iterator si;
5433 si = m_sectors.getIterator();
5434 for(; si.atEnd() == false; si++)
5437 timecheck_counter++;
5438 if(timecheck_counter > 50)
5440 timecheck_counter = 0;
5441 int time2 = time(0);
5442 if(time2 > time1 + 4)
5444 dstream<<"ClientMap::renderMap(): "
5445 "Rendering takes ages, returning."
5452 MapSector *sector = si.getNode()->getValue();
5453 v2s16 sp = sector->getPos();
5455 if(m_control.range_all == false)
5457 if(sp.X < p_blocks_min.X
5458 || sp.X > p_blocks_max.X
5459 || sp.Y < p_blocks_min.Z
5460 || sp.Y > p_blocks_max.Z)
5464 core::list< MapBlock * > sectorblocks;
5465 sector->getBlocks(sectorblocks);
5471 core::list< MapBlock * >::Iterator i;
5472 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5474 MapBlock *block = *i;
5477 Compare block position to camera position, skip
5478 if not seen on display
5481 float range = 100000 * BS;
5482 if(m_control.range_all == false)
5483 range = m_control.wanted_range * BS;
5486 if(isBlockInSight(block->getPos(), camera_position,
5487 camera_direction, range, &d) == false)
5492 // This is ugly (spherical distance limit?)
5493 /*if(m_control.range_all == false &&
5494 d - 0.5*BS*MAP_BLOCKSIZE > range)
5499 Update expired mesh (used for day/night change)
5501 It doesn't work exactly like it should now with the
5502 tasked mesh update but whatever.
5505 bool mesh_expired = false;
5508 JMutexAutoLock lock(block->mesh_mutex);
5510 mesh_expired = block->getMeshExpired();
5512 // Mesh has not been expired and there is no mesh:
5513 // block has no content
5514 if(block->mesh == NULL && mesh_expired == false)
5518 f32 faraway = BS*50;
5519 //f32 faraway = m_control.wanted_range * BS;
5522 This has to be done with the mesh_mutex unlocked
5524 // Pretty random but this should work somewhat nicely
5525 if(mesh_expired && (
5526 (mesh_update_count < 3
5527 && (d < faraway || mesh_update_count < 2)
5530 (m_control.range_all && mesh_update_count < 20)
5533 /*if(mesh_expired && mesh_update_count < 6
5534 && (d < faraway || mesh_update_count < 3))*/
5536 mesh_update_count++;
5538 // Mesh has been expired: generate new mesh
5539 //block->updateMesh(daynight_ratio);
5540 m_client->addUpdateMeshTask(block->getPos());
5542 mesh_expired = false;
5547 Draw the faces of the block
5550 JMutexAutoLock lock(block->mesh_mutex);
5552 scene::SMesh *mesh = block->mesh;
5557 blocks_would_have_drawn++;
5558 if(blocks_drawn >= m_control.wanted_max_blocks
5559 && m_control.range_all == false
5560 && d > m_control.wanted_min_range * BS)
5564 u32 c = mesh->getMeshBufferCount();
5566 for(u32 i=0; i<c; i++)
5568 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5569 const video::SMaterial& material = buf->getMaterial();
5570 video::IMaterialRenderer* rnd =
5571 driver->getMaterialRenderer(material.MaterialType);
5572 bool transparent = (rnd && rnd->isTransparent());
5573 // Render transparent on transparent pass and likewise.
5574 if(transparent == is_transparent_pass)
5577 This *shouldn't* hurt too much because Irrlicht
5578 doesn't change opengl textures if the old
5579 material is set again.
5581 driver->setMaterial(buf->getMaterial());
5582 driver->drawMeshBuffer(buf);
5583 vertex_count += buf->getVertexCount();
5587 } // foreach sectorblocks
5590 m_control.blocks_drawn = blocks_drawn;
5591 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5593 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5594 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5597 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5598 core::map<v3s16, MapBlock*> *affected_blocks)
5600 bool changed = false;
5602 Add it to all blocks touching it
5605 v3s16(0,0,0), // this
5606 v3s16(0,0,1), // back
5607 v3s16(0,1,0), // top
5608 v3s16(1,0,0), // right
5609 v3s16(0,0,-1), // front
5610 v3s16(0,-1,0), // bottom
5611 v3s16(-1,0,0), // left
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 // Relative position of requested node
5622 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5623 if(blockref->setTempMod(relpos, mod))
5628 if(changed && affected_blocks!=NULL)
5630 for(u16 i=0; i<7; i++)
5632 v3s16 p2 = p + dirs[i];
5633 // Block position of neighbor (or requested) node
5634 v3s16 blockpos = getNodeBlockPos(p2);
5635 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5636 if(blockref == NULL)
5638 affected_blocks->insert(blockpos, blockref);
5644 bool ClientMap::clearTempMod(v3s16 p,
5645 core::map<v3s16, MapBlock*> *affected_blocks)
5647 bool changed = false;
5649 v3s16(0,0,0), // this
5650 v3s16(0,0,1), // back
5651 v3s16(0,1,0), // top
5652 v3s16(1,0,0), // right
5653 v3s16(0,0,-1), // front
5654 v3s16(0,-1,0), // bottom
5655 v3s16(-1,0,0), // left
5657 for(u16 i=0; i<7; i++)
5659 v3s16 p2 = p + dirs[i];
5660 // Block position of neighbor (or requested) node
5661 v3s16 blockpos = getNodeBlockPos(p2);
5662 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5663 if(blockref == NULL)
5665 // Relative position of requested node
5666 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5667 if(blockref->clearTempMod(relpos))
5672 if(changed && affected_blocks!=NULL)
5674 for(u16 i=0; i<7; i++)
5676 v3s16 p2 = p + dirs[i];
5677 // Block position of neighbor (or requested) node
5678 v3s16 blockpos = getNodeBlockPos(p2);
5679 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5680 if(blockref == NULL)
5682 affected_blocks->insert(blockpos, blockref);
5688 void ClientMap::expireMeshes(bool only_daynight_diffed)
5690 TimeTaker timer("expireMeshes()");
5692 core::map<v2s16, MapSector*>::Iterator si;
5693 si = m_sectors.getIterator();
5694 for(; si.atEnd() == false; si++)
5696 MapSector *sector = si.getNode()->getValue();
5698 core::list< MapBlock * > sectorblocks;
5699 sector->getBlocks(sectorblocks);
5701 core::list< MapBlock * >::Iterator i;
5702 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5704 MapBlock *block = *i;
5706 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5712 JMutexAutoLock lock(block->mesh_mutex);
5713 if(block->mesh != NULL)
5715 /*block->mesh->drop();
5716 block->mesh = NULL;*/
5717 block->setMeshExpired(true);
5724 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5726 assert(mapType() == MAPTYPE_CLIENT);
5729 v3s16 p = blockpos + v3s16(0,0,0);
5730 MapBlock *b = getBlockNoCreate(p);
5731 b->updateMesh(daynight_ratio);
5732 //b->setMeshExpired(true);
5734 catch(InvalidPositionException &e){}
5737 v3s16 p = blockpos + v3s16(-1,0,0);
5738 MapBlock *b = getBlockNoCreate(p);
5739 b->updateMesh(daynight_ratio);
5740 //b->setMeshExpired(true);
5742 catch(InvalidPositionException &e){}
5744 v3s16 p = blockpos + v3s16(0,-1,0);
5745 MapBlock *b = getBlockNoCreate(p);
5746 b->updateMesh(daynight_ratio);
5747 //b->setMeshExpired(true);
5749 catch(InvalidPositionException &e){}
5751 v3s16 p = blockpos + v3s16(0,0,-1);
5752 MapBlock *b = getBlockNoCreate(p);
5753 b->updateMesh(daynight_ratio);
5754 //b->setMeshExpired(true);
5756 catch(InvalidPositionException &e){}
5761 Update mesh of block in which the node is, and if the node is at the
5762 leading edge, update the appropriate leading blocks too.
5764 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5772 v3s16 blockposes[4];
5773 for(u32 i=0; i<4; i++)
5775 v3s16 np = nodepos + dirs[i];
5776 blockposes[i] = getNodeBlockPos(np);
5777 // Don't update mesh of block if it has been done already
5778 bool already_updated = false;
5779 for(u32 j=0; j<i; j++)
5781 if(blockposes[j] == blockposes[i])
5783 already_updated = true;
5790 MapBlock *b = getBlockNoCreate(blockposes[i]);
5791 b->updateMesh(daynight_ratio);
5796 void ClientMap::PrintInfo(std::ostream &out)
5807 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5812 MapVoxelManipulator::~MapVoxelManipulator()
5814 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5818 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5820 TimeTaker timer1("emerge", &emerge_time);
5822 // Units of these are MapBlocks
5823 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5824 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5826 VoxelArea block_area_nodes
5827 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5829 addArea(block_area_nodes);
5831 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5832 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5833 for(s32 x=p_min.X; x<=p_max.X; x++)
5836 core::map<v3s16, bool>::Node *n;
5837 n = m_loaded_blocks.find(p);
5841 bool block_data_inexistent = false;
5844 TimeTaker timer1("emerge load", &emerge_load_time);
5846 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5847 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5850 dstream<<std::endl;*/
5852 MapBlock *block = m_map->getBlockNoCreate(p);
5853 if(block->isDummy())
5854 block_data_inexistent = true;
5856 block->copyTo(*this);
5858 catch(InvalidPositionException &e)
5860 block_data_inexistent = true;
5863 if(block_data_inexistent)
5865 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5866 // Fill with VOXELFLAG_INEXISTENT
5867 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5868 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5870 s32 i = m_area.index(a.MinEdge.X,y,z);
5871 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5875 m_loaded_blocks.insert(p, !block_data_inexistent);
5878 //dstream<<"emerge done"<<std::endl;
5882 SUGG: Add an option to only update eg. water and air nodes.
5883 This will make it interfere less with important stuff if
5886 void MapVoxelManipulator::blitBack
5887 (core::map<v3s16, MapBlock*> & modified_blocks)
5889 if(m_area.getExtent() == v3s16(0,0,0))
5892 //TimeTaker timer1("blitBack");
5894 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5895 <<m_loaded_blocks.size()<<std::endl;*/
5898 Initialize block cache
5900 v3s16 blockpos_last;
5901 MapBlock *block = NULL;
5902 bool block_checked_in_modified = false;
5904 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5905 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5906 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5910 u8 f = m_flags[m_area.index(p)];
5911 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5914 MapNode &n = m_data[m_area.index(p)];
5916 v3s16 blockpos = getNodeBlockPos(p);
5921 if(block == NULL || blockpos != blockpos_last){
5922 block = m_map->getBlockNoCreate(blockpos);
5923 blockpos_last = blockpos;
5924 block_checked_in_modified = false;
5927 // Calculate relative position in block
5928 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5930 // Don't continue if nothing has changed here
5931 if(block->getNode(relpos) == n)
5934 //m_map->setNode(m_area.MinEdge + p, n);
5935 block->setNode(relpos, n);
5938 Make sure block is in modified_blocks
5940 if(block_checked_in_modified == false)
5942 modified_blocks[blockpos] = block;
5943 block_checked_in_modified = true;
5946 catch(InvalidPositionException &e)
5952 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5953 MapVoxelManipulator(map)
5957 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5961 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5963 // Just create the area so that it can be pointed to
5964 VoxelManipulator::emerge(a, caller_id);
5967 void ManualMapVoxelManipulator::initialEmerge(
5968 v3s16 blockpos_min, v3s16 blockpos_max)
5970 TimeTaker timer1("initialEmerge", &emerge_time);
5972 // Units of these are MapBlocks
5973 v3s16 p_min = blockpos_min;
5974 v3s16 p_max = blockpos_max;
5976 VoxelArea block_area_nodes
5977 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5979 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5982 dstream<<"initialEmerge: area: ";
5983 block_area_nodes.print(dstream);
5984 dstream<<" ("<<size_MB<<"MB)";
5988 addArea(block_area_nodes);
5990 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5991 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5992 for(s32 x=p_min.X; x<=p_max.X; x++)
5995 core::map<v3s16, bool>::Node *n;
5996 n = m_loaded_blocks.find(p);
6000 bool block_data_inexistent = false;
6003 TimeTaker timer1("emerge load", &emerge_load_time);
6005 MapBlock *block = m_map->getBlockNoCreate(p);
6006 if(block->isDummy())
6007 block_data_inexistent = true;
6009 block->copyTo(*this);
6011 catch(InvalidPositionException &e)
6013 block_data_inexistent = true;
6016 if(block_data_inexistent)
6019 Mark area inexistent
6021 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6022 // Fill with VOXELFLAG_INEXISTENT
6023 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6024 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6026 s32 i = m_area.index(a.MinEdge.X,y,z);
6027 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6031 m_loaded_blocks.insert(p, !block_data_inexistent);
6035 void ManualMapVoxelManipulator::blitBackAll(
6036 core::map<v3s16, MapBlock*> * modified_blocks)
6038 if(m_area.getExtent() == v3s16(0,0,0))
6042 Copy data of all blocks
6044 for(core::map<v3s16, bool>::Iterator
6045 i = m_loaded_blocks.getIterator();
6046 i.atEnd() == false; i++)
6048 bool existed = i.getNode()->getValue();
6049 if(existed == false)
6051 v3s16 p = i.getNode()->getKey();
6052 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6055 dstream<<"WARNING: "<<__FUNCTION_NAME
6056 <<": got NULL block "
6057 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6062 block->copyFrom(*this);
6065 modified_blocks->insert(p, block);