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 f32 Map::getGroundHeight(v2s16 p, bool generate)
139 v2s16 sectorpos = getNodeSectorPos(p);
140 MapSector * sref = getSectorNoGenerate(sectorpos);
141 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
142 f32 y = sref->getGroundHeight(relpos);
145 catch(InvalidPositionException &e)
147 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
151 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
153 /*m_dout<<DTIME<<"Map::setGroundHeight(("
155 <<"), "<<y<<")"<<std::endl;*/
156 v2s16 sectorpos = getNodeSectorPos(p);
157 MapSector * sref = getSectorNoGenerate(sectorpos);
158 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
159 //sref->mutex.Lock();
160 sref->setGroundHeight(relpos, y);
161 //sref->mutex.Unlock();
164 bool Map::isNodeUnderground(v3s16 p)
166 v3s16 blockpos = getNodeBlockPos(p);
168 MapBlock * block = getBlockNoCreate(blockpos);
169 return block->getIsUnderground();
171 catch(InvalidPositionException &e)
178 Goes recursively through the neighbours of the node.
180 Alters only transparent nodes.
182 If the lighting of the neighbour is lower than the lighting of
183 the node was (before changing it to 0 at the step before), the
184 lighting of the neighbour is set to 0 and then the same stuff
185 repeats for the neighbour.
187 The ending nodes of the routine are stored in light_sources.
188 This is useful when a light is removed. In such case, this
189 routine can be called for the light node and then again for
190 light_sources to re-light the area without the removed light.
192 values of from_nodes are lighting values.
194 void Map::unspreadLight(enum LightBank bank,
195 core::map<v3s16, u8> & from_nodes,
196 core::map<v3s16, bool> & light_sources,
197 core::map<v3s16, MapBlock*> & modified_blocks)
200 v3s16(0,0,1), // back
202 v3s16(1,0,0), // right
203 v3s16(0,0,-1), // front
204 v3s16(0,-1,0), // bottom
205 v3s16(-1,0,0), // left
208 if(from_nodes.size() == 0)
211 u32 blockchangecount = 0;
213 core::map<v3s16, u8> unlighted_nodes;
214 core::map<v3s16, u8>::Iterator j;
215 j = from_nodes.getIterator();
218 Initialize block cache
221 MapBlock *block = NULL;
222 // Cache this a bit, too
223 bool block_checked_in_modified = false;
225 for(; j.atEnd() == false; j++)
227 v3s16 pos = j.getNode()->getKey();
228 v3s16 blockpos = getNodeBlockPos(pos);
230 // Only fetch a new block if the block position has changed
232 if(block == NULL || blockpos != blockpos_last){
233 block = getBlockNoCreate(blockpos);
234 blockpos_last = blockpos;
236 block_checked_in_modified = false;
240 catch(InvalidPositionException &e)
248 // Calculate relative position in block
249 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
251 // Get node straight from the block
252 MapNode n = block->getNode(relpos);
254 u8 oldlight = j.getNode()->getValue();
256 // Loop through 6 neighbors
257 for(u16 i=0; i<6; i++)
259 // Get the position of the neighbor node
260 v3s16 n2pos = pos + dirs[i];
262 // Get the block where the node is located
263 v3s16 blockpos = getNodeBlockPos(n2pos);
267 // Only fetch a new block if the block position has changed
269 if(block == NULL || blockpos != blockpos_last){
270 block = getBlockNoCreate(blockpos);
271 blockpos_last = blockpos;
273 block_checked_in_modified = false;
277 catch(InvalidPositionException &e)
282 // Calculate relative position in block
283 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
284 // Get node straight from the block
285 MapNode n2 = block->getNode(relpos);
287 bool changed = false;
289 //TODO: Optimize output by optimizing light_sources?
292 If the neighbor is dimmer than what was specified
293 as oldlight (the light of the previous node)
295 if(n2.getLight(bank) < oldlight)
298 And the neighbor is transparent and it has some light
300 if(n2.light_propagates() && n2.getLight(bank) != 0)
303 Set light to 0 and add to queue
306 u8 current_light = n2.getLight(bank);
307 n2.setLight(bank, 0);
308 block->setNode(relpos, n2);
310 unlighted_nodes.insert(n2pos, current_light);
314 Remove from light_sources if it is there
315 NOTE: This doesn't happen nearly at all
317 /*if(light_sources.find(n2pos))
319 std::cout<<"Removed from light_sources"<<std::endl;
320 light_sources.remove(n2pos);
325 if(light_sources.find(n2pos) != NULL)
326 light_sources.remove(n2pos);*/
329 light_sources.insert(n2pos, true);
332 // Add to modified_blocks
333 if(changed == true && block_checked_in_modified == false)
335 // If the block is not found in modified_blocks, add.
336 if(modified_blocks.find(blockpos) == NULL)
338 modified_blocks.insert(blockpos, block);
340 block_checked_in_modified = true;
343 catch(InvalidPositionException &e)
350 /*dstream<<"unspreadLight(): Changed block "
351 <<blockchangecount<<" times"
352 <<" for "<<from_nodes.size()<<" nodes"
355 if(unlighted_nodes.size() > 0)
356 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
360 A single-node wrapper of the above
362 void Map::unLightNeighbors(enum LightBank bank,
363 v3s16 pos, u8 lightwas,
364 core::map<v3s16, bool> & light_sources,
365 core::map<v3s16, MapBlock*> & modified_blocks)
367 core::map<v3s16, u8> from_nodes;
368 from_nodes.insert(pos, lightwas);
370 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
374 Lights neighbors of from_nodes, collects all them and then
377 void Map::spreadLight(enum LightBank bank,
378 core::map<v3s16, bool> & from_nodes,
379 core::map<v3s16, MapBlock*> & modified_blocks)
381 const v3s16 dirs[6] = {
382 v3s16(0,0,1), // back
384 v3s16(1,0,0), // right
385 v3s16(0,0,-1), // front
386 v3s16(0,-1,0), // bottom
387 v3s16(-1,0,0), // left
390 if(from_nodes.size() == 0)
393 u32 blockchangecount = 0;
395 core::map<v3s16, bool> lighted_nodes;
396 core::map<v3s16, bool>::Iterator j;
397 j = from_nodes.getIterator();
400 Initialize block cache
403 MapBlock *block = NULL;
404 // Cache this a bit, too
405 bool block_checked_in_modified = false;
407 for(; j.atEnd() == false; j++)
408 //for(; j != from_nodes.end(); j++)
410 v3s16 pos = j.getNode()->getKey();
412 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
413 v3s16 blockpos = getNodeBlockPos(pos);
415 // Only fetch a new block if the block position has changed
417 if(block == NULL || blockpos != blockpos_last){
418 block = getBlockNoCreate(blockpos);
419 blockpos_last = blockpos;
421 block_checked_in_modified = false;
425 catch(InvalidPositionException &e)
433 // Calculate relative position in block
434 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
436 // Get node straight from the block
437 MapNode n = block->getNode(relpos);
439 u8 oldlight = n.getLight(bank);
440 u8 newlight = diminish_light(oldlight);
442 // Loop through 6 neighbors
443 for(u16 i=0; i<6; i++){
444 // Get the position of the neighbor node
445 v3s16 n2pos = pos + dirs[i];
447 // Get the block where the node is located
448 v3s16 blockpos = getNodeBlockPos(n2pos);
452 // Only fetch a new block if the block position has changed
454 if(block == NULL || blockpos != blockpos_last){
455 block = getBlockNoCreate(blockpos);
456 blockpos_last = blockpos;
458 block_checked_in_modified = false;
462 catch(InvalidPositionException &e)
467 // Calculate relative position in block
468 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
469 // Get node straight from the block
470 MapNode n2 = block->getNode(relpos);
472 bool changed = false;
474 If the neighbor is brighter than the current node,
475 add to list (it will light up this node on its turn)
477 if(n2.getLight(bank) > undiminish_light(oldlight))
479 lighted_nodes.insert(n2pos, true);
480 //lighted_nodes.push_back(n2pos);
484 If the neighbor is dimmer than how much light this node
485 would spread on it, add to list
487 if(n2.getLight(bank) < newlight)
489 if(n2.light_propagates())
491 n2.setLight(bank, newlight);
492 block->setNode(relpos, n2);
493 lighted_nodes.insert(n2pos, true);
494 //lighted_nodes.push_back(n2pos);
499 // Add to modified_blocks
500 if(changed == true && block_checked_in_modified == false)
502 // If the block is not found in modified_blocks, add.
503 if(modified_blocks.find(blockpos) == NULL)
505 modified_blocks.insert(blockpos, block);
507 block_checked_in_modified = true;
510 catch(InvalidPositionException &e)
517 /*dstream<<"spreadLight(): Changed block "
518 <<blockchangecount<<" times"
519 <<" for "<<from_nodes.size()<<" nodes"
522 if(lighted_nodes.size() > 0)
523 spreadLight(bank, lighted_nodes, modified_blocks);
527 A single-node source variation of the above.
529 void Map::lightNeighbors(enum LightBank bank,
531 core::map<v3s16, MapBlock*> & modified_blocks)
533 core::map<v3s16, bool> from_nodes;
534 from_nodes.insert(pos, true);
535 spreadLight(bank, from_nodes, modified_blocks);
538 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
541 v3s16(0,0,1), // back
543 v3s16(1,0,0), // right
544 v3s16(0,0,-1), // front
545 v3s16(0,-1,0), // bottom
546 v3s16(-1,0,0), // left
549 u8 brightest_light = 0;
550 v3s16 brightest_pos(0,0,0);
551 bool found_something = false;
553 // Loop through 6 neighbors
554 for(u16 i=0; i<6; i++){
555 // Get the position of the neighbor node
556 v3s16 n2pos = p + dirs[i];
561 catch(InvalidPositionException &e)
565 if(n2.getLight(bank) > brightest_light || found_something == false){
566 brightest_light = n2.getLight(bank);
567 brightest_pos = n2pos;
568 found_something = true;
572 if(found_something == false)
573 throw InvalidPositionException();
575 return brightest_pos;
579 Propagates sunlight down from a node.
580 Starting point gets sunlight.
582 Returns the lowest y value of where the sunlight went.
584 Mud is turned into grass in where the sunlight stops.
586 s16 Map::propagateSunlight(v3s16 start,
587 core::map<v3s16, MapBlock*> & modified_blocks)
592 v3s16 pos(start.X, y, start.Z);
594 v3s16 blockpos = getNodeBlockPos(pos);
597 block = getBlockNoCreate(blockpos);
599 catch(InvalidPositionException &e)
604 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
605 MapNode n = block->getNode(relpos);
607 if(n.sunlight_propagates())
609 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
610 block->setNode(relpos, n);
612 modified_blocks.insert(blockpos, block);
616 // Turn mud into grass
617 if(n.d == CONTENT_MUD)
620 block->setNode(relpos, n);
621 modified_blocks.insert(blockpos, block);
624 // Sunlight goes no further
631 void Map::updateLighting(enum LightBank bank,
632 core::map<v3s16, MapBlock*> & a_blocks,
633 core::map<v3s16, MapBlock*> & modified_blocks)
635 /*m_dout<<DTIME<<"Map::updateLighting(): "
636 <<a_blocks.size()<<" blocks."<<std::endl;*/
638 //TimeTaker timer("updateLighting");
642 //u32 count_was = modified_blocks.size();
644 core::map<v3s16, MapBlock*> blocks_to_update;
646 core::map<v3s16, bool> light_sources;
648 core::map<v3s16, u8> unlight_from;
650 core::map<v3s16, MapBlock*>::Iterator i;
651 i = a_blocks.getIterator();
652 for(; i.atEnd() == false; i++)
654 MapBlock *block = i.getNode()->getValue();
658 // Don't bother with dummy blocks.
662 v3s16 pos = block->getPos();
663 modified_blocks.insert(pos, block);
665 blocks_to_update.insert(pos, block);
668 Clear all light from block
670 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
671 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
672 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
677 MapNode n = block->getNode(v3s16(x,y,z));
678 u8 oldlight = n.getLight(bank);
680 block->setNode(v3s16(x,y,z), n);
682 // Collect borders for unlighting
683 if(x==0 || x == MAP_BLOCKSIZE-1
684 || y==0 || y == MAP_BLOCKSIZE-1
685 || z==0 || z == MAP_BLOCKSIZE-1)
687 v3s16 p_map = p + v3s16(
690 MAP_BLOCKSIZE*pos.Z);
691 unlight_from.insert(p_map, oldlight);
694 catch(InvalidPositionException &e)
697 This would happen when dealing with a
701 dstream<<"updateLighting(): InvalidPositionException"
706 if(bank == LIGHTBANK_DAY)
708 bool bottom_valid = block->propagateSunlight(light_sources);
710 // If bottom is valid, we're done.
714 else if(bank == LIGHTBANK_NIGHT)
716 // For night lighting, sunlight is not propagated
721 // Invalid lighting bank
725 /*dstream<<"Bottom for sunlight-propagated block ("
726 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
729 // Bottom sunlight is not valid; get the block and loop to it
733 block = getBlockNoCreate(pos);
735 catch(InvalidPositionException &e)
745 TimeTaker timer("unspreadLight");
746 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
751 u32 diff = modified_blocks.size() - count_was;
752 count_was = modified_blocks.size();
753 dstream<<"unspreadLight modified "<<diff<<std::endl;
757 TimeTaker timer("spreadLight");
758 spreadLight(bank, light_sources, modified_blocks);
763 u32 diff = modified_blocks.size() - count_was;
764 count_was = modified_blocks.size();
765 dstream<<"spreadLight modified "<<diff<<std::endl;
770 //MapVoxelManipulator vmanip(this);
772 // Make a manual voxel manipulator and load all the blocks
773 // that touch the requested blocks
774 ManualMapVoxelManipulator vmanip(this);
775 core::map<v3s16, MapBlock*>::Iterator i;
776 i = blocks_to_update.getIterator();
777 for(; i.atEnd() == false; i++)
779 MapBlock *block = i.getNode()->getValue();
780 v3s16 p = block->getPos();
782 // Add all surrounding blocks
783 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
786 Add all surrounding blocks that have up-to-date lighting
787 NOTE: This doesn't quite do the job (not everything
788 appropriate is lighted)
790 /*for(s16 z=-1; z<=1; z++)
791 for(s16 y=-1; y<=1; y++)
792 for(s16 x=-1; x<=1; x++)
795 MapBlock *block = getBlockNoCreateNoEx(p);
800 if(block->getLightingExpired())
802 vmanip.initialEmerge(p, p);
805 // Lighting of block will be updated completely
806 block->setLightingExpired(false);
810 //TimeTaker timer("unSpreadLight");
811 vmanip.unspreadLight(bank, unlight_from, light_sources);
814 //TimeTaker timer("spreadLight");
815 vmanip.spreadLight(bank, light_sources);
818 //TimeTaker timer("blitBack");
819 vmanip.blitBack(modified_blocks);
821 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
825 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
828 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
829 core::map<v3s16, MapBlock*> & modified_blocks)
831 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
832 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
835 Update information about whether day and night light differ
837 for(core::map<v3s16, MapBlock*>::Iterator
838 i = modified_blocks.getIterator();
839 i.atEnd() == false; i++)
841 MapBlock *block = i.getNode()->getValue();
842 block->updateDayNightDiff();
847 This is called after changing a node from transparent to opaque.
848 The lighting value of the node should be left as-is after changing
849 other values. This sets the lighting value to 0.
851 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
852 core::map<v3s16, MapBlock*> &modified_blocks)*/
853 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
854 core::map<v3s16, MapBlock*> &modified_blocks)
857 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
858 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
861 From this node to nodes underneath:
862 If lighting is sunlight (1.0), unlight neighbours and
867 v3s16 toppos = p + v3s16(0,1,0);
868 v3s16 bottompos = p + v3s16(0,-1,0);
870 bool node_under_sunlight = true;
871 core::map<v3s16, bool> light_sources;
874 If there is a node at top and it doesn't have sunlight,
875 there has not been any sunlight going down.
877 Otherwise there probably is.
880 MapNode topnode = getNode(toppos);
882 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
883 node_under_sunlight = false;
885 catch(InvalidPositionException &e)
889 if(n.d != CONTENT_TORCH)
892 If there is grass below, change it to mud
895 MapNode bottomnode = getNode(bottompos);
897 if(bottomnode.d == CONTENT_GRASS
898 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
900 bottomnode.d = CONTENT_MUD;
901 setNode(bottompos, bottomnode);
904 catch(InvalidPositionException &e)
909 enum LightBank banks[] =
914 for(s32 i=0; i<2; i++)
916 enum LightBank bank = banks[i];
918 u8 lightwas = getNode(p).getLight(bank);
920 // Add the block of the added node to modified_blocks
921 v3s16 blockpos = getNodeBlockPos(p);
922 MapBlock * block = getBlockNoCreate(blockpos);
923 assert(block != NULL);
924 modified_blocks.insert(blockpos, block);
926 if(isValidPosition(p) == false)
929 // Unlight neighbours of node.
930 // This means setting light of all consequent dimmer nodes
932 // This also collects the nodes at the border which will spread
933 // light again into this.
934 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
942 If node is under sunlight, take all sunlighted nodes under
943 it and clear light from them and from where the light has
945 TODO: This could be optimized by mass-unlighting instead
948 if(node_under_sunlight)
952 //m_dout<<DTIME<<"y="<<y<<std::endl;
953 v3s16 n2pos(p.X, y, p.Z);
959 catch(InvalidPositionException &e)
964 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
966 //m_dout<<DTIME<<"doing"<<std::endl;
967 unLightNeighbors(LIGHTBANK_DAY,
968 n2pos, n2.getLight(LIGHTBANK_DAY),
969 light_sources, modified_blocks);
970 n2.setLight(LIGHTBANK_DAY, 0);
978 for(s32 i=0; i<2; i++)
980 enum LightBank bank = banks[i];
983 Spread light from all nodes that might be capable of doing so
984 TODO: Convert to spreadLight
986 spreadLight(bank, light_sources, modified_blocks);
990 Update information about whether day and night light differ
992 for(core::map<v3s16, MapBlock*>::Iterator
993 i = modified_blocks.getIterator();
994 i.atEnd() == false; i++)
996 MapBlock *block = i.getNode()->getValue();
997 block->updateDayNightDiff();
1001 Add neighboring liquid nodes and the node itself if it is
1002 liquid (=water node was added) to transform queue.
1005 v3s16(0,0,0), // self
1006 v3s16(0,0,1), // back
1007 v3s16(0,1,0), // top
1008 v3s16(1,0,0), // right
1009 v3s16(0,0,-1), // front
1010 v3s16(0,-1,0), // bottom
1011 v3s16(-1,0,0), // left
1013 for(u16 i=0; i<7; i++)
1018 v3s16 p2 = p + dirs[i];
1020 MapNode n2 = getNode(p2);
1021 if(content_liquid(n2.d))
1023 m_transforming_liquid.push_back(p2);
1026 }catch(InvalidPositionException &e)
1034 void Map::removeNodeAndUpdate(v3s16 p,
1035 core::map<v3s16, MapBlock*> &modified_blocks)
1037 /*PrintInfo(m_dout);
1038 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1039 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1041 bool node_under_sunlight = true;
1043 v3s16 toppos = p + v3s16(0,1,0);
1045 // Node will be replaced with this
1046 u8 replace_material = CONTENT_AIR;
1049 If there is a node at top and it doesn't have sunlight,
1050 there will be no sunlight going down.
1053 MapNode topnode = getNode(toppos);
1055 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1056 node_under_sunlight = false;
1058 catch(InvalidPositionException &e)
1062 core::map<v3s16, bool> light_sources;
1064 enum LightBank banks[] =
1069 for(s32 i=0; i<2; i++)
1071 enum LightBank bank = banks[i];
1074 Unlight neighbors (in case the node is a light source)
1076 unLightNeighbors(bank, p,
1077 getNode(p).getLight(bank),
1078 light_sources, modified_blocks);
1083 This also clears the lighting.
1087 n.d = replace_material;
1090 for(s32 i=0; i<2; i++)
1092 enum LightBank bank = banks[i];
1095 Recalculate lighting
1097 spreadLight(bank, light_sources, modified_blocks);
1100 // Add the block of the removed node to modified_blocks
1101 v3s16 blockpos = getNodeBlockPos(p);
1102 MapBlock * block = getBlockNoCreate(blockpos);
1103 assert(block != NULL);
1104 modified_blocks.insert(blockpos, block);
1107 If the removed node was under sunlight, propagate the
1108 sunlight down from it and then light all neighbors
1109 of the propagated blocks.
1111 if(node_under_sunlight)
1113 s16 ybottom = propagateSunlight(p, modified_blocks);
1114 /*m_dout<<DTIME<<"Node was under sunlight. "
1115 "Propagating sunlight";
1116 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1118 for(; y >= ybottom; y--)
1120 v3s16 p2(p.X, y, p.Z);
1121 /*m_dout<<DTIME<<"lighting neighbors of node ("
1122 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1124 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1129 // Set the lighting of this node to 0
1130 // TODO: Is this needed? Lighting is cleared up there already.
1132 MapNode n = getNode(p);
1133 n.setLight(LIGHTBANK_DAY, 0);
1136 catch(InvalidPositionException &e)
1142 for(s32 i=0; i<2; i++)
1144 enum LightBank bank = banks[i];
1146 // Get the brightest neighbour node and propagate light from it
1147 v3s16 n2p = getBrightestNeighbour(bank, p);
1149 MapNode n2 = getNode(n2p);
1150 lightNeighbors(bank, n2p, modified_blocks);
1152 catch(InvalidPositionException &e)
1158 Update information about whether day and night light differ
1160 for(core::map<v3s16, MapBlock*>::Iterator
1161 i = modified_blocks.getIterator();
1162 i.atEnd() == false; i++)
1164 MapBlock *block = i.getNode()->getValue();
1165 block->updateDayNightDiff();
1169 Add neighboring liquid nodes to transform queue.
1172 v3s16(0,0,1), // back
1173 v3s16(0,1,0), // top
1174 v3s16(1,0,0), // right
1175 v3s16(0,0,-1), // front
1176 v3s16(0,-1,0), // bottom
1177 v3s16(-1,0,0), // left
1179 for(u16 i=0; i<6; i++)
1184 v3s16 p2 = p + dirs[i];
1186 MapNode n2 = getNode(p2);
1187 if(content_liquid(n2.d))
1189 m_transforming_liquid.push_back(p2);
1192 }catch(InvalidPositionException &e)
1199 void Map::expireMeshes(bool only_daynight_diffed)
1201 TimeTaker timer("expireMeshes()");
1203 core::map<v2s16, MapSector*>::Iterator si;
1204 si = m_sectors.getIterator();
1205 for(; si.atEnd() == false; si++)
1207 MapSector *sector = si.getNode()->getValue();
1209 core::list< MapBlock * > sectorblocks;
1210 sector->getBlocks(sectorblocks);
1212 core::list< MapBlock * >::Iterator i;
1213 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1215 MapBlock *block = *i;
1217 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1223 JMutexAutoLock lock(block->mesh_mutex);
1224 if(block->mesh != NULL)
1226 /*block->mesh->drop();
1227 block->mesh = NULL;*/
1228 block->setMeshExpired(true);
1235 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1237 assert(mapType() == MAPTYPE_CLIENT);
1240 v3s16 p = blockpos + v3s16(0,0,0);
1241 MapBlock *b = getBlockNoCreate(p);
1242 b->updateMesh(daynight_ratio);
1244 catch(InvalidPositionException &e){}
1247 v3s16 p = blockpos + v3s16(-1,0,0);
1248 MapBlock *b = getBlockNoCreate(p);
1249 b->updateMesh(daynight_ratio);
1251 catch(InvalidPositionException &e){}
1253 v3s16 p = blockpos + v3s16(0,-1,0);
1254 MapBlock *b = getBlockNoCreate(p);
1255 b->updateMesh(daynight_ratio);
1257 catch(InvalidPositionException &e){}
1259 v3s16 p = blockpos + v3s16(0,0,-1);
1260 MapBlock *b = getBlockNoCreate(p);
1261 b->updateMesh(daynight_ratio);
1263 catch(InvalidPositionException &e){}
1266 v3s16 p = blockpos + v3s16(1,0,0);
1267 MapBlock *b = getBlockNoCreate(p);
1268 b->updateMesh(daynight_ratio);
1270 catch(InvalidPositionException &e){}
1272 v3s16 p = blockpos + v3s16(0,1,0);
1273 MapBlock *b = getBlockNoCreate(p);
1274 b->updateMesh(daynight_ratio);
1276 catch(InvalidPositionException &e){}
1278 v3s16 p = blockpos + v3s16(0,0,1);
1279 MapBlock *b = getBlockNoCreate(p);
1280 b->updateMesh(daynight_ratio);
1282 catch(InvalidPositionException &e){}*/
1287 bool Map::dayNightDiffed(v3s16 blockpos)
1290 v3s16 p = blockpos + v3s16(0,0,0);
1291 MapBlock *b = getBlockNoCreate(p);
1292 if(b->dayNightDiffed())
1295 catch(InvalidPositionException &e){}
1298 v3s16 p = blockpos + v3s16(-1,0,0);
1299 MapBlock *b = getBlockNoCreate(p);
1300 if(b->dayNightDiffed())
1303 catch(InvalidPositionException &e){}
1305 v3s16 p = blockpos + v3s16(0,-1,0);
1306 MapBlock *b = getBlockNoCreate(p);
1307 if(b->dayNightDiffed())
1310 catch(InvalidPositionException &e){}
1312 v3s16 p = blockpos + v3s16(0,0,-1);
1313 MapBlock *b = getBlockNoCreate(p);
1314 if(b->dayNightDiffed())
1317 catch(InvalidPositionException &e){}
1320 v3s16 p = blockpos + v3s16(1,0,0);
1321 MapBlock *b = getBlockNoCreate(p);
1322 if(b->dayNightDiffed())
1325 catch(InvalidPositionException &e){}
1327 v3s16 p = blockpos + v3s16(0,1,0);
1328 MapBlock *b = getBlockNoCreate(p);
1329 if(b->dayNightDiffed())
1332 catch(InvalidPositionException &e){}
1334 v3s16 p = blockpos + v3s16(0,0,1);
1335 MapBlock *b = getBlockNoCreate(p);
1336 if(b->dayNightDiffed())
1339 catch(InvalidPositionException &e){}
1345 Updates usage timers
1347 void Map::timerUpdate(float dtime)
1349 JMutexAutoLock lock(m_sector_mutex);
1351 core::map<v2s16, MapSector*>::Iterator si;
1353 si = m_sectors.getIterator();
1354 for(; si.atEnd() == false; si++)
1356 MapSector *sector = si.getNode()->getValue();
1357 sector->usage_timer += dtime;
1361 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1364 Wait for caches to be removed before continuing.
1366 This disables the existence of caches while locked
1368 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1370 core::list<v2s16>::Iterator j;
1371 for(j=list.begin(); j!=list.end(); j++)
1373 MapSector *sector = m_sectors[*j];
1376 sector->deleteBlocks();
1381 If sector is in sector cache, remove it from there
1383 if(m_sector_cache == sector)
1385 m_sector_cache = NULL;
1388 Remove from map and delete
1390 m_sectors.remove(*j);
1396 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1397 core::list<v3s16> *deleted_blocks)
1399 JMutexAutoLock lock(m_sector_mutex);
1401 core::list<v2s16> sector_deletion_queue;
1402 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1403 for(; i.atEnd() == false; i++)
1405 MapSector *sector = i.getNode()->getValue();
1407 Delete sector from memory if it hasn't been used in a long time
1409 if(sector->usage_timer > timeout)
1411 sector_deletion_queue.push_back(i.getNode()->getKey());
1413 if(deleted_blocks != NULL)
1415 // Collect positions of blocks of sector
1416 MapSector *sector = i.getNode()->getValue();
1417 core::list<MapBlock*> blocks;
1418 sector->getBlocks(blocks);
1419 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1420 i != blocks.end(); i++)
1422 deleted_blocks->push_back((*i)->getPos());
1427 deleteSectors(sector_deletion_queue, only_blocks);
1428 return sector_deletion_queue.getSize();
1431 void Map::PrintInfo(std::ostream &out)
1436 #define WATER_DROP_BOOST 4
1438 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1440 DSTACK(__FUNCTION_NAME);
1441 //TimeTaker timer("transformLiquids()");
1444 u32 initial_size = m_transforming_liquid.size();
1446 //dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1448 while(m_transforming_liquid.size() != 0)
1451 Get a queued transforming liquid node
1453 v3s16 p0 = m_transforming_liquid.pop_front();
1455 MapNode n0 = getNode(p0);
1457 // Don't deal with non-liquids
1458 if(content_liquid(n0.d) == false)
1461 bool is_source = !content_flowing_liquid(n0.d);
1463 u8 liquid_level = 8;
1464 if(is_source == false)
1465 liquid_level = n0.param2 & 0x0f;
1467 // Turn possible source into non-source
1468 u8 nonsource_c = make_liquid_flowing(n0.d);
1471 If not source, check that some node flows into this one
1472 and what is the level of liquid in this one
1474 if(is_source == false)
1476 s8 new_liquid_level_max = -1;
1478 v3s16 dirs_from[5] = {
1479 v3s16(0,1,0), // top
1480 v3s16(0,0,1), // back
1481 v3s16(1,0,0), // right
1482 v3s16(0,0,-1), // front
1483 v3s16(-1,0,0), // left
1485 for(u16 i=0; i<5; i++)
1490 bool from_top = (i==0);
1492 v3s16 p2 = p0 + dirs_from[i];
1493 MapNode n2 = getNode(p2);
1495 if(content_liquid(n2.d))
1497 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1498 // Check that the liquids are the same type
1499 if(n2_nonsource_c != nonsource_c)
1501 dstream<<"WARNING: Not handling: different liquids"
1502 " collide"<<std::endl;
1505 bool n2_is_source = !content_flowing_liquid(n2.d);
1506 s8 n2_liquid_level = 8;
1507 if(n2_is_source == false)
1508 n2_liquid_level = n2.param2 & 0x07;
1510 s8 new_liquid_level = -1;
1513 //new_liquid_level = 7;
1514 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1515 new_liquid_level = 7;
1517 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1519 else if(n2_liquid_level > 0)
1521 new_liquid_level = n2_liquid_level - 1;
1524 if(new_liquid_level > new_liquid_level_max)
1525 new_liquid_level_max = new_liquid_level;
1528 }catch(InvalidPositionException &e)
1534 If liquid level should be something else, update it and
1535 add all the neighboring water nodes to the transform queue.
1537 if(new_liquid_level_max != liquid_level)
1539 if(new_liquid_level_max == -1)
1541 // Remove water alltoghether
1548 n0.param2 = new_liquid_level_max;
1552 // Block has been modified
1554 v3s16 blockpos = getNodeBlockPos(p0);
1555 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1557 modified_blocks.insert(blockpos, block);
1561 Add neighboring non-source liquid nodes to transform queue.
1564 v3s16(0,0,1), // back
1565 v3s16(0,1,0), // top
1566 v3s16(1,0,0), // right
1567 v3s16(0,0,-1), // front
1568 v3s16(0,-1,0), // bottom
1569 v3s16(-1,0,0), // left
1571 for(u16 i=0; i<6; i++)
1576 v3s16 p2 = p0 + dirs[i];
1578 MapNode n2 = getNode(p2);
1579 if(content_flowing_liquid(n2.d))
1581 m_transforming_liquid.push_back(p2);
1584 }catch(InvalidPositionException &e)
1591 // Get a new one from queue if the node has turned into non-water
1592 if(content_liquid(n0.d) == false)
1596 Flow water from this node
1598 v3s16 dirs_to[5] = {
1599 v3s16(0,-1,0), // bottom
1600 v3s16(0,0,1), // back
1601 v3s16(1,0,0), // right
1602 v3s16(0,0,-1), // front
1603 v3s16(-1,0,0), // left
1605 for(u16 i=0; i<5; i++)
1610 bool to_bottom = (i == 0);
1612 // If liquid is at lowest possible height, it's not going
1613 // anywhere except down
1614 if(liquid_level == 0 && to_bottom == false)
1617 u8 liquid_next_level = 0;
1618 // If going to bottom
1621 //liquid_next_level = 7;
1622 if(liquid_level >= 7 - WATER_DROP_BOOST)
1623 liquid_next_level = 7;
1625 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1628 liquid_next_level = liquid_level - 1;
1630 bool n2_changed = false;
1631 bool flowed = false;
1633 v3s16 p2 = p0 + dirs_to[i];
1635 MapNode n2 = getNode(p2);
1636 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1638 if(content_liquid(n2.d))
1640 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1641 // Check that the liquids are the same type
1642 if(n2_nonsource_c != nonsource_c)
1644 dstream<<"WARNING: Not handling: different liquids"
1645 " collide"<<std::endl;
1648 bool n2_is_source = !content_flowing_liquid(n2.d);
1649 u8 n2_liquid_level = 8;
1650 if(n2_is_source == false)
1651 n2_liquid_level = n2.param2 & 0x07;
1660 // Just flow into the source, nothing changes.
1661 // n2_changed is not set because destination didn't change
1666 if(liquid_next_level > liquid_level)
1668 n2.param2 = liquid_next_level;
1676 else if(n2.d == CONTENT_AIR)
1679 n2.param2 = liquid_next_level;
1686 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1690 m_transforming_liquid.push_back(p2);
1692 v3s16 blockpos = getNodeBlockPos(p2);
1693 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1695 modified_blocks.insert(blockpos, block);
1698 // If n2_changed to bottom, don't flow anywhere else
1699 if(to_bottom && flowed && !is_source)
1702 }catch(InvalidPositionException &e)
1708 //if(loopcount >= 100000)
1709 if(loopcount >= initial_size * 1)
1712 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1719 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1730 Experimental and debug stuff
1734 dstream<<"Generating map point attribute lists"<<std::endl;
1736 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1737 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1738 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1739 PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1740 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1743 NOTE: BEWARE: Too big amount of these will make map generation
1744 slow. Especially those that are read by every block emerge.
1752 for(u32 i=0; i<5000; i++)
1754 /*u32 lim = MAP_GENERATION_LIMIT;
1758 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1761 -lim + myrand()%(lim*2),
1763 -lim + myrand()%(lim*2)
1765 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1766 plants_amount = pow(plants_amount, 5);
1767 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1769 float plants_amount = 0;
1772 plants_amount = 1.5;
1774 else if(myrand()%4 == 0)
1776 plants_amount = 0.5;
1778 else if(myrand()%2 == 0)
1780 plants_amount = 0.03;
1784 plants_amount = 0.0;
1788 list_plants_amount->addPoint(p, Attribute(plants_amount));
1791 for(u32 i=0; i<1000; i++)
1793 /*u32 lim = MAP_GENERATION_LIMIT;
1797 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000;
1800 -lim + myrand()%(lim*2),
1802 -lim + myrand()%(lim*2)
1805 float caves_amount = 0;
1810 else if(myrand()%3 == 0)
1816 caves_amount = 0.05;
1819 list_caves_amount->addPoint(p, Attribute(caves_amount));
1822 for(u32 i=0; i<5000; i++)
1824 /*u32 lim = MAP_GENERATION_LIMIT;
1828 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1831 -lim + (myrand()%(lim*2)),
1833 -lim + (myrand()%(lim*2))
1836 /*s32 bh_i = (myrand()%200) - 50;
1837 float baseheight = (float)bh_i;
1841 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1842 randmax = pow(randmax, e);
1844 //float randmax = (float)(myrand()%60);
1845 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1847 float baseheight = 0;
1849 float randfactor = 0;
1851 /*if(myrand()%5 == 0)
1857 else if(myrand()%6 == 0)
1863 else if(myrand()%4 == 0)
1869 else if(myrand()%3 == 0)
1895 list_baseheight->addPoint(p, Attribute(baseheight));
1896 list_randmax->addPoint(p, Attribute(randmax));
1897 list_randfactor->addPoint(p, Attribute(randfactor));
1900 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
1901 list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
1902 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
1905 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1906 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1907 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1911 Try to load map; if not found, create a new one.
1914 m_savedir = savedir;
1915 m_map_saving_enabled = false;
1919 // If directory exists, check contents and load if possible
1920 if(fs::PathExists(m_savedir))
1922 // If directory is empty, it is safe to save into it.
1923 if(fs::GetDirListing(m_savedir).size() == 0)
1925 dstream<<DTIME<<"Server: Empty save directory is valid."
1927 m_map_saving_enabled = true;
1931 // Load master heightmap
1932 loadMasterHeightmap();
1934 // Load sector (0,0) and throw and exception on fail
1935 if(loadSectorFull(v2s16(0,0)) == false)
1936 throw LoadError("Failed to load sector (0,0)");
1938 dstream<<DTIME<<"Server: Successfully loaded master "
1939 "heightmap and sector (0,0) from "<<savedir<<
1940 ", assuming valid save directory."
1943 m_map_saving_enabled = true;
1944 // Map loaded, not creating new one
1948 // If directory doesn't exist, it is safe to save to it
1950 m_map_saving_enabled = true;
1953 catch(std::exception &e)
1955 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1956 <<", exception: "<<e.what()<<std::endl;
1957 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1958 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1961 dstream<<DTIME<<"Initializing new map."<<std::endl;
1963 // Create master heightmap
1964 m_heightmap = new UnlimitedHeightmap
1967 // Set map parameters
1970 // Create zero sector
1971 emergeSector(v2s16(0,0));
1973 // Initially write whole map
1977 ServerMap::~ServerMap()
1981 if(m_map_saving_enabled)
1984 // Save only changed parts
1986 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1990 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1993 catch(std::exception &e)
1995 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1996 <<", exception: "<<e.what()<<std::endl;
1999 if(m_heightmap != NULL)
2005 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2006 for(; i.atEnd() == false; i++)
2008 MapChunk *chunk = i.getNode()->getValue();
2013 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos)
2015 // Return if chunk already exists
2016 MapChunk *chunk = getChunk(chunkpos);
2024 dstream<<"generateChunkRaw(): "
2025 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2028 TimeTaker timer("generateChunkRaw()");
2030 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2032 core::map<v3s16, MapBlock*> changed_blocks;
2033 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
2035 u32 generated_block_count = 0;
2037 for(s16 y=0; y<m_chunksize; y++)
2039 /*dstream<<"Generating sectors "
2040 <<"("<<sectorpos_base.X<<"..."
2041 <<(sectorpos_base.X+m_chunksize-1)
2045 // With caves_amount attribute fetch: ~90ms (379ms peaks)
2046 // Without: ~38ms (396ms peaks)
2047 //TimeTaker timer("Chunk sector row");
2049 for(s16 x=0; x<m_chunksize; x++)
2051 v2s16 sectorpos = sectorpos_base + v2s16(x,y);
2053 /*dstream<<"Generating sector "
2054 <<"("<<sectorpos.X<<","<<sectorpos.Y<<")"
2058 ServerMapSector *sector = generateSector(sectorpos);
2061 Generate main blocks of sector
2064 for(s16 y2=-d/2; y2<d/2; y2++)
2068 // Check that the block doesn't exist already
2069 if(sector->getBlockNoCreateNoEx(y2))
2072 generateBlock(p, NULL, sector, changed_blocks,
2073 lighting_invalidated_blocks);
2075 generated_block_count++;
2080 dstream<<"generateChunkRaw generated "<<generated_block_count
2081 <<" blocks"<<std::endl;
2084 TimeTaker timer2("generateChunkRaw() lighting");
2086 core::map<v3s16, MapBlock*> lighting_modified_blocks;
2087 updateLighting(lighting_invalidated_blocks, lighting_modified_blocks);
2090 // Add chunk meta information
2091 chunk = new MapChunk();
2092 m_chunks.insert(chunkpos, chunk);
2096 MapChunk* ServerMap::generateChunk(v2s16 chunkpos)
2099 Generate chunk and neighbors
2101 for(s16 x=-1; x<=1; x++)
2102 for(s16 y=-1; y<=1; y++)
2104 generateChunkRaw(chunkpos + v2s16(x,y));
2110 MapChunk *chunk = getChunk(chunkpos);
2113 chunk->setIsVolatile(false);
2118 ServerMapSector * ServerMap::generateSector(v2s16 p2d)
2120 DSTACK("%s: p2d=(%d,%d)",
2124 // Check that it doesn't exist already
2125 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2130 If there is no master heightmap, throw.
2132 if(m_heightmap == NULL)
2134 throw InvalidPositionException("emergeSector(): no heightmap");
2138 Do not generate over-limit
2140 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2141 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2142 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2143 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2144 throw InvalidPositionException("emergeSector(): pos. over limit");
2147 Generate sector and heightmaps
2150 // Number of heightmaps in sector in each direction
2151 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
2153 // Heightmap side width
2154 s16 hm_d = MAP_BLOCKSIZE / hm_split;
2156 sector = new ServerMapSector(this, p2d, hm_split);
2158 // Sector position on map in nodes
2159 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2161 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
2162 " heightmaps and objects"<<std::endl;*/
2165 Calculate some information about local properties
2168 v2s16 mhm_p = p2d * hm_split;
2170 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
2171 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
2172 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
2173 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
2176 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
2177 float avgslope = 0.0;
2178 avgslope += fabs(avgheight - corners[0]);
2179 avgslope += fabs(avgheight - corners[1]);
2180 avgslope += fabs(avgheight - corners[2]);
2181 avgslope += fabs(avgheight - corners[3]);
2183 avgslope /= MAP_BLOCKSIZE;
2184 //dstream<<"avgslope="<<avgslope<<std::endl;
2186 float pitness = 0.0;
2188 a = m_heightmap->getSlope(p2d+v2s16(0,0));
2191 a = m_heightmap->getSlope(p2d+v2s16(0,1));
2194 a = m_heightmap->getSlope(p2d+v2s16(1,1));
2197 a = m_heightmap->getSlope(p2d+v2s16(1,0));
2201 pitness /= MAP_BLOCKSIZE;
2202 //dstream<<"pitness="<<pitness<<std::endl;
2205 Get local attributes
2208 float local_plants_amount = 0.5;
2212 //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
2213 //TimeTaker attrtimer("emergeSector() attribute fetch");
2215 // Get plant amount from attributes
2216 PointAttributeList *palist = m_padb.getList("plants_amount");
2218 /*local_plants_amount =
2219 palist->getNearAttr(nodepos2d).getFloat();*/
2220 local_plants_amount =
2221 palist->getInterpolatedFloat(nodepos2d);
2226 Generate sector heightmap
2229 // Loop through sub-heightmaps
2230 for(s16 y=0; y<hm_split; y++)
2231 for(s16 x=0; x<hm_split; x++)
2233 v2s16 p_in_sector = v2s16(x,y);
2234 v2s16 mhm_p = p2d * hm_split + p_in_sector;
2236 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
2237 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
2238 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
2239 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
2242 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
2243 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
2246 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
2248 sector->setHeightmap(p_in_sector, hm);
2250 //TODO: Make these values configurable
2251 //hm->generateContinued(0.0, 0.0, corners);
2252 //hm->generateContinued(0.25, 0.2, corners);
2253 //hm->generateContinued(0.5, 0.2, corners);
2254 //hm->generateContinued(1.0, 0.2, corners);
2255 //hm->generateContinued(2.0, 0.2, corners);
2256 //hm->generateContinued(2.0 * avgslope, 0.5, corners);
2257 hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners);
2266 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
2267 sector->setObjects(objects);
2269 float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
2272 Plant some trees if there is not much slope
2275 // Avgslope is the derivative of a hill
2276 //float t = avgslope * avgslope;
2278 float a = area/16 * m_params.plants_amount * local_plants_amount;
2280 //float something = 0.17*0.17;
2281 float something = 0.3;
2283 tree_max = a / (t/something);
2287 u32 count = (myrand()%(tree_max+1));
2288 //u32 count = tree_max;
2289 for(u32 i=0; i<count; i++)
2291 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
2292 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
2293 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2296 objects->insert(v3s16(x, y, z),
2297 SECTOR_OBJECT_TREE_1);
2301 Plant some bushes if sector is pit-like
2304 // Pitness usually goes at around -0.5...0.5
2306 u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount;
2308 bush_max = (pitness*a*4);
2311 u32 count = (myrand()%(bush_max+1));
2312 for(u32 i=0; i<count; i++)
2314 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
2315 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
2316 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2319 objects->insert(v3s16(x, y, z),
2320 SECTOR_OBJECT_BUSH_1);
2324 Add ravine (randomly)
2326 if(m_params.ravines_amount > 0.001)
2328 if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
2331 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2332 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2335 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2336 objects->insert(v3s16(x, y, z),
2337 SECTOR_OBJECT_RAVINE);
2344 m_sectors.insert(p2d, sector);
2349 MapSector * ServerMap::emergeSector(v2s16 p2d)
2351 DSTACK("%s: p2d=(%d,%d)",
2356 Check if it exists already in memory
2358 MapSector *sector = getSectorNoGenerateNoEx(p2d);
2363 Try to load it from disk
2365 if(loadSectorFull(p2d) == true)
2367 MapSector *sector = getSectorNoGenerateNoEx(p2d);
2370 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
2371 throw InvalidPositionException("");
2379 v2s16 chunkpos = sector_to_chunk(p2d);
2380 bool chunk_exists = false;
2381 MapChunk *chunk = getChunk(chunkpos);
2382 if(chunk && chunk->getIsVolatile() == false)
2383 chunk_exists = true;
2386 If chunk is not generated, generate chunk
2388 if(chunk_exists == false)
2390 // Generate chunk and neighbors
2391 generateChunk(chunkpos);
2395 Return sector if it exists now
2397 sector = getSectorNoGenerateNoEx(p2d);
2402 generateChunk should have generated the sector
2409 //return generateSector();
2412 MapBlock * ServerMap::generateBlock(
2414 MapBlock *original_dummy,
2415 ServerMapSector *sector,
2416 core::map<v3s16, MapBlock*> &changed_blocks,
2417 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2420 DSTACK("%s: p=(%d,%d,%d)",
2424 /*dstream<<"generateBlock(): "
2425 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2428 MapBlock *block = original_dummy;
2430 v2s16 p2d(p.X, p.Z);
2434 Do not generate over-limit
2436 if(blockpos_over_limit(p))
2438 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2439 throw InvalidPositionException("generateBlock(): pos. over limit");
2443 If block doesn't exist, create one.
2444 If it exists, it is a dummy. In that case unDummify() it.
2446 NOTE: This already sets the map as the parent of the block
2450 block = sector->createBlankBlockNoInsert(block_y);
2454 // Remove the block so that nobody can get a half-generated one.
2455 sector->removeBlock(block);
2456 // Allocate the block to contain the generated data
2460 /*u8 water_material = CONTENT_WATER;
2461 if(g_settings.getBool("endless_water"))
2462 water_material = CONTENT_WATERSOURCE;*/
2463 u8 water_material = CONTENT_WATERSOURCE;
2465 s32 lowest_ground_y = 32767;
2466 s32 highest_ground_y = -32768;
2469 //sector->printHeightmaps();
2471 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2472 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2474 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
2476 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
2477 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
2478 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
2480 dstream<<"WARNING: Surface height not found in sector "
2481 "for block that is being emerged"<<std::endl;
2485 s16 surface_y = surface_y_f;
2486 //avg_ground_y += surface_y;
2487 if(surface_y < lowest_ground_y)
2488 lowest_ground_y = surface_y;
2489 if(surface_y > highest_ground_y)
2490 highest_ground_y = surface_y;
2492 s32 surface_depth = 0;
2494 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
2496 //float min_slope = 0.45;
2497 //float max_slope = 0.85;
2498 float min_slope = 0.60;
2499 float max_slope = 1.20;
2500 float min_slope_depth = 5.0;
2501 float max_slope_depth = 0;
2503 if(slope < min_slope)
2504 surface_depth = min_slope_depth;
2505 else if(slope > max_slope)
2506 surface_depth = max_slope_depth;
2508 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2510 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2512 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2517 NOTE: If there are some man-made structures above the
2518 newly created block, they won't be taken into account.
2520 if(real_y > surface_y)
2521 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2527 // If node is over heightmap y, it's air or water
2528 if(real_y > surface_y)
2530 // If under water level, it's water
2531 if(real_y < WATER_LEVEL)
2533 n.d = water_material;
2534 n.setLight(LIGHTBANK_DAY,
2535 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2537 Add to transforming liquid queue (in case it'd
2540 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
2541 m_transforming_liquid.push_back(real_pos);
2547 // Else it's ground or dungeons (air)
2550 // If it's surface_depth under ground, it's stone
2551 if(real_y <= surface_y - surface_depth)
2553 n.d = CONTENT_STONE;
2557 // It is mud if it is under the first ground
2558 // level or under water
2559 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2565 n.d = CONTENT_GRASS;
2568 //n.d = CONTENT_MUD;
2570 /*// If under water level, it's mud
2571 if(real_y < WATER_LEVEL)
2573 // Only the topmost node is grass
2574 else if(real_y <= surface_y - 1)
2577 n.d = CONTENT_GRASS;*/
2581 block->setNode(v3s16(x0,y0,z0), n);
2586 Calculate some helper variables
2589 // Completely underground if the highest part of block is under lowest
2591 // This has to be very sure; it's probably one too strict now but
2592 // that's just better.
2593 bool completely_underground =
2594 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2596 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2598 bool mostly_underwater_surface = false;
2599 if(highest_ground_y < WATER_LEVEL
2600 && some_part_underground && !completely_underground)
2601 mostly_underwater_surface = true;
2604 Get local attributes
2607 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
2609 float caves_amount = 0.5;
2614 NOTE: BEWARE: Too big amount of attribute points slows verything
2616 1 interpolation from 5000 points takes 2-3ms.
2618 //TimeTaker timer("generateBlock() local attribute retrieval");
2619 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2620 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
2621 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
2625 //dstream<<"generateBlock(): Done"<<std::endl;
2631 // Initialize temporary table
2632 const s32 ued = MAP_BLOCKSIZE;
2633 bool underground_emptiness[ued*ued*ued];
2634 for(s32 i=0; i<ued*ued*ued; i++)
2636 underground_emptiness[i] = 0;
2643 Initialize orp and ors. Try to find if some neighboring
2644 MapBlock has a tunnel ended in its side
2648 (float)(myrand()%ued)+0.5,
2649 (float)(myrand()%ued)+0.5,
2650 (float)(myrand()%ued)+0.5
2653 bool found_existing = false;
2659 for(s16 y=0; y<ued; y++)
2660 for(s16 x=0; x<ued; x++)
2662 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2663 if(getNode(ap).d == CONTENT_AIR)
2665 orp = v3f(x+1,y+1,0);
2666 found_existing = true;
2667 goto continue_generating;
2671 catch(InvalidPositionException &e){}
2677 for(s16 y=0; y<ued; y++)
2678 for(s16 x=0; x<ued; x++)
2680 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2681 if(getNode(ap).d == CONTENT_AIR)
2683 orp = v3f(x+1,y+1,ued-1);
2684 found_existing = true;
2685 goto continue_generating;
2689 catch(InvalidPositionException &e){}
2695 for(s16 y=0; y<ued; y++)
2696 for(s16 z=0; z<ued; z++)
2698 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2699 if(getNode(ap).d == CONTENT_AIR)
2701 orp = v3f(0,y+1,z+1);
2702 found_existing = true;
2703 goto continue_generating;
2707 catch(InvalidPositionException &e){}
2713 for(s16 y=0; y<ued; y++)
2714 for(s16 z=0; z<ued; z++)
2716 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2717 if(getNode(ap).d == CONTENT_AIR)
2719 orp = v3f(ued-1,y+1,z+1);
2720 found_existing = true;
2721 goto continue_generating;
2725 catch(InvalidPositionException &e){}
2731 for(s16 x=0; x<ued; x++)
2732 for(s16 z=0; z<ued; z++)
2734 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2735 if(getNode(ap).d == CONTENT_AIR)
2737 orp = v3f(x+1,0,z+1);
2738 found_existing = true;
2739 goto continue_generating;
2743 catch(InvalidPositionException &e){}
2749 for(s16 x=0; x<ued; x++)
2750 for(s16 z=0; z<ued; z++)
2752 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2753 if(getNode(ap).d == CONTENT_AIR)
2755 orp = v3f(x+1,ued-1,z+1);
2756 found_existing = true;
2757 goto continue_generating;
2761 catch(InvalidPositionException &e){}
2763 continue_generating:
2766 Choose whether to actually generate dungeon
2768 bool do_generate_dungeons = true;
2769 // Don't generate if no part is underground
2770 if(!some_part_underground)
2772 do_generate_dungeons = false;
2774 // Don't generate if mostly underwater surface
2775 /*else if(mostly_underwater_surface)
2777 do_generate_dungeons = false;
2779 // Partly underground = cave
2780 else if(!completely_underground)
2782 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2784 // Found existing dungeon underground
2785 else if(found_existing && completely_underground)
2787 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2789 // Underground and no dungeons found
2792 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
2795 if(do_generate_dungeons)
2798 Generate some tunnel starting from orp and ors
2800 for(u16 i=0; i<3; i++)
2803 (float)(myrand()%ued)+0.5,
2804 (float)(myrand()%ued)+0.5,
2805 (float)(myrand()%ued)+0.5
2809 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
2813 for(float f=0; f<1.0; f+=0.04)
2815 v3f fp = orp + vec * f;
2816 v3s16 cp(fp.X, fp.Y, fp.Z);
2818 s16 d1 = d0 + rs - 1;
2819 for(s16 z0=d0; z0<=d1; z0++)
2821 s16 si = rs - abs(z0);
2822 for(s16 x0=-si; x0<=si-1; x0++)
2824 s16 si2 = rs - abs(x0);
2825 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2831 if(isInArea(p, ued) == false)
2833 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2845 // Set to true if has caves.
2846 // Set when some non-air is changed to air when making caves.
2847 bool has_dungeons = false;
2850 Apply temporary cave data to block
2853 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2854 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2856 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2858 MapNode n = block->getNode(v3s16(x0,y0,z0));
2861 if(underground_emptiness[
2862 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2863 +ued*(y0*ued/MAP_BLOCKSIZE)
2864 +(x0*ued/MAP_BLOCKSIZE)])
2866 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
2869 has_dungeons = true;
2875 block->setNode(v3s16(x0,y0,z0), n);
2880 This is used for guessing whether or not the block should
2881 receive sunlight from the top if the block above doesn't exist
2883 block->setIsUnderground(completely_underground);
2886 Force lighting update if some part of block is partly
2887 underground and has caves.
2889 /*if(some_part_underground && !completely_underground && has_dungeons)
2891 //dstream<<"Half-ground caves"<<std::endl;
2892 lighting_invalidated_blocks[block->getPos()] = block;
2895 // DEBUG: Always update lighting
2896 //lighting_invalidated_blocks[block->getPos()] = block;
2902 if(some_part_underground)
2904 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2909 for(s16 i=0; i<underground_level/4 + 1; i++)
2911 if(myrand()%50 == 0)
2914 (myrand()%(MAP_BLOCKSIZE-2))+1,
2915 (myrand()%(MAP_BLOCKSIZE-2))+1,
2916 (myrand()%(MAP_BLOCKSIZE-2))+1
2922 for(u16 i=0; i<27; i++)
2924 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
2926 block->setNode(cp+g_27dirs[i], n);
2934 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2935 u16 coal_rareness = 60 / coal_amount;
2936 if(coal_rareness == 0)
2938 if(myrand()%coal_rareness == 0)
2940 u16 a = myrand() % 16;
2941 u16 amount = coal_amount * a*a*a / 1000;
2942 for(s16 i=0; i<amount; i++)
2945 (myrand()%(MAP_BLOCKSIZE-2))+1,
2946 (myrand()%(MAP_BLOCKSIZE-2))+1,
2947 (myrand()%(MAP_BLOCKSIZE-2))+1
2951 n.d = CONTENT_STONE;
2952 n.param = MINERAL_COAL;
2954 for(u16 i=0; i<27; i++)
2956 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
2958 block->setNode(cp+g_27dirs[i], n);
2966 //TODO: change to iron_amount or whatever
2967 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
2968 u16 iron_rareness = 60 / iron_amount;
2969 if(iron_rareness == 0)
2971 if(myrand()%iron_rareness == 0)
2973 u16 a = myrand() % 16;
2974 u16 amount = iron_amount * a*a*a / 1000;
2975 for(s16 i=0; i<amount; i++)
2978 (myrand()%(MAP_BLOCKSIZE-2))+1,
2979 (myrand()%(MAP_BLOCKSIZE-2))+1,
2980 (myrand()%(MAP_BLOCKSIZE-2))+1
2984 n.d = CONTENT_STONE;
2985 n.param = MINERAL_IRON;
2987 for(u16 i=0; i<27; i++)
2989 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
2991 block->setNode(cp+g_27dirs[i], n);
2998 Create a few rats in empty blocks underground
3000 if(completely_underground)
3002 //for(u16 i=0; i<2; i++)
3005 (myrand()%(MAP_BLOCKSIZE-2))+1,
3006 (myrand()%(MAP_BLOCKSIZE-2))+1,
3007 (myrand()%(MAP_BLOCKSIZE-2))+1
3010 // Check that the place is empty
3011 //if(!is_ground_content(block->getNode(cp).d))
3014 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
3015 block->addObject(obj);
3021 Add block to sector.
3023 sector->insertBlock(block);
3029 // An y-wise container of changed blocks
3030 core::map<s16, MapBlock*> changed_blocks_sector;
3033 Check if any sector's objects can be placed now.
3036 core::map<v3s16, u8> *objects = sector->getObjects();
3037 core::list<v3s16> objects_to_remove;
3038 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
3039 i.atEnd() == false; i++)
3041 v3s16 p = i.getNode()->getKey();
3043 u8 d = i.getNode()->getValue();
3045 // Ground level point (user for stuff that is on ground)
3047 bool ground_found = true;
3049 // Search real ground level
3053 MapNode n = sector->getNode(gp);
3055 // If not air, go one up and continue to placing the tree
3056 if(n.d != CONTENT_AIR)
3062 // If air, go one down
3063 gp += v3s16(0,-1,0);
3065 }catch(InvalidPositionException &e)
3067 // Ground not found.
3068 ground_found = false;
3069 // This is most close to ground
3076 if(d == SECTOR_OBJECT_TEST)
3078 if(sector->isValidArea(p + v3s16(0,0,0),
3079 p + v3s16(0,0,0), &changed_blocks_sector))
3082 n.d = CONTENT_TORCH;
3083 sector->setNode(p, n);
3084 objects_to_remove.push_back(p);
3087 else if(d == SECTOR_OBJECT_TREE_1)
3089 if(ground_found == false)
3092 v3s16 p_min = gp + v3s16(-1,0,-1);
3093 v3s16 p_max = gp + v3s16(1,5,1);
3094 if(sector->isValidArea(p_min, p_max,
3095 &changed_blocks_sector))
3099 sector->setNode(gp+v3s16(0,0,0), n);
3100 sector->setNode(gp+v3s16(0,1,0), n);
3101 sector->setNode(gp+v3s16(0,2,0), n);
3102 sector->setNode(gp+v3s16(0,3,0), n);
3104 n.d = CONTENT_LEAVES;
3106 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
3108 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
3109 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
3110 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
3111 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
3112 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
3113 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
3114 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
3115 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
3117 sector->setNode(gp+v3s16(0,4,0), n);
3119 sector->setNode(gp+v3s16(-1,4,0), n);
3120 sector->setNode(gp+v3s16(1,4,0), n);
3121 sector->setNode(gp+v3s16(0,4,-1), n);
3122 sector->setNode(gp+v3s16(0,4,1), n);
3123 sector->setNode(gp+v3s16(1,4,1), n);
3124 sector->setNode(gp+v3s16(-1,4,1), n);
3125 sector->setNode(gp+v3s16(-1,4,-1), n);
3126 sector->setNode(gp+v3s16(1,4,-1), n);
3128 sector->setNode(gp+v3s16(-1,3,0), n);
3129 sector->setNode(gp+v3s16(1,3,0), n);
3130 sector->setNode(gp+v3s16(0,3,-1), n);
3131 sector->setNode(gp+v3s16(0,3,1), n);
3132 sector->setNode(gp+v3s16(1,3,1), n);
3133 sector->setNode(gp+v3s16(-1,3,1), n);
3134 sector->setNode(gp+v3s16(-1,3,-1), n);
3135 sector->setNode(gp+v3s16(1,3,-1), n);
3137 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
3138 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
3139 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
3140 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
3141 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
3142 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
3143 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
3144 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
3146 // Objects are identified by wanted position
3147 objects_to_remove.push_back(p);
3149 // Lighting has to be recalculated for this one.
3150 sector->getBlocksInArea(p_min, p_max,
3151 lighting_invalidated_blocks);
3154 else if(d == SECTOR_OBJECT_BUSH_1)
3156 if(ground_found == false)
3159 if(sector->isValidArea(gp + v3s16(0,0,0),
3160 gp + v3s16(0,0,0), &changed_blocks_sector))
3163 n.d = CONTENT_LEAVES;
3164 sector->setNode(gp+v3s16(0,0,0), n);
3166 // Objects are identified by wanted position
3167 objects_to_remove.push_back(p);
3170 else if(d == SECTOR_OBJECT_RAVINE)
3173 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
3174 v3s16 p_max = p + v3s16(6,6,6);
3175 if(sector->isValidArea(p_min, p_max,
3176 &changed_blocks_sector))
3179 n.d = CONTENT_STONE;
3182 s16 depth = maxdepth + (myrand()%10);
3184 s16 minz = -6 - (-2);
3186 for(s16 x=-6; x<=6; x++)
3188 z += -1 + (myrand()%3);
3193 for(s16 y=depth+(myrand()%2); y<=6; y++)
3195 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
3198 v3s16 p2 = p + v3s16(x,y,z-2);
3199 //if(is_ground_content(sector->getNode(p2).d))
3200 if(content_features(sector->getNode(p2).d).walkable)
3201 sector->setNode(p2, n);
3204 v3s16 p2 = p + v3s16(x,y,z-1);
3205 if(content_features(sector->getNode(p2).d).walkable)
3206 sector->setNode(p2, n2);
3209 v3s16 p2 = p + v3s16(x,y,z+0);
3210 if(content_features(sector->getNode(p2).d).walkable)
3211 sector->setNode(p2, n2);
3214 v3s16 p2 = p + v3s16(x,y,z+1);
3215 if(content_features(sector->getNode(p2).d).walkable)
3216 sector->setNode(p2, n);
3219 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
3220 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
3224 objects_to_remove.push_back(p);
3226 // Lighting has to be recalculated for this one.
3227 sector->getBlocksInArea(p_min, p_max,
3228 lighting_invalidated_blocks);
3233 dstream<<"ServerMap::generateBlock(): "
3234 "Invalid heightmap object"
3239 catch(InvalidPositionException &e)
3241 dstream<<"WARNING: "<<__FUNCTION_NAME
3242 <<": while inserting object "<<(int)d
3243 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
3244 <<" InvalidPositionException.what()="
3245 <<e.what()<<std::endl;
3246 // This is not too fatal and seems to happen sometimes.
3251 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
3252 i != objects_to_remove.end(); i++)
3254 objects->remove(*i);
3258 Translate sector's changed blocks to global changed blocks
3261 for(core::map<s16, MapBlock*>::Iterator
3262 i = changed_blocks_sector.getIterator();
3263 i.atEnd() == false; i++)
3265 MapBlock *block = i.getNode()->getValue();
3267 changed_blocks.insert(block->getPos(), block);
3270 block->setLightingExpired(true);
3277 <<"lighting_invalidated_blocks.size()"
3281 <<" "<<lighting_invalidated_blocks.size()
3282 <<", "<<has_dungeons
3283 <<", "<<completely_underground
3284 <<", "<<some_part_underground
3291 MapBlock * ServerMap::emergeBlock(
3293 bool only_from_disk,
3294 core::map<v3s16, MapBlock*> &changed_blocks,
3295 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3298 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
3300 p.X, p.Y, p.Z, only_from_disk);
3302 /*dstream<<"emergeBlock(): "
3303 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3306 v2s16 p2d(p.X, p.Z);
3309 This will create or load a sector if not found in memory.
3310 If block exists on disk, it will be loaded.
3312 NOTE: On old save formats, this will be slow, as it generates
3313 lighting on blocks for them.
3315 ServerMapSector *sector;
3317 sector = (ServerMapSector*)emergeSector(p2d);
3318 assert(sector->getId() == MAPSECTOR_SERVER);
3320 /*catch(InvalidPositionException &e)
3322 dstream<<"emergeBlock: emergeSector() failed"<<std::endl;
3325 catch(std::exception &e)
3327 dstream<<"emergeBlock: emergeSector() failed: "
3328 <<e.what()<<std::endl;
3333 Try to get a block from the sector
3336 bool does_not_exist = false;
3337 bool lighting_expired = false;
3338 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
3342 does_not_exist = true;
3344 else if(block->isDummy() == true)
3346 does_not_exist = true;
3348 else if(block->getLightingExpired())
3350 lighting_expired = true;
3355 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
3360 If block was not found on disk and not going to generate a
3361 new one, make sure there is a dummy block in place.
3363 if(only_from_disk && (does_not_exist || lighting_expired))
3365 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
3369 // Create dummy block
3370 block = new MapBlock(this, p, true);
3372 // Add block to sector
3373 sector->insertBlock(block);
3379 //dstream<<"Not found on disk, generating."<<std::endl;
3381 //TimeTaker("emergeBlock() generate");
3383 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
3386 If the block doesn't exist, generate the block.
3390 block = generateBlock(p, block, sector, changed_blocks,
3391 lighting_invalidated_blocks);
3394 if(lighting_expired)
3396 lighting_invalidated_blocks.insert(p, block);
3400 Initially update sunlight
3404 core::map<v3s16, bool> light_sources;
3405 bool black_air_left = false;
3406 bool bottom_invalid =
3407 block->propagateSunlight(light_sources, true,
3408 &black_air_left, true);
3410 // If sunlight didn't reach everywhere and part of block is
3411 // above ground, lighting has to be properly updated
3412 //if(black_air_left && some_part_underground)
3415 lighting_invalidated_blocks[block->getPos()] = block;
3420 lighting_invalidated_blocks[block->getPos()] = block;
3425 Debug mode operation
3427 bool haxmode = g_settings.getBool("haxmode");
3430 // Don't calculate lighting at all
3431 //lighting_invalidated_blocks.clear();
3437 void ServerMap::createDir(std::string path)
3439 if(fs::CreateDir(path) == false)
3441 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3442 <<"\""<<path<<"\""<<std::endl;
3443 throw BaseException("ServerMap failed to create directory");
3447 std::string ServerMap::getSectorSubDir(v2s16 pos)
3450 snprintf(cc, 9, "%.4x%.4x",
3451 (unsigned int)pos.X&0xffff,
3452 (unsigned int)pos.Y&0xffff);
3454 return std::string(cc);
3457 std::string ServerMap::getSectorDir(v2s16 pos)
3459 return m_savedir + "/sectors/" + getSectorSubDir(pos);
3462 v2s16 ServerMap::getSectorPos(std::string dirname)
3464 if(dirname.size() != 8)
3465 throw InvalidFilenameException("Invalid sector directory name");
3467 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
3469 throw InvalidFilenameException("Invalid sector directory name");
3470 v2s16 pos((s16)x, (s16)y);
3474 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3476 v2s16 p2d = getSectorPos(sectordir);
3478 if(blockfile.size() != 4){
3479 throw InvalidFilenameException("Invalid block filename");
3482 int r = sscanf(blockfile.c_str(), "%4x", &y);
3484 throw InvalidFilenameException("Invalid block filename");
3485 return v3s16(p2d.X, y, p2d.Y);
3489 #define ENABLE_SECTOR_SAVING 1
3490 #define ENABLE_SECTOR_LOADING 1
3491 #define ENABLE_BLOCK_SAVING 1
3492 #define ENABLE_BLOCK_LOADING 1
3494 void ServerMap::save(bool only_changed)
3496 DSTACK(__FUNCTION_NAME);
3497 if(m_map_saving_enabled == false)
3499 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
3503 if(only_changed == false)
3504 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
3507 saveMasterHeightmap();
3509 u32 sector_meta_count = 0;
3510 u32 block_count = 0;
3513 JMutexAutoLock lock(m_sector_mutex);
3515 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3516 for(; i.atEnd() == false; i++)
3518 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3519 assert(sector->getId() == MAPSECTOR_SERVER);
3521 if(ENABLE_SECTOR_SAVING)
3523 if(sector->differs_from_disk || only_changed == false)
3525 saveSectorMeta(sector);
3526 sector_meta_count++;
3529 if(ENABLE_BLOCK_SAVING)
3531 core::list<MapBlock*> blocks;
3532 sector->getBlocks(blocks);
3533 core::list<MapBlock*>::Iterator j;
3534 for(j=blocks.begin(); j!=blocks.end(); j++)
3536 MapBlock *block = *j;
3537 if(block->getChangedFlag() || only_changed == false)
3549 Only print if something happened or saved whole map
3551 if(only_changed == false || sector_meta_count != 0
3552 || block_count != 0)
3554 dstream<<DTIME<<"ServerMap: Written: "
3555 <<sector_meta_count<<" sector metadata files, "
3556 <<block_count<<" block files"
3561 void ServerMap::loadAll()
3563 DSTACK(__FUNCTION_NAME);
3564 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
3566 loadMasterHeightmap();
3568 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
3570 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
3572 JMutexAutoLock lock(m_sector_mutex);
3575 s32 printed_counter = -100000;
3576 s32 count = list.size();
3578 std::vector<fs::DirListNode>::iterator i;
3579 for(i=list.begin(); i!=list.end(); i++)
3581 if(counter > printed_counter + 10)
3583 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
3584 printed_counter = counter;
3588 MapSector *sector = NULL;
3590 // We want directories
3594 sector = loadSectorMeta(i->name);
3596 catch(InvalidFilenameException &e)
3598 // This catches unknown crap in directory
3601 if(ENABLE_BLOCK_LOADING)
3603 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3604 (m_savedir+"/sectors/"+i->name);
3605 std::vector<fs::DirListNode>::iterator i2;
3606 for(i2=list2.begin(); i2!=list2.end(); i2++)
3612 loadBlock(i->name, i2->name, sector);
3614 catch(InvalidFilenameException &e)
3616 // This catches unknown crap in directory
3621 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
3624 void ServerMap::saveMasterHeightmap()
3626 DSTACK(__FUNCTION_NAME);
3627 createDir(m_savedir);
3629 std::string fullpath = m_savedir + "/master_heightmap";
3630 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3631 if(o.good() == false)
3632 throw FileNotGoodException("Cannot open master heightmap");
3634 // Format used for writing
3635 u8 version = SER_FMT_VER_HIGHEST;
3638 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
3640 [0] u8 serialization version
3641 [1] X master heightmap
3643 u32 fullsize = 1 + hmdata.getSize();
3644 SharedBuffer<u8> data(fullsize);
3647 memcpy(&data[1], *hmdata, hmdata.getSize());
3649 o.write((const char*)*data, fullsize);
3652 m_heightmap->serialize(o, version);
3655 void ServerMap::loadMasterHeightmap()
3657 DSTACK(__FUNCTION_NAME);
3658 std::string fullpath = m_savedir + "/master_heightmap";
3659 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3660 if(is.good() == false)
3661 throw FileNotGoodException("Cannot open master heightmap");
3663 if(m_heightmap != NULL)
3666 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
3669 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3671 DSTACK(__FUNCTION_NAME);
3672 // Format used for writing
3673 u8 version = SER_FMT_VER_HIGHEST;
3675 v2s16 pos = sector->getPos();
3676 createDir(m_savedir);
3677 createDir(m_savedir+"/sectors");
3678 std::string dir = getSectorDir(pos);
3681 std::string fullpath = dir + "/heightmap";
3682 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3683 if(o.good() == false)
3684 throw FileNotGoodException("Cannot open master heightmap");
3686 sector->serialize(o, version);
3688 sector->differs_from_disk = false;
3691 MapSector* ServerMap::loadSectorMeta(std::string dirname)
3693 DSTACK(__FUNCTION_NAME);
3695 v2s16 p2d = getSectorPos(dirname);
3696 std::string dir = m_savedir + "/sectors/" + dirname;
3698 std::string fullpath = dir + "/heightmap";
3699 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3700 if(is.good() == false)
3701 throw FileNotGoodException("Cannot open sector heightmap");
3703 ServerMapSector *sector = ServerMapSector::deSerialize
3704 (is, this, p2d, &m_hwrapper, m_sectors);
3706 sector->differs_from_disk = false;
3711 bool ServerMap::loadSectorFull(v2s16 p2d)
3713 DSTACK(__FUNCTION_NAME);
3714 std::string sectorsubdir = getSectorSubDir(p2d);
3716 MapSector *sector = NULL;
3718 JMutexAutoLock lock(m_sector_mutex);
3721 sector = loadSectorMeta(sectorsubdir);
3723 catch(InvalidFilenameException &e)
3727 catch(FileNotGoodException &e)
3731 catch(std::exception &e)
3736 if(ENABLE_BLOCK_LOADING)
3738 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3739 (m_savedir+"/sectors/"+sectorsubdir);
3740 std::vector<fs::DirListNode>::iterator i2;
3741 for(i2=list2.begin(); i2!=list2.end(); i2++)
3747 loadBlock(sectorsubdir, i2->name, sector);
3749 catch(InvalidFilenameException &e)
3751 // This catches unknown crap in directory
3759 bool ServerMap::deFlushSector(v2s16 p2d)
3761 DSTACK(__FUNCTION_NAME);
3762 // See if it already exists in memory
3764 MapSector *sector = getSectorNoGenerate(p2d);
3767 catch(InvalidPositionException &e)
3770 Try to load the sector from disk.
3772 if(loadSectorFull(p2d) == true)
3781 void ServerMap::saveBlock(MapBlock *block)
3783 DSTACK(__FUNCTION_NAME);
3785 Dummy blocks are not written
3787 if(block->isDummy())
3789 /*v3s16 p = block->getPos();
3790 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3791 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3795 // Format used for writing
3796 u8 version = SER_FMT_VER_HIGHEST;
3798 v3s16 p3d = block->getPos();
3799 v2s16 p2d(p3d.X, p3d.Z);
3800 createDir(m_savedir);
3801 createDir(m_savedir+"/sectors");
3802 std::string dir = getSectorDir(p2d);
3805 // Block file is map/sectors/xxxxxxxx/xxxx
3807 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
3808 std::string fullpath = dir + "/" + cc;
3809 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3810 if(o.good() == false)
3811 throw FileNotGoodException("Cannot open block data");
3814 [0] u8 serialization version
3817 o.write((char*)&version, 1);
3819 block->serialize(o, version);
3822 Versions up from 9 have block objects.
3826 block->serializeObjects(o, version);
3829 // We just wrote it to the disk
3830 block->resetChangedFlag();
3833 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
3835 DSTACK(__FUNCTION_NAME);
3839 // Block file is map/sectors/xxxxxxxx/xxxx
3840 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
3841 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3842 if(is.good() == false)
3843 throw FileNotGoodException("Cannot open block file");
3845 v3s16 p3d = getBlockPos(sectordir, blockfile);
3846 v2s16 p2d(p3d.X, p3d.Z);
3848 assert(sector->getPos() == p2d);
3850 u8 version = SER_FMT_VER_INVALID;
3851 is.read((char*)&version, 1);
3853 /*u32 block_size = MapBlock::serializedLength(version);
3854 SharedBuffer<u8> data(block_size);
3855 is.read((char*)*data, block_size);*/
3857 // This will always return a sector because we're the server
3858 //MapSector *sector = emergeSector(p2d);
3860 MapBlock *block = NULL;
3861 bool created_new = false;
3863 block = sector->getBlockNoCreate(p3d.Y);
3865 catch(InvalidPositionException &e)
3867 block = sector->createBlankBlockNoInsert(p3d.Y);
3871 // deserialize block data
3872 block->deSerialize(is, version);
3875 Versions up from 9 have block objects.
3879 block->updateObjects(is, version, NULL, 0);
3883 sector->insertBlock(block);
3886 Convert old formats to new and save
3889 // Save old format blocks in new format
3890 if(version < SER_FMT_VER_HIGHEST)
3895 // We just loaded it from the disk, so it's up-to-date.
3896 block->resetChangedFlag();
3899 catch(SerializationError &e)
3901 dstream<<"WARNING: Invalid block data on disk "
3902 "(SerializationError). Ignoring."
3907 // Gets from master heightmap
3908 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
3910 assert(m_heightmap != NULL);
3918 corners[0] = m_heightmap->getGroundHeight
3919 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
3920 corners[1] = m_heightmap->getGroundHeight
3921 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
3922 corners[2] = m_heightmap->getGroundHeight
3923 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
3924 corners[3] = m_heightmap->getGroundHeight
3925 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
3928 void ServerMap::PrintInfo(std::ostream &out)
3939 ClientMap::ClientMap(
3941 MapDrawControl &control,
3942 scene::ISceneNode* parent,
3943 scene::ISceneManager* mgr,
3947 scene::ISceneNode(parent, mgr, id),
3954 /*m_box = core::aabbox3d<f32>(0,0,0,
3955 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
3956 /*m_box = core::aabbox3d<f32>(0,0,0,
3957 map->getSizeNodes().X * BS,
3958 map->getSizeNodes().Y * BS,
3959 map->getSizeNodes().Z * BS);*/
3960 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3961 BS*1000000,BS*1000000,BS*1000000);
3963 //setPosition(v3f(BS,BS,BS));
3966 ClientMap::~ClientMap()
3968 JMutexAutoLock lock(mesh_mutex);
3977 MapSector * ClientMap::emergeSector(v2s16 p2d)
3979 DSTACK(__FUNCTION_NAME);
3980 // Check that it doesn't exist already
3982 return getSectorNoGenerate(p2d);
3984 catch(InvalidPositionException &e)
3988 // Create a sector with no heightmaps
3989 ClientMapSector *sector = new ClientMapSector(this, p2d);
3992 JMutexAutoLock lock(m_sector_mutex);
3993 m_sectors.insert(p2d, sector);
3999 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
4001 DSTACK(__FUNCTION_NAME);
4002 ClientMapSector *sector = NULL;
4004 JMutexAutoLock lock(m_sector_mutex);
4006 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
4010 sector = (ClientMapSector*)n->getValue();
4011 assert(sector->getId() == MAPSECTOR_CLIENT);
4015 sector = new ClientMapSector(this, p2d);
4017 JMutexAutoLock lock(m_sector_mutex);
4018 m_sectors.insert(p2d, sector);
4022 sector->deSerialize(is);
4025 void ClientMap::OnRegisterSceneNode()
4029 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
4030 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
4033 ISceneNode::OnRegisterSceneNode();
4036 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
4038 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
4039 DSTACK(__FUNCTION_NAME);
4041 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
4044 Get time for measuring timeout.
4046 Measuring time is very useful for long delays when the
4047 machine is swapping a lot.
4049 int time1 = time(0);
4051 u32 daynight_ratio = m_client->getDayNightRatio();
4053 m_camera_mutex.Lock();
4054 v3f camera_position = m_camera_position;
4055 v3f camera_direction = m_camera_direction;
4056 m_camera_mutex.Unlock();
4059 Get all blocks and draw all visible ones
4062 v3s16 cam_pos_nodes(
4063 camera_position.X / BS,
4064 camera_position.Y / BS,
4065 camera_position.Z / BS);
4067 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
4069 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
4070 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
4072 // Take a fair amount as we will be dropping more out later
4074 p_nodes_min.X / MAP_BLOCKSIZE - 1,
4075 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
4076 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
4078 p_nodes_max.X / MAP_BLOCKSIZE + 1,
4079 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
4080 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
4082 u32 vertex_count = 0;
4084 // For limiting number of mesh updates per frame
4085 u32 mesh_update_count = 0;
4087 u32 blocks_would_have_drawn = 0;
4088 u32 blocks_drawn = 0;
4090 //NOTE: The sectors map should be locked but we're not doing it
4091 // because it'd cause too much delays
4093 int timecheck_counter = 0;
4094 core::map<v2s16, MapSector*>::Iterator si;
4095 si = m_sectors.getIterator();
4096 for(; si.atEnd() == false; si++)
4099 timecheck_counter++;
4100 if(timecheck_counter > 50)
4102 int time2 = time(0);
4103 if(time2 > time1 + 4)
4105 dstream<<"ClientMap::renderMap(): "
4106 "Rendering takes ages, returning."
4113 MapSector *sector = si.getNode()->getValue();
4114 v2s16 sp = sector->getPos();
4116 if(m_control.range_all == false)
4118 if(sp.X < p_blocks_min.X
4119 || sp.X > p_blocks_max.X
4120 || sp.Y < p_blocks_min.Z
4121 || sp.Y > p_blocks_max.Z)
4125 core::list< MapBlock * > sectorblocks;
4126 sector->getBlocks(sectorblocks);
4132 core::list< MapBlock * >::Iterator i;
4133 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4135 MapBlock *block = *i;
4138 Compare block position to camera position, skip
4139 if not seen on display
4142 float range = 100000 * BS;
4143 if(m_control.range_all == false)
4144 range = m_control.wanted_range * BS;
4146 if(isBlockInSight(block->getPos(), camera_position,
4147 camera_direction, range) == false)
4153 v3s16 blockpos_nodes = block->getPosRelative();
4155 // Block center position
4157 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
4158 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
4159 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
4162 // Block position relative to camera
4163 v3f blockpos_relative = blockpos - camera_position;
4165 // Distance in camera direction (+=front, -=back)
4166 f32 dforward = blockpos_relative.dotProduct(camera_direction);
4169 f32 d = blockpos_relative.getLength();
4171 if(m_control.range_all == false)
4173 // If block is far away, don't draw it
4174 if(d > m_control.wanted_range * BS)
4178 // Maximum radius of a block
4179 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
4181 // If block is (nearly) touching the camera, don't
4182 // bother validating further (that is, render it anyway)
4183 if(d > block_max_radius * 1.5)
4185 // Cosine of the angle between the camera direction
4186 // and the block direction (camera_direction is an unit vector)
4187 f32 cosangle = dforward / d;
4189 // Compensate for the size of the block
4190 // (as the block has to be shown even if it's a bit off FOV)
4191 // This is an estimate.
4192 cosangle += block_max_radius / dforward;
4194 // If block is not in the field of view, skip it
4195 //if(cosangle < cos(FOV_ANGLE/2))
4196 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
4201 v3s16 blockpos_nodes = block->getPosRelative();
4203 // Block center position
4205 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
4206 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
4207 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
4210 // Block position relative to camera
4211 v3f blockpos_relative = blockpos - camera_position;
4214 f32 d = blockpos_relative.getLength();
4221 bool mesh_expired = false;
4224 JMutexAutoLock lock(block->mesh_mutex);
4226 mesh_expired = block->getMeshExpired();
4228 // Mesh has not been expired and there is no mesh:
4229 // block has no content
4230 if(block->mesh == NULL && mesh_expired == false)
4234 f32 faraway = BS*50;
4235 //f32 faraway = m_control.wanted_range * BS;
4238 This has to be done with the mesh_mutex unlocked
4240 // Pretty random but this should work somewhat nicely
4241 if(mesh_expired && (
4242 (mesh_update_count < 3
4243 && (d < faraway || mesh_update_count < 2)
4246 (m_control.range_all && mesh_update_count < 20)
4249 /*if(mesh_expired && mesh_update_count < 6
4250 && (d < faraway || mesh_update_count < 3))*/
4252 mesh_update_count++;
4254 // Mesh has been expired: generate new mesh
4255 //block->updateMeshes(daynight_i);
4256 block->updateMesh(daynight_ratio);
4258 mesh_expired = false;
4262 Don't draw an expired mesh that is far away
4264 /*if(mesh_expired && d >= faraway)
4267 // Instead, delete it
4268 JMutexAutoLock lock(block->mesh_mutex);
4271 block->mesh->drop();
4274 // And continue to next block
4279 Draw the faces of the block
4282 JMutexAutoLock lock(block->mesh_mutex);
4284 scene::SMesh *mesh = block->mesh;
4289 blocks_would_have_drawn++;
4290 if(blocks_drawn >= m_control.wanted_max_blocks
4291 && m_control.range_all == false
4292 && d > m_control.wanted_min_range * BS)
4296 u32 c = mesh->getMeshBufferCount();
4298 for(u32 i=0; i<c; i++)
4300 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
4301 const video::SMaterial& material = buf->getMaterial();
4302 video::IMaterialRenderer* rnd =
4303 driver->getMaterialRenderer(material.MaterialType);
4304 bool transparent = (rnd && rnd->isTransparent());
4305 // Render transparent on transparent pass and likewise.
4306 if(transparent == is_transparent_pass)
4308 driver->setMaterial(buf->getMaterial());
4309 driver->drawMeshBuffer(buf);
4310 vertex_count += buf->getVertexCount();
4314 } // foreach sectorblocks
4317 m_control.blocks_drawn = blocks_drawn;
4318 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4320 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4321 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4324 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4325 core::map<v3s16, MapBlock*> *affected_blocks)
4327 bool changed = false;
4329 Add it to all blocks touching it
4332 v3s16(0,0,0), // this
4333 v3s16(0,0,1), // back
4334 v3s16(0,1,0), // top
4335 v3s16(1,0,0), // right
4336 v3s16(0,0,-1), // front
4337 v3s16(0,-1,0), // bottom
4338 v3s16(-1,0,0), // left
4340 for(u16 i=0; i<7; i++)
4342 v3s16 p2 = p + dirs[i];
4343 // Block position of neighbor (or requested) node
4344 v3s16 blockpos = getNodeBlockPos(p2);
4345 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4346 if(blockref == NULL)
4348 // Relative position of requested node
4349 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4350 if(blockref->setTempMod(relpos, mod))
4355 if(changed && affected_blocks!=NULL)
4357 for(u16 i=0; i<7; i++)
4359 v3s16 p2 = p + dirs[i];
4360 // Block position of neighbor (or requested) node
4361 v3s16 blockpos = getNodeBlockPos(p2);
4362 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4363 if(blockref == NULL)
4365 affected_blocks->insert(blockpos, blockref);
4371 bool ClientMap::clearTempMod(v3s16 p,
4372 core::map<v3s16, MapBlock*> *affected_blocks)
4374 bool changed = false;
4376 v3s16(0,0,0), // this
4377 v3s16(0,0,1), // back
4378 v3s16(0,1,0), // top
4379 v3s16(1,0,0), // right
4380 v3s16(0,0,-1), // front
4381 v3s16(0,-1,0), // bottom
4382 v3s16(-1,0,0), // left
4384 for(u16 i=0; i<7; i++)
4386 v3s16 p2 = p + dirs[i];
4387 // Block position of neighbor (or requested) node
4388 v3s16 blockpos = getNodeBlockPos(p2);
4389 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4390 if(blockref == NULL)
4392 // Relative position of requested node
4393 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4394 if(blockref->clearTempMod(relpos))
4399 if(changed && affected_blocks!=NULL)
4401 for(u16 i=0; i<7; i++)
4403 v3s16 p2 = p + dirs[i];
4404 // Block position of neighbor (or requested) node
4405 v3s16 blockpos = getNodeBlockPos(p2);
4406 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4407 if(blockref == NULL)
4409 affected_blocks->insert(blockpos, blockref);
4415 void ClientMap::PrintInfo(std::ostream &out)
4426 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4431 MapVoxelManipulator::~MapVoxelManipulator()
4433 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4438 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4440 TimeTaker timer1("emerge", &emerge_time);
4442 // Units of these are MapBlocks
4443 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4444 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4446 VoxelArea block_area_nodes
4447 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4449 addArea(block_area_nodes);
4451 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4452 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4453 for(s32 x=p_min.X; x<=p_max.X; x++)
4456 core::map<v3s16, bool>::Node *n;
4457 n = m_loaded_blocks.find(p);
4461 bool block_data_inexistent = false;
4464 TimeTaker timer1("emerge load", &emerge_load_time);
4466 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4467 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4470 dstream<<std::endl;*/
4472 MapBlock *block = m_map->getBlockNoCreate(p);
4473 if(block->isDummy())
4474 block_data_inexistent = true;
4476 block->copyTo(*this);
4478 catch(InvalidPositionException &e)
4480 block_data_inexistent = true;
4483 if(block_data_inexistent)
4485 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4486 // Fill with VOXELFLAG_INEXISTENT
4487 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4488 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4490 s32 i = m_area.index(a.MinEdge.X,y,z);
4491 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4495 m_loaded_blocks.insert(p, true);
4498 //dstream<<"emerge done"<<std::endl;
4506 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4508 TimeTaker timer1("emerge", &emerge_time);
4510 v3s16 size = a.getExtent();
4512 VoxelArea padded = a;
4513 padded.pad(m_area.getExtent() / 4);
4516 for(s16 z=0; z<size.Z; z++)
4517 for(s16 y=0; y<size.Y; y++)
4518 for(s16 x=0; x<size.X; x++)
4521 s32 i = m_area.index(a.MinEdge + p);
4522 // Don't touch nodes that have already been loaded
4523 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4527 TimeTaker timer1("emerge load", &emerge_load_time);
4528 MapNode n = m_map->getNode(a.MinEdge + p);
4532 catch(InvalidPositionException &e)
4534 m_flags[i] = VOXELFLAG_INEXISTENT;
4542 SUGG: Add an option to only update eg. water and air nodes.
4543 This will make it interfere less with important stuff if
4546 void MapVoxelManipulator::blitBack
4547 (core::map<v3s16, MapBlock*> & modified_blocks)
4549 if(m_area.getExtent() == v3s16(0,0,0))
4552 //TimeTaker timer1("blitBack");
4554 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4555 <<m_loaded_blocks.size()<<std::endl;*/
4558 Initialize block cache
4560 v3s16 blockpos_last;
4561 MapBlock *block = NULL;
4562 bool block_checked_in_modified = false;
4564 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4565 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4566 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4570 u8 f = m_flags[m_area.index(p)];
4571 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4574 MapNode &n = m_data[m_area.index(p)];
4576 v3s16 blockpos = getNodeBlockPos(p);
4581 if(block == NULL || blockpos != blockpos_last){
4582 block = m_map->getBlockNoCreate(blockpos);
4583 blockpos_last = blockpos;
4584 block_checked_in_modified = false;
4587 // Calculate relative position in block
4588 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4590 // Don't continue if nothing has changed here
4591 if(block->getNode(relpos) == n)
4594 //m_map->setNode(m_area.MinEdge + p, n);
4595 block->setNode(relpos, n);
4598 Make sure block is in modified_blocks
4600 if(block_checked_in_modified == false)
4602 modified_blocks[blockpos] = block;
4603 block_checked_in_modified = true;
4606 catch(InvalidPositionException &e)
4612 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4613 MapVoxelManipulator(map)
4617 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4621 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4623 // Just create the area to avoid segfaults
4624 VoxelManipulator::emerge(a, caller_id);
4627 Just create the area to avoid segfaults
4630 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4631 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4632 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
4634 s32 i = m_area.index(x,y,z);
4635 // Don't touch nodes that have already been loaded
4636 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4638 m_flags[i] = VOXELFLAG_INEXISTENT;
4642 void ManualMapVoxelManipulator::initialEmerge(
4643 v3s16 blockpos_min, v3s16 blockpos_max)
4645 TimeTaker timer1("emerge", &emerge_time);
4647 // Units of these are MapBlocks
4648 v3s16 p_min = blockpos_min;
4649 v3s16 p_max = blockpos_max;
4651 VoxelArea block_area_nodes
4652 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4654 addArea(block_area_nodes);
4656 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4657 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4658 for(s32 x=p_min.X; x<=p_max.X; x++)
4661 core::map<v3s16, bool>::Node *n;
4662 n = m_loaded_blocks.find(p);
4666 bool block_data_inexistent = false;
4669 TimeTaker timer1("emerge load", &emerge_load_time);
4671 MapBlock *block = m_map->getBlockNoCreate(p);
4672 if(block->isDummy())
4673 block_data_inexistent = true;
4675 block->copyTo(*this);
4677 catch(InvalidPositionException &e)
4679 block_data_inexistent = true;
4682 if(block_data_inexistent)
4684 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4685 // Fill with VOXELFLAG_INEXISTENT
4686 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4687 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4689 s32 i = m_area.index(a.MinEdge.X,y,z);
4690 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4694 m_loaded_blocks.insert(p, true);