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