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"
34 Map::Map(std::ostream &dout):
36 m_camera_position(0,0,0),
37 m_camera_direction(0,0,1),
41 m_sector_mutex.Init();
42 m_camera_mutex.Init();
43 assert(m_sector_mutex.IsInitialized());
44 assert(m_camera_mutex.IsInitialized());
46 // Get this so that the player can stay on it at first
47 //getSector(v2s16(0,0));
55 /*updater.setRun(false);
56 while(updater.IsRunning())
62 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
63 for(; i.atEnd() == false; i++)
65 MapSector *sector = i.getNode()->getValue();
70 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
72 if(m_sector_cache != NULL && p == m_sector_cache_p){
73 MapSector * sector = m_sector_cache;
74 // Reset inactivity timer
75 sector->usage_timer = 0.0;
79 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
84 MapSector *sector = n->getValue();
86 // Cache the last result
88 m_sector_cache = sector;
90 // Reset inactivity timer
91 sector->usage_timer = 0.0;
95 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
97 JMutexAutoLock lock(m_sector_mutex);
99 return getSectorNoGenerateNoExNoLock(p);
102 MapSector * Map::getSectorNoGenerate(v2s16 p)
104 MapSector *sector = getSectorNoGenerateNoEx(p);
106 throw InvalidPositionException();
111 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
113 v2s16 p2d(p3d.X, p3d.Z);
114 MapSector * sector = getSectorNoGenerate(p2d);
116 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
121 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerate(p2d);
127 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
130 catch(InvalidPositionException &e)
136 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
138 v2s16 p2d(p3d.X, p3d.Z);
139 MapSector * sector = getSectorCreate(p2d);
141 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
144 block = sector->createBlankBlock(p3d.Y);
148 f32 Map::getGroundHeight(v2s16 p, bool generate)
151 v2s16 sectorpos = getNodeSectorPos(p);
152 MapSector * sref = getSectorNoGenerate(sectorpos);
153 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
154 f32 y = sref->getGroundHeight(relpos);
157 catch(InvalidPositionException &e)
159 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
163 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
165 /*m_dout<<DTIME<<"Map::setGroundHeight(("
167 <<"), "<<y<<")"<<std::endl;*/
168 v2s16 sectorpos = getNodeSectorPos(p);
169 MapSector * sref = getSectorNoGenerate(sectorpos);
170 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
171 //sref->mutex.Lock();
172 sref->setGroundHeight(relpos, y);
173 //sref->mutex.Unlock();
176 bool Map::isNodeUnderground(v3s16 p)
178 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock * block = getBlockNoCreate(blockpos);
181 return block->getIsUnderground();
183 catch(InvalidPositionException &e)
190 Goes recursively through the neighbours of the node.
192 Alters only transparent nodes.
194 If the lighting of the neighbour is lower than the lighting of
195 the node was (before changing it to 0 at the step before), the
196 lighting of the neighbour is set to 0 and then the same stuff
197 repeats for the neighbour.
199 The ending nodes of the routine are stored in light_sources.
200 This is useful when a light is removed. In such case, this
201 routine can be called for the light node and then again for
202 light_sources to re-light the area without the removed light.
204 values of from_nodes are lighting values.
206 void Map::unspreadLight(enum LightBank bank,
207 core::map<v3s16, u8> & from_nodes,
208 core::map<v3s16, bool> & light_sources,
209 core::map<v3s16, MapBlock*> & modified_blocks)
212 v3s16(0,0,1), // back
214 v3s16(1,0,0), // right
215 v3s16(0,0,-1), // front
216 v3s16(0,-1,0), // bottom
217 v3s16(-1,0,0), // left
220 if(from_nodes.size() == 0)
223 u32 blockchangecount = 0;
225 core::map<v3s16, u8> unlighted_nodes;
226 core::map<v3s16, u8>::Iterator j;
227 j = from_nodes.getIterator();
230 Initialize block cache
233 MapBlock *block = NULL;
234 // Cache this a bit, too
235 bool block_checked_in_modified = false;
237 for(; j.atEnd() == false; j++)
239 v3s16 pos = j.getNode()->getKey();
240 v3s16 blockpos = getNodeBlockPos(pos);
242 // Only fetch a new block if the block position has changed
244 if(block == NULL || blockpos != blockpos_last){
245 block = getBlockNoCreate(blockpos);
246 blockpos_last = blockpos;
248 block_checked_in_modified = false;
252 catch(InvalidPositionException &e)
260 // Calculate relative position in block
261 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
263 // Get node straight from the block
264 MapNode n = block->getNode(relpos);
266 u8 oldlight = j.getNode()->getValue();
268 // Loop through 6 neighbors
269 for(u16 i=0; i<6; i++)
271 // Get the position of the neighbor node
272 v3s16 n2pos = pos + dirs[i];
274 // Get the block where the node is located
275 v3s16 blockpos = getNodeBlockPos(n2pos);
279 // Only fetch a new block if the block position has changed
281 if(block == NULL || blockpos != blockpos_last){
282 block = getBlockNoCreate(blockpos);
283 blockpos_last = blockpos;
285 block_checked_in_modified = false;
289 catch(InvalidPositionException &e)
294 // Calculate relative position in block
295 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
296 // Get node straight from the block
297 MapNode n2 = block->getNode(relpos);
299 bool changed = false;
301 //TODO: Optimize output by optimizing light_sources?
304 If the neighbor is dimmer than what was specified
305 as oldlight (the light of the previous node)
307 if(n2.getLight(bank) < oldlight)
310 And the neighbor is transparent and it has some light
312 if(n2.light_propagates() && n2.getLight(bank) != 0)
315 Set light to 0 and add to queue
318 u8 current_light = n2.getLight(bank);
319 n2.setLight(bank, 0);
320 block->setNode(relpos, n2);
322 unlighted_nodes.insert(n2pos, current_light);
326 Remove from light_sources if it is there
327 NOTE: This doesn't happen nearly at all
329 /*if(light_sources.find(n2pos))
331 std::cout<<"Removed from light_sources"<<std::endl;
332 light_sources.remove(n2pos);
337 if(light_sources.find(n2pos) != NULL)
338 light_sources.remove(n2pos);*/
341 light_sources.insert(n2pos, true);
344 // Add to modified_blocks
345 if(changed == true && block_checked_in_modified == false)
347 // If the block is not found in modified_blocks, add.
348 if(modified_blocks.find(blockpos) == NULL)
350 modified_blocks.insert(blockpos, block);
352 block_checked_in_modified = true;
355 catch(InvalidPositionException &e)
362 /*dstream<<"unspreadLight(): Changed block "
363 <<blockchangecount<<" times"
364 <<" for "<<from_nodes.size()<<" nodes"
367 if(unlighted_nodes.size() > 0)
368 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
372 A single-node wrapper of the above
374 void Map::unLightNeighbors(enum LightBank bank,
375 v3s16 pos, u8 lightwas,
376 core::map<v3s16, bool> & light_sources,
377 core::map<v3s16, MapBlock*> & modified_blocks)
379 core::map<v3s16, u8> from_nodes;
380 from_nodes.insert(pos, lightwas);
382 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
386 Lights neighbors of from_nodes, collects all them and then
389 void Map::spreadLight(enum LightBank bank,
390 core::map<v3s16, bool> & from_nodes,
391 core::map<v3s16, MapBlock*> & modified_blocks)
393 const v3s16 dirs[6] = {
394 v3s16(0,0,1), // back
396 v3s16(1,0,0), // right
397 v3s16(0,0,-1), // front
398 v3s16(0,-1,0), // bottom
399 v3s16(-1,0,0), // left
402 if(from_nodes.size() == 0)
405 u32 blockchangecount = 0;
407 core::map<v3s16, bool> lighted_nodes;
408 core::map<v3s16, bool>::Iterator j;
409 j = from_nodes.getIterator();
412 Initialize block cache
415 MapBlock *block = NULL;
416 // Cache this a bit, too
417 bool block_checked_in_modified = false;
419 for(; j.atEnd() == false; j++)
420 //for(; j != from_nodes.end(); j++)
422 v3s16 pos = j.getNode()->getKey();
424 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
425 v3s16 blockpos = getNodeBlockPos(pos);
427 // Only fetch a new block if the block position has changed
429 if(block == NULL || blockpos != blockpos_last){
430 block = getBlockNoCreate(blockpos);
431 blockpos_last = blockpos;
433 block_checked_in_modified = false;
437 catch(InvalidPositionException &e)
445 // Calculate relative position in block
446 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
448 // Get node straight from the block
449 MapNode n = block->getNode(relpos);
451 u8 oldlight = n.getLight(bank);
452 u8 newlight = diminish_light(oldlight);
454 // Loop through 6 neighbors
455 for(u16 i=0; i<6; i++){
456 // Get the position of the neighbor node
457 v3s16 n2pos = pos + dirs[i];
459 // Get the block where the node is located
460 v3s16 blockpos = getNodeBlockPos(n2pos);
464 // Only fetch a new block if the block position has changed
466 if(block == NULL || blockpos != blockpos_last){
467 block = getBlockNoCreate(blockpos);
468 blockpos_last = blockpos;
470 block_checked_in_modified = false;
474 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
481 // Get node straight from the block
482 MapNode n2 = block->getNode(relpos);
484 bool changed = false;
486 If the neighbor is brighter than the current node,
487 add to list (it will light up this node on its turn)
489 if(n2.getLight(bank) > undiminish_light(oldlight))
491 lighted_nodes.insert(n2pos, true);
492 //lighted_nodes.push_back(n2pos);
496 If the neighbor is dimmer than how much light this node
497 would spread on it, add to list
499 if(n2.getLight(bank) < newlight)
501 if(n2.light_propagates())
503 n2.setLight(bank, newlight);
504 block->setNode(relpos, n2);
505 lighted_nodes.insert(n2pos, true);
506 //lighted_nodes.push_back(n2pos);
511 // Add to modified_blocks
512 if(changed == true && block_checked_in_modified == false)
514 // If the block is not found in modified_blocks, add.
515 if(modified_blocks.find(blockpos) == NULL)
517 modified_blocks.insert(blockpos, block);
519 block_checked_in_modified = true;
522 catch(InvalidPositionException &e)
529 /*dstream<<"spreadLight(): Changed block "
530 <<blockchangecount<<" times"
531 <<" for "<<from_nodes.size()<<" nodes"
534 if(lighted_nodes.size() > 0)
535 spreadLight(bank, lighted_nodes, modified_blocks);
539 A single-node source variation of the above.
541 void Map::lightNeighbors(enum LightBank bank,
543 core::map<v3s16, MapBlock*> & modified_blocks)
545 core::map<v3s16, bool> from_nodes;
546 from_nodes.insert(pos, true);
547 spreadLight(bank, from_nodes, modified_blocks);
550 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
553 v3s16(0,0,1), // back
555 v3s16(1,0,0), // right
556 v3s16(0,0,-1), // front
557 v3s16(0,-1,0), // bottom
558 v3s16(-1,0,0), // left
561 u8 brightest_light = 0;
562 v3s16 brightest_pos(0,0,0);
563 bool found_something = false;
565 // Loop through 6 neighbors
566 for(u16 i=0; i<6; i++){
567 // Get the position of the neighbor node
568 v3s16 n2pos = p + dirs[i];
573 catch(InvalidPositionException &e)
577 if(n2.getLight(bank) > brightest_light || found_something == false){
578 brightest_light = n2.getLight(bank);
579 brightest_pos = n2pos;
580 found_something = true;
584 if(found_something == false)
585 throw InvalidPositionException();
587 return brightest_pos;
591 Propagates sunlight down from a node.
592 Starting point gets sunlight.
594 Returns the lowest y value of where the sunlight went.
596 Mud is turned into grass in where the sunlight stops.
598 s16 Map::propagateSunlight(v3s16 start,
599 core::map<v3s16, MapBlock*> & modified_blocks)
604 v3s16 pos(start.X, y, start.Z);
606 v3s16 blockpos = getNodeBlockPos(pos);
609 block = getBlockNoCreate(blockpos);
611 catch(InvalidPositionException &e)
616 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
617 MapNode n = block->getNode(relpos);
619 if(n.sunlight_propagates())
621 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
622 block->setNode(relpos, n);
624 modified_blocks.insert(blockpos, block);
628 // Turn mud into grass
629 if(n.d == CONTENT_MUD)
632 block->setNode(relpos, n);
633 modified_blocks.insert(blockpos, block);
636 // Sunlight goes no further
643 void Map::updateLighting(enum LightBank bank,
644 core::map<v3s16, MapBlock*> & a_blocks,
645 core::map<v3s16, MapBlock*> & modified_blocks)
647 /*m_dout<<DTIME<<"Map::updateLighting(): "
648 <<a_blocks.size()<<" blocks."<<std::endl;*/
650 //TimeTaker timer("updateLighting");
654 //u32 count_was = modified_blocks.size();
656 core::map<v3s16, MapBlock*> blocks_to_update;
658 core::map<v3s16, bool> light_sources;
660 core::map<v3s16, u8> unlight_from;
662 core::map<v3s16, MapBlock*>::Iterator i;
663 i = a_blocks.getIterator();
664 for(; i.atEnd() == false; i++)
666 MapBlock *block = i.getNode()->getValue();
670 // Don't bother with dummy blocks.
674 v3s16 pos = block->getPos();
675 modified_blocks.insert(pos, block);
677 blocks_to_update.insert(pos, block);
680 Clear all light from block
682 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
683 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
684 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
689 MapNode n = block->getNode(v3s16(x,y,z));
690 u8 oldlight = n.getLight(bank);
692 block->setNode(v3s16(x,y,z), n);
694 // Collect borders for unlighting
695 if(x==0 || x == MAP_BLOCKSIZE-1
696 || y==0 || y == MAP_BLOCKSIZE-1
697 || z==0 || z == MAP_BLOCKSIZE-1)
699 v3s16 p_map = p + v3s16(
702 MAP_BLOCKSIZE*pos.Z);
703 unlight_from.insert(p_map, oldlight);
706 catch(InvalidPositionException &e)
709 This would happen when dealing with a
713 dstream<<"updateLighting(): InvalidPositionException"
718 if(bank == LIGHTBANK_DAY)
720 bool bottom_valid = block->propagateSunlight(light_sources);
722 // If bottom is valid, we're done.
726 else if(bank == LIGHTBANK_NIGHT)
728 // For night lighting, sunlight is not propagated
733 // Invalid lighting bank
737 /*dstream<<"Bottom for sunlight-propagated block ("
738 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
741 // Bottom sunlight is not valid; get the block and loop to it
745 block = getBlockNoCreate(pos);
747 catch(InvalidPositionException &e)
757 TimeTaker timer("unspreadLight");
758 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
763 u32 diff = modified_blocks.size() - count_was;
764 count_was = modified_blocks.size();
765 dstream<<"unspreadLight modified "<<diff<<std::endl;
769 TimeTaker timer("spreadLight");
770 spreadLight(bank, light_sources, modified_blocks);
775 u32 diff = modified_blocks.size() - count_was;
776 count_was = modified_blocks.size();
777 dstream<<"spreadLight modified "<<diff<<std::endl;
782 //MapVoxelManipulator vmanip(this);
784 // Make a manual voxel manipulator and load all the blocks
785 // that touch the requested blocks
786 ManualMapVoxelManipulator vmanip(this);
787 core::map<v3s16, MapBlock*>::Iterator i;
788 i = blocks_to_update.getIterator();
789 for(; i.atEnd() == false; i++)
791 MapBlock *block = i.getNode()->getValue();
792 v3s16 p = block->getPos();
794 // Add all surrounding blocks
795 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
798 Add all surrounding blocks that have up-to-date lighting
799 NOTE: This doesn't quite do the job (not everything
800 appropriate is lighted)
802 /*for(s16 z=-1; z<=1; z++)
803 for(s16 y=-1; y<=1; y++)
804 for(s16 x=-1; x<=1; x++)
807 MapBlock *block = getBlockNoCreateNoEx(p);
812 if(block->getLightingExpired())
814 vmanip.initialEmerge(p, p);
817 // Lighting of block will be updated completely
818 block->setLightingExpired(false);
822 //TimeTaker timer("unSpreadLight");
823 vmanip.unspreadLight(bank, unlight_from, light_sources);
826 //TimeTaker timer("spreadLight");
827 vmanip.spreadLight(bank, light_sources);
830 //TimeTaker timer("blitBack");
831 vmanip.blitBack(modified_blocks);
833 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
837 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
840 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
841 core::map<v3s16, MapBlock*> & modified_blocks)
843 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
844 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
847 Update information about whether day and night light differ
849 for(core::map<v3s16, MapBlock*>::Iterator
850 i = modified_blocks.getIterator();
851 i.atEnd() == false; i++)
853 MapBlock *block = i.getNode()->getValue();
854 block->updateDayNightDiff();
859 This is called after changing a node from transparent to opaque.
860 The lighting value of the node should be left as-is after changing
861 other values. This sets the lighting value to 0.
863 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
864 core::map<v3s16, MapBlock*> &modified_blocks)*/
865 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
866 core::map<v3s16, MapBlock*> &modified_blocks)
869 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
870 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
873 From this node to nodes underneath:
874 If lighting is sunlight (1.0), unlight neighbours and
879 v3s16 toppos = p + v3s16(0,1,0);
880 v3s16 bottompos = p + v3s16(0,-1,0);
882 bool node_under_sunlight = true;
883 core::map<v3s16, bool> light_sources;
886 If there is a node at top and it doesn't have sunlight,
887 there has not been any sunlight going down.
889 Otherwise there probably is.
892 MapNode topnode = getNode(toppos);
894 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
895 node_under_sunlight = false;
897 catch(InvalidPositionException &e)
901 if(n.d != CONTENT_TORCH)
904 If there is grass below, change it to mud
907 MapNode bottomnode = getNode(bottompos);
909 if(bottomnode.d == CONTENT_GRASS
910 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
912 bottomnode.d = CONTENT_MUD;
913 setNode(bottompos, bottomnode);
916 catch(InvalidPositionException &e)
921 enum LightBank banks[] =
926 for(s32 i=0; i<2; i++)
928 enum LightBank bank = banks[i];
930 u8 lightwas = getNode(p).getLight(bank);
932 // Add the block of the added node to modified_blocks
933 v3s16 blockpos = getNodeBlockPos(p);
934 MapBlock * block = getBlockNoCreate(blockpos);
935 assert(block != NULL);
936 modified_blocks.insert(blockpos, block);
938 if(isValidPosition(p) == false)
941 // Unlight neighbours of node.
942 // This means setting light of all consequent dimmer nodes
944 // This also collects the nodes at the border which will spread
945 // light again into this.
946 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
954 If node is under sunlight, take all sunlighted nodes under
955 it and clear light from them and from where the light has
957 TODO: This could be optimized by mass-unlighting instead
960 if(node_under_sunlight)
964 //m_dout<<DTIME<<"y="<<y<<std::endl;
965 v3s16 n2pos(p.X, y, p.Z);
971 catch(InvalidPositionException &e)
976 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
978 //m_dout<<DTIME<<"doing"<<std::endl;
979 unLightNeighbors(LIGHTBANK_DAY,
980 n2pos, n2.getLight(LIGHTBANK_DAY),
981 light_sources, modified_blocks);
982 n2.setLight(LIGHTBANK_DAY, 0);
990 for(s32 i=0; i<2; i++)
992 enum LightBank bank = banks[i];
995 Spread light from all nodes that might be capable of doing so
996 TODO: Convert to spreadLight
998 spreadLight(bank, light_sources, modified_blocks);
1002 Update information about whether day and night light differ
1004 for(core::map<v3s16, MapBlock*>::Iterator
1005 i = modified_blocks.getIterator();
1006 i.atEnd() == false; i++)
1008 MapBlock *block = i.getNode()->getValue();
1009 block->updateDayNightDiff();
1013 Add neighboring liquid nodes and the node itself if it is
1014 liquid (=water node was added) to transform queue.
1017 v3s16(0,0,0), // self
1018 v3s16(0,0,1), // back
1019 v3s16(0,1,0), // top
1020 v3s16(1,0,0), // right
1021 v3s16(0,0,-1), // front
1022 v3s16(0,-1,0), // bottom
1023 v3s16(-1,0,0), // left
1025 for(u16 i=0; i<7; i++)
1030 v3s16 p2 = p + dirs[i];
1032 MapNode n2 = getNode(p2);
1033 if(content_liquid(n2.d))
1035 m_transforming_liquid.push_back(p2);
1038 }catch(InvalidPositionException &e)
1046 void Map::removeNodeAndUpdate(v3s16 p,
1047 core::map<v3s16, MapBlock*> &modified_blocks)
1049 /*PrintInfo(m_dout);
1050 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1051 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1053 bool node_under_sunlight = true;
1055 v3s16 toppos = p + v3s16(0,1,0);
1057 // Node will be replaced with this
1058 u8 replace_material = CONTENT_AIR;
1061 If there is a node at top and it doesn't have sunlight,
1062 there will be no sunlight going down.
1065 MapNode topnode = getNode(toppos);
1067 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1068 node_under_sunlight = false;
1070 catch(InvalidPositionException &e)
1074 core::map<v3s16, bool> light_sources;
1076 enum LightBank banks[] =
1081 for(s32 i=0; i<2; i++)
1083 enum LightBank bank = banks[i];
1086 Unlight neighbors (in case the node is a light source)
1088 unLightNeighbors(bank, p,
1089 getNode(p).getLight(bank),
1090 light_sources, modified_blocks);
1095 This also clears the lighting.
1099 n.d = replace_material;
1102 for(s32 i=0; i<2; i++)
1104 enum LightBank bank = banks[i];
1107 Recalculate lighting
1109 spreadLight(bank, light_sources, modified_blocks);
1112 // Add the block of the removed node to modified_blocks
1113 v3s16 blockpos = getNodeBlockPos(p);
1114 MapBlock * block = getBlockNoCreate(blockpos);
1115 assert(block != NULL);
1116 modified_blocks.insert(blockpos, block);
1119 If the removed node was under sunlight, propagate the
1120 sunlight down from it and then light all neighbors
1121 of the propagated blocks.
1123 if(node_under_sunlight)
1125 s16 ybottom = propagateSunlight(p, modified_blocks);
1126 /*m_dout<<DTIME<<"Node was under sunlight. "
1127 "Propagating sunlight";
1128 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1130 for(; y >= ybottom; y--)
1132 v3s16 p2(p.X, y, p.Z);
1133 /*m_dout<<DTIME<<"lighting neighbors of node ("
1134 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1136 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1141 // Set the lighting of this node to 0
1142 // TODO: Is this needed? Lighting is cleared up there already.
1144 MapNode n = getNode(p);
1145 n.setLight(LIGHTBANK_DAY, 0);
1148 catch(InvalidPositionException &e)
1154 for(s32 i=0; i<2; i++)
1156 enum LightBank bank = banks[i];
1158 // Get the brightest neighbour node and propagate light from it
1159 v3s16 n2p = getBrightestNeighbour(bank, p);
1161 MapNode n2 = getNode(n2p);
1162 lightNeighbors(bank, n2p, modified_blocks);
1164 catch(InvalidPositionException &e)
1170 Update information about whether day and night light differ
1172 for(core::map<v3s16, MapBlock*>::Iterator
1173 i = modified_blocks.getIterator();
1174 i.atEnd() == false; i++)
1176 MapBlock *block = i.getNode()->getValue();
1177 block->updateDayNightDiff();
1181 Add neighboring liquid nodes to transform queue.
1184 v3s16(0,0,1), // back
1185 v3s16(0,1,0), // top
1186 v3s16(1,0,0), // right
1187 v3s16(0,0,-1), // front
1188 v3s16(0,-1,0), // bottom
1189 v3s16(-1,0,0), // left
1191 for(u16 i=0; i<6; i++)
1196 v3s16 p2 = p + dirs[i];
1198 MapNode n2 = getNode(p2);
1199 if(content_liquid(n2.d))
1201 m_transforming_liquid.push_back(p2);
1204 }catch(InvalidPositionException &e)
1211 void Map::expireMeshes(bool only_daynight_diffed)
1213 TimeTaker timer("expireMeshes()");
1215 core::map<v2s16, MapSector*>::Iterator si;
1216 si = m_sectors.getIterator();
1217 for(; si.atEnd() == false; si++)
1219 MapSector *sector = si.getNode()->getValue();
1221 core::list< MapBlock * > sectorblocks;
1222 sector->getBlocks(sectorblocks);
1224 core::list< MapBlock * >::Iterator i;
1225 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1227 MapBlock *block = *i;
1229 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1235 JMutexAutoLock lock(block->mesh_mutex);
1236 if(block->mesh != NULL)
1238 /*block->mesh->drop();
1239 block->mesh = NULL;*/
1240 block->setMeshExpired(true);
1247 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1249 assert(mapType() == MAPTYPE_CLIENT);
1252 v3s16 p = blockpos + v3s16(0,0,0);
1253 MapBlock *b = getBlockNoCreate(p);
1254 b->updateMesh(daynight_ratio);
1256 catch(InvalidPositionException &e){}
1259 v3s16 p = blockpos + v3s16(-1,0,0);
1260 MapBlock *b = getBlockNoCreate(p);
1261 b->updateMesh(daynight_ratio);
1263 catch(InvalidPositionException &e){}
1265 v3s16 p = blockpos + v3s16(0,-1,0);
1266 MapBlock *b = getBlockNoCreate(p);
1267 b->updateMesh(daynight_ratio);
1269 catch(InvalidPositionException &e){}
1271 v3s16 p = blockpos + v3s16(0,0,-1);
1272 MapBlock *b = getBlockNoCreate(p);
1273 b->updateMesh(daynight_ratio);
1275 catch(InvalidPositionException &e){}
1278 v3s16 p = blockpos + v3s16(1,0,0);
1279 MapBlock *b = getBlockNoCreate(p);
1280 b->updateMesh(daynight_ratio);
1282 catch(InvalidPositionException &e){}
1284 v3s16 p = blockpos + v3s16(0,1,0);
1285 MapBlock *b = getBlockNoCreate(p);
1286 b->updateMesh(daynight_ratio);
1288 catch(InvalidPositionException &e){}
1290 v3s16 p = blockpos + v3s16(0,0,1);
1291 MapBlock *b = getBlockNoCreate(p);
1292 b->updateMesh(daynight_ratio);
1294 catch(InvalidPositionException &e){}*/
1299 bool Map::dayNightDiffed(v3s16 blockpos)
1302 v3s16 p = blockpos + v3s16(0,0,0);
1303 MapBlock *b = getBlockNoCreate(p);
1304 if(b->dayNightDiffed())
1307 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(-1,0,0);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1317 v3s16 p = blockpos + v3s16(0,-1,0);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1324 v3s16 p = blockpos + v3s16(0,0,-1);
1325 MapBlock *b = getBlockNoCreate(p);
1326 if(b->dayNightDiffed())
1329 catch(InvalidPositionException &e){}
1332 v3s16 p = blockpos + v3s16(1,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1339 v3s16 p = blockpos + v3s16(0,1,0);
1340 MapBlock *b = getBlockNoCreate(p);
1341 if(b->dayNightDiffed())
1344 catch(InvalidPositionException &e){}
1346 v3s16 p = blockpos + v3s16(0,0,1);
1347 MapBlock *b = getBlockNoCreate(p);
1348 if(b->dayNightDiffed())
1351 catch(InvalidPositionException &e){}
1357 Updates usage timers
1359 void Map::timerUpdate(float dtime)
1361 JMutexAutoLock lock(m_sector_mutex);
1363 core::map<v2s16, MapSector*>::Iterator si;
1365 si = m_sectors.getIterator();
1366 for(; si.atEnd() == false; si++)
1368 MapSector *sector = si.getNode()->getValue();
1369 sector->usage_timer += dtime;
1373 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1376 Wait for caches to be removed before continuing.
1378 This disables the existence of caches while locked
1380 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1382 core::list<v2s16>::Iterator j;
1383 for(j=list.begin(); j!=list.end(); j++)
1385 MapSector *sector = m_sectors[*j];
1388 sector->deleteBlocks();
1393 If sector is in sector cache, remove it from there
1395 if(m_sector_cache == sector)
1397 m_sector_cache = NULL;
1400 Remove from map and delete
1402 m_sectors.remove(*j);
1408 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1409 core::list<v3s16> *deleted_blocks)
1411 JMutexAutoLock lock(m_sector_mutex);
1413 core::list<v2s16> sector_deletion_queue;
1414 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1415 for(; i.atEnd() == false; i++)
1417 MapSector *sector = i.getNode()->getValue();
1419 Delete sector from memory if it hasn't been used in a long time
1421 if(sector->usage_timer > timeout)
1423 sector_deletion_queue.push_back(i.getNode()->getKey());
1425 if(deleted_blocks != NULL)
1427 // Collect positions of blocks of sector
1428 MapSector *sector = i.getNode()->getValue();
1429 core::list<MapBlock*> blocks;
1430 sector->getBlocks(blocks);
1431 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1432 i != blocks.end(); i++)
1434 deleted_blocks->push_back((*i)->getPos());
1439 deleteSectors(sector_deletion_queue, only_blocks);
1440 return sector_deletion_queue.getSize();
1443 void Map::PrintInfo(std::ostream &out)
1448 #define WATER_DROP_BOOST 4
1450 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1452 DSTACK(__FUNCTION_NAME);
1453 //TimeTaker timer("transformLiquids()");
1456 u32 initial_size = m_transforming_liquid.size();
1458 //dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1460 while(m_transforming_liquid.size() != 0)
1463 Get a queued transforming liquid node
1465 v3s16 p0 = m_transforming_liquid.pop_front();
1467 MapNode n0 = getNode(p0);
1469 // Don't deal with non-liquids
1470 if(content_liquid(n0.d) == false)
1473 bool is_source = !content_flowing_liquid(n0.d);
1475 u8 liquid_level = 8;
1476 if(is_source == false)
1477 liquid_level = n0.param2 & 0x0f;
1479 // Turn possible source into non-source
1480 u8 nonsource_c = make_liquid_flowing(n0.d);
1483 If not source, check that some node flows into this one
1484 and what is the level of liquid in this one
1486 if(is_source == false)
1488 s8 new_liquid_level_max = -1;
1490 v3s16 dirs_from[5] = {
1491 v3s16(0,1,0), // top
1492 v3s16(0,0,1), // back
1493 v3s16(1,0,0), // right
1494 v3s16(0,0,-1), // front
1495 v3s16(-1,0,0), // left
1497 for(u16 i=0; i<5; i++)
1502 bool from_top = (i==0);
1504 v3s16 p2 = p0 + dirs_from[i];
1505 MapNode n2 = getNode(p2);
1507 if(content_liquid(n2.d))
1509 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1510 // Check that the liquids are the same type
1511 if(n2_nonsource_c != nonsource_c)
1513 dstream<<"WARNING: Not handling: different liquids"
1514 " collide"<<std::endl;
1517 bool n2_is_source = !content_flowing_liquid(n2.d);
1518 s8 n2_liquid_level = 8;
1519 if(n2_is_source == false)
1520 n2_liquid_level = n2.param2 & 0x07;
1522 s8 new_liquid_level = -1;
1525 //new_liquid_level = 7;
1526 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1527 new_liquid_level = 7;
1529 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1531 else if(n2_liquid_level > 0)
1533 new_liquid_level = n2_liquid_level - 1;
1536 if(new_liquid_level > new_liquid_level_max)
1537 new_liquid_level_max = new_liquid_level;
1540 }catch(InvalidPositionException &e)
1546 If liquid level should be something else, update it and
1547 add all the neighboring water nodes to the transform queue.
1549 if(new_liquid_level_max != liquid_level)
1551 if(new_liquid_level_max == -1)
1553 // Remove water alltoghether
1560 n0.param2 = new_liquid_level_max;
1564 // Block has been modified
1566 v3s16 blockpos = getNodeBlockPos(p0);
1567 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1569 modified_blocks.insert(blockpos, block);
1573 Add neighboring non-source liquid nodes to transform queue.
1576 v3s16(0,0,1), // back
1577 v3s16(0,1,0), // top
1578 v3s16(1,0,0), // right
1579 v3s16(0,0,-1), // front
1580 v3s16(0,-1,0), // bottom
1581 v3s16(-1,0,0), // left
1583 for(u16 i=0; i<6; i++)
1588 v3s16 p2 = p0 + dirs[i];
1590 MapNode n2 = getNode(p2);
1591 if(content_flowing_liquid(n2.d))
1593 m_transforming_liquid.push_back(p2);
1596 }catch(InvalidPositionException &e)
1603 // Get a new one from queue if the node has turned into non-water
1604 if(content_liquid(n0.d) == false)
1608 Flow water from this node
1610 v3s16 dirs_to[5] = {
1611 v3s16(0,-1,0), // bottom
1612 v3s16(0,0,1), // back
1613 v3s16(1,0,0), // right
1614 v3s16(0,0,-1), // front
1615 v3s16(-1,0,0), // left
1617 for(u16 i=0; i<5; i++)
1622 bool to_bottom = (i == 0);
1624 // If liquid is at lowest possible height, it's not going
1625 // anywhere except down
1626 if(liquid_level == 0 && to_bottom == false)
1629 u8 liquid_next_level = 0;
1630 // If going to bottom
1633 //liquid_next_level = 7;
1634 if(liquid_level >= 7 - WATER_DROP_BOOST)
1635 liquid_next_level = 7;
1637 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1640 liquid_next_level = liquid_level - 1;
1642 bool n2_changed = false;
1643 bool flowed = false;
1645 v3s16 p2 = p0 + dirs_to[i];
1647 MapNode n2 = getNode(p2);
1648 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1650 if(content_liquid(n2.d))
1652 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1653 // Check that the liquids are the same type
1654 if(n2_nonsource_c != nonsource_c)
1656 dstream<<"WARNING: Not handling: different liquids"
1657 " collide"<<std::endl;
1660 bool n2_is_source = !content_flowing_liquid(n2.d);
1661 u8 n2_liquid_level = 8;
1662 if(n2_is_source == false)
1663 n2_liquid_level = n2.param2 & 0x07;
1672 // Just flow into the source, nothing changes.
1673 // n2_changed is not set because destination didn't change
1678 if(liquid_next_level > liquid_level)
1680 n2.param2 = liquid_next_level;
1688 else if(n2.d == CONTENT_AIR)
1691 n2.param2 = liquid_next_level;
1698 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1702 m_transforming_liquid.push_back(p2);
1704 v3s16 blockpos = getNodeBlockPos(p2);
1705 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1707 modified_blocks.insert(blockpos, block);
1710 // If n2_changed to bottom, don't flow anywhere else
1711 if(to_bottom && flowed && !is_source)
1714 }catch(InvalidPositionException &e)
1720 //if(loopcount >= 100000)
1721 if(loopcount >= initial_size * 1)
1724 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1731 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1743 Experimental and debug stuff
1747 dstream<<"Generating map point attribute lists"<<std::endl;
1749 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1750 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1751 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1752 //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1753 //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1757 NOTE: BEWARE: Too big amount of these will make map generation
1758 slow. Especially those that are read by every block emerge.
1766 for(u32 i=0; i<500; i++)
1768 /*u32 lim = MAP_GENERATION_LIMIT;
1772 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1775 -lim + myrand()%(lim*2),
1777 -lim + myrand()%(lim*2)
1779 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1780 plants_amount = pow(plants_amount, 5);
1781 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1783 float plants_amount = 0;
1786 plants_amount = 1.5;
1788 else if(myrand()%4 == 0)
1790 plants_amount = 0.5;
1792 else if(myrand()%2 == 0)
1794 plants_amount = 0.03;
1798 plants_amount = 0.0;
1802 list_plants_amount->addPoint(p, Attribute(plants_amount));
1805 for(u32 i=0; i<500; i++)
1807 /*u32 lim = MAP_GENERATION_LIMIT;
1811 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1814 -lim + myrand()%(lim*2),
1816 -lim + myrand()%(lim*2)
1819 float caves_amount = 0;
1824 else if(myrand()%3 == 0)
1830 caves_amount = 0.05;
1833 list_caves_amount->addPoint(p, Attribute(caves_amount));
1836 for(u32 i=0; i<500; i++)
1838 /*u32 lim = MAP_GENERATION_LIMIT;
1842 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1845 -lim + (myrand()%(lim*2)),
1847 -lim + (myrand()%(lim*2))
1850 /*s32 bh_i = (myrand()%200) - 50;
1851 float baseheight = (float)bh_i;
1855 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1856 randmax = pow(randmax, e);
1858 //float randmax = (float)(myrand()%60);
1859 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1861 float baseheight = 0;
1863 float randfactor = 0;
1865 /*if(myrand()%5 == 0)
1871 else if(myrand()%6 == 0)
1877 else if(myrand()%4 == 0)
1883 else if(myrand()%3 == 0)
1909 list_baseheight->addPoint(p, Attribute(baseheight));
1910 list_randmax->addPoint(p, Attribute(randmax));
1911 list_randfactor->addPoint(p, Attribute(randfactor));
1915 // Add only one entry
1916 list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1917 list_randmax->addPoint(v3s16(0,0,0), Attribute(30));
1918 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1921 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1922 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1923 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1927 Try to load map; if not found, create a new one.
1930 m_savedir = savedir;
1931 m_map_saving_enabled = false;
1935 // If directory exists, check contents and load if possible
1936 if(fs::PathExists(m_savedir))
1938 // If directory is empty, it is safe to save into it.
1939 if(fs::GetDirListing(m_savedir).size() == 0)
1941 dstream<<DTIME<<"Server: Empty save directory is valid."
1943 m_map_saving_enabled = true;
1947 // Load master heightmap
1948 loadMasterHeightmap();
1950 // Load sector (0,0) and throw and exception on fail
1951 if(loadSectorFull(v2s16(0,0)) == false)
1952 throw LoadError("Failed to load sector (0,0)");
1954 dstream<<DTIME<<"Server: Successfully loaded master "
1955 "heightmap and sector (0,0) from "<<savedir<<
1956 ", assuming valid save directory."
1959 m_map_saving_enabled = true;
1960 // Map loaded, not creating new one
1964 // If directory doesn't exist, it is safe to save to it
1966 m_map_saving_enabled = true;
1969 catch(std::exception &e)
1971 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1972 <<", exception: "<<e.what()<<std::endl;
1973 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1974 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1977 dstream<<DTIME<<"Initializing new map."<<std::endl;
1979 // Create master heightmap
1980 m_heightmap = new UnlimitedHeightmap
1983 // Set map parameters
1986 // Create zero sector
1987 emergeSector(v2s16(0,0));
1989 // Initially write whole map
1993 ServerMap::~ServerMap()
1997 if(m_map_saving_enabled)
2000 // Save only changed parts
2002 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2006 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2009 catch(std::exception &e)
2011 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2012 <<", exception: "<<e.what()<<std::endl;
2015 if(m_heightmap != NULL)
2021 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2022 for(; i.atEnd() == false; i++)
2024 MapChunk *chunk = i.getNode()->getValue();
2030 Some helper functions
2033 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2035 v3s16 em = vmanip.m_area.getExtent();
2036 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2037 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2038 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2040 for(y=y_nodes_max; y>=y_nodes_min; y--)
2042 MapNode &n = vmanip.m_data[i];
2043 if(content_walkable(n.d))
2046 vmanip.m_area.add_y(em, i, -1);
2048 if(y >= y_nodes_min)
2054 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2056 MapNode treenode(CONTENT_TREE);
2057 MapNode leavesnode(CONTENT_LEAVES);
2059 s16 trunk_h = myrand_range(2, 6);
2061 for(s16 ii=0; ii<trunk_h; ii++)
2063 if(vmanip.m_area.contains(p1))
2064 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2068 // p1 is now the last piece of the trunk
2071 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2072 SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2073 for(s32 i=0; i<leaves_a.getVolume(); i++)
2076 // Force leaves at near the end of the trunk
2079 for(s16 z=-d; z<=d; z++)
2080 for(s16 y=-d; y<=d; y++)
2081 for(s16 x=-d; x<=d; x++)
2083 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2087 // Add leaves randomly
2088 for(u32 iii=0; iii<7; iii++)
2093 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2094 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2095 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2098 for(s16 z=0; z<=d; z++)
2099 for(s16 y=0; y<=d; y++)
2100 for(s16 x=0; x<=d; x++)
2102 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2106 // Blit leaves to vmanip
2107 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2108 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2109 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2113 if(vmanip.m_area.contains(p) == false)
2115 u32 vi = vmanip.m_area.index(p);
2116 if(vmanip.m_data[vi].d != CONTENT_AIR)
2118 u32 i = leaves_a.index(x,y,z);
2119 if(leaves_d[i] == 1)
2120 vmanip.m_data[vi] = leavesnode;
2124 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2125 core::map<v3s16, MapBlock*> &changed_blocks)
2128 Don't generate if already fully generated
2131 MapChunk *chunk = getChunk(chunkpos);
2132 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2134 dstream<<"generateChunkRaw(): Chunk "
2135 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2136 <<" already generated"<<std::endl;
2141 dstream<<"generateChunkRaw(): Generating chunk "
2142 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2145 TimeTaker timer("generateChunkRaw()");
2147 // The distance how far into the neighbors the generator is allowed to go
2148 s16 max_spread_amount_sectors = 2;
2149 assert(max_spread_amount_sectors <= m_chunksize);
2150 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2151 // Minimum amount of space left on sides for mud to fall in
2152 s16 min_mud_fall_space = 2;
2153 // Maximum diameter of stone obstacles in X and Z
2154 s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2155 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2157 s16 y_blocks_min = -4;
2158 s16 y_blocks_max = 3;
2159 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2160 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2161 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2163 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2164 s16 sectorpos_base_size = m_chunksize;
2166 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2167 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2168 v2s16 sectorpos_bigbase =
2169 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2170 s16 sectorpos_bigbase_size =
2171 sectorpos_base_size + 2 * max_spread_amount_sectors;
2173 v3s16 bigarea_blocks_min(
2174 sectorpos_bigbase.X,
2179 v3s16 bigarea_blocks_max(
2180 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2182 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2185 // Relative values to control amount of stuff in one chunk
2186 u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2187 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;
2188 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2189 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2190 *(u32)h_blocks*MAP_BLOCKSIZE;
2193 Create the whole area of this and the neighboring chunks
2196 TimeTaker timer("generateChunkRaw() create area");
2198 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2199 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2201 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2202 ServerMapSector *sector = createSector(sectorpos);
2205 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2207 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2208 MapBlock *block = createBlock(blockpos);
2210 // Lighting won't be calculated
2211 //block->setLightingExpired(true);
2212 // Lighting will be calculated
2213 block->setLightingExpired(false);
2216 Block gets sunlight if this is true.
2218 This should be set to true when the top side of a block
2219 is completely exposed to the sky.
2221 Actually this doesn't matter now because the
2222 initial lighting is done here.
2224 block->setIsUnderground(y != y_blocks_max);
2230 Now we have a big empty area.
2232 Make a ManualMapVoxelManipulator that contains this and the
2236 ManualMapVoxelManipulator vmanip(this);
2237 // Add the area we just generated
2239 TimeTaker timer("generateChunkRaw() initialEmerge");
2240 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2243 TimeTaker timer_generate("generateChunkRaw() generate");
2246 Generate general ground level to full area
2251 //TimeTaker timer1("ground level");
2253 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2254 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2257 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2260 Skip of already generated
2263 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2264 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2268 // Ground height at this point
2269 float surface_y_f = 0.0;
2271 A hack to get the ground height from the sector.
2275 v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
2276 v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
2277 MapSector *sector = getSectorNoGenerate(sectorpos);
2279 float h = sector->getGroundHeight(sector_relpos);
2280 if(h > GROUNDHEIGHT_VALID_MINVALUE)
2283 dstream<<"WARNING: "<<__FUNCTION_NAME
2284 <<": sector->getGroundHeight returned bad height"<<std::endl;
2286 // Convert to integer
2287 s16 surface_y = (s16)surface_y_f;
2290 Fill ground with stone
2293 // Use fast index incrementing
2294 v3s16 em = vmanip.m_area.getExtent();
2295 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2296 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2298 vmanip.m_data[i].d = CONTENT_STONE;
2300 vmanip.m_area.add_y(em, i, 1);
2307 // This is set during the next operation.
2308 // Maximum height of the stone surface and obstacles.
2309 // This is used to disable dungeon generation from going too high.
2310 s16 stone_surface_max_y = 0;
2314 //TimeTaker timer1("stone obstacles");
2317 Add some random stone obstacles
2320 for(u32 ri=0; ri<10; ri++)
2322 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2324 myrand_range(5, stone_obstacle_max_size),
2325 myrand_range(0, 23),
2326 myrand_range(5, stone_obstacle_max_size)
2329 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2330 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2333 // Minimum space left on top of the obstacle
2334 s16 min_head_space = 10;
2336 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2337 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2339 // Node position in 2d
2340 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2342 // Find stone ground level
2343 // (ignore everything else than mud in already generated chunks)
2344 // and mud amount over the stone level
2348 v3s16 em = vmanip.m_area.getExtent();
2349 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2351 // Go to ground level
2352 for(y=y_nodes_max; y>=y_nodes_min; y--)
2354 MapNode &n = vmanip.m_data[i];
2355 /*if(content_walkable(n.d)
2356 && n.d != CONTENT_MUD
2357 && n.d != CONTENT_GRASS)
2359 if(n.d == CONTENT_STONE)
2362 if(n.d == CONTENT_MUD || n.d == CONTENT_GRASS)
2365 vmanip.m_area.add_y(em, i, -1);
2367 if(y >= y_nodes_min)
2370 surface_y = y_nodes_min;
2378 v3s16 em = vmanip.m_area.getExtent();
2379 s16 y_start = surface_y+1;
2380 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2384 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2386 MapNode &n = vmanip.m_data[i];
2387 n.d = CONTENT_STONE;
2389 if(y > stone_surface_max_y)
2390 stone_surface_max_y = y;
2393 if(count >= ob_size.Y)
2396 vmanip.m_area.add_y(em, i, 1);
2400 for(; y<=y_nodes_max; y++)
2402 MapNode &n = vmanip.m_data[i];
2405 if(count >= mud_amount)
2408 vmanip.m_area.add_y(em, i, 1);
2418 //TimeTaker timer1("dungeons");
2423 u32 dungeons_count = relative_volume/200000;
2424 for(u32 jj=0; jj<dungeons_count; jj++)
2426 s16 min_tunnel_diameter = 1;
2427 s16 max_tunnel_diameter = 5;
2428 u16 tunnel_routepoints = 15;
2430 bool bruise_surface = (jj < dungeons_count / 3);
2434 min_tunnel_diameter = 5;
2435 max_tunnel_diameter = 10;
2436 tunnel_routepoints = 10;
2439 // Allowed route area size in nodes
2441 sectorpos_base_size*MAP_BLOCKSIZE,
2442 h_blocks*MAP_BLOCKSIZE,
2443 sectorpos_base_size*MAP_BLOCKSIZE
2446 // Area starting point in nodes
2448 sectorpos_base.X*MAP_BLOCKSIZE,
2449 y_blocks_min*MAP_BLOCKSIZE,
2450 sectorpos_base.Y*MAP_BLOCKSIZE
2454 //(this should be more than the maximum radius of the tunnel)
2456 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2457 ar += v3s16(1,0,1) * more * 2;
2458 of -= v3s16(1,0,1) * more;
2460 s16 route_y_min = 0;
2461 //s16 route_y_max = ar.Y-1;
2462 s16 route_y_max = stone_surface_max_y - of.Y;
2466 // Minimum is at y=0
2467 route_y_min = -of.Y - 0;
2468 route_y_min = rangelim(route_y_min, 0, route_y_max);
2471 /*dstream<<"route_y_min = "<<route_y_min
2472 <<", route_y_max = "<<route_y_max<<std::endl;*/
2474 // Randomize starting position
2476 (float)(myrand()%ar.X)+0.5,
2477 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2478 (float)(myrand()%ar.Z)+0.5
2481 MapNode airnode(CONTENT_AIR);
2484 Generate some tunnel starting from orp
2487 for(u16 j=0; j<tunnel_routepoints; j++)
2489 v3s16 maxlen(20, 10, 20);
2493 maxlen = v3s16(60,60,60);
2497 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2498 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2499 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2504 else if(rp.X >= ar.X)
2506 if(rp.Y < route_y_min)
2508 else if(rp.Y >= route_y_max)
2509 rp.Y = route_y_max-1;
2512 else if(rp.Z >= ar.Z)
2517 s16 min_d = min_tunnel_diameter;
2518 s16 max_d = max_tunnel_diameter;
2519 s16 rs = myrand_range(min_d, max_d);
2521 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2523 v3f fp = orp + vec * f;
2524 v3s16 cp(fp.X, fp.Y, fp.Z);
2527 s16 d1 = d0 + rs - 1;
2528 for(s16 z0=d0; z0<=d1; z0++)
2530 s16 si = rs - abs(z0);
2531 for(s16 x0=-si; x0<=si-1; x0++)
2533 s16 si2 = rs - abs(x0);
2534 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2540 /*if(isInArea(p, ar) == false)
2542 // Check only height
2543 if(y < 0 || y >= ar.Y)
2547 //assert(vmanip.m_area.contains(p));
2548 if(vmanip.m_area.contains(p) == false)
2550 dstream<<"WARNING: "<<__FUNCTION_NAME
2551 <<":"<<__LINE__<<": "
2552 <<"point not in area"
2557 // Just set it to air, it will be changed to
2559 u32 i = vmanip.m_area.index(p);
2560 vmanip.m_data[i] = airnode;
2574 //TimeTaker timer1("ore veins");
2579 for(u32 jj=0; jj<relative_volume/524; jj++)
2581 s16 max_vein_diameter = 3;
2583 // Allowed route area size in nodes
2585 sectorpos_base_size*MAP_BLOCKSIZE,
2586 h_blocks*MAP_BLOCKSIZE,
2587 sectorpos_base_size*MAP_BLOCKSIZE
2590 // Area starting point in nodes
2592 sectorpos_base.X*MAP_BLOCKSIZE,
2593 y_blocks_min*MAP_BLOCKSIZE,
2594 sectorpos_base.Y*MAP_BLOCKSIZE
2598 //(this should be more than the maximum radius of the tunnel)
2600 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2601 ar += v3s16(1,0,1) * more * 2;
2602 of -= v3s16(1,0,1) * more;
2604 // Randomize starting position
2606 (float)(myrand()%ar.X)+0.5,
2607 (float)(myrand()%ar.Y)+0.5,
2608 (float)(myrand()%ar.Z)+0.5
2611 // Randomize mineral
2612 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2615 Generate some vein starting from orp
2618 for(u16 j=0; j<2; j++)
2621 (float)(myrand()%ar.X)+0.5,
2622 (float)(myrand()%ar.Y)+0.5,
2623 (float)(myrand()%ar.Z)+0.5
2625 v3f vec = rp - orp;*/
2627 v3s16 maxlen(10, 10, 10);
2629 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2630 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2631 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2636 else if(rp.X >= ar.X)
2640 else if(rp.Y >= ar.Y)
2644 else if(rp.Z >= ar.Z)
2650 s16 max_d = max_vein_diameter;
2651 s16 rs = myrand_range(min_d, max_d);
2653 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2655 v3f fp = orp + vec * f;
2656 v3s16 cp(fp.X, fp.Y, fp.Z);
2658 s16 d1 = d0 + rs - 1;
2659 for(s16 z0=d0; z0<=d1; z0++)
2661 s16 si = rs - abs(z0);
2662 for(s16 x0=-si; x0<=si-1; x0++)
2664 s16 si2 = rs - abs(x0);
2665 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2667 // Don't put mineral to every place
2675 /*if(isInArea(p, ar) == false)
2677 // Check only height
2678 if(y < 0 || y >= ar.Y)
2682 assert(vmanip.m_area.contains(p));
2684 // Just set it to air, it will be changed to
2686 u32 i = vmanip.m_area.index(p);
2687 MapNode *n = &vmanip.m_data[i];
2688 if(n->d == CONTENT_STONE)
2703 //TimeTaker timer1("add mud");
2706 Add mud to the central chunk
2709 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2710 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2712 // Node position in 2d
2713 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2715 // Find ground level
2716 s16 surface_y = find_ground_level(vmanip, p2d);
2723 v3s16 em = vmanip.m_area.getExtent();
2724 s16 y_start = surface_y+1;
2725 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2726 for(s16 y=y_start; y<=y_nodes_max; y++)
2728 MapNode &n = vmanip.m_data[i];
2734 vmanip.m_area.add_y(em, i, 1);
2743 //TimeTaker timer1("flow mud");
2746 Flow mud away from steep edges
2749 // Iterate a few times
2750 for(s16 k=0; k<4; k++)
2753 for(s16 x=0-max_spread_amount+1;
2754 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2756 for(s16 z=0-max_spread_amount+1;
2757 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2760 // Node position in 2d
2761 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2763 v3s16 em = vmanip.m_area.getExtent();
2764 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2766 // Go to ground level
2767 for(y=y_nodes_max; y>=y_nodes_min; y--)
2769 MapNode &n = vmanip.m_data[i];
2770 //if(n.d != CONTENT_AIR)
2771 if(content_walkable(n.d))
2774 vmanip.m_area.add_y(em, i, -1);
2777 // If not mud, do nothing to it
2778 MapNode *n = &vmanip.m_data[i];
2779 if(n->d != CONTENT_MUD)
2783 v3s16(0,0,1), // back
2784 v3s16(1,0,0), // right
2785 v3s16(0,0,-1), // front
2786 v3s16(-1,0,0), // left
2791 for(u32 di=0; di<4; di++)
2793 v3s16 dirp = dirs4[di];
2795 // Check that side is air
2796 vmanip.m_area.add_p(em, i2, dirp);
2797 MapNode *n2 = &vmanip.m_data[i2];
2798 if(content_walkable(n2->d))
2800 // Check that under side is air
2801 vmanip.m_area.add_y(em, i2, -1);
2802 n2 = &vmanip.m_data[i2];
2803 if(content_walkable(n2->d))
2805 // Loop further down until not air
2807 vmanip.m_area.add_y(em, i2, -1);
2808 n2 = &vmanip.m_data[i2];
2809 }while(content_walkable(n2->d) == false);
2810 // Loop one up so that we're in air
2811 vmanip.m_area.add_y(em, i2, 1);
2812 n2 = &vmanip.m_data[i2];
2814 // Move mud to new place
2816 // Set old place to be air
2817 *n = MapNode(CONTENT_AIR);
2820 // Switch mud and other and change mud source to air
2821 //MapNode tempnode = *n2;
2824 // Force old mud position to be air
2838 //TimeTaker timer1("add water");
2841 Add water to the central chunk (and a bit more)
2844 for(s16 x=0-max_spread_amount;
2845 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2847 for(s16 z=0-max_spread_amount;
2848 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2851 // Node position in 2d
2852 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2854 // Find ground level
2855 //s16 surface_y = find_ground_level(vmanip, p2d);
2858 If ground level is over water level, skip.
2859 NOTE: This leaves caves near water without water,
2860 which looks especially crappy when the nearby water
2861 won't start flowing either for some reason
2863 /*if(surface_y > WATER_LEVEL)
2870 v3s16 em = vmanip.m_area.getExtent();
2871 s16 y_start = WATER_LEVEL;
2872 u8 light = LIGHT_MAX;
2873 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2874 MapNode *n = &vmanip.m_data[i];
2876 Add first one to transforming liquid queue
2878 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2880 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2881 m_transforming_liquid.push_back(p);
2883 for(s16 y=y_start; y>=y_nodes_min; y--)
2885 n = &vmanip.m_data[i];
2887 // Stop when there is no water and no air
2888 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2889 && n->d != CONTENT_WATER)
2892 Add bottom one to transforming liquid queue
2894 vmanip.m_area.add_y(em, i, 1);
2895 n = &vmanip.m_data[i];
2896 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2898 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2899 m_transforming_liquid.push_back(p);
2905 n->d = CONTENT_WATERSOURCE;
2906 n->setLight(LIGHTBANK_DAY, light);
2908 /*// Add to transforming liquid queue (in case it'd
2910 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2911 m_transforming_liquid.push_back(p);*/
2914 vmanip.m_area.add_y(em, i, -1);
2925 //TimeTaker timer1("plant trees");
2931 u32 tree_max = relative_area / 100;
2933 u32 count = myrand_range(0, tree_max);
2934 for(u32 i=0; i<count; i++)
2936 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2937 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2938 x += sectorpos_base.X*MAP_BLOCKSIZE;
2939 z += sectorpos_base.Y*MAP_BLOCKSIZE;
2940 s16 y = find_ground_level(vmanip, v2s16(x,z));
2941 // Don't make a tree under water level
2946 make_tree(vmanip, p);
2953 //TimeTaker timer1("grow grass");
2959 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
2960 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
2961 for(s16 x=0-max_spread_amount;
2962 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2964 for(s16 z=0-max_spread_amount;
2965 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2968 // Node position in 2d
2969 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2972 Find the lowest surface to which enough light ends up
2975 Basically just wait until not air and not leaves.
2979 v3s16 em = vmanip.m_area.getExtent();
2980 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2982 // Go to ground level
2983 for(y=y_nodes_max; y>=y_nodes_min; y--)
2985 MapNode &n = vmanip.m_data[i];
2986 if(n.d != CONTENT_AIR
2987 && n.d != CONTENT_LEAVES)
2989 vmanip.m_area.add_y(em, i, -1);
2991 if(y >= y_nodes_min)
2994 surface_y = y_nodes_min;
2997 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
2998 MapNode *n = &vmanip.m_data[i];
2999 if(n->d == CONTENT_MUD)
3000 n->d = CONTENT_GRASS;
3009 core::map<v3s16, bool> light_sources;
3012 // 750ms @cs=8, can't optimize more
3013 //TimeTaker timer1("initial lighting");
3015 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3016 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3017 for(s16 x=0-max_spread_amount+1;
3018 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3020 for(s16 z=0-max_spread_amount+1;
3021 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3024 // Node position in 2d
3025 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3028 Apply initial sunlight
3031 u8 light = LIGHT_SUN;
3032 bool add_to_sources = false;
3033 v3s16 em = vmanip.m_area.getExtent();
3034 s16 y_start = y_nodes_max;
3035 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3036 for(s16 y=y_start; y>=y_nodes_min; y--)
3038 MapNode *n = &vmanip.m_data[i];
3040 if(light_propagates_content(n->d) == false)
3044 else if(light != LIGHT_SUN
3045 || sunlight_propagates_content(n->d) == false)
3051 // This doesn't take much time
3052 if(add_to_sources == false)
3055 Check sides. If side is not air or water, start
3056 adding to light_sources.
3059 v3s16(0,0,1), // back
3060 v3s16(1,0,0), // right
3061 v3s16(0,0,-1), // front
3062 v3s16(-1,0,0), // left
3064 for(u32 di=0; di<4; di++)
3066 v3s16 dirp = dirs4[di];
3068 vmanip.m_area.add_p(em, i2, dirp);
3069 MapNode *n2 = &vmanip.m_data[i2];
3071 n2->d != CONTENT_AIR
3072 && n2->d != CONTENT_WATERSOURCE
3073 && n2->d != CONTENT_WATER
3075 add_to_sources = true;
3081 n->setLight(LIGHTBANK_DAY, light);
3082 n->setLight(LIGHTBANK_NIGHT, 0);
3084 // This doesn't take much time
3085 if(light != 0 && add_to_sources)
3087 // Insert light source
3088 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3091 // Increment index by y
3092 vmanip.m_area.add_y(em, i, -1);
3099 // Spread light around
3101 TimeTaker timer("generateChunkRaw() spreadLight");
3102 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3109 timer_generate.stop();
3112 Blit generated stuff to map
3116 //TimeTaker timer("generateChunkRaw() blitBackAll");
3117 vmanip.blitBackAll(&changed_blocks);
3120 Update day/night difference cache of the MapBlocks
3123 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3124 i.atEnd() == false; i++)
3126 MapBlock *block = i.getNode()->getValue();
3127 block->updateDayNightDiff();
3133 Create chunk metadata
3136 for(s16 x=-1; x<=1; x++)
3137 for(s16 y=-1; y<=1; y++)
3139 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3140 // Add chunk meta information
3141 MapChunk *chunk = getChunk(chunkpos0);
3144 chunk = new MapChunk();
3145 m_chunks.insert(chunkpos0, chunk);
3147 //chunk->setIsVolatile(true);
3148 if(chunk->getGenLevel() > GENERATED_PARTLY)
3149 chunk->setGenLevel(GENERATED_PARTLY);
3153 Set central chunk non-volatile and return it
3155 MapChunk *chunk = getChunk(chunkpos);
3158 //chunk->setIsVolatile(false);
3159 chunk->setGenLevel(GENERATED_FULLY);
3164 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3165 core::map<v3s16, MapBlock*> &changed_blocks)
3167 dstream<<"generateChunk(): Generating chunk "
3168 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3171 /*for(s16 x=-1; x<=1; x++)
3172 for(s16 y=-1; y<=1; y++)*/
3173 for(s16 x=-0; x<=0; x++)
3174 for(s16 y=-0; y<=0; y++)
3176 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3177 MapChunk *chunk = getChunk(chunkpos0);
3178 // Skip if already generated
3179 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3181 generateChunkRaw(chunkpos0, changed_blocks);
3184 assert(chunkNonVolatile(chunkpos1));
3186 MapChunk *chunk = getChunk(chunkpos1);
3190 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3192 DSTACK("%s: p2d=(%d,%d)",
3197 Check if it exists already in memory
3199 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3204 Try to load it from disk (with blocks)
3206 if(loadSectorFull(p2d) == true)
3208 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3211 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3212 throw InvalidPositionException("");
3218 If there is no master heightmap, throw.
3220 if(m_heightmap == NULL)
3222 throw InvalidPositionException("createSector(): no heightmap");
3226 Do not create over-limit
3228 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3229 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3230 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3231 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3232 throw InvalidPositionException("createSector(): pos. over limit");
3235 Generate blank sector
3238 // Number of heightmaps in sector in each direction
3239 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3241 // Heightmap side width
3242 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3244 sector = new ServerMapSector(this, p2d, hm_split);
3246 // Sector position on map in nodes
3247 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3249 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3250 " heightmaps and objects"<<std::endl;*/
3253 Generate sector heightmap
3256 v2s16 mhm_p = p2d * hm_split;
3257 /*f32 corners[4] = {
3258 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3259 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3260 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3261 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3264 // Loop through sub-heightmaps
3265 for(s16 y=0; y<hm_split; y++)
3266 for(s16 x=0; x<hm_split; x++)
3268 v2s16 p_in_sector = v2s16(x,y);
3269 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3271 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3272 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3273 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3274 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3277 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3278 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3281 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3283 sector->setHeightmap(p_in_sector, hm);
3285 //hm->generateContinued(1.0, 0.5, corners);
3286 hm->generateContinued(0.5, 0.5, corners);
3291 // Add dummy objects
3292 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3293 sector->setObjects(objects);
3298 m_sectors.insert(p2d, sector);
3303 MapSector * ServerMap::emergeSector(v2s16 p2d,
3304 core::map<v3s16, MapBlock*> &changed_blocks)
3306 DSTACK("%s: p2d=(%d,%d)",
3313 v2s16 chunkpos = sector_to_chunk(p2d);
3314 /*bool chunk_nonvolatile = false;
3315 MapChunk *chunk = getChunk(chunkpos);
3316 if(chunk && chunk->getIsVolatile() == false)
3317 chunk_nonvolatile = true;*/
3318 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3321 If chunk is not fully generated, generate chunk
3323 if(chunk_nonvolatile == false)
3325 // Generate chunk and neighbors
3326 generateChunk(chunkpos, changed_blocks);
3330 Return sector if it exists now
3332 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3337 Try to load it from disk
3339 if(loadSectorFull(p2d) == true)
3341 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3344 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3345 throw InvalidPositionException("");
3351 generateChunk should have generated the sector
3358 //return generateSector();
3362 NOTE: This is not used for main map generation, only for blocks
3363 that are very high or low
3365 MapBlock * ServerMap::generateBlock(
3367 MapBlock *original_dummy,
3368 ServerMapSector *sector,
3369 core::map<v3s16, MapBlock*> &changed_blocks,
3370 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3373 DSTACK("%s: p=(%d,%d,%d)",
3377 /*dstream<<"generateBlock(): "
3378 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3381 MapBlock *block = original_dummy;
3383 v2s16 p2d(p.X, p.Z);
3387 Do not generate over-limit
3389 if(blockpos_over_limit(p))
3391 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3392 throw InvalidPositionException("generateBlock(): pos. over limit");
3396 If block doesn't exist, create one.
3397 If it exists, it is a dummy. In that case unDummify() it.
3399 NOTE: This already sets the map as the parent of the block
3403 block = sector->createBlankBlockNoInsert(block_y);
3407 // Remove the block so that nobody can get a half-generated one.
3408 sector->removeBlock(block);
3409 // Allocate the block to contain the generated data
3413 /*u8 water_material = CONTENT_WATER;
3414 if(g_settings.getBool("endless_water"))
3415 water_material = CONTENT_WATERSOURCE;*/
3416 u8 water_material = CONTENT_WATERSOURCE;
3418 s32 lowest_ground_y = 32767;
3419 s32 highest_ground_y = -32768;
3422 //sector->printHeightmaps();
3424 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3425 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3427 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3429 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3430 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3431 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3433 dstream<<"WARNING: Surface height not found in sector "
3434 "for block that is being emerged"<<std::endl;
3438 s16 surface_y = surface_y_f;
3439 //avg_ground_y += surface_y;
3440 if(surface_y < lowest_ground_y)
3441 lowest_ground_y = surface_y;
3442 if(surface_y > highest_ground_y)
3443 highest_ground_y = surface_y;
3445 s32 surface_depth = 0;
3447 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3449 //float min_slope = 0.45;
3450 //float max_slope = 0.85;
3451 float min_slope = 0.60;
3452 float max_slope = 1.20;
3453 float min_slope_depth = 5.0;
3454 float max_slope_depth = 0;
3456 if(slope < min_slope)
3457 surface_depth = min_slope_depth;
3458 else if(slope > max_slope)
3459 surface_depth = max_slope_depth;
3461 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3463 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3465 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3470 NOTE: If there are some man-made structures above the
3471 newly created block, they won't be taken into account.
3473 if(real_y > surface_y)
3474 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3480 // If node is over heightmap y, it's air or water
3481 if(real_y > surface_y)
3483 // If under water level, it's water
3484 if(real_y < WATER_LEVEL)
3486 n.d = water_material;
3487 n.setLight(LIGHTBANK_DAY,
3488 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3490 Add to transforming liquid queue (in case it'd
3493 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3494 m_transforming_liquid.push_back(real_pos);
3500 // Else it's ground or dungeons (air)
3503 // If it's surface_depth under ground, it's stone
3504 if(real_y <= surface_y - surface_depth)
3506 n.d = CONTENT_STONE;
3510 // It is mud if it is under the first ground
3511 // level or under water
3512 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3518 n.d = CONTENT_GRASS;
3521 //n.d = CONTENT_MUD;
3523 /*// If under water level, it's mud
3524 if(real_y < WATER_LEVEL)
3526 // Only the topmost node is grass
3527 else if(real_y <= surface_y - 1)
3530 n.d = CONTENT_GRASS;*/
3534 block->setNode(v3s16(x0,y0,z0), n);
3539 Calculate some helper variables
3542 // Completely underground if the highest part of block is under lowest
3544 // This has to be very sure; it's probably one too strict now but
3545 // that's just better.
3546 bool completely_underground =
3547 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3549 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3551 bool mostly_underwater_surface = false;
3552 if(highest_ground_y < WATER_LEVEL
3553 && some_part_underground && !completely_underground)
3554 mostly_underwater_surface = true;
3557 Get local attributes
3560 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3562 float caves_amount = 0.5;
3567 NOTE: BEWARE: Too big amount of attribute points slows verything
3569 1 interpolation from 5000 points takes 2-3ms.
3571 //TimeTaker timer("generateBlock() local attribute retrieval");
3572 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3573 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3574 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3578 //dstream<<"generateBlock(): Done"<<std::endl;
3584 // Initialize temporary table
3585 const s32 ued = MAP_BLOCKSIZE;
3586 bool underground_emptiness[ued*ued*ued];
3587 for(s32 i=0; i<ued*ued*ued; i++)
3589 underground_emptiness[i] = 0;
3596 Initialize orp and ors. Try to find if some neighboring
3597 MapBlock has a tunnel ended in its side
3601 (float)(myrand()%ued)+0.5,
3602 (float)(myrand()%ued)+0.5,
3603 (float)(myrand()%ued)+0.5
3606 bool found_existing = false;
3612 for(s16 y=0; y<ued; y++)
3613 for(s16 x=0; x<ued; x++)
3615 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3616 if(getNode(ap).d == CONTENT_AIR)
3618 orp = v3f(x+1,y+1,0);
3619 found_existing = true;
3620 goto continue_generating;
3624 catch(InvalidPositionException &e){}
3630 for(s16 y=0; y<ued; y++)
3631 for(s16 x=0; x<ued; x++)
3633 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3634 if(getNode(ap).d == CONTENT_AIR)
3636 orp = v3f(x+1,y+1,ued-1);
3637 found_existing = true;
3638 goto continue_generating;
3642 catch(InvalidPositionException &e){}
3648 for(s16 y=0; y<ued; y++)
3649 for(s16 z=0; z<ued; z++)
3651 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3652 if(getNode(ap).d == CONTENT_AIR)
3654 orp = v3f(0,y+1,z+1);
3655 found_existing = true;
3656 goto continue_generating;
3660 catch(InvalidPositionException &e){}
3666 for(s16 y=0; y<ued; y++)
3667 for(s16 z=0; z<ued; z++)
3669 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3670 if(getNode(ap).d == CONTENT_AIR)
3672 orp = v3f(ued-1,y+1,z+1);
3673 found_existing = true;
3674 goto continue_generating;
3678 catch(InvalidPositionException &e){}
3684 for(s16 x=0; x<ued; x++)
3685 for(s16 z=0; z<ued; z++)
3687 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3688 if(getNode(ap).d == CONTENT_AIR)
3690 orp = v3f(x+1,0,z+1);
3691 found_existing = true;
3692 goto continue_generating;
3696 catch(InvalidPositionException &e){}
3702 for(s16 x=0; x<ued; x++)
3703 for(s16 z=0; z<ued; z++)
3705 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3706 if(getNode(ap).d == CONTENT_AIR)
3708 orp = v3f(x+1,ued-1,z+1);
3709 found_existing = true;
3710 goto continue_generating;
3714 catch(InvalidPositionException &e){}
3716 continue_generating:
3719 Choose whether to actually generate dungeon
3721 bool do_generate_dungeons = true;
3722 // Don't generate if no part is underground
3723 if(!some_part_underground)
3725 do_generate_dungeons = false;
3727 // Don't generate if mostly underwater surface
3728 /*else if(mostly_underwater_surface)
3730 do_generate_dungeons = false;
3732 // Partly underground = cave
3733 else if(!completely_underground)
3735 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3737 // Found existing dungeon underground
3738 else if(found_existing && completely_underground)
3740 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3742 // Underground and no dungeons found
3745 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3748 if(do_generate_dungeons)
3751 Generate some tunnel starting from orp and ors
3753 for(u16 i=0; i<3; i++)
3756 (float)(myrand()%ued)+0.5,
3757 (float)(myrand()%ued)+0.5,
3758 (float)(myrand()%ued)+0.5
3762 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3766 for(float f=0; f<1.0; f+=0.04)
3768 v3f fp = orp + vec * f;
3769 v3s16 cp(fp.X, fp.Y, fp.Z);
3771 s16 d1 = d0 + rs - 1;
3772 for(s16 z0=d0; z0<=d1; z0++)
3774 s16 si = rs - abs(z0);
3775 for(s16 x0=-si; x0<=si-1; x0++)
3777 s16 si2 = rs - abs(x0);
3778 for(s16 y0=-si2+1; y0<=si2-1; y0++)
3784 if(isInArea(p, ued) == false)
3786 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3798 // Set to true if has caves.
3799 // Set when some non-air is changed to air when making caves.
3800 bool has_dungeons = false;
3803 Apply temporary cave data to block
3806 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3807 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3809 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3811 MapNode n = block->getNode(v3s16(x0,y0,z0));
3814 if(underground_emptiness[
3815 ued*ued*(z0*ued/MAP_BLOCKSIZE)
3816 +ued*(y0*ued/MAP_BLOCKSIZE)
3817 +(x0*ued/MAP_BLOCKSIZE)])
3819 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
3822 has_dungeons = true;
3828 block->setNode(v3s16(x0,y0,z0), n);
3833 This is used for guessing whether or not the block should
3834 receive sunlight from the top if the block above doesn't exist
3836 block->setIsUnderground(completely_underground);
3839 Force lighting update if some part of block is partly
3840 underground and has caves.
3842 /*if(some_part_underground && !completely_underground && has_dungeons)
3844 //dstream<<"Half-ground caves"<<std::endl;
3845 lighting_invalidated_blocks[block->getPos()] = block;
3848 // DEBUG: Always update lighting
3849 //lighting_invalidated_blocks[block->getPos()] = block;
3855 if(some_part_underground)
3857 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
3862 for(s16 i=0; i<underground_level/4 + 1; i++)
3864 if(myrand()%50 == 0)
3867 (myrand()%(MAP_BLOCKSIZE-2))+1,
3868 (myrand()%(MAP_BLOCKSIZE-2))+1,
3869 (myrand()%(MAP_BLOCKSIZE-2))+1
3875 for(u16 i=0; i<27; i++)
3877 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3879 block->setNode(cp+g_27dirs[i], n);
3887 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
3888 u16 coal_rareness = 60 / coal_amount;
3889 if(coal_rareness == 0)
3891 if(myrand()%coal_rareness == 0)
3893 u16 a = myrand() % 16;
3894 u16 amount = coal_amount * a*a*a / 1000;
3895 for(s16 i=0; i<amount; i++)
3898 (myrand()%(MAP_BLOCKSIZE-2))+1,
3899 (myrand()%(MAP_BLOCKSIZE-2))+1,
3900 (myrand()%(MAP_BLOCKSIZE-2))+1
3904 n.d = CONTENT_STONE;
3905 n.param = MINERAL_COAL;
3907 for(u16 i=0; i<27; i++)
3909 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3911 block->setNode(cp+g_27dirs[i], n);
3919 //TODO: change to iron_amount or whatever
3920 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
3921 u16 iron_rareness = 60 / iron_amount;
3922 if(iron_rareness == 0)
3924 if(myrand()%iron_rareness == 0)
3926 u16 a = myrand() % 16;
3927 u16 amount = iron_amount * a*a*a / 1000;
3928 for(s16 i=0; i<amount; i++)
3931 (myrand()%(MAP_BLOCKSIZE-2))+1,
3932 (myrand()%(MAP_BLOCKSIZE-2))+1,
3933 (myrand()%(MAP_BLOCKSIZE-2))+1
3937 n.d = CONTENT_STONE;
3938 n.param = MINERAL_IRON;
3940 for(u16 i=0; i<27; i++)
3942 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3944 block->setNode(cp+g_27dirs[i], n);
3951 Create a few rats in empty blocks underground
3953 if(completely_underground)
3955 //for(u16 i=0; i<2; i++)
3958 (myrand()%(MAP_BLOCKSIZE-2))+1,
3959 (myrand()%(MAP_BLOCKSIZE-2))+1,
3960 (myrand()%(MAP_BLOCKSIZE-2))+1
3963 // Check that the place is empty
3964 //if(!is_ground_content(block->getNode(cp).d))
3967 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
3968 block->addObject(obj);
3974 Add block to sector.
3976 sector->insertBlock(block);
3982 // An y-wise container of changed blocks
3983 core::map<s16, MapBlock*> changed_blocks_sector;
3986 Check if any sector's objects can be placed now.
3989 core::map<v3s16, u8> *objects = sector->getObjects();
3990 core::list<v3s16> objects_to_remove;
3991 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
3992 i.atEnd() == false; i++)
3994 v3s16 p = i.getNode()->getKey();
3996 u8 d = i.getNode()->getValue();
3998 // Ground level point (user for stuff that is on ground)
4000 bool ground_found = true;
4002 // Search real ground level
4006 MapNode n = sector->getNode(gp);
4008 // If not air, go one up and continue to placing the tree
4009 if(n.d != CONTENT_AIR)
4015 // If air, go one down
4016 gp += v3s16(0,-1,0);
4018 }catch(InvalidPositionException &e)
4020 // Ground not found.
4021 ground_found = false;
4022 // This is most close to ground
4029 if(d == SECTOR_OBJECT_TEST)
4031 if(sector->isValidArea(p + v3s16(0,0,0),
4032 p + v3s16(0,0,0), &changed_blocks_sector))
4035 n.d = CONTENT_TORCH;
4036 sector->setNode(p, n);
4037 objects_to_remove.push_back(p);
4040 else if(d == SECTOR_OBJECT_TREE_1)
4042 if(ground_found == false)
4045 v3s16 p_min = gp + v3s16(-1,0,-1);
4046 v3s16 p_max = gp + v3s16(1,5,1);
4047 if(sector->isValidArea(p_min, p_max,
4048 &changed_blocks_sector))
4052 sector->setNode(gp+v3s16(0,0,0), n);
4053 sector->setNode(gp+v3s16(0,1,0), n);
4054 sector->setNode(gp+v3s16(0,2,0), n);
4055 sector->setNode(gp+v3s16(0,3,0), n);
4057 n.d = CONTENT_LEAVES;
4059 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4061 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4062 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4063 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4064 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4065 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4066 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4067 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4068 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4070 sector->setNode(gp+v3s16(0,4,0), n);
4072 sector->setNode(gp+v3s16(-1,4,0), n);
4073 sector->setNode(gp+v3s16(1,4,0), n);
4074 sector->setNode(gp+v3s16(0,4,-1), n);
4075 sector->setNode(gp+v3s16(0,4,1), n);
4076 sector->setNode(gp+v3s16(1,4,1), n);
4077 sector->setNode(gp+v3s16(-1,4,1), n);
4078 sector->setNode(gp+v3s16(-1,4,-1), n);
4079 sector->setNode(gp+v3s16(1,4,-1), n);
4081 sector->setNode(gp+v3s16(-1,3,0), n);
4082 sector->setNode(gp+v3s16(1,3,0), n);
4083 sector->setNode(gp+v3s16(0,3,-1), n);
4084 sector->setNode(gp+v3s16(0,3,1), n);
4085 sector->setNode(gp+v3s16(1,3,1), n);
4086 sector->setNode(gp+v3s16(-1,3,1), n);
4087 sector->setNode(gp+v3s16(-1,3,-1), n);
4088 sector->setNode(gp+v3s16(1,3,-1), n);
4090 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4091 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4092 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4093 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4094 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4095 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4096 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4097 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4099 // Objects are identified by wanted position
4100 objects_to_remove.push_back(p);
4102 // Lighting has to be recalculated for this one.
4103 sector->getBlocksInArea(p_min, p_max,
4104 lighting_invalidated_blocks);
4107 else if(d == SECTOR_OBJECT_BUSH_1)
4109 if(ground_found == false)
4112 if(sector->isValidArea(gp + v3s16(0,0,0),
4113 gp + v3s16(0,0,0), &changed_blocks_sector))
4116 n.d = CONTENT_LEAVES;
4117 sector->setNode(gp+v3s16(0,0,0), n);
4119 // Objects are identified by wanted position
4120 objects_to_remove.push_back(p);
4123 else if(d == SECTOR_OBJECT_RAVINE)
4126 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4127 v3s16 p_max = p + v3s16(6,6,6);
4128 if(sector->isValidArea(p_min, p_max,
4129 &changed_blocks_sector))
4132 n.d = CONTENT_STONE;
4135 s16 depth = maxdepth + (myrand()%10);
4137 s16 minz = -6 - (-2);
4139 for(s16 x=-6; x<=6; x++)
4141 z += -1 + (myrand()%3);
4146 for(s16 y=depth+(myrand()%2); y<=6; y++)
4148 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4151 v3s16 p2 = p + v3s16(x,y,z-2);
4152 //if(is_ground_content(sector->getNode(p2).d))
4153 if(content_features(sector->getNode(p2).d).walkable)
4154 sector->setNode(p2, n);
4157 v3s16 p2 = p + v3s16(x,y,z-1);
4158 if(content_features(sector->getNode(p2).d).walkable)
4159 sector->setNode(p2, n2);
4162 v3s16 p2 = p + v3s16(x,y,z+0);
4163 if(content_features(sector->getNode(p2).d).walkable)
4164 sector->setNode(p2, n2);
4167 v3s16 p2 = p + v3s16(x,y,z+1);
4168 if(content_features(sector->getNode(p2).d).walkable)
4169 sector->setNode(p2, n);
4172 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4173 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4177 objects_to_remove.push_back(p);
4179 // Lighting has to be recalculated for this one.
4180 sector->getBlocksInArea(p_min, p_max,
4181 lighting_invalidated_blocks);
4186 dstream<<"ServerMap::generateBlock(): "
4187 "Invalid heightmap object"
4192 catch(InvalidPositionException &e)
4194 dstream<<"WARNING: "<<__FUNCTION_NAME
4195 <<": while inserting object "<<(int)d
4196 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4197 <<" InvalidPositionException.what()="
4198 <<e.what()<<std::endl;
4199 // This is not too fatal and seems to happen sometimes.
4204 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4205 i != objects_to_remove.end(); i++)
4207 objects->remove(*i);
4211 Translate sector's changed blocks to global changed blocks
4214 for(core::map<s16, MapBlock*>::Iterator
4215 i = changed_blocks_sector.getIterator();
4216 i.atEnd() == false; i++)
4218 MapBlock *block = i.getNode()->getValue();
4220 changed_blocks.insert(block->getPos(), block);
4223 block->setLightingExpired(true);
4230 <<"lighting_invalidated_blocks.size()"
4234 <<" "<<lighting_invalidated_blocks.size()
4235 <<", "<<has_dungeons
4236 <<", "<<completely_underground
4237 <<", "<<some_part_underground
4244 MapBlock * ServerMap::createBlock(v3s16 p)
4246 DSTACK("%s: p=(%d,%d,%d)",
4247 __FUNCTION_NAME, p.X, p.Y, p.Z);
4249 v2s16 p2d(p.X, p.Z);
4252 This will create or load a sector if not found in memory.
4253 If block exists on disk, it will be loaded.
4255 NOTE: On old save formats, this will be slow, as it generates
4256 lighting on blocks for them.
4258 ServerMapSector *sector;
4260 sector = (ServerMapSector*)createSector(p2d);
4261 assert(sector->getId() == MAPSECTOR_SERVER);
4263 /*catch(InvalidPositionException &e)
4265 dstream<<"createBlock: createSector() failed"<<std::endl;
4268 catch(std::exception &e)
4270 dstream<<"createBlock: createSector() failed: "
4271 <<e.what()<<std::endl;
4276 Try to get a block from the sector
4279 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4283 block = sector->createBlankBlock(block_y);
4287 MapBlock * ServerMap::emergeBlock(
4289 bool only_from_disk,
4290 core::map<v3s16, MapBlock*> &changed_blocks,
4291 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4294 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4296 p.X, p.Y, p.Z, only_from_disk);
4298 v2s16 p2d(p.X, p.Z);
4301 This will create or load a sector if not found in memory.
4302 If block exists on disk, it will be loaded.
4304 NOTE: On old save formats, this will be slow, as it generates
4305 lighting on blocks for them.
4307 ServerMapSector *sector;
4309 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4310 assert(sector->getId() == MAPSECTOR_SERVER);
4312 catch(std::exception &e)
4314 dstream<<"emergeBlock: emergeSector() failed: "
4315 <<e.what()<<std::endl;
4320 Try to get a block from the sector
4323 bool does_not_exist = false;
4324 bool lighting_expired = false;
4325 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4329 does_not_exist = true;
4331 else if(block->isDummy() == true)
4333 does_not_exist = true;
4335 else if(block->getLightingExpired())
4337 lighting_expired = true;
4342 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4347 If block was not found on disk and not going to generate a
4348 new one, make sure there is a dummy block in place.
4350 if(only_from_disk && (does_not_exist || lighting_expired))
4352 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4356 // Create dummy block
4357 block = new MapBlock(this, p, true);
4359 // Add block to sector
4360 sector->insertBlock(block);
4366 //dstream<<"Not found on disk, generating."<<std::endl;
4368 //TimeTaker("emergeBlock() generate");
4370 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4373 If the block doesn't exist, generate the block.
4377 block = generateBlock(p, block, sector, changed_blocks,
4378 lighting_invalidated_blocks);
4381 if(lighting_expired)
4383 lighting_invalidated_blocks.insert(p, block);
4387 Initially update sunlight
4391 core::map<v3s16, bool> light_sources;
4392 bool black_air_left = false;
4393 bool bottom_invalid =
4394 block->propagateSunlight(light_sources, true,
4395 &black_air_left, true);
4397 // If sunlight didn't reach everywhere and part of block is
4398 // above ground, lighting has to be properly updated
4399 //if(black_air_left && some_part_underground)
4402 lighting_invalidated_blocks[block->getPos()] = block;
4407 lighting_invalidated_blocks[block->getPos()] = block;
4412 Debug mode operation
4414 bool haxmode = g_settings.getBool("haxmode");
4417 // Don't calculate lighting at all
4418 //lighting_invalidated_blocks.clear();
4424 void ServerMap::createDir(std::string path)
4426 if(fs::CreateDir(path) == false)
4428 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4429 <<"\""<<path<<"\""<<std::endl;
4430 throw BaseException("ServerMap failed to create directory");
4434 std::string ServerMap::getSectorSubDir(v2s16 pos)
4437 snprintf(cc, 9, "%.4x%.4x",
4438 (unsigned int)pos.X&0xffff,
4439 (unsigned int)pos.Y&0xffff);
4441 return std::string(cc);
4444 std::string ServerMap::getSectorDir(v2s16 pos)
4446 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4449 v2s16 ServerMap::getSectorPos(std::string dirname)
4451 if(dirname.size() != 8)
4452 throw InvalidFilenameException("Invalid sector directory name");
4454 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4456 throw InvalidFilenameException("Invalid sector directory name");
4457 v2s16 pos((s16)x, (s16)y);
4461 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4463 v2s16 p2d = getSectorPos(sectordir);
4465 if(blockfile.size() != 4){
4466 throw InvalidFilenameException("Invalid block filename");
4469 int r = sscanf(blockfile.c_str(), "%4x", &y);
4471 throw InvalidFilenameException("Invalid block filename");
4472 return v3s16(p2d.X, y, p2d.Y);
4476 #define ENABLE_SECTOR_SAVING 1
4477 #define ENABLE_SECTOR_LOADING 1
4478 #define ENABLE_BLOCK_SAVING 1
4479 #define ENABLE_BLOCK_LOADING 1
4481 void ServerMap::save(bool only_changed)
4483 DSTACK(__FUNCTION_NAME);
4484 if(m_map_saving_enabled == false)
4486 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4490 if(only_changed == false)
4491 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4494 saveMasterHeightmap();
4496 u32 sector_meta_count = 0;
4497 u32 block_count = 0;
4500 JMutexAutoLock lock(m_sector_mutex);
4502 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4503 for(; i.atEnd() == false; i++)
4505 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4506 assert(sector->getId() == MAPSECTOR_SERVER);
4508 if(ENABLE_SECTOR_SAVING)
4510 if(sector->differs_from_disk || only_changed == false)
4512 saveSectorMeta(sector);
4513 sector_meta_count++;
4516 if(ENABLE_BLOCK_SAVING)
4518 core::list<MapBlock*> blocks;
4519 sector->getBlocks(blocks);
4520 core::list<MapBlock*>::Iterator j;
4521 for(j=blocks.begin(); j!=blocks.end(); j++)
4523 MapBlock *block = *j;
4524 if(block->getChangedFlag() || only_changed == false)
4536 Only print if something happened or saved whole map
4538 if(only_changed == false || sector_meta_count != 0
4539 || block_count != 0)
4541 dstream<<DTIME<<"ServerMap: Written: "
4542 <<sector_meta_count<<" sector metadata files, "
4543 <<block_count<<" block files"
4548 void ServerMap::loadAll()
4550 DSTACK(__FUNCTION_NAME);
4551 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4553 loadMasterHeightmap();
4555 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4557 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4559 JMutexAutoLock lock(m_sector_mutex);
4562 s32 printed_counter = -100000;
4563 s32 count = list.size();
4565 std::vector<fs::DirListNode>::iterator i;
4566 for(i=list.begin(); i!=list.end(); i++)
4568 if(counter > printed_counter + 10)
4570 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4571 printed_counter = counter;
4575 MapSector *sector = NULL;
4577 // We want directories
4581 sector = loadSectorMeta(i->name);
4583 catch(InvalidFilenameException &e)
4585 // This catches unknown crap in directory
4588 if(ENABLE_BLOCK_LOADING)
4590 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4591 (m_savedir+"/sectors/"+i->name);
4592 std::vector<fs::DirListNode>::iterator i2;
4593 for(i2=list2.begin(); i2!=list2.end(); i2++)
4599 loadBlock(i->name, i2->name, sector);
4601 catch(InvalidFilenameException &e)
4603 // This catches unknown crap in directory
4608 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4611 void ServerMap::saveMasterHeightmap()
4613 DSTACK(__FUNCTION_NAME);
4614 createDir(m_savedir);
4616 std::string fullpath = m_savedir + "/master_heightmap";
4617 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4618 if(o.good() == false)
4619 throw FileNotGoodException("Cannot open master heightmap");
4621 // Format used for writing
4622 u8 version = SER_FMT_VER_HIGHEST;
4625 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4627 [0] u8 serialization version
4628 [1] X master heightmap
4630 u32 fullsize = 1 + hmdata.getSize();
4631 SharedBuffer<u8> data(fullsize);
4634 memcpy(&data[1], *hmdata, hmdata.getSize());
4636 o.write((const char*)*data, fullsize);
4639 m_heightmap->serialize(o, version);
4642 void ServerMap::loadMasterHeightmap()
4644 DSTACK(__FUNCTION_NAME);
4645 std::string fullpath = m_savedir + "/master_heightmap";
4646 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4647 if(is.good() == false)
4648 throw FileNotGoodException("Cannot open master heightmap");
4650 if(m_heightmap != NULL)
4653 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4656 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4658 DSTACK(__FUNCTION_NAME);
4659 // Format used for writing
4660 u8 version = SER_FMT_VER_HIGHEST;
4662 v2s16 pos = sector->getPos();
4663 createDir(m_savedir);
4664 createDir(m_savedir+"/sectors");
4665 std::string dir = getSectorDir(pos);
4668 std::string fullpath = dir + "/heightmap";
4669 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4670 if(o.good() == false)
4671 throw FileNotGoodException("Cannot open master heightmap");
4673 sector->serialize(o, version);
4675 sector->differs_from_disk = false;
4678 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4680 DSTACK(__FUNCTION_NAME);
4682 v2s16 p2d = getSectorPos(dirname);
4683 std::string dir = m_savedir + "/sectors/" + dirname;
4685 std::string fullpath = dir + "/heightmap";
4686 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4687 if(is.good() == false)
4688 throw FileNotGoodException("Cannot open sector heightmap");
4690 ServerMapSector *sector = ServerMapSector::deSerialize
4691 (is, this, p2d, &m_hwrapper, m_sectors);
4693 sector->differs_from_disk = false;
4698 bool ServerMap::loadSectorFull(v2s16 p2d)
4700 DSTACK(__FUNCTION_NAME);
4701 std::string sectorsubdir = getSectorSubDir(p2d);
4703 MapSector *sector = NULL;
4705 JMutexAutoLock lock(m_sector_mutex);
4708 sector = loadSectorMeta(sectorsubdir);
4710 catch(InvalidFilenameException &e)
4714 catch(FileNotGoodException &e)
4718 catch(std::exception &e)
4723 if(ENABLE_BLOCK_LOADING)
4725 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4726 (m_savedir+"/sectors/"+sectorsubdir);
4727 std::vector<fs::DirListNode>::iterator i2;
4728 for(i2=list2.begin(); i2!=list2.end(); i2++)
4734 loadBlock(sectorsubdir, i2->name, sector);
4736 catch(InvalidFilenameException &e)
4738 // This catches unknown crap in directory
4746 bool ServerMap::deFlushSector(v2s16 p2d)
4748 DSTACK(__FUNCTION_NAME);
4749 // See if it already exists in memory
4751 MapSector *sector = getSectorNoGenerate(p2d);
4754 catch(InvalidPositionException &e)
4757 Try to load the sector from disk.
4759 if(loadSectorFull(p2d) == true)
4768 void ServerMap::saveBlock(MapBlock *block)
4770 DSTACK(__FUNCTION_NAME);
4772 Dummy blocks are not written
4774 if(block->isDummy())
4776 /*v3s16 p = block->getPos();
4777 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4778 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4782 // Format used for writing
4783 u8 version = SER_FMT_VER_HIGHEST;
4785 v3s16 p3d = block->getPos();
4786 v2s16 p2d(p3d.X, p3d.Z);
4787 createDir(m_savedir);
4788 createDir(m_savedir+"/sectors");
4789 std::string dir = getSectorDir(p2d);
4792 // Block file is map/sectors/xxxxxxxx/xxxx
4794 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4795 std::string fullpath = dir + "/" + cc;
4796 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4797 if(o.good() == false)
4798 throw FileNotGoodException("Cannot open block data");
4801 [0] u8 serialization version
4804 o.write((char*)&version, 1);
4806 block->serialize(o, version);
4809 Versions up from 9 have block objects.
4813 block->serializeObjects(o, version);
4816 // We just wrote it to the disk
4817 block->resetChangedFlag();
4820 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4822 DSTACK(__FUNCTION_NAME);
4826 // Block file is map/sectors/xxxxxxxx/xxxx
4827 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4828 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4829 if(is.good() == false)
4830 throw FileNotGoodException("Cannot open block file");
4832 v3s16 p3d = getBlockPos(sectordir, blockfile);
4833 v2s16 p2d(p3d.X, p3d.Z);
4835 assert(sector->getPos() == p2d);
4837 u8 version = SER_FMT_VER_INVALID;
4838 is.read((char*)&version, 1);
4840 /*u32 block_size = MapBlock::serializedLength(version);
4841 SharedBuffer<u8> data(block_size);
4842 is.read((char*)*data, block_size);*/
4844 // This will always return a sector because we're the server
4845 //MapSector *sector = emergeSector(p2d);
4847 MapBlock *block = NULL;
4848 bool created_new = false;
4850 block = sector->getBlockNoCreate(p3d.Y);
4852 catch(InvalidPositionException &e)
4854 block = sector->createBlankBlockNoInsert(p3d.Y);
4858 // deserialize block data
4859 block->deSerialize(is, version);
4862 Versions up from 9 have block objects.
4866 block->updateObjects(is, version, NULL, 0);
4870 sector->insertBlock(block);
4873 Convert old formats to new and save
4876 // Save old format blocks in new format
4877 if(version < SER_FMT_VER_HIGHEST)
4882 // We just loaded it from the disk, so it's up-to-date.
4883 block->resetChangedFlag();
4886 catch(SerializationError &e)
4888 dstream<<"WARNING: Invalid block data on disk "
4889 "(SerializationError). Ignoring."
4894 // Gets from master heightmap
4895 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4897 assert(m_heightmap != NULL);
4905 corners[0] = m_heightmap->getGroundHeight
4906 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4907 corners[1] = m_heightmap->getGroundHeight
4908 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4909 corners[2] = m_heightmap->getGroundHeight
4910 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4911 corners[3] = m_heightmap->getGroundHeight
4912 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
4915 void ServerMap::PrintInfo(std::ostream &out)
4926 ClientMap::ClientMap(
4928 MapDrawControl &control,
4929 scene::ISceneNode* parent,
4930 scene::ISceneManager* mgr,
4934 scene::ISceneNode(parent, mgr, id),
4941 /*m_box = core::aabbox3d<f32>(0,0,0,
4942 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4943 /*m_box = core::aabbox3d<f32>(0,0,0,
4944 map->getSizeNodes().X * BS,
4945 map->getSizeNodes().Y * BS,
4946 map->getSizeNodes().Z * BS);*/
4947 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4948 BS*1000000,BS*1000000,BS*1000000);
4950 //setPosition(v3f(BS,BS,BS));
4953 ClientMap::~ClientMap()
4955 JMutexAutoLock lock(mesh_mutex);
4964 MapSector * ClientMap::emergeSector(v2s16 p2d)
4966 DSTACK(__FUNCTION_NAME);
4967 // Check that it doesn't exist already
4969 return getSectorNoGenerate(p2d);
4971 catch(InvalidPositionException &e)
4975 // Create a sector with no heightmaps
4976 ClientMapSector *sector = new ClientMapSector(this, p2d);
4979 JMutexAutoLock lock(m_sector_mutex);
4980 m_sectors.insert(p2d, sector);
4986 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
4988 DSTACK(__FUNCTION_NAME);
4989 ClientMapSector *sector = NULL;
4991 JMutexAutoLock lock(m_sector_mutex);
4993 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
4997 sector = (ClientMapSector*)n->getValue();
4998 assert(sector->getId() == MAPSECTOR_CLIENT);
5002 sector = new ClientMapSector(this, p2d);
5004 JMutexAutoLock lock(m_sector_mutex);
5005 m_sectors.insert(p2d, sector);
5009 sector->deSerialize(is);
5012 void ClientMap::OnRegisterSceneNode()
5016 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5017 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5020 ISceneNode::OnRegisterSceneNode();
5023 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5025 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5026 DSTACK(__FUNCTION_NAME);
5028 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5031 Get time for measuring timeout.
5033 Measuring time is very useful for long delays when the
5034 machine is swapping a lot.
5036 int time1 = time(0);
5038 u32 daynight_ratio = m_client->getDayNightRatio();
5040 m_camera_mutex.Lock();
5041 v3f camera_position = m_camera_position;
5042 v3f camera_direction = m_camera_direction;
5043 m_camera_mutex.Unlock();
5046 Get all blocks and draw all visible ones
5049 v3s16 cam_pos_nodes(
5050 camera_position.X / BS,
5051 camera_position.Y / BS,
5052 camera_position.Z / BS);
5054 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5056 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5057 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5059 // Take a fair amount as we will be dropping more out later
5061 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5062 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5063 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5065 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5066 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5067 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5069 u32 vertex_count = 0;
5071 // For limiting number of mesh updates per frame
5072 u32 mesh_update_count = 0;
5074 u32 blocks_would_have_drawn = 0;
5075 u32 blocks_drawn = 0;
5077 //NOTE: The sectors map should be locked but we're not doing it
5078 // because it'd cause too much delays
5080 int timecheck_counter = 0;
5081 core::map<v2s16, MapSector*>::Iterator si;
5082 si = m_sectors.getIterator();
5083 for(; si.atEnd() == false; si++)
5086 timecheck_counter++;
5087 if(timecheck_counter > 50)
5089 int time2 = time(0);
5090 if(time2 > time1 + 4)
5092 dstream<<"ClientMap::renderMap(): "
5093 "Rendering takes ages, returning."
5100 MapSector *sector = si.getNode()->getValue();
5101 v2s16 sp = sector->getPos();
5103 if(m_control.range_all == false)
5105 if(sp.X < p_blocks_min.X
5106 || sp.X > p_blocks_max.X
5107 || sp.Y < p_blocks_min.Z
5108 || sp.Y > p_blocks_max.Z)
5112 core::list< MapBlock * > sectorblocks;
5113 sector->getBlocks(sectorblocks);
5119 core::list< MapBlock * >::Iterator i;
5120 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5122 MapBlock *block = *i;
5125 Compare block position to camera position, skip
5126 if not seen on display
5129 float range = 100000 * BS;
5130 if(m_control.range_all == false)
5131 range = m_control.wanted_range * BS;
5133 if(isBlockInSight(block->getPos(), camera_position,
5134 camera_direction, range) == false)
5140 v3s16 blockpos_nodes = block->getPosRelative();
5142 // Block center position
5144 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5145 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5146 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5149 // Block position relative to camera
5150 v3f blockpos_relative = blockpos - camera_position;
5152 // Distance in camera direction (+=front, -=back)
5153 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5156 f32 d = blockpos_relative.getLength();
5158 if(m_control.range_all == false)
5160 // If block is far away, don't draw it
5161 if(d > m_control.wanted_range * BS)
5165 // Maximum radius of a block
5166 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5168 // If block is (nearly) touching the camera, don't
5169 // bother validating further (that is, render it anyway)
5170 if(d > block_max_radius * 1.5)
5172 // Cosine of the angle between the camera direction
5173 // and the block direction (camera_direction is an unit vector)
5174 f32 cosangle = dforward / d;
5176 // Compensate for the size of the block
5177 // (as the block has to be shown even if it's a bit off FOV)
5178 // This is an estimate.
5179 cosangle += block_max_radius / dforward;
5181 // If block is not in the field of view, skip it
5182 //if(cosangle < cos(FOV_ANGLE/2))
5183 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5188 v3s16 blockpos_nodes = block->getPosRelative();
5190 // Block center position
5192 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5193 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5194 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5197 // Block position relative to camera
5198 v3f blockpos_relative = blockpos - camera_position;
5201 f32 d = blockpos_relative.getLength();
5208 bool mesh_expired = false;
5211 JMutexAutoLock lock(block->mesh_mutex);
5213 mesh_expired = block->getMeshExpired();
5215 // Mesh has not been expired and there is no mesh:
5216 // block has no content
5217 if(block->mesh == NULL && mesh_expired == false)
5221 f32 faraway = BS*50;
5222 //f32 faraway = m_control.wanted_range * BS;
5225 This has to be done with the mesh_mutex unlocked
5227 // Pretty random but this should work somewhat nicely
5228 if(mesh_expired && (
5229 (mesh_update_count < 3
5230 && (d < faraway || mesh_update_count < 2)
5233 (m_control.range_all && mesh_update_count < 20)
5236 /*if(mesh_expired && mesh_update_count < 6
5237 && (d < faraway || mesh_update_count < 3))*/
5239 mesh_update_count++;
5241 // Mesh has been expired: generate new mesh
5242 //block->updateMeshes(daynight_i);
5243 block->updateMesh(daynight_ratio);
5245 mesh_expired = false;
5249 Don't draw an expired mesh that is far away
5251 /*if(mesh_expired && d >= faraway)
5254 // Instead, delete it
5255 JMutexAutoLock lock(block->mesh_mutex);
5258 block->mesh->drop();
5261 // And continue to next block
5266 Draw the faces of the block
5269 JMutexAutoLock lock(block->mesh_mutex);
5271 scene::SMesh *mesh = block->mesh;
5276 blocks_would_have_drawn++;
5277 if(blocks_drawn >= m_control.wanted_max_blocks
5278 && m_control.range_all == false
5279 && d > m_control.wanted_min_range * BS)
5283 u32 c = mesh->getMeshBufferCount();
5285 for(u32 i=0; i<c; i++)
5287 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5288 const video::SMaterial& material = buf->getMaterial();
5289 video::IMaterialRenderer* rnd =
5290 driver->getMaterialRenderer(material.MaterialType);
5291 bool transparent = (rnd && rnd->isTransparent());
5292 // Render transparent on transparent pass and likewise.
5293 if(transparent == is_transparent_pass)
5295 driver->setMaterial(buf->getMaterial());
5296 driver->drawMeshBuffer(buf);
5297 vertex_count += buf->getVertexCount();
5301 } // foreach sectorblocks
5304 m_control.blocks_drawn = blocks_drawn;
5305 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5307 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5308 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5311 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5312 core::map<v3s16, MapBlock*> *affected_blocks)
5314 bool changed = false;
5316 Add it to all blocks touching it
5319 v3s16(0,0,0), // this
5320 v3s16(0,0,1), // back
5321 v3s16(0,1,0), // top
5322 v3s16(1,0,0), // right
5323 v3s16(0,0,-1), // front
5324 v3s16(0,-1,0), // bottom
5325 v3s16(-1,0,0), // left
5327 for(u16 i=0; i<7; i++)
5329 v3s16 p2 = p + dirs[i];
5330 // Block position of neighbor (or requested) node
5331 v3s16 blockpos = getNodeBlockPos(p2);
5332 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5333 if(blockref == NULL)
5335 // Relative position of requested node
5336 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5337 if(blockref->setTempMod(relpos, mod))
5342 if(changed && affected_blocks!=NULL)
5344 for(u16 i=0; i<7; i++)
5346 v3s16 p2 = p + dirs[i];
5347 // Block position of neighbor (or requested) node
5348 v3s16 blockpos = getNodeBlockPos(p2);
5349 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5350 if(blockref == NULL)
5352 affected_blocks->insert(blockpos, blockref);
5358 bool ClientMap::clearTempMod(v3s16 p,
5359 core::map<v3s16, MapBlock*> *affected_blocks)
5361 bool changed = false;
5363 v3s16(0,0,0), // this
5364 v3s16(0,0,1), // back
5365 v3s16(0,1,0), // top
5366 v3s16(1,0,0), // right
5367 v3s16(0,0,-1), // front
5368 v3s16(0,-1,0), // bottom
5369 v3s16(-1,0,0), // left
5371 for(u16 i=0; i<7; i++)
5373 v3s16 p2 = p + dirs[i];
5374 // Block position of neighbor (or requested) node
5375 v3s16 blockpos = getNodeBlockPos(p2);
5376 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5377 if(blockref == NULL)
5379 // Relative position of requested node
5380 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5381 if(blockref->clearTempMod(relpos))
5386 if(changed && affected_blocks!=NULL)
5388 for(u16 i=0; i<7; i++)
5390 v3s16 p2 = p + dirs[i];
5391 // Block position of neighbor (or requested) node
5392 v3s16 blockpos = getNodeBlockPos(p2);
5393 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5394 if(blockref == NULL)
5396 affected_blocks->insert(blockpos, blockref);
5402 void ClientMap::PrintInfo(std::ostream &out)
5413 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5418 MapVoxelManipulator::~MapVoxelManipulator()
5420 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5424 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5426 TimeTaker timer1("emerge", &emerge_time);
5428 // Units of these are MapBlocks
5429 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5430 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5432 VoxelArea block_area_nodes
5433 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5435 addArea(block_area_nodes);
5437 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5438 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5439 for(s32 x=p_min.X; x<=p_max.X; x++)
5442 core::map<v3s16, bool>::Node *n;
5443 n = m_loaded_blocks.find(p);
5447 bool block_data_inexistent = false;
5450 TimeTaker timer1("emerge load", &emerge_load_time);
5452 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5453 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5456 dstream<<std::endl;*/
5458 MapBlock *block = m_map->getBlockNoCreate(p);
5459 if(block->isDummy())
5460 block_data_inexistent = true;
5462 block->copyTo(*this);
5464 catch(InvalidPositionException &e)
5466 block_data_inexistent = true;
5469 if(block_data_inexistent)
5471 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5472 // Fill with VOXELFLAG_INEXISTENT
5473 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5474 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5476 s32 i = m_area.index(a.MinEdge.X,y,z);
5477 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5481 m_loaded_blocks.insert(p, !block_data_inexistent);
5484 //dstream<<"emerge done"<<std::endl;
5488 SUGG: Add an option to only update eg. water and air nodes.
5489 This will make it interfere less with important stuff if
5492 void MapVoxelManipulator::blitBack
5493 (core::map<v3s16, MapBlock*> & modified_blocks)
5495 if(m_area.getExtent() == v3s16(0,0,0))
5498 //TimeTaker timer1("blitBack");
5500 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5501 <<m_loaded_blocks.size()<<std::endl;*/
5504 Initialize block cache
5506 v3s16 blockpos_last;
5507 MapBlock *block = NULL;
5508 bool block_checked_in_modified = false;
5510 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5511 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5512 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5516 u8 f = m_flags[m_area.index(p)];
5517 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5520 MapNode &n = m_data[m_area.index(p)];
5522 v3s16 blockpos = getNodeBlockPos(p);
5527 if(block == NULL || blockpos != blockpos_last){
5528 block = m_map->getBlockNoCreate(blockpos);
5529 blockpos_last = blockpos;
5530 block_checked_in_modified = false;
5533 // Calculate relative position in block
5534 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5536 // Don't continue if nothing has changed here
5537 if(block->getNode(relpos) == n)
5540 //m_map->setNode(m_area.MinEdge + p, n);
5541 block->setNode(relpos, n);
5544 Make sure block is in modified_blocks
5546 if(block_checked_in_modified == false)
5548 modified_blocks[blockpos] = block;
5549 block_checked_in_modified = true;
5552 catch(InvalidPositionException &e)
5558 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5559 MapVoxelManipulator(map)
5563 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5567 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5569 // Just create the area so that it can be pointed to
5570 VoxelManipulator::emerge(a, caller_id);
5573 void ManualMapVoxelManipulator::initialEmerge(
5574 v3s16 blockpos_min, v3s16 blockpos_max)
5576 TimeTaker timer1("initialEmerge", &emerge_time);
5578 // Units of these are MapBlocks
5579 v3s16 p_min = blockpos_min;
5580 v3s16 p_max = blockpos_max;
5582 VoxelArea block_area_nodes
5583 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5585 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5588 dstream<<"initialEmerge: area: ";
5589 block_area_nodes.print(dstream);
5590 dstream<<" ("<<size_MB<<"MB)";
5594 addArea(block_area_nodes);
5596 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5597 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5598 for(s32 x=p_min.X; x<=p_max.X; x++)
5601 core::map<v3s16, bool>::Node *n;
5602 n = m_loaded_blocks.find(p);
5606 bool block_data_inexistent = false;
5609 TimeTaker timer1("emerge load", &emerge_load_time);
5611 MapBlock *block = m_map->getBlockNoCreate(p);
5612 if(block->isDummy())
5613 block_data_inexistent = true;
5615 block->copyTo(*this);
5617 catch(InvalidPositionException &e)
5619 block_data_inexistent = true;
5622 if(block_data_inexistent)
5625 Mark area inexistent
5627 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5628 // Fill with VOXELFLAG_INEXISTENT
5629 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5630 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5632 s32 i = m_area.index(a.MinEdge.X,y,z);
5633 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5637 m_loaded_blocks.insert(p, !block_data_inexistent);
5641 void ManualMapVoxelManipulator::blitBackAll(
5642 core::map<v3s16, MapBlock*> * modified_blocks)
5644 if(m_area.getExtent() == v3s16(0,0,0))
5648 Copy data of all blocks
5650 for(core::map<v3s16, bool>::Iterator
5651 i = m_loaded_blocks.getIterator();
5652 i.atEnd() == false; i++)
5654 bool existed = i.getNode()->getValue();
5655 if(existed == false)
5657 v3s16 p = i.getNode()->getKey();
5658 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5661 dstream<<"WARNING: "<<__FUNCTION_NAME
5662 <<": got NULL block "
5663 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5668 block->copyFrom(*this);
5671 modified_blocks->insert(p, block);