3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
35 Map::Map(std::ostream &dout):
39 m_sector_mutex.Init();
40 assert(m_sector_mutex.IsInitialized());
48 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
49 for(; i.atEnd() == false; i++)
51 MapSector *sector = i.getNode()->getValue();
56 void Map::addEventReceiver(MapEventReceiver *event_receiver)
58 m_event_receivers.insert(event_receiver, false);
61 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
63 if(m_event_receivers.find(event_receiver) == NULL)
65 m_event_receivers.remove(event_receiver);
68 void Map::dispatchEvent(MapEditEvent *event)
70 for(core::map<MapEventReceiver*, bool>::Iterator
71 i = m_event_receivers.getIterator();
72 i.atEnd()==false; i++)
74 MapEventReceiver* event_receiver = i.getNode()->getKey();
75 event_receiver->onMapEditEvent(event);
79 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
81 if(m_sector_cache != NULL && p == m_sector_cache_p){
82 MapSector * sector = m_sector_cache;
83 // Reset inactivity timer
84 sector->usage_timer = 0.0;
88 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
93 MapSector *sector = n->getValue();
95 // Cache the last result
97 m_sector_cache = sector;
99 // Reset inactivity timer
100 sector->usage_timer = 0.0;
104 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
106 JMutexAutoLock lock(m_sector_mutex);
108 return getSectorNoGenerateNoExNoLock(p);
111 MapSector * Map::getSectorNoGenerate(v2s16 p)
113 MapSector *sector = getSectorNoGenerateNoEx(p);
115 throw InvalidPositionException();
120 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
122 v2s16 p2d(p3d.X, p3d.Z);
123 MapSector * sector = getSectorNoGenerate(p2d);
125 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
134 v2s16 p2d(p3d.X, p3d.Z);
135 MapSector * sector = getSectorNoGenerate(p2d);
136 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
139 catch(InvalidPositionException &e)
145 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorCreate(p2d);
150 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
153 block = sector->createBlankBlock(p3d.Y);
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
171 Goes recursively through the neighbours of the node.
173 Alters only transparent nodes.
175 If the lighting of the neighbour is lower than the lighting of
176 the node was (before changing it to 0 at the step before), the
177 lighting of the neighbour is set to 0 and then the same stuff
178 repeats for the neighbour.
180 The ending nodes of the routine are stored in light_sources.
181 This is useful when a light is removed. In such case, this
182 routine can be called for the light node and then again for
183 light_sources to re-light the area without the removed light.
185 values of from_nodes are lighting values.
187 void Map::unspreadLight(enum LightBank bank,
188 core::map<v3s16, u8> & from_nodes,
189 core::map<v3s16, bool> & light_sources,
190 core::map<v3s16, MapBlock*> & modified_blocks)
193 v3s16(0,0,1), // back
195 v3s16(1,0,0), // right
196 v3s16(0,0,-1), // front
197 v3s16(0,-1,0), // bottom
198 v3s16(-1,0,0), // left
201 if(from_nodes.size() == 0)
204 u32 blockchangecount = 0;
206 core::map<v3s16, u8> unlighted_nodes;
207 core::map<v3s16, u8>::Iterator j;
208 j = from_nodes.getIterator();
211 Initialize block cache
214 MapBlock *block = NULL;
215 // Cache this a bit, too
216 bool block_checked_in_modified = false;
218 for(; j.atEnd() == false; j++)
220 v3s16 pos = j.getNode()->getKey();
221 v3s16 blockpos = getNodeBlockPos(pos);
223 // Only fetch a new block if the block position has changed
225 if(block == NULL || blockpos != blockpos_last){
226 block = getBlockNoCreate(blockpos);
227 blockpos_last = blockpos;
229 block_checked_in_modified = false;
233 catch(InvalidPositionException &e)
241 // Calculate relative position in block
242 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
244 // Get node straight from the block
245 MapNode n = block->getNode(relpos);
247 u8 oldlight = j.getNode()->getValue();
249 // Loop through 6 neighbors
250 for(u16 i=0; i<6; i++)
252 // Get the position of the neighbor node
253 v3s16 n2pos = pos + dirs[i];
255 // Get the block where the node is located
256 v3s16 blockpos = getNodeBlockPos(n2pos);
260 // Only fetch a new block if the block position has changed
262 if(block == NULL || blockpos != blockpos_last){
263 block = getBlockNoCreate(blockpos);
264 blockpos_last = blockpos;
266 block_checked_in_modified = false;
270 catch(InvalidPositionException &e)
275 // Calculate relative position in block
276 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
277 // Get node straight from the block
278 MapNode n2 = block->getNode(relpos);
280 bool changed = false;
282 //TODO: Optimize output by optimizing light_sources?
285 If the neighbor is dimmer than what was specified
286 as oldlight (the light of the previous node)
288 if(n2.getLight(bank) < oldlight)
291 And the neighbor is transparent and it has some light
293 if(n2.light_propagates() && n2.getLight(bank) != 0)
296 Set light to 0 and add to queue
299 u8 current_light = n2.getLight(bank);
300 n2.setLight(bank, 0);
301 block->setNode(relpos, n2);
303 unlighted_nodes.insert(n2pos, current_light);
307 Remove from light_sources if it is there
308 NOTE: This doesn't happen nearly at all
310 /*if(light_sources.find(n2pos))
312 std::cout<<"Removed from light_sources"<<std::endl;
313 light_sources.remove(n2pos);
318 if(light_sources.find(n2pos) != NULL)
319 light_sources.remove(n2pos);*/
322 light_sources.insert(n2pos, true);
325 // Add to modified_blocks
326 if(changed == true && block_checked_in_modified == false)
328 // If the block is not found in modified_blocks, add.
329 if(modified_blocks.find(blockpos) == NULL)
331 modified_blocks.insert(blockpos, block);
333 block_checked_in_modified = true;
336 catch(InvalidPositionException &e)
343 /*dstream<<"unspreadLight(): Changed block "
344 <<blockchangecount<<" times"
345 <<" for "<<from_nodes.size()<<" nodes"
348 if(unlighted_nodes.size() > 0)
349 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
353 A single-node wrapper of the above
355 void Map::unLightNeighbors(enum LightBank bank,
356 v3s16 pos, u8 lightwas,
357 core::map<v3s16, bool> & light_sources,
358 core::map<v3s16, MapBlock*> & modified_blocks)
360 core::map<v3s16, u8> from_nodes;
361 from_nodes.insert(pos, lightwas);
363 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
367 Lights neighbors of from_nodes, collects all them and then
370 void Map::spreadLight(enum LightBank bank,
371 core::map<v3s16, bool> & from_nodes,
372 core::map<v3s16, MapBlock*> & modified_blocks)
374 const v3s16 dirs[6] = {
375 v3s16(0,0,1), // back
377 v3s16(1,0,0), // right
378 v3s16(0,0,-1), // front
379 v3s16(0,-1,0), // bottom
380 v3s16(-1,0,0), // left
383 if(from_nodes.size() == 0)
386 u32 blockchangecount = 0;
388 core::map<v3s16, bool> lighted_nodes;
389 core::map<v3s16, bool>::Iterator j;
390 j = from_nodes.getIterator();
393 Initialize block cache
396 MapBlock *block = NULL;
397 // Cache this a bit, too
398 bool block_checked_in_modified = false;
400 for(; j.atEnd() == false; j++)
401 //for(; j != from_nodes.end(); j++)
403 v3s16 pos = j.getNode()->getKey();
405 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
406 v3s16 blockpos = getNodeBlockPos(pos);
408 // Only fetch a new block if the block position has changed
410 if(block == NULL || blockpos != blockpos_last){
411 block = getBlockNoCreate(blockpos);
412 blockpos_last = blockpos;
414 block_checked_in_modified = false;
418 catch(InvalidPositionException &e)
426 // Calculate relative position in block
427 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
429 // Get node straight from the block
430 MapNode n = block->getNode(relpos);
432 u8 oldlight = n.getLight(bank);
433 u8 newlight = diminish_light(oldlight);
435 // Loop through 6 neighbors
436 for(u16 i=0; i<6; i++){
437 // Get the position of the neighbor node
438 v3s16 n2pos = pos + dirs[i];
440 // Get the block where the node is located
441 v3s16 blockpos = getNodeBlockPos(n2pos);
445 // Only fetch a new block if the block position has changed
447 if(block == NULL || blockpos != blockpos_last){
448 block = getBlockNoCreate(blockpos);
449 blockpos_last = blockpos;
451 block_checked_in_modified = false;
455 catch(InvalidPositionException &e)
460 // Calculate relative position in block
461 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
462 // Get node straight from the block
463 MapNode n2 = block->getNode(relpos);
465 bool changed = false;
467 If the neighbor is brighter than the current node,
468 add to list (it will light up this node on its turn)
470 if(n2.getLight(bank) > undiminish_light(oldlight))
472 lighted_nodes.insert(n2pos, true);
473 //lighted_nodes.push_back(n2pos);
477 If the neighbor is dimmer than how much light this node
478 would spread on it, add to list
480 if(n2.getLight(bank) < newlight)
482 if(n2.light_propagates())
484 n2.setLight(bank, newlight);
485 block->setNode(relpos, n2);
486 lighted_nodes.insert(n2pos, true);
487 //lighted_nodes.push_back(n2pos);
492 // Add to modified_blocks
493 if(changed == true && block_checked_in_modified == false)
495 // If the block is not found in modified_blocks, add.
496 if(modified_blocks.find(blockpos) == NULL)
498 modified_blocks.insert(blockpos, block);
500 block_checked_in_modified = true;
503 catch(InvalidPositionException &e)
510 /*dstream<<"spreadLight(): Changed block "
511 <<blockchangecount<<" times"
512 <<" for "<<from_nodes.size()<<" nodes"
515 if(lighted_nodes.size() > 0)
516 spreadLight(bank, lighted_nodes, modified_blocks);
520 A single-node source variation of the above.
522 void Map::lightNeighbors(enum LightBank bank,
524 core::map<v3s16, MapBlock*> & modified_blocks)
526 core::map<v3s16, bool> from_nodes;
527 from_nodes.insert(pos, true);
528 spreadLight(bank, from_nodes, modified_blocks);
531 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
534 v3s16(0,0,1), // back
536 v3s16(1,0,0), // right
537 v3s16(0,0,-1), // front
538 v3s16(0,-1,0), // bottom
539 v3s16(-1,0,0), // left
542 u8 brightest_light = 0;
543 v3s16 brightest_pos(0,0,0);
544 bool found_something = false;
546 // Loop through 6 neighbors
547 for(u16 i=0; i<6; i++){
548 // Get the position of the neighbor node
549 v3s16 n2pos = p + dirs[i];
554 catch(InvalidPositionException &e)
558 if(n2.getLight(bank) > brightest_light || found_something == false){
559 brightest_light = n2.getLight(bank);
560 brightest_pos = n2pos;
561 found_something = true;
565 if(found_something == false)
566 throw InvalidPositionException();
568 return brightest_pos;
572 Propagates sunlight down from a node.
573 Starting point gets sunlight.
575 Returns the lowest y value of where the sunlight went.
577 Mud is turned into grass in where the sunlight stops.
579 s16 Map::propagateSunlight(v3s16 start,
580 core::map<v3s16, MapBlock*> & modified_blocks)
585 v3s16 pos(start.X, y, start.Z);
587 v3s16 blockpos = getNodeBlockPos(pos);
590 block = getBlockNoCreate(blockpos);
592 catch(InvalidPositionException &e)
597 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
598 MapNode n = block->getNode(relpos);
600 if(n.sunlight_propagates())
602 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
603 block->setNode(relpos, n);
605 modified_blocks.insert(blockpos, block);
609 // Turn mud into grass
610 if(n.d == CONTENT_MUD)
613 block->setNode(relpos, n);
614 modified_blocks.insert(blockpos, block);
617 // Sunlight goes no further
624 void Map::updateLighting(enum LightBank bank,
625 core::map<v3s16, MapBlock*> & a_blocks,
626 core::map<v3s16, MapBlock*> & modified_blocks)
628 /*m_dout<<DTIME<<"Map::updateLighting(): "
629 <<a_blocks.size()<<" blocks."<<std::endl;*/
631 //TimeTaker timer("updateLighting");
635 //u32 count_was = modified_blocks.size();
637 core::map<v3s16, MapBlock*> blocks_to_update;
639 core::map<v3s16, bool> light_sources;
641 core::map<v3s16, u8> unlight_from;
643 core::map<v3s16, MapBlock*>::Iterator i;
644 i = a_blocks.getIterator();
645 for(; i.atEnd() == false; i++)
647 MapBlock *block = i.getNode()->getValue();
651 // Don't bother with dummy blocks.
655 v3s16 pos = block->getPos();
656 modified_blocks.insert(pos, block);
658 blocks_to_update.insert(pos, block);
661 Clear all light from block
663 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
664 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
665 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
670 MapNode n = block->getNode(v3s16(x,y,z));
671 u8 oldlight = n.getLight(bank);
673 block->setNode(v3s16(x,y,z), n);
675 // Collect borders for unlighting
676 if(x==0 || x == MAP_BLOCKSIZE-1
677 || y==0 || y == MAP_BLOCKSIZE-1
678 || z==0 || z == MAP_BLOCKSIZE-1)
680 v3s16 p_map = p + v3s16(
683 MAP_BLOCKSIZE*pos.Z);
684 unlight_from.insert(p_map, oldlight);
687 catch(InvalidPositionException &e)
690 This would happen when dealing with a
694 dstream<<"updateLighting(): InvalidPositionException"
699 if(bank == LIGHTBANK_DAY)
701 bool bottom_valid = block->propagateSunlight(light_sources);
703 // If bottom is valid, we're done.
707 else if(bank == LIGHTBANK_NIGHT)
709 // For night lighting, sunlight is not propagated
714 // Invalid lighting bank
718 /*dstream<<"Bottom for sunlight-propagated block ("
719 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
722 // Bottom sunlight is not valid; get the block and loop to it
726 block = getBlockNoCreate(pos);
728 catch(InvalidPositionException &e)
738 TimeTaker timer("unspreadLight");
739 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
744 u32 diff = modified_blocks.size() - count_was;
745 count_was = modified_blocks.size();
746 dstream<<"unspreadLight modified "<<diff<<std::endl;
750 TimeTaker timer("spreadLight");
751 spreadLight(bank, light_sources, modified_blocks);
756 u32 diff = modified_blocks.size() - count_was;
757 count_was = modified_blocks.size();
758 dstream<<"spreadLight modified "<<diff<<std::endl;
763 //MapVoxelManipulator vmanip(this);
765 // Make a manual voxel manipulator and load all the blocks
766 // that touch the requested blocks
767 ManualMapVoxelManipulator vmanip(this);
768 core::map<v3s16, MapBlock*>::Iterator i;
769 i = blocks_to_update.getIterator();
770 for(; i.atEnd() == false; i++)
772 MapBlock *block = i.getNode()->getValue();
773 v3s16 p = block->getPos();
775 // Add all surrounding blocks
776 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
779 Add all surrounding blocks that have up-to-date lighting
780 NOTE: This doesn't quite do the job (not everything
781 appropriate is lighted)
783 /*for(s16 z=-1; z<=1; z++)
784 for(s16 y=-1; y<=1; y++)
785 for(s16 x=-1; x<=1; x++)
788 MapBlock *block = getBlockNoCreateNoEx(p);
793 if(block->getLightingExpired())
795 vmanip.initialEmerge(p, p);
798 // Lighting of block will be updated completely
799 block->setLightingExpired(false);
803 //TimeTaker timer("unSpreadLight");
804 vmanip.unspreadLight(bank, unlight_from, light_sources);
807 //TimeTaker timer("spreadLight");
808 vmanip.spreadLight(bank, light_sources);
811 //TimeTaker timer("blitBack");
812 vmanip.blitBack(modified_blocks);
814 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
818 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
821 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
822 core::map<v3s16, MapBlock*> & modified_blocks)
824 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
825 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
828 Update information about whether day and night light differ
830 for(core::map<v3s16, MapBlock*>::Iterator
831 i = modified_blocks.getIterator();
832 i.atEnd() == false; i++)
834 MapBlock *block = i.getNode()->getValue();
835 block->updateDayNightDiff();
840 This is called after changing a node from transparent to opaque.
841 The lighting value of the node should be left as-is after changing
842 other values. This sets the lighting value to 0.
844 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
845 core::map<v3s16, MapBlock*> &modified_blocks)
848 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
849 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
852 From this node to nodes underneath:
853 If lighting is sunlight (1.0), unlight neighbours and
858 v3s16 toppos = p + v3s16(0,1,0);
859 v3s16 bottompos = p + v3s16(0,-1,0);
861 bool node_under_sunlight = true;
862 core::map<v3s16, bool> light_sources;
865 If there is a node at top and it doesn't have sunlight,
866 there has not been any sunlight going down.
868 Otherwise there probably is.
871 MapNode topnode = getNode(toppos);
873 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
874 node_under_sunlight = false;
876 catch(InvalidPositionException &e)
881 If the new node doesn't propagate sunlight and there is
882 grass below, change it to mud
884 if(content_features(n.d).sunlight_propagates == false)
887 MapNode bottomnode = getNode(bottompos);
889 if(bottomnode.d == CONTENT_GRASS
890 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
892 bottomnode.d = CONTENT_MUD;
893 setNode(bottompos, bottomnode);
896 catch(InvalidPositionException &e)
902 If the new node is mud and it is under sunlight, change it
905 if(n.d == CONTENT_MUD && node_under_sunlight)
911 Remove all light that has come out of this node
914 enum LightBank banks[] =
919 for(s32 i=0; i<2; i++)
921 enum LightBank bank = banks[i];
923 u8 lightwas = getNode(p).getLight(bank);
925 // Add the block of the added node to modified_blocks
926 v3s16 blockpos = getNodeBlockPos(p);
927 MapBlock * block = getBlockNoCreate(blockpos);
928 assert(block != NULL);
929 modified_blocks.insert(blockpos, block);
931 assert(isValidPosition(p));
933 // Unlight neighbours of node.
934 // This means setting light of all consequent dimmer nodes
936 // This also collects the nodes at the border which will spread
937 // light again into this.
938 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
944 If node lets sunlight through and is under sunlight, it has
947 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
949 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
953 Set the node on the map
962 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
965 NodeMetadata *meta = meta_proto->clone();
966 setNodeMetadata(p, meta);
970 If node is under sunlight and doesn't let sunlight through,
971 take all sunlighted nodes under it and clear light from them
972 and from where the light has been spread.
973 TODO: This could be optimized by mass-unlighting instead
976 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
980 //m_dout<<DTIME<<"y="<<y<<std::endl;
981 v3s16 n2pos(p.X, y, p.Z);
987 catch(InvalidPositionException &e)
992 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
994 unLightNeighbors(LIGHTBANK_DAY,
995 n2pos, n2.getLight(LIGHTBANK_DAY),
996 light_sources, modified_blocks);
997 n2.setLight(LIGHTBANK_DAY, 0);
1005 for(s32 i=0; i<2; i++)
1007 enum LightBank bank = banks[i];
1010 Spread light from all nodes that might be capable of doing so
1012 spreadLight(bank, light_sources, modified_blocks);
1016 Update information about whether day and night light differ
1018 for(core::map<v3s16, MapBlock*>::Iterator
1019 i = modified_blocks.getIterator();
1020 i.atEnd() == false; i++)
1022 MapBlock *block = i.getNode()->getValue();
1023 block->updateDayNightDiff();
1027 Add neighboring liquid nodes and the node itself if it is
1028 liquid (=water node was added) to transform queue.
1031 v3s16(0,0,0), // self
1032 v3s16(0,0,1), // back
1033 v3s16(0,1,0), // top
1034 v3s16(1,0,0), // right
1035 v3s16(0,0,-1), // front
1036 v3s16(0,-1,0), // bottom
1037 v3s16(-1,0,0), // left
1039 for(u16 i=0; i<7; i++)
1044 v3s16 p2 = p + dirs[i];
1046 MapNode n2 = getNode(p2);
1047 if(content_liquid(n2.d))
1049 m_transforming_liquid.push_back(p2);
1052 }catch(InvalidPositionException &e)
1060 void Map::removeNodeAndUpdate(v3s16 p,
1061 core::map<v3s16, MapBlock*> &modified_blocks)
1063 /*PrintInfo(m_dout);
1064 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1065 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1067 bool node_under_sunlight = true;
1069 v3s16 toppos = p + v3s16(0,1,0);
1071 // Node will be replaced with this
1072 u8 replace_material = CONTENT_AIR;
1075 If there is a node at top and it doesn't have sunlight,
1076 there will be no sunlight going down.
1079 MapNode topnode = getNode(toppos);
1081 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1082 node_under_sunlight = false;
1084 catch(InvalidPositionException &e)
1088 core::map<v3s16, bool> light_sources;
1090 enum LightBank banks[] =
1095 for(s32 i=0; i<2; i++)
1097 enum LightBank bank = banks[i];
1100 Unlight neighbors (in case the node is a light source)
1102 unLightNeighbors(bank, p,
1103 getNode(p).getLight(bank),
1104 light_sources, modified_blocks);
1108 Remove node metadata
1111 removeNodeMetadata(p);
1115 This also clears the lighting.
1119 n.d = replace_material;
1122 for(s32 i=0; i<2; i++)
1124 enum LightBank bank = banks[i];
1127 Recalculate lighting
1129 spreadLight(bank, light_sources, modified_blocks);
1132 // Add the block of the removed node to modified_blocks
1133 v3s16 blockpos = getNodeBlockPos(p);
1134 MapBlock * block = getBlockNoCreate(blockpos);
1135 assert(block != NULL);
1136 modified_blocks.insert(blockpos, block);
1139 If the removed node was under sunlight, propagate the
1140 sunlight down from it and then light all neighbors
1141 of the propagated blocks.
1143 if(node_under_sunlight)
1145 s16 ybottom = propagateSunlight(p, modified_blocks);
1146 /*m_dout<<DTIME<<"Node was under sunlight. "
1147 "Propagating sunlight";
1148 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1150 for(; y >= ybottom; y--)
1152 v3s16 p2(p.X, y, p.Z);
1153 /*m_dout<<DTIME<<"lighting neighbors of node ("
1154 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1156 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1161 // Set the lighting of this node to 0
1162 // TODO: Is this needed? Lighting is cleared up there already.
1164 MapNode n = getNode(p);
1165 n.setLight(LIGHTBANK_DAY, 0);
1168 catch(InvalidPositionException &e)
1174 for(s32 i=0; i<2; i++)
1176 enum LightBank bank = banks[i];
1178 // Get the brightest neighbour node and propagate light from it
1179 v3s16 n2p = getBrightestNeighbour(bank, p);
1181 MapNode n2 = getNode(n2p);
1182 lightNeighbors(bank, n2p, modified_blocks);
1184 catch(InvalidPositionException &e)
1190 Update information about whether day and night light differ
1192 for(core::map<v3s16, MapBlock*>::Iterator
1193 i = modified_blocks.getIterator();
1194 i.atEnd() == false; i++)
1196 MapBlock *block = i.getNode()->getValue();
1197 block->updateDayNightDiff();
1201 Add neighboring liquid nodes to transform queue.
1204 v3s16(0,0,1), // back
1205 v3s16(0,1,0), // top
1206 v3s16(1,0,0), // right
1207 v3s16(0,0,-1), // front
1208 v3s16(0,-1,0), // bottom
1209 v3s16(-1,0,0), // left
1211 for(u16 i=0; i<6; i++)
1216 v3s16 p2 = p + dirs[i];
1218 MapNode n2 = getNode(p2);
1219 if(content_liquid(n2.d))
1221 m_transforming_liquid.push_back(p2);
1224 }catch(InvalidPositionException &e)
1230 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1233 event.type = MEET_ADDNODE;
1237 bool succeeded = true;
1239 core::map<v3s16, MapBlock*> modified_blocks;
1240 addNodeAndUpdate(p, n, modified_blocks);
1242 // Copy modified_blocks to event
1243 for(core::map<v3s16, MapBlock*>::Iterator
1244 i = modified_blocks.getIterator();
1245 i.atEnd()==false; i++)
1247 event.modified_blocks.insert(i.getNode()->getKey(), false);
1250 catch(InvalidPositionException &e){
1254 dispatchEvent(&event);
1259 bool Map::removeNodeWithEvent(v3s16 p)
1262 event.type = MEET_REMOVENODE;
1265 bool succeeded = true;
1267 core::map<v3s16, MapBlock*> modified_blocks;
1268 removeNodeAndUpdate(p, modified_blocks);
1270 // Copy modified_blocks to event
1271 for(core::map<v3s16, MapBlock*>::Iterator
1272 i = modified_blocks.getIterator();
1273 i.atEnd()==false; i++)
1275 event.modified_blocks.insert(i.getNode()->getKey(), false);
1278 catch(InvalidPositionException &e){
1282 dispatchEvent(&event);
1287 bool Map::dayNightDiffed(v3s16 blockpos)
1290 v3s16 p = blockpos + v3s16(0,0,0);
1291 MapBlock *b = getBlockNoCreate(p);
1292 if(b->dayNightDiffed())
1295 catch(InvalidPositionException &e){}
1298 v3s16 p = blockpos + v3s16(-1,0,0);
1299 MapBlock *b = getBlockNoCreate(p);
1300 if(b->dayNightDiffed())
1303 catch(InvalidPositionException &e){}
1305 v3s16 p = blockpos + v3s16(0,-1,0);
1306 MapBlock *b = getBlockNoCreate(p);
1307 if(b->dayNightDiffed())
1310 catch(InvalidPositionException &e){}
1312 v3s16 p = blockpos + v3s16(0,0,-1);
1313 MapBlock *b = getBlockNoCreate(p);
1314 if(b->dayNightDiffed())
1317 catch(InvalidPositionException &e){}
1320 v3s16 p = blockpos + v3s16(1,0,0);
1321 MapBlock *b = getBlockNoCreate(p);
1322 if(b->dayNightDiffed())
1325 catch(InvalidPositionException &e){}
1327 v3s16 p = blockpos + v3s16(0,1,0);
1328 MapBlock *b = getBlockNoCreate(p);
1329 if(b->dayNightDiffed())
1332 catch(InvalidPositionException &e){}
1334 v3s16 p = blockpos + v3s16(0,0,1);
1335 MapBlock *b = getBlockNoCreate(p);
1336 if(b->dayNightDiffed())
1339 catch(InvalidPositionException &e){}
1345 Updates usage timers
1347 void Map::timerUpdate(float dtime)
1349 JMutexAutoLock lock(m_sector_mutex);
1351 core::map<v2s16, MapSector*>::Iterator si;
1353 si = m_sectors.getIterator();
1354 for(; si.atEnd() == false; si++)
1356 MapSector *sector = si.getNode()->getValue();
1357 sector->usage_timer += dtime;
1361 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1364 Wait for caches to be removed before continuing.
1366 This disables the existence of caches while locked
1368 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1370 core::list<v2s16>::Iterator j;
1371 for(j=list.begin(); j!=list.end(); j++)
1373 MapSector *sector = m_sectors[*j];
1376 sector->deleteBlocks();
1381 If sector is in sector cache, remove it from there
1383 if(m_sector_cache == sector)
1385 m_sector_cache = NULL;
1388 Remove from map and delete
1390 m_sectors.remove(*j);
1396 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1397 core::list<v3s16> *deleted_blocks)
1399 JMutexAutoLock lock(m_sector_mutex);
1401 core::list<v2s16> sector_deletion_queue;
1402 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1403 for(; i.atEnd() == false; i++)
1405 MapSector *sector = i.getNode()->getValue();
1407 Delete sector from memory if it hasn't been used in a long time
1409 if(sector->usage_timer > timeout)
1411 sector_deletion_queue.push_back(i.getNode()->getKey());
1413 if(deleted_blocks != NULL)
1415 // Collect positions of blocks of sector
1416 MapSector *sector = i.getNode()->getValue();
1417 core::list<MapBlock*> blocks;
1418 sector->getBlocks(blocks);
1419 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1420 i != blocks.end(); i++)
1422 deleted_blocks->push_back((*i)->getPos());
1427 deleteSectors(sector_deletion_queue, only_blocks);
1428 return sector_deletion_queue.getSize();
1431 void Map::PrintInfo(std::ostream &out)
1436 #define WATER_DROP_BOOST 4
1438 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1440 DSTACK(__FUNCTION_NAME);
1441 //TimeTaker timer("transformLiquids()");
1444 u32 initial_size = m_transforming_liquid.size();
1446 /*if(initial_size != 0)
1447 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1449 while(m_transforming_liquid.size() != 0)
1452 Get a queued transforming liquid node
1454 v3s16 p0 = m_transforming_liquid.pop_front();
1456 MapNode n0 = getNode(p0);
1458 // Don't deal with non-liquids
1459 if(content_liquid(n0.d) == false)
1462 bool is_source = !content_flowing_liquid(n0.d);
1464 u8 liquid_level = 8;
1465 if(is_source == false)
1466 liquid_level = n0.param2 & 0x0f;
1468 // Turn possible source into non-source
1469 u8 nonsource_c = make_liquid_flowing(n0.d);
1472 If not source, check that some node flows into this one
1473 and what is the level of liquid in this one
1475 if(is_source == false)
1477 s8 new_liquid_level_max = -1;
1479 v3s16 dirs_from[5] = {
1480 v3s16(0,1,0), // top
1481 v3s16(0,0,1), // back
1482 v3s16(1,0,0), // right
1483 v3s16(0,0,-1), // front
1484 v3s16(-1,0,0), // left
1486 for(u16 i=0; i<5; i++)
1491 bool from_top = (i==0);
1493 v3s16 p2 = p0 + dirs_from[i];
1494 MapNode n2 = getNode(p2);
1496 if(content_liquid(n2.d))
1498 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1499 // Check that the liquids are the same type
1500 if(n2_nonsource_c != nonsource_c)
1502 dstream<<"WARNING: Not handling: different liquids"
1503 " collide"<<std::endl;
1506 bool n2_is_source = !content_flowing_liquid(n2.d);
1507 s8 n2_liquid_level = 8;
1508 if(n2_is_source == false)
1509 n2_liquid_level = n2.param2 & 0x07;
1511 s8 new_liquid_level = -1;
1514 //new_liquid_level = 7;
1515 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1516 new_liquid_level = 7;
1518 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1520 else if(n2_liquid_level > 0)
1522 new_liquid_level = n2_liquid_level - 1;
1525 if(new_liquid_level > new_liquid_level_max)
1526 new_liquid_level_max = new_liquid_level;
1529 }catch(InvalidPositionException &e)
1535 If liquid level should be something else, update it and
1536 add all the neighboring water nodes to the transform queue.
1538 if(new_liquid_level_max != liquid_level)
1540 if(new_liquid_level_max == -1)
1542 // Remove water alltoghether
1549 n0.param2 = new_liquid_level_max;
1553 // Block has been modified
1555 v3s16 blockpos = getNodeBlockPos(p0);
1556 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1558 modified_blocks.insert(blockpos, block);
1562 Add neighboring non-source liquid nodes to transform queue.
1565 v3s16(0,0,1), // back
1566 v3s16(0,1,0), // top
1567 v3s16(1,0,0), // right
1568 v3s16(0,0,-1), // front
1569 v3s16(0,-1,0), // bottom
1570 v3s16(-1,0,0), // left
1572 for(u16 i=0; i<6; i++)
1577 v3s16 p2 = p0 + dirs[i];
1579 MapNode n2 = getNode(p2);
1580 if(content_flowing_liquid(n2.d))
1582 m_transforming_liquid.push_back(p2);
1585 }catch(InvalidPositionException &e)
1592 // Get a new one from queue if the node has turned into non-water
1593 if(content_liquid(n0.d) == false)
1597 Flow water from this node
1599 v3s16 dirs_to[5] = {
1600 v3s16(0,-1,0), // bottom
1601 v3s16(0,0,1), // back
1602 v3s16(1,0,0), // right
1603 v3s16(0,0,-1), // front
1604 v3s16(-1,0,0), // left
1606 for(u16 i=0; i<5; i++)
1611 bool to_bottom = (i == 0);
1613 // If liquid is at lowest possible height, it's not going
1614 // anywhere except down
1615 if(liquid_level == 0 && to_bottom == false)
1618 u8 liquid_next_level = 0;
1619 // If going to bottom
1622 //liquid_next_level = 7;
1623 if(liquid_level >= 7 - WATER_DROP_BOOST)
1624 liquid_next_level = 7;
1626 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1629 liquid_next_level = liquid_level - 1;
1631 bool n2_changed = false;
1632 bool flowed = false;
1634 v3s16 p2 = p0 + dirs_to[i];
1636 MapNode n2 = getNode(p2);
1637 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1639 if(content_liquid(n2.d))
1641 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1642 // Check that the liquids are the same type
1643 if(n2_nonsource_c != nonsource_c)
1645 dstream<<"WARNING: Not handling: different liquids"
1646 " collide"<<std::endl;
1649 bool n2_is_source = !content_flowing_liquid(n2.d);
1650 u8 n2_liquid_level = 8;
1651 if(n2_is_source == false)
1652 n2_liquid_level = n2.param2 & 0x07;
1661 // Just flow into the source, nothing changes.
1662 // n2_changed is not set because destination didn't change
1667 if(liquid_next_level > liquid_level)
1669 n2.param2 = liquid_next_level;
1677 else if(n2.d == CONTENT_AIR)
1680 n2.param2 = liquid_next_level;
1687 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1691 m_transforming_liquid.push_back(p2);
1693 v3s16 blockpos = getNodeBlockPos(p2);
1694 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1696 modified_blocks.insert(blockpos, block);
1699 // If n2_changed to bottom, don't flow anywhere else
1700 if(to_bottom && flowed && !is_source)
1703 }catch(InvalidPositionException &e)
1709 //if(loopcount >= 100000)
1710 if(loopcount >= initial_size * 1)
1713 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1716 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1718 v3s16 blockpos = getNodeBlockPos(p);
1719 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1720 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1723 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1727 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1731 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1733 v3s16 blockpos = getNodeBlockPos(p);
1734 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1735 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1738 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1742 block->m_node_metadata.set(p_rel, meta);
1745 void Map::removeNodeMetadata(v3s16 p)
1747 v3s16 blockpos = getNodeBlockPos(p);
1748 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1749 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1752 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1756 block->m_node_metadata.remove(p_rel);
1759 void Map::nodeMetadataStep(float dtime,
1760 core::map<v3s16, MapBlock*> &changed_blocks)
1764 Currently there is no way to ensure that all the necessary
1765 blocks are loaded when this is run. (They might get unloaded)
1766 NOTE: ^- Actually, that might not be so. In a quick test it
1767 reloaded a block with a furnace when I walked back to it from
1770 core::map<v2s16, MapSector*>::Iterator si;
1771 si = m_sectors.getIterator();
1772 for(; si.atEnd() == false; si++)
1774 MapSector *sector = si.getNode()->getValue();
1775 core::list< MapBlock * > sectorblocks;
1776 sector->getBlocks(sectorblocks);
1777 core::list< MapBlock * >::Iterator i;
1778 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1780 MapBlock *block = *i;
1781 bool changed = block->m_node_metadata.step(dtime);
1783 changed_blocks[block->getPos()] = block;
1792 ServerMap::ServerMap(std::string savedir):
1798 //m_chunksize = 16; // Too slow
1799 m_chunksize = 8; // Takes a few seconds
1803 // TODO: Save to and load from a file
1804 m_seed = (((u64)(myrand()%0xffff)<<0)
1805 + ((u64)(myrand()%0xffff)<<16)
1806 + ((u64)(myrand()%0xffff)<<32)
1807 + ((u64)(myrand()%0xffff)<<48));
1810 Experimental and debug stuff
1817 Try to load map; if not found, create a new one.
1820 m_savedir = savedir;
1821 m_map_saving_enabled = false;
1825 // If directory exists, check contents and load if possible
1826 if(fs::PathExists(m_savedir))
1828 // If directory is empty, it is safe to save into it.
1829 if(fs::GetDirListing(m_savedir).size() == 0)
1831 dstream<<DTIME<<"Server: Empty save directory is valid."
1833 m_map_saving_enabled = true;
1837 // Load map metadata (seed, chunksize)
1840 // Load chunk metadata
1843 /*// Load sector (0,0) and throw and exception on fail
1844 if(loadSectorFull(v2s16(0,0)) == false)
1845 throw LoadError("Failed to load sector (0,0)");*/
1847 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1848 "metadata and sector (0,0) from "<<savedir<<
1849 ", assuming valid save directory."
1852 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1853 <<"and chunk metadata from "<<savedir
1854 <<", assuming valid save directory."
1857 m_map_saving_enabled = true;
1858 // Map loaded, not creating new one
1862 // If directory doesn't exist, it is safe to save to it
1864 m_map_saving_enabled = true;
1867 catch(std::exception &e)
1869 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1870 <<", exception: "<<e.what()<<std::endl;
1871 dstream<<"Please remove the map or fix it."<<std::endl;
1872 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1875 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1877 // Create zero sector
1878 emergeSector(v2s16(0,0));
1880 // Initially write whole map
1884 ServerMap::~ServerMap()
1888 if(m_map_saving_enabled)
1891 // Save only changed parts
1893 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1897 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1900 catch(std::exception &e)
1902 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1903 <<", exception: "<<e.what()<<std::endl;
1909 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1910 for(; i.atEnd() == false; i++)
1912 MapChunk *chunk = i.getNode()->getValue();
1918 Some helper functions for the map generator
1921 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1923 v3s16 em = vmanip.m_area.getExtent();
1924 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1925 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1926 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1928 for(y=y_nodes_max; y>=y_nodes_min; y--)
1930 MapNode &n = vmanip.m_data[i];
1931 if(content_walkable(n.d))
1934 vmanip.m_area.add_y(em, i, -1);
1936 if(y >= y_nodes_min)
1942 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1944 v3s16 em = vmanip.m_area.getExtent();
1945 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1946 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1947 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1949 for(y=y_nodes_max; y>=y_nodes_min; y--)
1951 MapNode &n = vmanip.m_data[i];
1952 if(content_walkable(n.d)
1953 && n.d != CONTENT_TREE
1954 && n.d != CONTENT_LEAVES)
1957 vmanip.m_area.add_y(em, i, -1);
1959 if(y >= y_nodes_min)
1965 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1967 MapNode treenode(CONTENT_TREE);
1968 MapNode leavesnode(CONTENT_LEAVES);
1970 s16 trunk_h = myrand_range(3, 6);
1972 for(s16 ii=0; ii<trunk_h; ii++)
1974 if(vmanip.m_area.contains(p1))
1975 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1979 // p1 is now the last piece of the trunk
1982 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1983 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1984 Buffer<u8> leaves_d(leaves_a.getVolume());
1985 for(s32 i=0; i<leaves_a.getVolume(); i++)
1988 // Force leaves at near the end of the trunk
1991 for(s16 z=-d; z<=d; z++)
1992 for(s16 y=-d; y<=d; y++)
1993 for(s16 x=-d; x<=d; x++)
1995 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1999 // Add leaves randomly
2000 for(u32 iii=0; iii<7; iii++)
2005 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2006 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2007 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2010 for(s16 z=0; z<=d; z++)
2011 for(s16 y=0; y<=d; y++)
2012 for(s16 x=0; x<=d; x++)
2014 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2018 // Blit leaves to vmanip
2019 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2020 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2021 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2025 if(vmanip.m_area.contains(p) == false)
2027 u32 vi = vmanip.m_area.index(p);
2028 if(vmanip.m_data[vi].d != CONTENT_AIR)
2030 u32 i = leaves_a.index(x,y,z);
2031 if(leaves_d[i] == 1)
2032 vmanip.m_data[vi] = leavesnode;
2037 Noise functions. Make sure seed is mangled differently in each one.
2040 // Amount of trees per area in nodes
2041 double tree_amount_2d(u64 seed, v2s16 p)
2043 double noise = noise2d_perlin(
2044 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2046 double zeroval = -0.3;
2050 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2053 #define AVERAGE_MUD_AMOUNT 4
2055 double base_rock_level_2d(u64 seed, v2s16 p)
2057 // The base ground level
2058 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2059 + 25. * noise2d_perlin(
2060 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2061 (seed>>32)+654879876, 6, 0.6);
2063 /*// A bit hillier one
2064 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2065 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2066 (seed>>27)+90340, 6, 0.69);
2070 // Higher ground level
2071 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2072 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2073 seed+85039, 5, 0.69);
2074 //higher = 30; // For debugging
2076 // Limit higher to at least base
2080 // Steepness factor of cliffs
2081 double b = 1.0 + 1.0 * noise2d_perlin(
2082 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2084 b = rangelim(b, 0.0, 1000.0);
2087 b = rangelim(b, 3.0, 1000.0);
2088 //dstream<<"b="<<b<<std::endl;
2091 // Offset to more low
2092 double a_off = -0.2;
2093 // High/low selector
2094 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2095 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2096 seed-359, 6, 0.7));*/
2097 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2098 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2099 seed-359, 5, 0.60));
2101 a = rangelim(a, 0.0, 1.0);
2103 //dstream<<"a="<<a<<std::endl;
2105 double h = base*(1.0-a) + higher*a;
2112 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2115 This is the main map generation method
2118 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2119 core::map<v3s16, MapBlock*> &changed_blocks,
2122 DSTACK(__FUNCTION_NAME);
2125 Don't generate if already fully generated
2129 MapChunk *chunk = getChunk(chunkpos);
2130 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2132 dstream<<"generateChunkRaw(): Chunk "
2133 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2134 <<" already generated"<<std::endl;
2139 dstream<<"generateChunkRaw(): Generating chunk "
2140 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2143 TimeTaker timer("generateChunkRaw()");
2145 // The distance how far into the neighbors the generator is allowed to go.
2146 s16 max_spread_amount_sectors = 2;
2147 assert(max_spread_amount_sectors <= m_chunksize);
2148 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2150 // Minimum amount of space left on sides for mud to fall in
2151 //s16 min_mud_fall_space = 2;
2153 // Maximum diameter of stone obstacles in X and Z
2154 /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2155 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2157 s16 y_blocks_min = -4;
2158 s16 y_blocks_max = 3;
2159 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2160 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2161 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2163 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2164 s16 sectorpos_base_size = m_chunksize;
2166 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2167 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2168 v2s16 sectorpos_bigbase =
2169 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2170 s16 sectorpos_bigbase_size =
2171 sectorpos_base_size + 2 * max_spread_amount_sectors;
2173 v3s16 bigarea_blocks_min(
2174 sectorpos_bigbase.X,
2179 v3s16 bigarea_blocks_max(
2180 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2182 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2185 // Relative values to control amount of stuff in one chunk
2186 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2187 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2188 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2189 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2190 *(u32)h_blocks*MAP_BLOCKSIZE;
2193 The limiting edges of the lighting update, inclusive.
2195 s16 lighting_min_d = 0-max_spread_amount;
2196 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2199 Create the whole area of this and the neighboring chunks
2202 TimeTaker timer("generateChunkRaw() create area");
2204 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2205 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2207 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2208 ServerMapSector *sector = createSector(sectorpos);
2211 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2213 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2214 MapBlock *block = createBlock(blockpos);
2216 // Lighting won't be calculated
2217 //block->setLightingExpired(true);
2218 // Lighting will be calculated
2219 block->setLightingExpired(false);
2222 Block gets sunlight if this is true.
2224 This should be set to true when the top side of a block
2225 is completely exposed to the sky.
2227 Actually this doesn't matter now because the
2228 initial lighting is done here.
2230 block->setIsUnderground(y != y_blocks_max);
2236 Now we have a big empty area.
2238 Make a ManualMapVoxelManipulator that contains this and the
2242 ManualMapVoxelManipulator vmanip(this);
2243 // Add the area we just generated
2245 TimeTaker timer("generateChunkRaw() initialEmerge");
2246 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2250 vmanip.clearFlag(0xff);
2252 TimeTaker timer_generate("generateChunkRaw() generate");
2254 // Maximum height of the stone surface and obstacles.
2255 // This is used to disable cave generation from going too high.
2256 s16 stone_surface_max_y = 0;
2259 Generate general ground level to full area
2264 //TimeTaker timer1("ground level");
2266 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2267 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2270 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2273 Skip of already generated
2276 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2277 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2281 // Ground height at this point
2282 float surface_y_f = 0.0;
2284 // Use perlin noise for ground height
2285 surface_y_f = base_rock_level_2d(m_seed, p2d);
2287 /*// Experimental stuff
2289 float a = highlands_level_2d(m_seed, p2d);
2294 // Convert to integer
2295 s16 surface_y = (s16)surface_y_f;
2298 if(surface_y > stone_surface_max_y)
2299 stone_surface_max_y = surface_y;
2302 Fill ground with stone
2305 // Use fast index incrementing
2306 v3s16 em = vmanip.m_area.getExtent();
2307 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2308 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2310 vmanip.m_data[i].d = CONTENT_STONE;
2312 vmanip.m_area.add_y(em, i, 1);
2320 Randomize some parameters
2323 //s32 stone_obstacle_count = 0;
2324 /*s32 stone_obstacle_count =
2325 rangelim((1.0+noise2d(m_seed+897,
2326 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2328 //s16 stone_obstacle_max_height = 0;
2329 /*s16 stone_obstacle_max_height =
2330 rangelim((1.0+noise2d(m_seed+5902,
2331 sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2334 Loop this part, it will make stuff look older and newer nicely
2336 //for(u32 i_age=0; i_age<1; i_age++)
2337 for(u32 i_age=0; i_age<2; i_age++)
2339 /******************************
2340 BEGINNING OF AGING LOOP
2341 ******************************/
2346 //TimeTaker timer1("stone obstacles");
2349 Add some random stone obstacles
2352 for(s32 ri=0; ri<stone_obstacle_count; ri++)
2354 // Randomize max height so usually stuff will be quite low
2355 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2357 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2358 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2361 myrand_range(5, stone_obstacle_max_size),
2362 myrand_range(0, maxheight_randomized),
2363 myrand_range(5, stone_obstacle_max_size)
2366 // Don't make stupid small rectangle bumps
2371 myrand_range(1+ob_size.X/2+2,
2372 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2373 myrand_range(1+ob_size.Z/2+2,
2374 sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2377 // Minimum space left on top of the obstacle
2378 s16 min_head_space = 12;
2380 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2381 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2383 // Node position in 2d
2384 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2386 // Find stone ground level
2387 // (ignore everything else than mud in already generated chunks)
2388 // and mud amount over the stone level
2392 v3s16 em = vmanip.m_area.getExtent();
2393 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2395 // Go to ground level
2396 for(y=y_nodes_max; y>=y_nodes_min; y--)
2398 MapNode *n = &vmanip.m_data[i];
2399 /*if(content_walkable(n.d)
2400 && n.d != CONTENT_MUD
2401 && n.d != CONTENT_GRASS)
2403 if(n->d == CONTENT_STONE)
2406 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2410 Change to mud because otherwise we might
2411 be throwing mud on grass at the next
2417 vmanip.m_area.add_y(em, i, -1);
2419 if(y >= y_nodes_min)
2422 surface_y = y_nodes_min;
2430 v3s16 em = vmanip.m_area.getExtent();
2431 s16 y_start = surface_y+1;
2432 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2436 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2438 MapNode &n = vmanip.m_data[i];
2439 n.d = CONTENT_STONE;
2441 if(y > stone_surface_max_y)
2442 stone_surface_max_y = y;
2445 if(count >= ob_size.Y)
2448 vmanip.m_area.add_y(em, i, 1);
2452 for(; y<=y_nodes_max - min_head_space; y++)
2454 MapNode &n = vmanip.m_data[i];
2457 if(count >= mud_amount)
2460 vmanip.m_area.add_y(em, i, 1);
2472 //TimeTaker timer1("caves");
2477 u32 caves_count = relative_volume / 400000;
2478 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2479 if(stone_surface_max_y < WATER_LEVEL)
2481 /*u32 caves_count = 0;
2482 u32 bruises_count = 0;*/
2483 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2485 s16 min_tunnel_diameter = 3;
2486 s16 max_tunnel_diameter = 5;
2487 u16 tunnel_routepoints = 20;
2489 v3f main_direction(0,0,0);
2491 bool bruise_surface = (jj > caves_count);
2495 min_tunnel_diameter = 5;
2496 max_tunnel_diameter = myrand_range(10, 20);
2497 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2498 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2500 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2501 sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2503 tunnel_routepoints = 5;
2509 // Allowed route area size in nodes
2511 sectorpos_base_size*MAP_BLOCKSIZE,
2512 h_blocks*MAP_BLOCKSIZE,
2513 sectorpos_base_size*MAP_BLOCKSIZE
2516 // Area starting point in nodes
2518 sectorpos_base.X*MAP_BLOCKSIZE,
2519 y_blocks_min*MAP_BLOCKSIZE,
2520 sectorpos_base.Y*MAP_BLOCKSIZE
2524 //(this should be more than the maximum radius of the tunnel)
2525 //s16 insure = 5; // Didn't work with max_d = 20
2527 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2528 ar += v3s16(1,0,1) * more * 2;
2529 of -= v3s16(1,0,1) * more;
2531 s16 route_y_min = 0;
2532 // Allow half a diameter + 7 over stone surface
2533 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2535 /*// If caves, don't go through surface too often
2536 if(bruise_surface == false)
2537 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2539 // Limit maximum to area
2540 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2544 /*// Minimum is at y=0
2545 route_y_min = -of.Y - 0;*/
2546 // Minimum is at y=max_tunnel_diameter/4
2547 //route_y_min = -of.Y + max_tunnel_diameter/4;
2548 //s16 min = -of.Y + max_tunnel_diameter/4;
2549 s16 min = -of.Y + 0;
2550 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2551 route_y_min = rangelim(route_y_min, 0, route_y_max);
2554 /*dstream<<"route_y_min = "<<route_y_min
2555 <<", route_y_max = "<<route_y_max<<std::endl;*/
2557 s16 route_start_y_min = route_y_min;
2558 s16 route_start_y_max = route_y_max;
2560 // Start every 2nd cave from surface
2561 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2563 if(coming_from_surface)
2565 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2568 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2569 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2571 // Randomize starting position
2573 (float)(myrand()%ar.X)+0.5,
2574 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2575 (float)(myrand()%ar.Z)+0.5
2578 MapNode airnode(CONTENT_AIR);
2581 Generate some tunnel starting from orp
2584 for(u16 j=0; j<tunnel_routepoints; j++)
2586 if(j%7==0 && bruise_surface == false)
2588 main_direction = v3f(
2589 ((float)(myrand()%20)-(float)10)/10,
2590 ((float)(myrand()%20)-(float)10)/30,
2591 ((float)(myrand()%20)-(float)10)/10
2593 main_direction *= (float)myrand_range(1, 3);
2597 s16 min_d = min_tunnel_diameter;
2598 s16 max_d = max_tunnel_diameter;
2599 s16 rs = myrand_range(min_d, max_d);
2604 maxlen = v3s16(rs*7,rs*7,rs*7);
2608 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2613 if(coming_from_surface && j < 3)
2616 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2617 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2618 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2624 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2625 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2626 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2630 vec += main_direction;
2635 else if(rp.X >= ar.X)
2637 if(rp.Y < route_y_min)
2639 else if(rp.Y >= route_y_max)
2640 rp.Y = route_y_max-1;
2643 else if(rp.Z >= ar.Z)
2647 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2649 v3f fp = orp + vec * f;
2650 v3s16 cp(fp.X, fp.Y, fp.Z);
2653 s16 d1 = d0 + rs - 1;
2654 for(s16 z0=d0; z0<=d1; z0++)
2656 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2657 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2658 for(s16 x0=-si; x0<=si-1; x0++)
2660 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2661 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2662 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2663 //s16 si2 = rs - abs(x0);
2664 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2670 /*if(isInArea(p, ar) == false)
2672 // Check only height
2673 if(y < 0 || y >= ar.Y)
2677 //assert(vmanip.m_area.contains(p));
2678 if(vmanip.m_area.contains(p) == false)
2680 dstream<<"WARNING: "<<__FUNCTION_NAME
2681 <<":"<<__LINE__<<": "
2682 <<"point not in area"
2687 // Just set it to air, it will be changed to
2689 u32 i = vmanip.m_area.index(p);
2690 vmanip.m_data[i] = airnode;
2692 if(bruise_surface == false)
2695 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2710 //TimeTaker timer1("ore veins");
2715 for(u32 jj=0; jj<relative_volume/1000; jj++)
2717 s16 max_vein_diameter = 3;
2719 // Allowed route area size in nodes
2721 sectorpos_base_size*MAP_BLOCKSIZE,
2722 h_blocks*MAP_BLOCKSIZE,
2723 sectorpos_base_size*MAP_BLOCKSIZE
2726 // Area starting point in nodes
2728 sectorpos_base.X*MAP_BLOCKSIZE,
2729 y_blocks_min*MAP_BLOCKSIZE,
2730 sectorpos_base.Y*MAP_BLOCKSIZE
2734 //(this should be more than the maximum radius of the tunnel)
2736 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2737 ar += v3s16(1,0,1) * more * 2;
2738 of -= v3s16(1,0,1) * more;
2740 // Randomize starting position
2742 (float)(myrand()%ar.X)+0.5,
2743 (float)(myrand()%ar.Y)+0.5,
2744 (float)(myrand()%ar.Z)+0.5
2747 // Randomize mineral
2750 mineral = MINERAL_COAL;
2752 mineral = MINERAL_IRON;
2755 Generate some vein starting from orp
2758 for(u16 j=0; j<2; j++)
2761 (float)(myrand()%ar.X)+0.5,
2762 (float)(myrand()%ar.Y)+0.5,
2763 (float)(myrand()%ar.Z)+0.5
2765 v3f vec = rp - orp;*/
2767 v3s16 maxlen(5, 5, 5);
2769 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2770 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2771 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2776 else if(rp.X >= ar.X)
2780 else if(rp.Y >= ar.Y)
2784 else if(rp.Z >= ar.Z)
2790 s16 max_d = max_vein_diameter;
2791 s16 rs = myrand_range(min_d, max_d);
2793 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2795 v3f fp = orp + vec * f;
2796 v3s16 cp(fp.X, fp.Y, fp.Z);
2798 s16 d1 = d0 + rs - 1;
2799 for(s16 z0=d0; z0<=d1; z0++)
2801 s16 si = rs - abs(z0);
2802 for(s16 x0=-si; x0<=si-1; x0++)
2804 s16 si2 = rs - abs(x0);
2805 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2807 // Don't put mineral to every place
2815 /*if(isInArea(p, ar) == false)
2817 // Check only height
2818 if(y < 0 || y >= ar.Y)
2822 assert(vmanip.m_area.contains(p));
2824 // Just set it to air, it will be changed to
2826 u32 i = vmanip.m_area.index(p);
2827 MapNode *n = &vmanip.m_data[i];
2828 if(n->d == CONTENT_STONE)
2843 //TimeTaker timer1("add mud");
2846 Add mud to the central chunk
2849 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2850 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2852 // Node position in 2d
2853 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2855 // Randomize mud amount
2856 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2857 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2858 m_seed+1, 3, 0.55));
2860 // Find ground level
2861 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2864 If topmost node is grass, change it to mud.
2865 It might be if it was flown to there from a neighboring
2866 chunk and then converted.
2869 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2870 MapNode *n = &vmanip.m_data[i];
2871 if(n->d == CONTENT_GRASS)
2880 v3s16 em = vmanip.m_area.getExtent();
2881 s16 y_start = surface_y+1;
2882 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2883 for(s16 y=y_start; y<=y_nodes_max; y++)
2885 if(mudcount >= mud_add_amount)
2888 MapNode &n = vmanip.m_data[i];
2892 vmanip.m_area.add_y(em, i, 1);
2901 TimeTaker timer1("flow mud");
2904 Flow mud away from steep edges
2907 // Limit area by 1 because mud is flown into neighbors.
2908 s16 mudflow_minpos = 0-max_spread_amount+1;
2909 s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2911 // Iterate a few times
2912 for(s16 k=0; k<3; k++)
2915 for(s16 x=mudflow_minpos;
2918 for(s16 z=mudflow_minpos;
2922 // Invert coordinates every 2nd iteration
2925 x = mudflow_maxpos - (x-mudflow_minpos);
2926 z = mudflow_maxpos - (z-mudflow_minpos);
2929 // Node position in 2d
2930 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2932 v3s16 em = vmanip.m_area.getExtent();
2933 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2940 for(; y>=y_nodes_min; y--)
2942 n = &vmanip.m_data[i];
2943 //if(content_walkable(n->d))
2945 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2948 vmanip.m_area.add_y(em, i, -1);
2951 // Stop if out of area
2952 //if(vmanip.m_area.contains(i) == false)
2956 /*// If not mud, do nothing to it
2957 MapNode *n = &vmanip.m_data[i];
2958 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2962 Don't flow it if the stuff under it is not mud
2966 vmanip.m_area.add_y(em, i2, -1);
2967 // Cancel if out of area
2968 if(vmanip.m_area.contains(i2) == false)
2970 MapNode *n2 = &vmanip.m_data[i2];
2971 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2975 // Make it exactly mud
2978 /*s16 recurse_count = 0;
2982 v3s16(0,0,1), // back
2983 v3s16(1,0,0), // right
2984 v3s16(0,0,-1), // front
2985 v3s16(-1,0,0), // left
2988 // Theck that upper is air or doesn't exist.
2989 // Cancel dropping if upper keeps it in place
2991 vmanip.m_area.add_y(em, i3, 1);
2992 if(vmanip.m_area.contains(i3) == true
2993 && content_walkable(vmanip.m_data[i3].d) == true)
3000 for(u32 di=0; di<4; di++)
3002 v3s16 dirp = dirs4[di];
3005 vmanip.m_area.add_p(em, i2, dirp);
3006 // Fail if out of area
3007 if(vmanip.m_area.contains(i2) == false)
3009 // Check that side is air
3010 MapNode *n2 = &vmanip.m_data[i2];
3011 if(content_walkable(n2->d))
3013 // Check that under side is air
3014 vmanip.m_area.add_y(em, i2, -1);
3015 if(vmanip.m_area.contains(i2) == false)
3017 n2 = &vmanip.m_data[i2];
3018 if(content_walkable(n2->d))
3020 /*// Check that under that is air (need a drop of 2)
3021 vmanip.m_area.add_y(em, i2, -1);
3022 if(vmanip.m_area.contains(i2) == false)
3024 n2 = &vmanip.m_data[i2];
3025 if(content_walkable(n2->d))
3027 // Loop further down until not air
3029 vmanip.m_area.add_y(em, i2, -1);
3030 // Fail if out of area
3031 if(vmanip.m_area.contains(i2) == false)
3033 n2 = &vmanip.m_data[i2];
3034 }while(content_walkable(n2->d) == false);
3035 // Loop one up so that we're in air
3036 vmanip.m_area.add_y(em, i2, 1);
3037 n2 = &vmanip.m_data[i2];
3039 // Move mud to new place
3041 // Set old place to be air
3042 *n = MapNode(CONTENT_AIR);
3055 //TimeTaker timer1("add water");
3058 Add water to the central chunk (and a bit more)
3061 for(s16 x=0-max_spread_amount;
3062 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3064 for(s16 z=0-max_spread_amount;
3065 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3068 // Node position in 2d
3069 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3071 // Find ground level
3072 //s16 surface_y = find_ground_level(vmanip, p2d);
3075 If ground level is over water level, skip.
3076 NOTE: This leaves caves near water without water,
3077 which looks especially crappy when the nearby water
3078 won't start flowing either for some reason
3080 /*if(surface_y > WATER_LEVEL)
3087 v3s16 em = vmanip.m_area.getExtent();
3088 u8 light = LIGHT_MAX;
3089 // Start at global water surface level
3090 s16 y_start = WATER_LEVEL;
3091 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3092 MapNode *n = &vmanip.m_data[i];
3094 /*// Add first one to transforming liquid queue, if water
3095 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3097 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3098 m_transforming_liquid.push_back(p);
3101 for(s16 y=y_start; y>=y_nodes_min; y--)
3103 n = &vmanip.m_data[i];
3105 // Stop when there is no water and no air
3106 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3107 && n->d != CONTENT_WATER)
3109 /*// Add bottom one to transforming liquid queue
3110 vmanip.m_area.add_y(em, i, 1);
3111 n = &vmanip.m_data[i];
3112 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3114 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3115 m_transforming_liquid.push_back(p);
3121 // Make water only not in caves
3122 if(!(vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3124 n->d = CONTENT_WATERSOURCE;
3125 //n->setLight(LIGHTBANK_DAY, light);
3127 // Add to transforming liquid queue (in case it'd
3129 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3130 m_transforming_liquid.push_back(p);
3134 vmanip.m_area.add_y(em, i, -1);
3145 /***********************
3147 ************************/
3150 //TimeTaker timer1("convert mud to sand");
3156 //s16 mud_add_amount = myrand_range(2, 4);
3157 //s16 mud_add_amount = 0;
3159 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3160 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3161 for(s16 x=0-max_spread_amount+1;
3162 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3164 for(s16 z=0-max_spread_amount+1;
3165 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3168 // Node position in 2d
3169 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3171 // Determine whether to have sand here
3172 double sandnoise = noise2d_perlin(
3173 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3174 m_seed+59420, 3, 0.50);
3176 bool have_sand = (sandnoise > -0.15);
3178 if(have_sand == false)
3181 // Find ground level
3182 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3184 if(surface_y > WATER_LEVEL + 2)
3188 v3s16 em = vmanip.m_area.getExtent();
3189 s16 y_start = surface_y;
3190 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3191 u32 not_sand_counter = 0;
3192 for(s16 y=y_start; y>=y_nodes_min; y--)
3194 MapNode *n = &vmanip.m_data[i];
3195 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3197 n->d = CONTENT_SAND;
3202 if(not_sand_counter > 3)
3206 vmanip.m_area.add_y(em, i, -1);
3215 //TimeTaker timer1("generate trees");
3221 // Divide area into parts
3223 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3224 double area = sidelen * sidelen;
3225 for(s16 x0=0; x0<div; x0++)
3226 for(s16 z0=0; z0<div; z0++)
3228 // Center position of part of division
3230 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3231 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3233 // Minimum edge of part of division
3235 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3236 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3238 // Maximum edge of part of division
3240 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3241 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3244 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3245 // Put trees in random places on part of division
3246 for(u32 i=0; i<tree_count; i++)
3248 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3249 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3250 s16 y = find_ground_level(vmanip, v2s16(x,z));
3251 // Don't make a tree under water level
3254 // Don't make a tree so high that it doesn't fit
3255 if(y > y_nodes_max - 6)
3259 Trees grow only on mud and grass
3262 u32 i = vmanip.m_area.index(v3s16(p));
3263 MapNode *n = &vmanip.m_data[i];
3264 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3269 make_tree(vmanip, p);
3272 /*u32 tree_max = relative_area / 60;
3273 //u32 count = myrand_range(0, tree_max);
3274 for(u32 i=0; i<count; i++)
3276 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3277 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3278 x += sectorpos_base.X*MAP_BLOCKSIZE;
3279 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3280 s16 y = find_ground_level(vmanip, v2s16(x,z));
3281 // Don't make a tree under water level
3286 make_tree(vmanip, p);
3294 //TimeTaker timer1("grow grass");
3300 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3301 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3302 for(s16 x=0-max_spread_amount;
3303 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3305 for(s16 z=0-max_spread_amount;
3306 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3309 // Node position in 2d
3310 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3313 Find the lowest surface to which enough light ends up
3316 Basically just wait until not air and not leaves.
3320 v3s16 em = vmanip.m_area.getExtent();
3321 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3323 // Go to ground level
3324 for(y=y_nodes_max; y>=y_nodes_min; y--)
3326 MapNode &n = vmanip.m_data[i];
3327 if(n.d != CONTENT_AIR
3328 && n.d != CONTENT_LEAVES)
3330 vmanip.m_area.add_y(em, i, -1);
3332 if(y >= y_nodes_min)
3335 surface_y = y_nodes_min;
3338 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3339 MapNode *n = &vmanip.m_data[i];
3340 if(n->d == CONTENT_MUD)
3341 n->d = CONTENT_GRASS;
3347 Initial lighting (sunlight)
3350 core::map<v3s16, bool> light_sources;
3353 // 750ms @cs=8, can't optimize more
3354 TimeTaker timer1("initial lighting");
3358 Go through the edges and add all nodes that have light to light_sources
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);
3393 v3s16 em = vmanip.m_area.getExtent();
3394 s16 y_start = y_nodes_max;
3395 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3396 for(s16 y=y_start; y>=y_nodes_min; y--)
3398 MapNode *n = &vmanip.m_data[i];
3399 if(n->getLight(LIGHTBANK_DAY) != 0)
3401 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3403 //NOTE: This is broken, at least the index has to
3412 Go through the edges and apply sunlight to them, not caring
3417 for(s16 i=0; i<4; i++)
3419 for(s16 j=lighting_min_d;
3426 if(i == 0 || i == 1)
3428 x = (i==0) ? lighting_min_d : lighting_max_d;
3437 z = (i==0) ? lighting_min_d : lighting_max_d;
3444 // Node position in 2d
3445 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3447 // Loop from top to down
3449 u8 light = LIGHT_SUN;
3450 v3s16 em = vmanip.m_area.getExtent();
3451 s16 y_start = y_nodes_max;
3452 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3453 for(s16 y=y_start; y>=y_nodes_min; y--)
3455 MapNode *n = &vmanip.m_data[i];
3456 if(light_propagates_content(n->d) == false)
3460 else if(light != LIGHT_SUN
3461 || sunlight_propagates_content(n->d) == false)
3467 n->setLight(LIGHTBANK_DAY, light);
3468 n->setLight(LIGHTBANK_NIGHT, 0);
3472 // Insert light source
3473 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3476 // Increment index by y
3477 vmanip.m_area.add_y(em, i, -1);
3483 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3484 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3485 /*for(s16 x=0-max_spread_amount+1;
3486 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3488 for(s16 z=0-max_spread_amount+1;
3489 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3493 This has to be 1 smaller than the actual area, because
3494 neighboring nodes are checked.
3496 for(s16 x=lighting_min_d+1;
3497 x<=lighting_max_d-1;
3499 for(s16 z=lighting_min_d+1;
3500 z<=lighting_max_d-1;
3503 // Node position in 2d
3504 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3507 Apply initial sunlight
3510 u8 light = LIGHT_SUN;
3511 bool add_to_sources = false;
3512 v3s16 em = vmanip.m_area.getExtent();
3513 s16 y_start = y_nodes_max;
3514 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3515 for(s16 y=y_start; y>=y_nodes_min; y--)
3517 MapNode *n = &vmanip.m_data[i];
3519 if(light_propagates_content(n->d) == false)
3523 else if(light != LIGHT_SUN
3524 || sunlight_propagates_content(n->d) == false)
3530 // This doesn't take much time
3531 if(add_to_sources == false)
3534 Check sides. If side is not air or water, start
3535 adding to light_sources.
3538 v3s16(0,0,1), // back
3539 v3s16(1,0,0), // right
3540 v3s16(0,0,-1), // front
3541 v3s16(-1,0,0), // left
3543 for(u32 di=0; di<4; di++)
3545 v3s16 dirp = dirs4[di];
3547 vmanip.m_area.add_p(em, i2, dirp);
3548 MapNode *n2 = &vmanip.m_data[i2];
3550 n2->d != CONTENT_AIR
3551 && n2->d != CONTENT_WATERSOURCE
3552 && n2->d != CONTENT_WATER
3554 add_to_sources = true;
3560 n->setLight(LIGHTBANK_DAY, light);
3561 n->setLight(LIGHTBANK_NIGHT, 0);
3563 // This doesn't take much time
3564 if(light != 0 && add_to_sources)
3566 // Insert light source
3567 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3570 // Increment index by y
3571 vmanip.m_area.add_y(em, i, -1);
3578 for(s16 x=lighting_min_d+1;
3579 x<=lighting_max_d-1;
3581 for(s16 z=lighting_min_d+1;
3582 z<=lighting_max_d-1;
3585 // Node position in 2d
3586 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3589 Apply initial sunlight
3592 u8 light = LIGHT_SUN;
3593 v3s16 em = vmanip.m_area.getExtent();
3594 s16 y_start = y_nodes_max;
3595 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3596 for(s16 y=y_start; y>=y_nodes_min; y--)
3598 MapNode *n = &vmanip.m_data[i];
3600 if(light_propagates_content(n->d) == false)
3604 else if(light != LIGHT_SUN
3605 || sunlight_propagates_content(n->d) == false)
3611 n->setLight(LIGHTBANK_DAY, light);
3612 n->setLight(LIGHTBANK_NIGHT, 0);
3614 // This doesn't take much time
3617 // Insert light source
3618 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3621 // Increment index by y
3622 vmanip.m_area.add_y(em, i, -1);
3630 // Spread light around
3632 TimeTaker timer("generateChunkRaw() spreadLight");
3633 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3640 timer_generate.stop();
3643 Blit generated stuff to map
3647 //TimeTaker timer("generateChunkRaw() blitBackAll");
3648 vmanip.blitBackAll(&changed_blocks);
3652 Update day/night difference cache of the MapBlocks
3655 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3656 i.atEnd() == false; i++)
3658 MapBlock *block = i.getNode()->getValue();
3659 block->updateDayNightDiff();
3665 Create chunk metadata
3668 for(s16 x=-1; x<=1; x++)
3669 for(s16 y=-1; y<=1; y++)
3671 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3672 // Add chunk meta information
3673 MapChunk *chunk = getChunk(chunkpos0);
3676 chunk = new MapChunk();
3677 m_chunks.insert(chunkpos0, chunk);
3679 //chunk->setIsVolatile(true);
3680 if(chunk->getGenLevel() > GENERATED_PARTLY)
3681 chunk->setGenLevel(GENERATED_PARTLY);
3685 Set central chunk non-volatile
3687 MapChunk *chunk = getChunk(chunkpos);
3690 //chunk->setIsVolatile(false);
3691 chunk->setGenLevel(GENERATED_FULLY);
3694 Save changed parts of map
3699 Return central chunk (which was requested)
3704 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3705 core::map<v3s16, MapBlock*> &changed_blocks)
3707 dstream<<"generateChunk(): Generating chunk "
3708 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3711 /*for(s16 x=-1; x<=1; x++)
3712 for(s16 y=-1; y<=1; y++)*/
3713 for(s16 x=-0; x<=0; x++)
3714 for(s16 y=-0; y<=0; y++)
3716 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3717 MapChunk *chunk = getChunk(chunkpos0);
3718 // Skip if already generated
3719 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3721 generateChunkRaw(chunkpos0, changed_blocks);
3724 assert(chunkNonVolatile(chunkpos1));
3726 MapChunk *chunk = getChunk(chunkpos1);
3730 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3732 DSTACK("%s: p2d=(%d,%d)",
3737 Check if it exists already in memory
3739 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3744 Try to load it from disk (with blocks)
3746 if(loadSectorFull(p2d) == true)
3748 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3751 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3752 throw InvalidPositionException("");
3758 Do not create over-limit
3760 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3761 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3762 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3763 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3764 throw InvalidPositionException("createSector(): pos. over limit");
3767 Generate blank sector
3770 sector = new ServerMapSector(this, p2d);
3772 // Sector position on map in nodes
3773 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3778 m_sectors.insert(p2d, sector);
3783 MapSector * ServerMap::emergeSector(v2s16 p2d,
3784 core::map<v3s16, MapBlock*> &changed_blocks)
3786 DSTACK("%s: p2d=(%d,%d)",
3793 v2s16 chunkpos = sector_to_chunk(p2d);
3794 /*bool chunk_nonvolatile = false;
3795 MapChunk *chunk = getChunk(chunkpos);
3796 if(chunk && chunk->getIsVolatile() == false)
3797 chunk_nonvolatile = true;*/
3798 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3801 If chunk is not fully generated, generate chunk
3803 if(chunk_nonvolatile == false)
3805 // Generate chunk and neighbors
3806 generateChunk(chunkpos, changed_blocks);
3810 Return sector if it exists now
3812 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3817 Try to load it from disk
3819 if(loadSectorFull(p2d) == true)
3821 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3824 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3825 throw InvalidPositionException("");
3831 generateChunk should have generated the sector
3835 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3836 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3840 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3842 return createSector(p2d);
3847 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3850 generateChunkRaw(chunkpos, changed_blocks, true);
3853 Return sector if it exists now
3855 sector = getSectorNoGenerateNoEx(p2d);
3859 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3867 //return generateSector();
3871 NOTE: This is not used for main map generation, only for blocks
3872 that are very high or low
3874 MapBlock * ServerMap::generateBlock(
3876 MapBlock *original_dummy,
3877 ServerMapSector *sector,
3878 core::map<v3s16, MapBlock*> &changed_blocks,
3879 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3882 DSTACK("%s: p=(%d,%d,%d)",
3886 /*dstream<<"generateBlock(): "
3887 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3890 MapBlock *block = original_dummy;
3892 v2s16 p2d(p.X, p.Z);
3894 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3897 Do not generate over-limit
3899 if(blockpos_over_limit(p))
3901 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3902 throw InvalidPositionException("generateBlock(): pos. over limit");
3906 If block doesn't exist, create one.
3907 If it exists, it is a dummy. In that case unDummify() it.
3909 NOTE: This already sets the map as the parent of the block
3913 block = sector->createBlankBlockNoInsert(block_y);
3917 // Remove the block so that nobody can get a half-generated one.
3918 sector->removeBlock(block);
3919 // Allocate the block to contain the generated data
3923 u8 water_material = CONTENT_WATERSOURCE;
3925 s32 lowest_ground_y = 32767;
3926 s32 highest_ground_y = -32768;
3928 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3929 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3931 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3933 //s16 surface_y = 0;
3935 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3936 + AVERAGE_MUD_AMOUNT;
3938 if(surface_y < lowest_ground_y)
3939 lowest_ground_y = surface_y;
3940 if(surface_y > highest_ground_y)
3941 highest_ground_y = surface_y;
3943 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3945 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3947 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3952 NOTE: If there are some man-made structures above the
3953 newly created block, they won't be taken into account.
3955 if(real_y > surface_y)
3956 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3962 // If node is over heightmap y, it's air or water
3963 if(real_y > surface_y)
3965 // If under water level, it's water
3966 if(real_y < WATER_LEVEL)
3968 n.d = water_material;
3969 n.setLight(LIGHTBANK_DAY,
3970 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3972 Add to transforming liquid queue (in case it'd
3975 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3976 m_transforming_liquid.push_back(real_pos);
3982 // Else it's ground or caves (air)
3985 // If it's surface_depth under ground, it's stone
3986 if(real_y <= surface_y - surface_depth)
3988 n.d = CONTENT_STONE;
3992 // It is mud if it is under the first ground
3993 // level or under water
3994 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4000 n.d = CONTENT_GRASS;
4003 //n.d = CONTENT_MUD;
4005 /*// If under water level, it's mud
4006 if(real_y < WATER_LEVEL)
4008 // Only the topmost node is grass
4009 else if(real_y <= surface_y - 1)
4012 n.d = CONTENT_GRASS;*/
4016 block->setNode(v3s16(x0,y0,z0), n);
4021 Calculate some helper variables
4024 // Completely underground if the highest part of block is under lowest
4026 // This has to be very sure; it's probably one too strict now but
4027 // that's just better.
4028 bool completely_underground =
4029 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4031 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4033 bool mostly_underwater_surface = false;
4034 if(highest_ground_y < WATER_LEVEL
4035 && some_part_underground && !completely_underground)
4036 mostly_underwater_surface = true;
4039 Get local attributes
4042 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4044 float caves_amount = 0.5;
4049 NOTE: BEWARE: Too big amount of attribute points slows verything
4051 1 interpolation from 5000 points takes 2-3ms.
4053 //TimeTaker timer("generateBlock() local attribute retrieval");
4054 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4055 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4056 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4060 //dstream<<"generateBlock(): Done"<<std::endl;
4066 // Initialize temporary table
4067 const s32 ued = MAP_BLOCKSIZE;
4068 bool underground_emptiness[ued*ued*ued];
4069 for(s32 i=0; i<ued*ued*ued; i++)
4071 underground_emptiness[i] = 0;
4078 Initialize orp and ors. Try to find if some neighboring
4079 MapBlock has a tunnel ended in its side
4083 (float)(myrand()%ued)+0.5,
4084 (float)(myrand()%ued)+0.5,
4085 (float)(myrand()%ued)+0.5
4088 bool found_existing = false;
4094 for(s16 y=0; y<ued; y++)
4095 for(s16 x=0; x<ued; x++)
4097 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4098 if(getNode(ap).d == CONTENT_AIR)
4100 orp = v3f(x+1,y+1,0);
4101 found_existing = true;
4102 goto continue_generating;
4106 catch(InvalidPositionException &e){}
4112 for(s16 y=0; y<ued; y++)
4113 for(s16 x=0; x<ued; x++)
4115 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4116 if(getNode(ap).d == CONTENT_AIR)
4118 orp = v3f(x+1,y+1,ued-1);
4119 found_existing = true;
4120 goto continue_generating;
4124 catch(InvalidPositionException &e){}
4130 for(s16 y=0; y<ued; y++)
4131 for(s16 z=0; z<ued; z++)
4133 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4134 if(getNode(ap).d == CONTENT_AIR)
4136 orp = v3f(0,y+1,z+1);
4137 found_existing = true;
4138 goto continue_generating;
4142 catch(InvalidPositionException &e){}
4148 for(s16 y=0; y<ued; y++)
4149 for(s16 z=0; z<ued; z++)
4151 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4152 if(getNode(ap).d == CONTENT_AIR)
4154 orp = v3f(ued-1,y+1,z+1);
4155 found_existing = true;
4156 goto continue_generating;
4160 catch(InvalidPositionException &e){}
4166 for(s16 x=0; x<ued; x++)
4167 for(s16 z=0; z<ued; z++)
4169 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4170 if(getNode(ap).d == CONTENT_AIR)
4172 orp = v3f(x+1,0,z+1);
4173 found_existing = true;
4174 goto continue_generating;
4178 catch(InvalidPositionException &e){}
4184 for(s16 x=0; x<ued; x++)
4185 for(s16 z=0; z<ued; z++)
4187 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4188 if(getNode(ap).d == CONTENT_AIR)
4190 orp = v3f(x+1,ued-1,z+1);
4191 found_existing = true;
4192 goto continue_generating;
4196 catch(InvalidPositionException &e){}
4198 continue_generating:
4201 Choose whether to actually generate cave
4203 bool do_generate_caves = true;
4204 // Don't generate if no part is underground
4205 if(!some_part_underground)
4207 do_generate_caves = false;
4209 // Don't generate if mostly underwater surface
4210 /*else if(mostly_underwater_surface)
4212 do_generate_caves = false;
4214 // Partly underground = cave
4215 else if(!completely_underground)
4217 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4219 // Found existing cave underground
4220 else if(found_existing && completely_underground)
4222 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4224 // Underground and no caves found
4227 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4230 if(do_generate_caves)
4233 Generate some tunnel starting from orp and ors
4235 for(u16 i=0; i<3; i++)
4238 (float)(myrand()%ued)+0.5,
4239 (float)(myrand()%ued)+0.5,
4240 (float)(myrand()%ued)+0.5
4244 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4248 for(float f=0; f<1.0; f+=0.04)
4250 v3f fp = orp + vec * f;
4251 v3s16 cp(fp.X, fp.Y, fp.Z);
4253 s16 d1 = d0 + rs - 1;
4254 for(s16 z0=d0; z0<=d1; z0++)
4256 s16 si = rs - abs(z0);
4257 for(s16 x0=-si; x0<=si-1; x0++)
4259 s16 si2 = rs - abs(x0);
4260 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4266 if(isInArea(p, ued) == false)
4268 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4280 // Set to true if has caves.
4281 // Set when some non-air is changed to air when making caves.
4282 bool has_caves = false;
4285 Apply temporary cave data to block
4288 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4289 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4291 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4293 MapNode n = block->getNode(v3s16(x0,y0,z0));
4296 if(underground_emptiness[
4297 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4298 +ued*(y0*ued/MAP_BLOCKSIZE)
4299 +(x0*ued/MAP_BLOCKSIZE)])
4301 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4310 block->setNode(v3s16(x0,y0,z0), n);
4315 This is used for guessing whether or not the block should
4316 receive sunlight from the top if the block above doesn't exist
4318 block->setIsUnderground(completely_underground);
4321 Force lighting update if some part of block is partly
4322 underground and has caves.
4324 /*if(some_part_underground && !completely_underground && has_caves)
4326 //dstream<<"Half-ground caves"<<std::endl;
4327 lighting_invalidated_blocks[block->getPos()] = block;
4330 // DEBUG: Always update lighting
4331 //lighting_invalidated_blocks[block->getPos()] = block;
4337 if(some_part_underground)
4339 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4344 for(s16 i=0; i<underground_level/4 + 1; i++)
4346 if(myrand()%50 == 0)
4349 (myrand()%(MAP_BLOCKSIZE-2))+1,
4350 (myrand()%(MAP_BLOCKSIZE-2))+1,
4351 (myrand()%(MAP_BLOCKSIZE-2))+1
4357 for(u16 i=0; i<27; i++)
4359 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4361 block->setNode(cp+g_27dirs[i], n);
4369 u16 coal_amount = 30;
4370 u16 coal_rareness = 60 / coal_amount;
4371 if(coal_rareness == 0)
4373 if(myrand()%coal_rareness == 0)
4375 u16 a = myrand() % 16;
4376 u16 amount = coal_amount * a*a*a / 1000;
4377 for(s16 i=0; i<amount; i++)
4380 (myrand()%(MAP_BLOCKSIZE-2))+1,
4381 (myrand()%(MAP_BLOCKSIZE-2))+1,
4382 (myrand()%(MAP_BLOCKSIZE-2))+1
4386 n.d = CONTENT_STONE;
4387 n.param = MINERAL_COAL;
4389 for(u16 i=0; i<27; i++)
4391 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4393 block->setNode(cp+g_27dirs[i], n);
4401 //TODO: change to iron_amount or whatever
4402 u16 iron_amount = 15;
4403 u16 iron_rareness = 60 / iron_amount;
4404 if(iron_rareness == 0)
4406 if(myrand()%iron_rareness == 0)
4408 u16 a = myrand() % 16;
4409 u16 amount = iron_amount * a*a*a / 1000;
4410 for(s16 i=0; i<amount; i++)
4413 (myrand()%(MAP_BLOCKSIZE-2))+1,
4414 (myrand()%(MAP_BLOCKSIZE-2))+1,
4415 (myrand()%(MAP_BLOCKSIZE-2))+1
4419 n.d = CONTENT_STONE;
4420 n.param = MINERAL_IRON;
4422 for(u16 i=0; i<27; i++)
4424 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4426 block->setNode(cp+g_27dirs[i], n);
4433 Create a few rats in empty blocks underground
4435 if(completely_underground)
4437 //for(u16 i=0; i<2; i++)
4440 (myrand()%(MAP_BLOCKSIZE-2))+1,
4441 (myrand()%(MAP_BLOCKSIZE-2))+1,
4442 (myrand()%(MAP_BLOCKSIZE-2))+1
4445 // Check that the place is empty
4446 //if(!is_ground_content(block->getNode(cp).d))
4449 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4450 block->addObject(obj);
4456 Add block to sector.
4458 sector->insertBlock(block);
4460 // Lighting is invalid after generation.
4461 block->setLightingExpired(true);
4468 <<"lighting_invalidated_blocks.size()"
4472 <<" "<<lighting_invalidated_blocks.size()
4474 <<", "<<completely_underground
4475 <<", "<<some_part_underground
4482 MapBlock * ServerMap::createBlock(v3s16 p)
4484 DSTACK("%s: p=(%d,%d,%d)",
4485 __FUNCTION_NAME, p.X, p.Y, p.Z);
4488 Do not create over-limit
4490 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4491 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4492 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4493 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4494 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4495 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4496 throw InvalidPositionException("createBlock(): pos. over limit");
4498 v2s16 p2d(p.X, p.Z);
4501 This will create or load a sector if not found in memory.
4502 If block exists on disk, it will be loaded.
4504 NOTE: On old save formats, this will be slow, as it generates
4505 lighting on blocks for them.
4507 ServerMapSector *sector;
4509 sector = (ServerMapSector*)createSector(p2d);
4510 assert(sector->getId() == MAPSECTOR_SERVER);
4512 catch(InvalidPositionException &e)
4514 dstream<<"createBlock: createSector() failed"<<std::endl;
4518 NOTE: This should not be done, or at least the exception
4519 should not be passed on as std::exception, because it
4520 won't be catched at all.
4522 /*catch(std::exception &e)
4524 dstream<<"createBlock: createSector() failed: "
4525 <<e.what()<<std::endl;
4530 Try to get a block from the sector
4533 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4537 block = sector->createBlankBlock(block_y);
4541 MapBlock * ServerMap::emergeBlock(
4543 bool only_from_disk,
4544 core::map<v3s16, MapBlock*> &changed_blocks,
4545 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4548 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4550 p.X, p.Y, p.Z, only_from_disk);
4553 Do not generate over-limit
4555 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4556 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4557 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4558 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4559 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4560 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4561 throw InvalidPositionException("emergeBlock(): pos. over limit");
4563 v2s16 p2d(p.X, p.Z);
4566 This will create or load a sector if not found in memory.
4567 If block exists on disk, it will be loaded.
4569 ServerMapSector *sector;
4571 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4572 assert(sector->getId() == MAPSECTOR_SERVER);
4574 catch(InvalidPositionException &e)
4576 dstream<<"emergeBlock: emergeSector() failed: "
4577 <<e.what()<<std::endl;
4578 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4580 <<"You could try to delete it."<<std::endl;
4583 catch(VersionMismatchException &e)
4585 dstream<<"emergeBlock: emergeSector() failed: "
4586 <<e.what()<<std::endl;
4587 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4589 <<"You could try to delete it."<<std::endl;
4593 NOTE: This should not be done, or at least the exception
4594 should not be passed on as std::exception, because it
4595 won't be catched at all.
4597 /*catch(std::exception &e)
4599 dstream<<"emergeBlock: emergeSector() failed: "
4600 <<e.what()<<std::endl;
4601 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4603 <<"You could try to delete it."<<std::endl;
4608 Try to get a block from the sector
4611 bool does_not_exist = false;
4612 bool lighting_expired = false;
4613 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4617 does_not_exist = true;
4619 else if(block->isDummy() == true)
4621 does_not_exist = true;
4623 else if(block->getLightingExpired())
4625 lighting_expired = true;
4630 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4635 If block was not found on disk and not going to generate a
4636 new one, make sure there is a dummy block in place.
4638 if(only_from_disk && (does_not_exist || lighting_expired))
4640 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4644 // Create dummy block
4645 block = new MapBlock(this, p, true);
4647 // Add block to sector
4648 sector->insertBlock(block);
4654 //dstream<<"Not found on disk, generating."<<std::endl;
4656 //TimeTaker("emergeBlock() generate");
4658 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4661 If the block doesn't exist, generate the block.
4665 block = generateBlock(p, block, sector, changed_blocks,
4666 lighting_invalidated_blocks);
4669 if(lighting_expired)
4671 lighting_invalidated_blocks.insert(p, block);
4675 Initially update sunlight
4679 core::map<v3s16, bool> light_sources;
4680 bool black_air_left = false;
4681 bool bottom_invalid =
4682 block->propagateSunlight(light_sources, true,
4683 &black_air_left, true);
4685 // If sunlight didn't reach everywhere and part of block is
4686 // above ground, lighting has to be properly updated
4687 //if(black_air_left && some_part_underground)
4690 lighting_invalidated_blocks[block->getPos()] = block;
4695 lighting_invalidated_blocks[block->getPos()] = block;
4702 s16 ServerMap::findGroundLevel(v2s16 p2d)
4705 Uh, just do something random...
4707 // Find existing map from top to down
4710 v3s16 p(p2d.X, max, p2d.Y);
4711 for(; p.Y>min; p.Y--)
4713 MapNode n = getNodeNoEx(p);
4714 if(n.d != CONTENT_IGNORE)
4719 // If this node is not air, go to plan b
4720 if(getNodeNoEx(p).d != CONTENT_AIR)
4722 // Search existing walkable and return it
4723 for(; p.Y>min; p.Y--)
4725 MapNode n = getNodeNoEx(p);
4726 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4732 Plan B: Get from map generator perlin noise function
4734 double level = base_rock_level_2d(m_seed, p2d);
4738 void ServerMap::createDir(std::string path)
4740 if(fs::CreateDir(path) == false)
4742 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4743 <<"\""<<path<<"\""<<std::endl;
4744 throw BaseException("ServerMap failed to create directory");
4748 std::string ServerMap::getSectorSubDir(v2s16 pos)
4751 snprintf(cc, 9, "%.4x%.4x",
4752 (unsigned int)pos.X&0xffff,
4753 (unsigned int)pos.Y&0xffff);
4755 return std::string(cc);
4758 std::string ServerMap::getSectorDir(v2s16 pos)
4760 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4763 v2s16 ServerMap::getSectorPos(std::string dirname)
4765 if(dirname.size() != 8)
4766 throw InvalidFilenameException("Invalid sector directory name");
4768 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4770 throw InvalidFilenameException("Invalid sector directory name");
4771 v2s16 pos((s16)x, (s16)y);
4775 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4777 v2s16 p2d = getSectorPos(sectordir);
4779 if(blockfile.size() != 4){
4780 throw InvalidFilenameException("Invalid block filename");
4783 int r = sscanf(blockfile.c_str(), "%4x", &y);
4785 throw InvalidFilenameException("Invalid block filename");
4786 return v3s16(p2d.X, y, p2d.Y);
4789 void ServerMap::save(bool only_changed)
4791 DSTACK(__FUNCTION_NAME);
4792 if(m_map_saving_enabled == false)
4794 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4798 if(only_changed == false)
4799 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4805 u32 sector_meta_count = 0;
4806 u32 block_count = 0;
4809 JMutexAutoLock lock(m_sector_mutex);
4811 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4812 for(; i.atEnd() == false; i++)
4814 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4815 assert(sector->getId() == MAPSECTOR_SERVER);
4817 if(sector->differs_from_disk || only_changed == false)
4819 saveSectorMeta(sector);
4820 sector_meta_count++;
4822 core::list<MapBlock*> blocks;
4823 sector->getBlocks(blocks);
4824 core::list<MapBlock*>::Iterator j;
4825 for(j=blocks.begin(); j!=blocks.end(); j++)
4827 MapBlock *block = *j;
4828 if(block->getChangedFlag() || only_changed == false)
4833 /*dstream<<"ServerMap: Written block ("
4834 <<block->getPos().X<<","
4835 <<block->getPos().Y<<","
4836 <<block->getPos().Z<<")"
4845 Only print if something happened or saved whole map
4847 if(only_changed == false || sector_meta_count != 0
4848 || block_count != 0)
4850 dstream<<DTIME<<"ServerMap: Written: "
4851 <<sector_meta_count<<" sector metadata files, "
4852 <<block_count<<" block files"
4857 void ServerMap::loadAll()
4859 DSTACK(__FUNCTION_NAME);
4860 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4865 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4867 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4869 JMutexAutoLock lock(m_sector_mutex);
4872 s32 printed_counter = -100000;
4873 s32 count = list.size();
4875 std::vector<fs::DirListNode>::iterator i;
4876 for(i=list.begin(); i!=list.end(); i++)
4878 if(counter > printed_counter + 10)
4880 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4881 printed_counter = counter;
4885 MapSector *sector = NULL;
4887 // We want directories
4891 sector = loadSectorMeta(i->name);
4893 catch(InvalidFilenameException &e)
4895 // This catches unknown crap in directory
4898 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4899 (m_savedir+"/sectors/"+i->name);
4900 std::vector<fs::DirListNode>::iterator i2;
4901 for(i2=list2.begin(); i2!=list2.end(); i2++)
4907 loadBlock(i->name, i2->name, sector);
4909 catch(InvalidFilenameException &e)
4911 // This catches unknown crap in directory
4915 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4919 void ServerMap::saveMasterHeightmap()
4921 DSTACK(__FUNCTION_NAME);
4923 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4925 createDir(m_savedir);
4927 /*std::string fullpath = m_savedir + "/master_heightmap";
4928 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4929 if(o.good() == false)
4930 throw FileNotGoodException("Cannot open master heightmap");*/
4932 // Format used for writing
4933 //u8 version = SER_FMT_VER_HIGHEST;
4936 void ServerMap::loadMasterHeightmap()
4938 DSTACK(__FUNCTION_NAME);
4940 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4942 /*std::string fullpath = m_savedir + "/master_heightmap";
4943 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4944 if(is.good() == false)
4945 throw FileNotGoodException("Cannot open master heightmap");*/
4949 void ServerMap::saveMapMeta()
4951 DSTACK(__FUNCTION_NAME);
4953 dstream<<"INFO: ServerMap::saveMapMeta(): "
4954 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4957 createDir(m_savedir);
4959 std::string fullpath = m_savedir + "/map_meta.txt";
4960 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4961 if(os.good() == false)
4963 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4964 <<"could not open"<<fullpath<<std::endl;
4965 throw FileNotGoodException("Cannot open chunk metadata");
4969 params.setU64("seed", m_seed);
4970 params.setS32("chunksize", m_chunksize);
4972 params.writeLines(os);
4974 os<<"[end_of_params]\n";
4978 void ServerMap::loadMapMeta()
4980 DSTACK(__FUNCTION_NAME);
4982 dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
4985 std::string fullpath = m_savedir + "/map_meta.txt";
4986 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4987 if(is.good() == false)
4989 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4990 <<"could not open"<<fullpath<<std::endl;
4991 throw FileNotGoodException("Cannot open chunk metadata");
4999 throw SerializationError
5000 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5002 std::getline(is, line);
5003 std::string trimmedline = trim(line);
5004 if(trimmedline == "[end_of_params]")
5006 params.parseConfigLine(line);
5009 m_seed = params.getU64("seed");
5010 m_chunksize = params.getS32("chunksize");
5012 dstream<<"INFO: ServerMap::loadMapMeta(): "
5013 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5017 void ServerMap::saveChunkMeta()
5019 DSTACK(__FUNCTION_NAME);
5021 u32 count = m_chunks.size();
5023 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5024 <<count<<" chunks"<<std::endl;
5026 createDir(m_savedir);
5028 std::string fullpath = m_savedir + "/chunk_meta";
5029 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5030 if(os.good() == false)
5032 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5033 <<"could not open"<<fullpath<<std::endl;
5034 throw FileNotGoodException("Cannot open chunk metadata");
5040 os.write((char*)&version, 1);
5045 writeU32(buf, count);
5046 os.write((char*)buf, 4);
5048 for(core::map<v2s16, MapChunk*>::Iterator
5049 i = m_chunks.getIterator();
5050 i.atEnd()==false; i++)
5052 v2s16 p = i.getNode()->getKey();
5053 MapChunk *chunk = i.getNode()->getValue();
5056 os.write((char*)buf, 4);
5058 chunk->serialize(os, version);
5062 void ServerMap::loadChunkMeta()
5064 DSTACK(__FUNCTION_NAME);
5066 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5069 std::string fullpath = m_savedir + "/chunk_meta";
5070 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5071 if(is.good() == false)
5073 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5074 <<"could not open"<<fullpath<<std::endl;
5075 throw FileNotGoodException("Cannot open chunk metadata");
5081 is.read((char*)&version, 1);
5086 is.read((char*)buf, 4);
5087 u32 count = readU32(buf);
5089 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5090 <<count<<" chunks"<<std::endl;
5092 for(u32 i=0; i<count; i++)
5095 MapChunk *chunk = new MapChunk();
5097 is.read((char*)buf, 4);
5100 chunk->deSerialize(is, version);
5101 m_chunks.insert(p, chunk);
5105 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5107 DSTACK(__FUNCTION_NAME);
5108 // Format used for writing
5109 u8 version = SER_FMT_VER_HIGHEST;
5111 v2s16 pos = sector->getPos();
5112 createDir(m_savedir);
5113 createDir(m_savedir+"/sectors");
5114 std::string dir = getSectorDir(pos);
5117 std::string fullpath = dir + "/meta";
5118 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5119 if(o.good() == false)
5120 throw FileNotGoodException("Cannot open sector metafile");
5122 sector->serialize(o, version);
5124 sector->differs_from_disk = false;
5127 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5129 DSTACK(__FUNCTION_NAME);
5131 v2s16 p2d = getSectorPos(dirname);
5132 std::string dir = m_savedir + "/sectors/" + dirname;
5134 std::string fullpath = dir + "/meta";
5135 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5136 if(is.good() == false)
5137 throw FileNotGoodException("Cannot open sector metafile");
5139 ServerMapSector *sector = ServerMapSector::deSerialize
5140 (is, this, p2d, m_sectors);
5142 sector->differs_from_disk = false;
5147 bool ServerMap::loadSectorFull(v2s16 p2d)
5149 DSTACK(__FUNCTION_NAME);
5150 std::string sectorsubdir = getSectorSubDir(p2d);
5152 MapSector *sector = NULL;
5154 JMutexAutoLock lock(m_sector_mutex);
5157 sector = loadSectorMeta(sectorsubdir);
5159 catch(InvalidFilenameException &e)
5163 catch(FileNotGoodException &e)
5167 catch(std::exception &e)
5175 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5176 (m_savedir+"/sectors/"+sectorsubdir);
5177 std::vector<fs::DirListNode>::iterator i2;
5178 for(i2=list2.begin(); i2!=list2.end(); i2++)
5184 loadBlock(sectorsubdir, i2->name, sector);
5186 catch(InvalidFilenameException &e)
5188 // This catches unknown crap in directory
5194 void ServerMap::saveBlock(MapBlock *block)
5196 DSTACK(__FUNCTION_NAME);
5198 Dummy blocks are not written
5200 if(block->isDummy())
5202 /*v3s16 p = block->getPos();
5203 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5204 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5208 // Format used for writing
5209 u8 version = SER_FMT_VER_HIGHEST;
5211 v3s16 p3d = block->getPos();
5212 v2s16 p2d(p3d.X, p3d.Z);
5213 createDir(m_savedir);
5214 createDir(m_savedir+"/sectors");
5215 std::string dir = getSectorDir(p2d);
5218 // Block file is map/sectors/xxxxxxxx/xxxx
5220 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5221 std::string fullpath = dir + "/" + cc;
5222 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5223 if(o.good() == false)
5224 throw FileNotGoodException("Cannot open block data");
5227 [0] u8 serialization version
5230 o.write((char*)&version, 1);
5232 block->serialize(o, version);
5235 Versions up from 9 have block objects.
5239 block->serializeObjects(o, version);
5242 // We just wrote it to the disk
5243 block->resetChangedFlag();
5246 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5248 DSTACK(__FUNCTION_NAME);
5250 // Block file is map/sectors/xxxxxxxx/xxxx
5251 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5254 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5255 if(is.good() == false)
5256 throw FileNotGoodException("Cannot open block file");
5258 v3s16 p3d = getBlockPos(sectordir, blockfile);
5259 v2s16 p2d(p3d.X, p3d.Z);
5261 assert(sector->getPos() == p2d);
5263 u8 version = SER_FMT_VER_INVALID;
5264 is.read((char*)&version, 1);
5267 throw SerializationError("ServerMap::loadBlock(): Failed"
5268 " to read MapBlock version");
5270 /*u32 block_size = MapBlock::serializedLength(version);
5271 SharedBuffer<u8> data(block_size);
5272 is.read((char*)*data, block_size);*/
5274 // This will always return a sector because we're the server
5275 //MapSector *sector = emergeSector(p2d);
5277 MapBlock *block = NULL;
5278 bool created_new = false;
5280 block = sector->getBlockNoCreate(p3d.Y);
5282 catch(InvalidPositionException &e)
5284 block = sector->createBlankBlockNoInsert(p3d.Y);
5288 // deserialize block data
5289 block->deSerialize(is, version);
5292 Versions up from 9 have block objects.
5296 block->updateObjects(is, version, NULL, 0);
5300 sector->insertBlock(block);
5303 Convert old formats to new and save
5306 // Save old format blocks in new format
5307 if(version < SER_FMT_VER_HIGHEST)
5312 // We just loaded it from the disk, so it's up-to-date.
5313 block->resetChangedFlag();
5316 catch(SerializationError &e)
5318 dstream<<"WARNING: Invalid block data on disk "
5319 "(SerializationError). Ignoring. "
5320 "A new one will be generated."
5323 // TODO: Backup file; name is in fullpath.
5327 void ServerMap::PrintInfo(std::ostream &out)
5338 ClientMap::ClientMap(
5340 MapDrawControl &control,
5341 scene::ISceneNode* parent,
5342 scene::ISceneManager* mgr,
5346 scene::ISceneNode(parent, mgr, id),
5349 m_camera_position(0,0,0),
5350 m_camera_direction(0,0,1)
5352 m_camera_mutex.Init();
5353 assert(m_camera_mutex.IsInitialized());
5355 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5356 BS*1000000,BS*1000000,BS*1000000);
5359 ClientMap::~ClientMap()
5361 /*JMutexAutoLock lock(mesh_mutex);
5370 MapSector * ClientMap::emergeSector(v2s16 p2d)
5372 DSTACK(__FUNCTION_NAME);
5373 // Check that it doesn't exist already
5375 return getSectorNoGenerate(p2d);
5377 catch(InvalidPositionException &e)
5382 ClientMapSector *sector = new ClientMapSector(this, p2d);
5385 JMutexAutoLock lock(m_sector_mutex);
5386 m_sectors.insert(p2d, sector);
5392 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5394 DSTACK(__FUNCTION_NAME);
5395 ClientMapSector *sector = NULL;
5397 JMutexAutoLock lock(m_sector_mutex);
5399 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5403 sector = (ClientMapSector*)n->getValue();
5404 assert(sector->getId() == MAPSECTOR_CLIENT);
5408 sector = new ClientMapSector(this, p2d);
5410 JMutexAutoLock lock(m_sector_mutex);
5411 m_sectors.insert(p2d, sector);
5415 sector->deSerialize(is);
5418 void ClientMap::OnRegisterSceneNode()
5422 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5423 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5426 ISceneNode::OnRegisterSceneNode();
5429 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5431 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5432 DSTACK(__FUNCTION_NAME);
5434 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5437 Get time for measuring timeout.
5439 Measuring time is very useful for long delays when the
5440 machine is swapping a lot.
5442 int time1 = time(0);
5444 //u32 daynight_ratio = m_client->getDayNightRatio();
5446 m_camera_mutex.Lock();
5447 v3f camera_position = m_camera_position;
5448 v3f camera_direction = m_camera_direction;
5449 m_camera_mutex.Unlock();
5452 Get all blocks and draw all visible ones
5455 v3s16 cam_pos_nodes(
5456 camera_position.X / BS,
5457 camera_position.Y / BS,
5458 camera_position.Z / BS);
5460 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5462 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5463 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5465 // Take a fair amount as we will be dropping more out later
5467 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5468 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5469 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5471 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5472 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5473 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5475 u32 vertex_count = 0;
5477 // For limiting number of mesh updates per frame
5478 u32 mesh_update_count = 0;
5480 u32 blocks_would_have_drawn = 0;
5481 u32 blocks_drawn = 0;
5483 //NOTE: The sectors map should be locked but we're not doing it
5484 // because it'd cause too much delays
5486 int timecheck_counter = 0;
5487 core::map<v2s16, MapSector*>::Iterator si;
5488 si = m_sectors.getIterator();
5489 for(; si.atEnd() == false; si++)
5492 timecheck_counter++;
5493 if(timecheck_counter > 50)
5495 timecheck_counter = 0;
5496 int time2 = time(0);
5497 if(time2 > time1 + 4)
5499 dstream<<"ClientMap::renderMap(): "
5500 "Rendering takes ages, returning."
5507 MapSector *sector = si.getNode()->getValue();
5508 v2s16 sp = sector->getPos();
5510 if(m_control.range_all == false)
5512 if(sp.X < p_blocks_min.X
5513 || sp.X > p_blocks_max.X
5514 || sp.Y < p_blocks_min.Z
5515 || sp.Y > p_blocks_max.Z)
5519 core::list< MapBlock * > sectorblocks;
5520 sector->getBlocks(sectorblocks);
5526 core::list< MapBlock * >::Iterator i;
5527 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5529 MapBlock *block = *i;
5532 Compare block position to camera position, skip
5533 if not seen on display
5536 float range = 100000 * BS;
5537 if(m_control.range_all == false)
5538 range = m_control.wanted_range * BS;
5541 if(isBlockInSight(block->getPos(), camera_position,
5542 camera_direction, range, &d) == false)
5547 // This is ugly (spherical distance limit?)
5548 /*if(m_control.range_all == false &&
5549 d - 0.5*BS*MAP_BLOCKSIZE > range)
5554 Update expired mesh (used for day/night change)
5556 It doesn't work exactly like it should now with the
5557 tasked mesh update but whatever.
5560 bool mesh_expired = false;
5563 JMutexAutoLock lock(block->mesh_mutex);
5565 mesh_expired = block->getMeshExpired();
5567 // Mesh has not been expired and there is no mesh:
5568 // block has no content
5569 if(block->mesh == NULL && mesh_expired == false)
5573 f32 faraway = BS*50;
5574 //f32 faraway = m_control.wanted_range * BS;
5577 This has to be done with the mesh_mutex unlocked
5579 // Pretty random but this should work somewhat nicely
5580 if(mesh_expired && (
5581 (mesh_update_count < 3
5582 && (d < faraway || mesh_update_count < 2)
5585 (m_control.range_all && mesh_update_count < 20)
5588 /*if(mesh_expired && mesh_update_count < 6
5589 && (d < faraway || mesh_update_count < 3))*/
5591 mesh_update_count++;
5593 // Mesh has been expired: generate new mesh
5594 //block->updateMesh(daynight_ratio);
5595 m_client->addUpdateMeshTask(block->getPos());
5597 mesh_expired = false;
5602 Draw the faces of the block
5605 JMutexAutoLock lock(block->mesh_mutex);
5607 scene::SMesh *mesh = block->mesh;
5612 blocks_would_have_drawn++;
5613 if(blocks_drawn >= m_control.wanted_max_blocks
5614 && m_control.range_all == false
5615 && d > m_control.wanted_min_range * BS)
5619 u32 c = mesh->getMeshBufferCount();
5621 for(u32 i=0; i<c; i++)
5623 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5624 const video::SMaterial& material = buf->getMaterial();
5625 video::IMaterialRenderer* rnd =
5626 driver->getMaterialRenderer(material.MaterialType);
5627 bool transparent = (rnd && rnd->isTransparent());
5628 // Render transparent on transparent pass and likewise.
5629 if(transparent == is_transparent_pass)
5632 This *shouldn't* hurt too much because Irrlicht
5633 doesn't change opengl textures if the old
5634 material is set again.
5636 driver->setMaterial(buf->getMaterial());
5637 driver->drawMeshBuffer(buf);
5638 vertex_count += buf->getVertexCount();
5642 } // foreach sectorblocks
5645 m_control.blocks_drawn = blocks_drawn;
5646 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5648 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5649 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5652 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5653 core::map<v3s16, MapBlock*> *affected_blocks)
5655 bool changed = false;
5657 Add it to all blocks touching it
5660 v3s16(0,0,0), // this
5661 v3s16(0,0,1), // back
5662 v3s16(0,1,0), // top
5663 v3s16(1,0,0), // right
5664 v3s16(0,0,-1), // front
5665 v3s16(0,-1,0), // bottom
5666 v3s16(-1,0,0), // left
5668 for(u16 i=0; i<7; i++)
5670 v3s16 p2 = p + dirs[i];
5671 // Block position of neighbor (or requested) node
5672 v3s16 blockpos = getNodeBlockPos(p2);
5673 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5674 if(blockref == NULL)
5676 // Relative position of requested node
5677 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5678 if(blockref->setTempMod(relpos, mod))
5683 if(changed && affected_blocks!=NULL)
5685 for(u16 i=0; i<7; i++)
5687 v3s16 p2 = p + dirs[i];
5688 // Block position of neighbor (or requested) node
5689 v3s16 blockpos = getNodeBlockPos(p2);
5690 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5691 if(blockref == NULL)
5693 affected_blocks->insert(blockpos, blockref);
5699 bool ClientMap::clearTempMod(v3s16 p,
5700 core::map<v3s16, MapBlock*> *affected_blocks)
5702 bool changed = false;
5704 v3s16(0,0,0), // this
5705 v3s16(0,0,1), // back
5706 v3s16(0,1,0), // top
5707 v3s16(1,0,0), // right
5708 v3s16(0,0,-1), // front
5709 v3s16(0,-1,0), // bottom
5710 v3s16(-1,0,0), // left
5712 for(u16 i=0; i<7; i++)
5714 v3s16 p2 = p + dirs[i];
5715 // Block position of neighbor (or requested) node
5716 v3s16 blockpos = getNodeBlockPos(p2);
5717 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5718 if(blockref == NULL)
5720 // Relative position of requested node
5721 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5722 if(blockref->clearTempMod(relpos))
5727 if(changed && affected_blocks!=NULL)
5729 for(u16 i=0; i<7; i++)
5731 v3s16 p2 = p + dirs[i];
5732 // Block position of neighbor (or requested) node
5733 v3s16 blockpos = getNodeBlockPos(p2);
5734 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5735 if(blockref == NULL)
5737 affected_blocks->insert(blockpos, blockref);
5743 void ClientMap::expireMeshes(bool only_daynight_diffed)
5745 TimeTaker timer("expireMeshes()");
5747 core::map<v2s16, MapSector*>::Iterator si;
5748 si = m_sectors.getIterator();
5749 for(; si.atEnd() == false; si++)
5751 MapSector *sector = si.getNode()->getValue();
5753 core::list< MapBlock * > sectorblocks;
5754 sector->getBlocks(sectorblocks);
5756 core::list< MapBlock * >::Iterator i;
5757 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5759 MapBlock *block = *i;
5761 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5767 JMutexAutoLock lock(block->mesh_mutex);
5768 if(block->mesh != NULL)
5770 /*block->mesh->drop();
5771 block->mesh = NULL;*/
5772 block->setMeshExpired(true);
5779 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5781 assert(mapType() == MAPTYPE_CLIENT);
5784 v3s16 p = blockpos + v3s16(0,0,0);
5785 MapBlock *b = getBlockNoCreate(p);
5786 b->updateMesh(daynight_ratio);
5787 //b->setMeshExpired(true);
5789 catch(InvalidPositionException &e){}
5792 v3s16 p = blockpos + v3s16(-1,0,0);
5793 MapBlock *b = getBlockNoCreate(p);
5794 b->updateMesh(daynight_ratio);
5795 //b->setMeshExpired(true);
5797 catch(InvalidPositionException &e){}
5799 v3s16 p = blockpos + v3s16(0,-1,0);
5800 MapBlock *b = getBlockNoCreate(p);
5801 b->updateMesh(daynight_ratio);
5802 //b->setMeshExpired(true);
5804 catch(InvalidPositionException &e){}
5806 v3s16 p = blockpos + v3s16(0,0,-1);
5807 MapBlock *b = getBlockNoCreate(p);
5808 b->updateMesh(daynight_ratio);
5809 //b->setMeshExpired(true);
5811 catch(InvalidPositionException &e){}
5816 Update mesh of block in which the node is, and if the node is at the
5817 leading edge, update the appropriate leading blocks too.
5819 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5827 v3s16 blockposes[4];
5828 for(u32 i=0; i<4; i++)
5830 v3s16 np = nodepos + dirs[i];
5831 blockposes[i] = getNodeBlockPos(np);
5832 // Don't update mesh of block if it has been done already
5833 bool already_updated = false;
5834 for(u32 j=0; j<i; j++)
5836 if(blockposes[j] == blockposes[i])
5838 already_updated = true;
5845 MapBlock *b = getBlockNoCreate(blockposes[i]);
5846 b->updateMesh(daynight_ratio);
5851 void ClientMap::PrintInfo(std::ostream &out)
5862 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5867 MapVoxelManipulator::~MapVoxelManipulator()
5869 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5873 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5875 TimeTaker timer1("emerge", &emerge_time);
5877 // Units of these are MapBlocks
5878 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5879 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5881 VoxelArea block_area_nodes
5882 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5884 addArea(block_area_nodes);
5886 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5887 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5888 for(s32 x=p_min.X; x<=p_max.X; x++)
5891 core::map<v3s16, bool>::Node *n;
5892 n = m_loaded_blocks.find(p);
5896 bool block_data_inexistent = false;
5899 TimeTaker timer1("emerge load", &emerge_load_time);
5901 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5902 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5905 dstream<<std::endl;*/
5907 MapBlock *block = m_map->getBlockNoCreate(p);
5908 if(block->isDummy())
5909 block_data_inexistent = true;
5911 block->copyTo(*this);
5913 catch(InvalidPositionException &e)
5915 block_data_inexistent = true;
5918 if(block_data_inexistent)
5920 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5921 // Fill with VOXELFLAG_INEXISTENT
5922 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5923 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5925 s32 i = m_area.index(a.MinEdge.X,y,z);
5926 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5930 m_loaded_blocks.insert(p, !block_data_inexistent);
5933 //dstream<<"emerge done"<<std::endl;
5937 SUGG: Add an option to only update eg. water and air nodes.
5938 This will make it interfere less with important stuff if
5941 void MapVoxelManipulator::blitBack
5942 (core::map<v3s16, MapBlock*> & modified_blocks)
5944 if(m_area.getExtent() == v3s16(0,0,0))
5947 //TimeTaker timer1("blitBack");
5949 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5950 <<m_loaded_blocks.size()<<std::endl;*/
5953 Initialize block cache
5955 v3s16 blockpos_last;
5956 MapBlock *block = NULL;
5957 bool block_checked_in_modified = false;
5959 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5960 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5961 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5965 u8 f = m_flags[m_area.index(p)];
5966 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5969 MapNode &n = m_data[m_area.index(p)];
5971 v3s16 blockpos = getNodeBlockPos(p);
5976 if(block == NULL || blockpos != blockpos_last){
5977 block = m_map->getBlockNoCreate(blockpos);
5978 blockpos_last = blockpos;
5979 block_checked_in_modified = false;
5982 // Calculate relative position in block
5983 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5985 // Don't continue if nothing has changed here
5986 if(block->getNode(relpos) == n)
5989 //m_map->setNode(m_area.MinEdge + p, n);
5990 block->setNode(relpos, n);
5993 Make sure block is in modified_blocks
5995 if(block_checked_in_modified == false)
5997 modified_blocks[blockpos] = block;
5998 block_checked_in_modified = true;
6001 catch(InvalidPositionException &e)
6007 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6008 MapVoxelManipulator(map)
6012 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6016 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6018 // Just create the area so that it can be pointed to
6019 VoxelManipulator::emerge(a, caller_id);
6022 void ManualMapVoxelManipulator::initialEmerge(
6023 v3s16 blockpos_min, v3s16 blockpos_max)
6025 TimeTaker timer1("initialEmerge", &emerge_time);
6027 // Units of these are MapBlocks
6028 v3s16 p_min = blockpos_min;
6029 v3s16 p_max = blockpos_max;
6031 VoxelArea block_area_nodes
6032 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6034 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6037 dstream<<"initialEmerge: area: ";
6038 block_area_nodes.print(dstream);
6039 dstream<<" ("<<size_MB<<"MB)";
6043 addArea(block_area_nodes);
6045 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6046 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6047 for(s32 x=p_min.X; x<=p_max.X; x++)
6050 core::map<v3s16, bool>::Node *n;
6051 n = m_loaded_blocks.find(p);
6055 bool block_data_inexistent = false;
6058 TimeTaker timer1("emerge load", &emerge_load_time);
6060 MapBlock *block = m_map->getBlockNoCreate(p);
6061 if(block->isDummy())
6062 block_data_inexistent = true;
6064 block->copyTo(*this);
6066 catch(InvalidPositionException &e)
6068 block_data_inexistent = true;
6071 if(block_data_inexistent)
6074 Mark area inexistent
6076 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6077 // Fill with VOXELFLAG_INEXISTENT
6078 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6079 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6081 s32 i = m_area.index(a.MinEdge.X,y,z);
6082 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6086 m_loaded_blocks.insert(p, !block_data_inexistent);
6090 void ManualMapVoxelManipulator::blitBackAll(
6091 core::map<v3s16, MapBlock*> * modified_blocks)
6093 if(m_area.getExtent() == v3s16(0,0,0))
6097 Copy data of all blocks
6099 for(core::map<v3s16, bool>::Iterator
6100 i = m_loaded_blocks.getIterator();
6101 i.atEnd() == false; i++)
6103 bool existed = i.getNode()->getValue();
6104 if(existed == false)
6106 v3s16 p = i.getNode()->getKey();
6107 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6110 dstream<<"WARNING: "<<__FUNCTION_NAME
6111 <<": got NULL block "
6112 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6117 block->copyFrom(*this);
6120 modified_blocks->insert(p, block);