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"
33 Map::Map(std::ostream &dout):
35 m_camera_position(0,0,0),
36 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::getSectorNoGenerate(v2s16 p)
72 JMutexAutoLock lock(m_sector_mutex);
74 if(m_sector_cache != NULL && p == m_sector_cache_p){
75 MapSector * sector = m_sector_cache;
76 // Reset inactivity timer
77 sector->usage_timer = 0.0;
81 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
82 // If sector doesn't exist, throw an exception
85 throw InvalidPositionException();
88 MapSector *sector = n->getValue();
90 // Cache the last result
92 m_sector_cache = sector;
94 //MapSector * ref(sector);
96 // Reset inactivity timer
97 sector->usage_timer = 0.0;
101 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
103 v2s16 p2d(p3d.X, p3d.Z);
104 MapSector * sector = getSectorNoGenerate(p2d);
106 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
111 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
115 v2s16 p2d(p3d.X, p3d.Z);
116 MapSector * sector = getSectorNoGenerate(p2d);
117 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
120 catch(InvalidPositionException &e)
126 f32 Map::getGroundHeight(v2s16 p, bool generate)
129 v2s16 sectorpos = getNodeSectorPos(p);
130 MapSector * sref = getSectorNoGenerate(sectorpos);
131 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
132 f32 y = sref->getGroundHeight(relpos);
135 catch(InvalidPositionException &e)
137 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
141 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
143 /*m_dout<<DTIME<<"Map::setGroundHeight(("
145 <<"), "<<y<<")"<<std::endl;*/
146 v2s16 sectorpos = getNodeSectorPos(p);
147 MapSector * sref = getSectorNoGenerate(sectorpos);
148 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
149 //sref->mutex.Lock();
150 sref->setGroundHeight(relpos, y);
151 //sref->mutex.Unlock();
154 bool Map::isNodeUnderground(v3s16 p)
156 v3s16 blockpos = getNodeBlockPos(p);
158 MapBlock * block = getBlockNoCreate(blockpos);
159 return block->getIsUnderground();
161 catch(InvalidPositionException &e)
168 Goes recursively through the neighbours of the node.
170 Alters only transparent nodes.
172 If the lighting of the neighbour is lower than the lighting of
173 the node was (before changing it to 0 at the step before), the
174 lighting of the neighbour is set to 0 and then the same stuff
175 repeats for the neighbour.
177 The ending nodes of the routine are stored in light_sources.
178 This is useful when a light is removed. In such case, this
179 routine can be called for the light node and then again for
180 light_sources to re-light the area without the removed light.
182 values of from_nodes are lighting values.
184 void Map::unspreadLight(enum LightBank bank,
185 core::map<v3s16, u8> & from_nodes,
186 core::map<v3s16, bool> & light_sources,
187 core::map<v3s16, MapBlock*> & modified_blocks)
190 v3s16(0,0,1), // back
192 v3s16(1,0,0), // right
193 v3s16(0,0,-1), // front
194 v3s16(0,-1,0), // bottom
195 v3s16(-1,0,0), // left
198 if(from_nodes.size() == 0)
201 u32 blockchangecount = 0;
203 core::map<v3s16, u8> unlighted_nodes;
204 core::map<v3s16, u8>::Iterator j;
205 j = from_nodes.getIterator();
208 Initialize block cache
211 MapBlock *block = NULL;
212 // Cache this a bit, too
213 bool block_checked_in_modified = false;
215 for(; j.atEnd() == false; j++)
217 v3s16 pos = j.getNode()->getKey();
218 v3s16 blockpos = getNodeBlockPos(pos);
220 // Only fetch a new block if the block position has changed
222 if(block == NULL || blockpos != blockpos_last){
223 block = getBlockNoCreate(blockpos);
224 blockpos_last = blockpos;
226 block_checked_in_modified = false;
230 catch(InvalidPositionException &e)
238 // Calculate relative position in block
239 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
241 // Get node straight from the block
242 MapNode n = block->getNode(relpos);
244 u8 oldlight = j.getNode()->getValue();
246 // Loop through 6 neighbors
247 for(u16 i=0; i<6; i++)
249 // Get the position of the neighbor node
250 v3s16 n2pos = pos + dirs[i];
252 // Get the block where the node is located
253 v3s16 blockpos = getNodeBlockPos(n2pos);
257 // Only fetch a new block if the block position has changed
259 if(block == NULL || blockpos != blockpos_last){
260 block = getBlockNoCreate(blockpos);
261 blockpos_last = blockpos;
263 block_checked_in_modified = false;
267 catch(InvalidPositionException &e)
272 // Calculate relative position in block
273 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
274 // Get node straight from the block
275 MapNode n2 = block->getNode(relpos);
277 bool changed = false;
279 //TODO: Optimize output by optimizing light_sources?
282 If the neighbor is dimmer than what was specified
283 as oldlight (the light of the previous node)
285 if(n2.getLight(bank) < oldlight)
288 And the neighbor is transparent and it has some light
290 if(n2.light_propagates() && n2.getLight(bank) != 0)
293 Set light to 0 and add to queue
296 u8 current_light = n2.getLight(bank);
297 n2.setLight(bank, 0);
298 block->setNode(relpos, n2);
300 unlighted_nodes.insert(n2pos, current_light);
304 Remove from light_sources if it is there
305 NOTE: This doesn't happen nearly at all
307 /*if(light_sources.find(n2pos))
309 std::cout<<"Removed from light_sources"<<std::endl;
310 light_sources.remove(n2pos);
315 if(light_sources.find(n2pos) != NULL)
316 light_sources.remove(n2pos);*/
319 light_sources.insert(n2pos, true);
322 // Add to modified_blocks
323 if(changed == true && block_checked_in_modified == false)
325 // If the block is not found in modified_blocks, add.
326 if(modified_blocks.find(blockpos) == NULL)
328 modified_blocks.insert(blockpos, block);
330 block_checked_in_modified = true;
333 catch(InvalidPositionException &e)
340 /*dstream<<"unspreadLight(): Changed block "
341 <<blockchangecount<<" times"
342 <<" for "<<from_nodes.size()<<" nodes"
345 if(unlighted_nodes.size() > 0)
346 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
350 A single-node wrapper of the above
352 void Map::unLightNeighbors(enum LightBank bank,
353 v3s16 pos, u8 lightwas,
354 core::map<v3s16, bool> & light_sources,
355 core::map<v3s16, MapBlock*> & modified_blocks)
357 core::map<v3s16, u8> from_nodes;
358 from_nodes.insert(pos, lightwas);
360 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
364 Lights neighbors of from_nodes, collects all them and then
367 void Map::spreadLight(enum LightBank bank,
368 core::map<v3s16, bool> & from_nodes,
369 core::map<v3s16, MapBlock*> & modified_blocks)
371 const v3s16 dirs[6] = {
372 v3s16(0,0,1), // back
374 v3s16(1,0,0), // right
375 v3s16(0,0,-1), // front
376 v3s16(0,-1,0), // bottom
377 v3s16(-1,0,0), // left
380 if(from_nodes.size() == 0)
383 u32 blockchangecount = 0;
385 core::map<v3s16, bool> lighted_nodes;
386 core::map<v3s16, bool>::Iterator j;
387 j = from_nodes.getIterator();
390 Initialize block cache
393 MapBlock *block = NULL;
394 // Cache this a bit, too
395 bool block_checked_in_modified = false;
397 for(; j.atEnd() == false; j++)
398 //for(; j != from_nodes.end(); j++)
400 v3s16 pos = j.getNode()->getKey();
402 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
403 v3s16 blockpos = getNodeBlockPos(pos);
405 // Only fetch a new block if the block position has changed
407 if(block == NULL || blockpos != blockpos_last){
408 block = getBlockNoCreate(blockpos);
409 blockpos_last = blockpos;
411 block_checked_in_modified = false;
415 catch(InvalidPositionException &e)
423 // Calculate relative position in block
424 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
426 // Get node straight from the block
427 MapNode n = block->getNode(relpos);
429 u8 oldlight = n.getLight(bank);
430 u8 newlight = diminish_light(oldlight);
432 // Loop through 6 neighbors
433 for(u16 i=0; i<6; i++){
434 // Get the position of the neighbor node
435 v3s16 n2pos = pos + dirs[i];
437 // Get the block where the node is located
438 v3s16 blockpos = getNodeBlockPos(n2pos);
442 // Only fetch a new block if the block position has changed
444 if(block == NULL || blockpos != blockpos_last){
445 block = getBlockNoCreate(blockpos);
446 blockpos_last = blockpos;
448 block_checked_in_modified = false;
452 catch(InvalidPositionException &e)
457 // Calculate relative position in block
458 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
459 // Get node straight from the block
460 MapNode n2 = block->getNode(relpos);
462 bool changed = false;
464 If the neighbor is brighter than the current node,
465 add to list (it will light up this node on its turn)
467 if(n2.getLight(bank) > undiminish_light(oldlight))
469 lighted_nodes.insert(n2pos, true);
470 //lighted_nodes.push_back(n2pos);
474 If the neighbor is dimmer than how much light this node
475 would spread on it, add to list
477 if(n2.getLight(bank) < newlight)
479 if(n2.light_propagates())
481 n2.setLight(bank, newlight);
482 block->setNode(relpos, n2);
483 lighted_nodes.insert(n2pos, true);
484 //lighted_nodes.push_back(n2pos);
489 // Add to modified_blocks
490 if(changed == true && block_checked_in_modified == false)
492 // If the block is not found in modified_blocks, add.
493 if(modified_blocks.find(blockpos) == NULL)
495 modified_blocks.insert(blockpos, block);
497 block_checked_in_modified = true;
500 catch(InvalidPositionException &e)
507 /*dstream<<"spreadLight(): Changed block "
508 <<blockchangecount<<" times"
509 <<" for "<<from_nodes.size()<<" nodes"
512 if(lighted_nodes.size() > 0)
513 spreadLight(bank, lighted_nodes, modified_blocks);
517 A single-node source variation of the above.
519 void Map::lightNeighbors(enum LightBank bank,
521 core::map<v3s16, MapBlock*> & modified_blocks)
523 core::map<v3s16, bool> from_nodes;
524 from_nodes.insert(pos, true);
525 spreadLight(bank, from_nodes, modified_blocks);
528 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
531 v3s16(0,0,1), // back
533 v3s16(1,0,0), // right
534 v3s16(0,0,-1), // front
535 v3s16(0,-1,0), // bottom
536 v3s16(-1,0,0), // left
539 u8 brightest_light = 0;
540 v3s16 brightest_pos(0,0,0);
541 bool found_something = false;
543 // Loop through 6 neighbors
544 for(u16 i=0; i<6; i++){
545 // Get the position of the neighbor node
546 v3s16 n2pos = p + dirs[i];
551 catch(InvalidPositionException &e)
555 if(n2.getLight(bank) > brightest_light || found_something == false){
556 brightest_light = n2.getLight(bank);
557 brightest_pos = n2pos;
558 found_something = true;
562 if(found_something == false)
563 throw InvalidPositionException();
565 return brightest_pos;
569 Propagates sunlight down from a node.
570 Starting point gets sunlight.
572 Returns the lowest y value of where the sunlight went.
574 s16 Map::propagateSunlight(v3s16 start,
575 core::map<v3s16, MapBlock*> & modified_blocks)
580 v3s16 pos(start.X, y, start.Z);
582 v3s16 blockpos = getNodeBlockPos(pos);
585 block = getBlockNoCreate(blockpos);
587 catch(InvalidPositionException &e)
592 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
593 MapNode n = block->getNode(relpos);
595 if(n.sunlight_propagates())
597 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
598 block->setNode(relpos, n);
600 modified_blocks.insert(blockpos, block);
609 void Map::updateLighting(enum LightBank bank,
610 core::map<v3s16, MapBlock*> & a_blocks,
611 core::map<v3s16, MapBlock*> & modified_blocks)
613 /*m_dout<<DTIME<<"Map::updateLighting(): "
614 <<a_blocks.getSize()<<" blocks... ";*/
618 u32 count_was = modified_blocks.size();
620 core::map<v3s16, bool> light_sources;
622 core::map<v3s16, u8> unlight_from;
624 core::map<v3s16, MapBlock*>::Iterator i;
625 i = a_blocks.getIterator();
626 for(; i.atEnd() == false; i++)
628 MapBlock *block = i.getNode()->getValue();
632 // Don't bother with dummy blocks.
636 v3s16 pos = block->getPos();
637 modified_blocks.insert(pos, block);
640 Clear all light from block
642 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
643 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
644 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
649 MapNode n = block->getNode(v3s16(x,y,z));
650 u8 oldlight = n.getLight(bank);
652 block->setNode(v3s16(x,y,z), n);
654 // Collect borders for unlighting
655 if(x==0 || x == MAP_BLOCKSIZE-1
656 || y==0 || y == MAP_BLOCKSIZE-1
657 || z==0 || z == MAP_BLOCKSIZE-1)
659 v3s16 p_map = p + v3s16(
662 MAP_BLOCKSIZE*pos.Z);
663 unlight_from.insert(p_map, oldlight);
666 catch(InvalidPositionException &e)
669 This would happen when dealing with a
673 dstream<<"updateLighting(): InvalidPositionException"
678 if(bank == LIGHTBANK_DAY)
680 bool bottom_valid = block->propagateSunlight(light_sources);
682 // If bottom is valid, we're done.
686 else if(bank == LIGHTBANK_NIGHT)
695 /*dstream<<"Bottom for sunlight-propagated block ("
696 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
699 // Else get the block below and loop to it
703 block = getBlockNoCreate(pos);
705 catch(InvalidPositionException &e)
714 //TimeTaker timer("unspreadLight");
715 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
720 u32 diff = modified_blocks.size() - count_was;
721 count_was = modified_blocks.size();
722 dstream<<"unspreadLight modified "<<diff<<std::endl;
725 // TODO: Spread light from propagated sunlight?
726 // Yes, add it to light_sources... somehow.
727 // It has to be added at somewhere above, in the loop.
729 // NOTE: This actually works fine without doing so
730 // - Find out why it works
733 //TimeTaker timer("spreadLight");
734 spreadLight(bank, light_sources, modified_blocks);
739 u32 diff = modified_blocks.size() - count_was;
740 count_was = modified_blocks.size();
741 dstream<<"spreadLight modified "<<diff<<std::endl;
744 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
747 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
748 core::map<v3s16, MapBlock*> & modified_blocks)
750 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
751 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
754 Update information about whether day and night light differ
756 for(core::map<v3s16, MapBlock*>::Iterator
757 i = modified_blocks.getIterator();
758 i.atEnd() == false; i++)
760 MapBlock *block = i.getNode()->getValue();
761 block->updateDayNightDiff();
766 This is called after changing a node from transparent to opaque.
767 The lighting value of the node should be left as-is after changing
768 other values. This sets the lighting value to 0.
770 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
771 core::map<v3s16, MapBlock*> &modified_blocks)*/
772 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
773 core::map<v3s16, MapBlock*> &modified_blocks)
776 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
777 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
780 From this node to nodes underneath:
781 If lighting is sunlight (1.0), unlight neighbours and
786 v3s16 toppos = p + v3s16(0,1,0);
787 v3s16 bottompos = p + v3s16(0,-1,0);
789 bool node_under_sunlight = true;
790 core::map<v3s16, bool> light_sources;
793 If there is a node at top and it doesn't have sunlight,
794 there has not been any sunlight going down.
796 Otherwise there probably is.
799 MapNode topnode = getNode(toppos);
801 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
802 node_under_sunlight = false;
804 catch(InvalidPositionException &e)
808 if(n.d != CONTENT_TORCH)
811 If there is grass below, change it to mud
814 MapNode bottomnode = getNode(bottompos);
816 if(bottomnode.d == CONTENT_GRASS
817 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
819 bottomnode.d = CONTENT_MUD;
820 setNode(bottompos, bottomnode);
823 catch(InvalidPositionException &e)
828 enum LightBank banks[] =
833 for(s32 i=0; i<2; i++)
835 enum LightBank bank = banks[i];
837 u8 lightwas = getNode(p).getLight(bank);
839 // Add the block of the added node to modified_blocks
840 v3s16 blockpos = getNodeBlockPos(p);
841 MapBlock * block = getBlockNoCreate(blockpos);
842 assert(block != NULL);
843 modified_blocks.insert(blockpos, block);
845 if(isValidPosition(p) == false)
848 // Unlight neighbours of node.
849 // This means setting light of all consequent dimmer nodes
851 // This also collects the nodes at the border which will spread
852 // light again into this.
853 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
861 If node is under sunlight, take all sunlighted nodes under
862 it and clear light from them and from where the light has
864 TODO: This could be optimized by mass-unlighting instead
867 if(node_under_sunlight)
871 //m_dout<<DTIME<<"y="<<y<<std::endl;
872 v3s16 n2pos(p.X, y, p.Z);
878 catch(InvalidPositionException &e)
883 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
885 //m_dout<<DTIME<<"doing"<<std::endl;
886 unLightNeighbors(LIGHTBANK_DAY,
887 n2pos, n2.getLight(LIGHTBANK_DAY),
888 light_sources, modified_blocks);
889 n2.setLight(LIGHTBANK_DAY, 0);
897 for(s32 i=0; i<2; i++)
899 enum LightBank bank = banks[i];
902 Spread light from all nodes that might be capable of doing so
903 TODO: Convert to spreadLight
905 spreadLight(bank, light_sources, modified_blocks);
909 Update information about whether day and night light differ
911 for(core::map<v3s16, MapBlock*>::Iterator
912 i = modified_blocks.getIterator();
913 i.atEnd() == false; i++)
915 MapBlock *block = i.getNode()->getValue();
916 block->updateDayNightDiff();
922 void Map::removeNodeAndUpdate(v3s16 p,
923 core::map<v3s16, MapBlock*> &modified_blocks)
926 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
927 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
929 bool node_under_sunlight = true;
931 v3s16 toppos = p + v3s16(0,1,0);
933 // Node will be replaced with this
934 u8 replace_material = CONTENT_AIR;
937 If there is a node at top and it doesn't have sunlight,
938 there will be no sunlight going down.
941 MapNode topnode = getNode(toppos);
943 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
944 node_under_sunlight = false;
946 catch(InvalidPositionException &e)
950 core::map<v3s16, bool> light_sources;
952 enum LightBank banks[] =
957 for(s32 i=0; i<2; i++)
959 enum LightBank bank = banks[i];
962 Unlight neighbors (in case the node is a light source)
964 unLightNeighbors(bank, p,
965 getNode(p).getLight(bank),
966 light_sources, modified_blocks);
971 This also clears the lighting.
975 n.d = replace_material;
978 for(s32 i=0; i<2; i++)
980 enum LightBank bank = banks[i];
985 spreadLight(bank, light_sources, modified_blocks);
988 // Add the block of the removed node to modified_blocks
989 v3s16 blockpos = getNodeBlockPos(p);
990 MapBlock * block = getBlockNoCreate(blockpos);
991 assert(block != NULL);
992 modified_blocks.insert(blockpos, block);
995 If the removed node was under sunlight, propagate the
996 sunlight down from it and then light all neighbors
997 of the propagated blocks.
999 if(node_under_sunlight)
1001 s16 ybottom = propagateSunlight(p, modified_blocks);
1002 /*m_dout<<DTIME<<"Node was under sunlight. "
1003 "Propagating sunlight";
1004 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1006 for(; y >= ybottom; y--)
1008 v3s16 p2(p.X, y, p.Z);
1009 /*m_dout<<DTIME<<"lighting neighbors of node ("
1010 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1012 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1017 // Set the lighting of this node to 0
1018 // TODO: Is this needed? Lighting is cleared up there already.
1020 MapNode n = getNode(p);
1021 n.setLight(LIGHTBANK_DAY, 0);
1024 catch(InvalidPositionException &e)
1030 for(s32 i=0; i<2; i++)
1032 enum LightBank bank = banks[i];
1034 // Get the brightest neighbour node and propagate light from it
1035 v3s16 n2p = getBrightestNeighbour(bank, p);
1037 MapNode n2 = getNode(n2p);
1038 lightNeighbors(bank, n2p, modified_blocks);
1040 catch(InvalidPositionException &e)
1046 Update information about whether day and night light differ
1048 for(core::map<v3s16, MapBlock*>::Iterator
1049 i = modified_blocks.getIterator();
1050 i.atEnd() == false; i++)
1052 MapBlock *block = i.getNode()->getValue();
1053 block->updateDayNightDiff();
1058 void Map::expireMeshes(bool only_daynight_diffed)
1060 TimeTaker timer("expireMeshes()");
1062 core::map<v2s16, MapSector*>::Iterator si;
1063 si = m_sectors.getIterator();
1064 for(; si.atEnd() == false; si++)
1066 MapSector *sector = si.getNode()->getValue();
1068 core::list< MapBlock * > sectorblocks;
1069 sector->getBlocks(sectorblocks);
1071 core::list< MapBlock * >::Iterator i;
1072 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1074 MapBlock *block = *i;
1076 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1082 JMutexAutoLock lock(block->mesh_mutex);
1083 if(block->mesh != NULL)
1085 /*block->mesh->drop();
1086 block->mesh = NULL;*/
1087 block->setMeshExpired(true);
1094 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1096 assert(mapType() == MAPTYPE_CLIENT);
1099 v3s16 p = blockpos + v3s16(0,0,0);
1100 MapBlock *b = getBlockNoCreate(p);
1101 b->updateMesh(daynight_ratio);
1103 catch(InvalidPositionException &e){}
1106 v3s16 p = blockpos + v3s16(-1,0,0);
1107 MapBlock *b = getBlockNoCreate(p);
1108 b->updateMesh(daynight_ratio);
1110 catch(InvalidPositionException &e){}
1112 v3s16 p = blockpos + v3s16(0,-1,0);
1113 MapBlock *b = getBlockNoCreate(p);
1114 b->updateMesh(daynight_ratio);
1116 catch(InvalidPositionException &e){}
1118 v3s16 p = blockpos + v3s16(0,0,-1);
1119 MapBlock *b = getBlockNoCreate(p);
1120 b->updateMesh(daynight_ratio);
1122 catch(InvalidPositionException &e){}
1125 v3s16 p = blockpos + v3s16(1,0,0);
1126 MapBlock *b = getBlockNoCreate(p);
1127 b->updateMesh(daynight_ratio);
1129 catch(InvalidPositionException &e){}
1131 v3s16 p = blockpos + v3s16(0,1,0);
1132 MapBlock *b = getBlockNoCreate(p);
1133 b->updateMesh(daynight_ratio);
1135 catch(InvalidPositionException &e){}
1137 v3s16 p = blockpos + v3s16(0,0,1);
1138 MapBlock *b = getBlockNoCreate(p);
1139 b->updateMesh(daynight_ratio);
1141 catch(InvalidPositionException &e){}*/
1146 bool Map::dayNightDiffed(v3s16 blockpos)
1149 v3s16 p = blockpos + v3s16(0,0,0);
1150 MapBlock *b = getBlockNoCreate(p);
1151 if(b->dayNightDiffed())
1154 catch(InvalidPositionException &e){}
1157 v3s16 p = blockpos + v3s16(-1,0,0);
1158 MapBlock *b = getBlockNoCreate(p);
1159 if(b->dayNightDiffed())
1162 catch(InvalidPositionException &e){}
1164 v3s16 p = blockpos + v3s16(0,-1,0);
1165 MapBlock *b = getBlockNoCreate(p);
1166 if(b->dayNightDiffed())
1169 catch(InvalidPositionException &e){}
1171 v3s16 p = blockpos + v3s16(0,0,-1);
1172 MapBlock *b = getBlockNoCreate(p);
1173 if(b->dayNightDiffed())
1176 catch(InvalidPositionException &e){}
1179 v3s16 p = blockpos + v3s16(1,0,0);
1180 MapBlock *b = getBlockNoCreate(p);
1181 if(b->dayNightDiffed())
1184 catch(InvalidPositionException &e){}
1186 v3s16 p = blockpos + v3s16(0,1,0);
1187 MapBlock *b = getBlockNoCreate(p);
1188 if(b->dayNightDiffed())
1191 catch(InvalidPositionException &e){}
1193 v3s16 p = blockpos + v3s16(0,0,1);
1194 MapBlock *b = getBlockNoCreate(p);
1195 if(b->dayNightDiffed())
1198 catch(InvalidPositionException &e){}
1204 Updates usage timers
1206 void Map::timerUpdate(float dtime)
1208 JMutexAutoLock lock(m_sector_mutex);
1210 core::map<v2s16, MapSector*>::Iterator si;
1212 si = m_sectors.getIterator();
1213 for(; si.atEnd() == false; si++)
1215 MapSector *sector = si.getNode()->getValue();
1216 sector->usage_timer += dtime;
1220 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1223 Wait for caches to be removed before continuing.
1225 This disables the existence of caches while locked
1227 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1229 core::list<v2s16>::Iterator j;
1230 for(j=list.begin(); j!=list.end(); j++)
1232 MapSector *sector = m_sectors[*j];
1235 sector->deleteBlocks();
1240 If sector is in sector cache, remove it from there
1242 if(m_sector_cache == sector)
1244 m_sector_cache = NULL;
1247 Remove from map and delete
1249 m_sectors.remove(*j);
1255 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1256 core::list<v3s16> *deleted_blocks)
1258 JMutexAutoLock lock(m_sector_mutex);
1260 core::list<v2s16> sector_deletion_queue;
1261 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1262 for(; i.atEnd() == false; i++)
1264 MapSector *sector = i.getNode()->getValue();
1266 Delete sector from memory if it hasn't been used in a long time
1268 if(sector->usage_timer > timeout)
1270 sector_deletion_queue.push_back(i.getNode()->getKey());
1272 if(deleted_blocks != NULL)
1274 // Collect positions of blocks of sector
1275 MapSector *sector = i.getNode()->getValue();
1276 core::list<MapBlock*> blocks;
1277 sector->getBlocks(blocks);
1278 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1279 i != blocks.end(); i++)
1281 deleted_blocks->push_back((*i)->getPos());
1286 deleteSectors(sector_deletion_queue, only_blocks);
1287 return sector_deletion_queue.getSize();
1290 void Map::PrintInfo(std::ostream &out)
1299 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1303 m_savedir = savedir;
1304 m_map_saving_enabled = false;
1308 // If directory exists, check contents and load if possible
1309 if(fs::PathExists(m_savedir))
1311 // If directory is empty, it is safe to save into it.
1312 if(fs::GetDirListing(m_savedir).size() == 0)
1314 dstream<<DTIME<<"Server: Empty save directory is valid."
1316 m_map_saving_enabled = true;
1320 // Load master heightmap
1321 loadMasterHeightmap();
1323 // Load sector (0,0) and throw and exception on fail
1324 if(loadSectorFull(v2s16(0,0)) == false)
1325 throw LoadError("Failed to load sector (0,0)");
1327 dstream<<DTIME<<"Server: Successfully loaded master "
1328 "heightmap and sector (0,0) from "<<savedir<<
1329 ", assuming valid save directory."
1332 m_map_saving_enabled = true;
1333 // Map loaded, not creating new one
1337 // If directory doesn't exist, it is safe to save to it
1339 m_map_saving_enabled = true;
1342 catch(std::exception &e)
1344 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1345 <<", exception: "<<e.what()<<std::endl;
1346 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1347 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1350 dstream<<DTIME<<"Initializing new map."<<std::endl;
1352 // Create master heightmap
1353 ValueGenerator *maxgen =
1354 ValueGenerator::deSerialize(hmp.randmax);
1355 ValueGenerator *factorgen =
1356 ValueGenerator::deSerialize(hmp.randfactor);
1357 ValueGenerator *basegen =
1358 ValueGenerator::deSerialize(hmp.base);
1359 m_heightmap = new UnlimitedHeightmap
1360 (hmp.blocksize, maxgen, factorgen, basegen);
1362 // Set map parameters
1365 // Create zero sector
1366 emergeSector(v2s16(0,0));
1368 // Initially write whole map
1372 ServerMap::~ServerMap()
1376 if(m_map_saving_enabled)
1379 // Save only changed parts
1381 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1385 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1388 catch(std::exception &e)
1390 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1391 <<", exception: "<<e.what()<<std::endl;
1394 if(m_heightmap != NULL)
1398 MapSector * ServerMap::emergeSector(v2s16 p2d)
1400 DSTACK("%s: p2d=(%d,%d)",
1403 // Check that it doesn't exist already
1405 return getSectorNoGenerate(p2d);
1407 catch(InvalidPositionException &e)
1412 Try to load the sector from disk.
1414 if(loadSectorFull(p2d) == true)
1416 return getSectorNoGenerate(p2d);
1420 If there is no master heightmap, throw.
1422 if(m_heightmap == NULL)
1424 throw InvalidPositionException("emergeSector(): no heightmap");
1428 Do not generate over-limit
1430 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1431 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1432 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1433 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1434 throw InvalidPositionException("emergeSector(): pos. over limit");
1437 Generate sector and heightmaps
1440 // Number of heightmaps in sector in each direction
1441 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1443 // Heightmap side width
1444 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1446 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1448 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1449 " heightmaps and objects"<<std::endl;*/
1451 // Loop through sub-heightmaps
1452 for(s16 y=0; y<hm_split; y++)
1453 for(s16 x=0; x<hm_split; x++)
1455 v2s16 p_in_sector = v2s16(x,y);
1456 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1458 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1459 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1460 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1461 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1464 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1465 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1468 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1470 sector->setHeightmap(p_in_sector, hm);
1472 //TODO: Make these values configurable
1473 //hm->generateContinued(0.0, 0.0, corners);
1474 hm->generateContinued(0.5, 0.2, corners);
1475 //hm->generateContinued(1.0, 0.2, corners);
1476 //hm->generateContinued(2.0, 0.2, corners);
1486 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1487 sector->setObjects(objects);
1489 v2s16 mhm_p = p2d * hm_split;
1491 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1492 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1493 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1494 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1497 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1498 float avgslope = 0.0;
1499 avgslope += fabs(avgheight - corners[0]);
1500 avgslope += fabs(avgheight - corners[1]);
1501 avgslope += fabs(avgheight - corners[2]);
1502 avgslope += fabs(avgheight - corners[3]);
1504 avgslope /= MAP_BLOCKSIZE;
1505 //dstream<<"avgslope="<<avgslope<<std::endl;
1507 float pitness = 0.0;
1509 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1512 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1515 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1518 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1522 pitness /= MAP_BLOCKSIZE;
1523 //dstream<<"pitness="<<pitness<<std::endl;
1526 Plant some trees if there is not much slope
1529 // Avgslope is the derivative of a hill
1530 float t = avgslope * avgslope;
1531 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1534 tree_max = a / (t/0.03);
1537 u32 count = (myrand()%(tree_max+1));
1538 //u32 count = tree_max;
1539 for(u32 i=0; i<count; i++)
1541 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
1542 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
1543 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1546 objects->insert(v3s16(x, y, z),
1547 SECTOR_OBJECT_TREE_1);
1551 Plant some bushes if sector is pit-like
1554 // Pitness usually goes at around -0.5...0.5
1556 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1558 bush_max = (pitness*a*4);
1561 u32 count = (myrand()%(bush_max+1));
1562 for(u32 i=0; i<count; i++)
1564 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
1565 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
1566 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1569 objects->insert(v3s16(x, y, z),
1570 SECTOR_OBJECT_BUSH_1);
1574 Add ravine (randomly)
1576 if(m_params.ravines_amount != 0)
1578 if(myrand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1581 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1582 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1585 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1586 objects->insert(v3s16(x, y, z),
1587 SECTOR_OBJECT_RAVINE);
1594 JMutexAutoLock lock(m_sector_mutex);
1595 m_sectors.insert(p2d, sector);
1600 MapBlock * ServerMap::emergeBlock(
1602 bool only_from_disk,
1603 core::map<v3s16, MapBlock*> &changed_blocks,
1604 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1607 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1609 p.X, p.Y, p.Z, only_from_disk);
1611 /*dstream<<"ServerMap::emergeBlock(): "
1612 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1613 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1614 v2s16 p2d(p.X, p.Z);
1617 This will create or load a sector if not found in memory.
1618 If block exists on disk, it will be loaded.
1620 NOTE: On old save formats, this will be slow, as it generates
1621 lighting on blocks for them.
1623 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1624 assert(sector->getId() == MAPSECTOR_SERVER);
1626 // Try to get a block from the sector
1627 MapBlock *block = NULL;
1628 bool not_on_disk = false;
1630 block = sector->getBlockNoCreate(block_y);
1631 if(block->isDummy() == true)
1636 catch(InvalidPositionException &e)
1642 If block was not found on disk and not going to generate a
1643 new one, make sure there is a dummy block in place.
1645 if(not_on_disk && only_from_disk)
1649 // Create dummy block
1650 block = new MapBlock(this, p, true);
1652 // Add block to sector
1653 sector->insertBlock(block);
1659 //dstream<<"Not found on disk, generating."<<std::endl;
1660 //TimeTaker("emergeBlock()", g_irrlicht);
1663 Do not generate over-limit
1665 if(blockpos_over_limit(p))
1666 throw InvalidPositionException("emergeBlock(): pos. over limit");
1671 Go on generating the block.
1673 TODO: If a dungeon gets generated so that it's side gets
1674 revealed to the outside air, the lighting should be
1679 If block doesn't exist, create one.
1680 If it exists, it is a dummy. In that case unDummify() it.
1684 block = sector->createBlankBlockNoInsert(block_y);
1688 // Remove the block so that nobody can get a half-generated one.
1689 sector->removeBlock(block);
1690 // Allocate the block to be a proper one.
1696 Initialize dungeon making by creating a random table
1698 const s32 ued_max = 5;
1699 const s32 ued_min = 3;
1700 bool underground_emptiness[ued_max*ued_max*ued_max];
1701 s32 ued = (myrand()%(ued_max-ued_min+1))+1;
1702 //s32 ued = ued_max;
1703 for(s32 i=0; i<ued*ued*ued; i++)
1705 underground_emptiness[i] = ((myrand() % 5) == 0);
1709 This is a messy hack to sort the emptiness a bit
1711 // Iterator through a few times
1712 for(s32 j=0; j<2; j++)
1713 for(s32 y0=0; y0<ued; y0++)
1714 for(s32 z0=0; z0<ued; z0++)
1715 for(s32 x0=0; x0<ued; x0++)
1718 bool &e0 = underground_emptiness[
1719 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1720 +ued*(y0*ued/MAP_BLOCKSIZE)
1721 +(x0*ued/MAP_BLOCKSIZE)];
1724 v3s16(0,0,1), // back
1725 v3s16(1,0,0), // right
1726 v3s16(0,0,-1), // front
1727 v3s16(-1,0,0), // left
1728 /*v3s16(0,1,0), // top
1729 v3s16(0,-1,0), // bottom*/
1732 for(s32 i=0; i<4; i++)
1734 v3s16 p1 = p0 + dirs[i];
1735 if(isInArea(p1, ued) == false)
1737 bool &e1 = underground_emptiness[
1738 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1739 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1740 +(p1.X*ued/MAP_BLOCKSIZE)];
1745 v3s16(0,1,0), // top
1746 v3s16(0,-1,0), // bottom
1747 /*v3s16(0,0,1), // back
1748 v3s16(1,0,0), // right
1749 v3s16(0,0,-1), // front
1750 v3s16(-1,0,0), // left*/
1752 for(s32 i=0; i<2; i++)
1754 v3s16 p2 = p1 + dirs[i];
1757 if(isInArea(p2, ued) == false)
1759 bool &e2 = underground_emptiness[
1760 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1761 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1762 +(p2.X*ued/MAP_BLOCKSIZE)];
1778 Create dungeon making table
1780 const s32 ued = MAP_BLOCKSIZE;
1781 bool underground_emptiness[ued*ued*ued];
1782 for(s32 i=0; i<ued*ued*ued; i++)
1784 underground_emptiness[i] = 0;
1786 // Generate dungeons
1789 Initialize orp and ors. Try to find if some neighboring
1790 MapBlock has a tunnel ended in its side
1794 (float)(myrand()%ued)+0.5,
1795 (float)(myrand()%ued)+0.5,
1796 (float)(myrand()%ued)+0.5
1803 for(s16 y=0; y<ued; y++)
1804 for(s16 x=0; x<ued; x++)
1806 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1807 if(getNode(ap).d == CONTENT_AIR)
1809 orp = v3f(x+1,y+1,0);
1810 goto continue_generating;
1814 catch(InvalidPositionException &e){}
1820 for(s16 y=0; y<ued; y++)
1821 for(s16 x=0; x<ued; x++)
1823 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1824 if(getNode(ap).d == CONTENT_AIR)
1826 orp = v3f(x+1,y+1,ued-1);
1827 goto continue_generating;
1831 catch(InvalidPositionException &e){}
1837 for(s16 y=0; y<ued; y++)
1838 for(s16 z=0; z<ued; z++)
1840 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1841 if(getNode(ap).d == CONTENT_AIR)
1843 orp = v3f(0,y+1,z+1);
1844 goto continue_generating;
1848 catch(InvalidPositionException &e){}
1854 for(s16 y=0; y<ued; y++)
1855 for(s16 z=0; z<ued; z++)
1857 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1858 if(getNode(ap).d == CONTENT_AIR)
1860 orp = v3f(ued-1,y+1,z+1);
1861 goto continue_generating;
1865 catch(InvalidPositionException &e){}
1867 continue_generating:
1870 Generate some tunnel starting from orp and ors
1872 for(u16 i=0; i<3; i++)
1875 (float)(myrand()%ued)+0.5,
1876 (float)(myrand()%ued)+0.5,
1877 (float)(myrand()%ued)+0.5
1881 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
1885 for(float f=0; f<1.0; f+=0.04)
1887 v3f fp = orp + vec * f;
1888 v3s16 cp(fp.X, fp.Y, fp.Z);
1890 s16 d1 = d0 + rs - 1;
1891 for(s16 z0=d0; z0<=d1; z0++)
1893 s16 si = rs - abs(z0);
1894 for(s16 x0=-si; x0<=si-1; x0++)
1896 s16 si2 = rs - abs(x0);
1897 for(s16 y0=-si2+1; y0<=si2-1; y0++)
1903 if(isInArea(p, ued) == false)
1905 underground_emptiness[ued*ued*z + ued*y + x] = 1;
1915 // This is the basic material of what the visible flat ground
1917 u8 material = CONTENT_GRASS;
1919 u8 water_material = CONTENT_WATER;
1920 if(g_settings.getBool("endless_water"))
1921 water_material = CONTENT_OCEAN;
1923 s32 lowest_ground_y = 32767;
1924 s32 highest_ground_y = -32768;
1927 //sector->printHeightmaps();
1929 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1930 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1932 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1934 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1935 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1936 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1938 dstream<<"WARNING: Surface height not found in sector "
1939 "for block that is being emerged"<<std::endl;
1943 s16 surface_y = surface_y_f;
1944 //avg_ground_y += surface_y;
1945 if(surface_y < lowest_ground_y)
1946 lowest_ground_y = surface_y;
1947 if(surface_y > highest_ground_y)
1948 highest_ground_y = surface_y;
1950 s32 surface_depth = 0;
1952 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1954 //float min_slope = 0.45;
1955 //float max_slope = 0.85;
1956 float min_slope = 0.60;
1957 float max_slope = 1.20;
1958 float min_slope_depth = 5.0;
1959 float max_slope_depth = 0;
1960 if(slope < min_slope)
1961 surface_depth = min_slope_depth;
1962 else if(slope > max_slope)
1963 surface_depth = max_slope_depth;
1965 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1967 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1969 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1974 NOTE: If there are some man-made structures above the
1975 newly created block, they won't be taken into account.
1977 if(real_y > surface_y)
1978 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1984 if(real_y <= surface_y - surface_depth)
1987 if(underground_emptiness[
1988 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1989 +ued*(y0*ued/MAP_BLOCKSIZE)
1990 +(x0*ued/MAP_BLOCKSIZE)])
1996 n.d = CONTENT_STONE;
1999 // If node is at or under heightmap y
2000 else if(real_y <= surface_y)
2002 // If under water level, it's mud
2003 if(real_y < WATER_LEVEL)
2005 // Only the topmost node is grass
2006 else if(real_y <= surface_y - 1)
2008 // Else it's the main material
2012 // If node is over heightmap y
2014 // If under water level, it's water
2015 if(real_y < WATER_LEVEL)
2017 n.d = water_material;
2018 n.setLight(LIGHTBANK_DAY,
2019 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2025 block->setNode(v3s16(x0,y0,z0), n);
2030 Calculate is_underground
2032 // Probably underground if the highest part of block is under lowest
2034 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
2035 block->setIsUnderground(is_underground);
2038 Force lighting update if some part of block is underground
2039 This is needed because of caves.
2042 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
2043 if(some_part_underground)
2044 //if(is_underground)
2046 lighting_invalidated_blocks[block->getPos()] = block;
2053 if(some_part_underground)
2055 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2060 for(s16 i=0; i<underground_level*1; i++)
2065 (myrand()%(MAP_BLOCKSIZE-2))+1,
2066 (myrand()%(MAP_BLOCKSIZE-2))+1,
2067 (myrand()%(MAP_BLOCKSIZE-2))+1
2073 //if(is_ground_content(block->getNode(cp).d))
2074 if(block->getNode(cp).d == CONTENT_STONE)
2076 block->setNode(cp, n);
2078 for(u16 i=0; i<26; i++)
2080 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2081 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2083 block->setNode(cp+g_26dirs[i], n);
2091 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2092 u16 coal_rareness = 60 / coal_amount;
2093 if(coal_rareness == 0)
2095 if(myrand()%coal_rareness == 0)
2097 u16 a = myrand() % 16;
2098 u16 amount = coal_amount * a*a*a / 1000;
2099 for(s16 i=0; i<amount; i++)
2102 (myrand()%(MAP_BLOCKSIZE-2))+1,
2103 (myrand()%(MAP_BLOCKSIZE-2))+1,
2104 (myrand()%(MAP_BLOCKSIZE-2))+1
2108 n.d = CONTENT_COALSTONE;
2110 //dstream<<"Adding coalstone"<<std::endl;
2112 //if(is_ground_content(block->getNode(cp).d))
2113 if(block->getNode(cp).d == CONTENT_STONE)
2115 block->setNode(cp, n);
2117 for(u16 i=0; i<26; i++)
2119 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2120 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2122 block->setNode(cp+g_26dirs[i], n);
2129 Create a few rats in empty blocks underground
2133 //for(u16 i=0; i<2; i++)
2136 (myrand()%(MAP_BLOCKSIZE-2))+1,
2137 (myrand()%(MAP_BLOCKSIZE-2))+1,
2138 (myrand()%(MAP_BLOCKSIZE-2))+1
2141 // Check that the place is empty
2142 //if(!is_ground_content(block->getNode(cp).d))
2145 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2146 block->addObject(obj);
2152 Add block to sector.
2154 sector->insertBlock(block);
2160 // An y-wise container of changed blocks
2161 core::map<s16, MapBlock*> changed_blocks_sector;
2164 Check if any sector's objects can be placed now.
2167 core::map<v3s16, u8> *objects = sector->getObjects();
2168 core::list<v3s16> objects_to_remove;
2169 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2170 i.atEnd() == false; i++)
2172 v3s16 p = i.getNode()->getKey();
2174 u8 d = i.getNode()->getValue();
2176 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2181 if(d == SECTOR_OBJECT_TEST)
2183 if(sector->isValidArea(p + v3s16(0,0,0),
2184 p + v3s16(0,0,0), &changed_blocks_sector))
2187 n.d = CONTENT_TORCH;
2188 sector->setNode(p, n);
2189 objects_to_remove.push_back(p);
2192 else if(d == SECTOR_OBJECT_TREE_1)
2194 v3s16 p_min = p + v3s16(-1,0,-1);
2195 v3s16 p_max = p + v3s16(1,4,1);
2196 if(sector->isValidArea(p_min, p_max,
2197 &changed_blocks_sector))
2201 sector->setNode(p+v3s16(0,0,0), n);
2202 sector->setNode(p+v3s16(0,1,0), n);
2203 sector->setNode(p+v3s16(0,2,0), n);
2204 sector->setNode(p+v3s16(0,3,0), n);
2206 n.d = CONTENT_LEAVES;
2208 sector->setNode(p+v3s16(0,4,0), n);
2210 sector->setNode(p+v3s16(-1,4,0), n);
2211 sector->setNode(p+v3s16(1,4,0), n);
2212 sector->setNode(p+v3s16(0,4,-1), n);
2213 sector->setNode(p+v3s16(0,4,1), n);
2214 sector->setNode(p+v3s16(1,4,1), n);
2215 sector->setNode(p+v3s16(-1,4,1), n);
2216 sector->setNode(p+v3s16(-1,4,-1), n);
2217 sector->setNode(p+v3s16(1,4,-1), n);
2219 sector->setNode(p+v3s16(-1,3,0), n);
2220 sector->setNode(p+v3s16(1,3,0), n);
2221 sector->setNode(p+v3s16(0,3,-1), n);
2222 sector->setNode(p+v3s16(0,3,1), n);
2223 sector->setNode(p+v3s16(1,3,1), n);
2224 sector->setNode(p+v3s16(-1,3,1), n);
2225 sector->setNode(p+v3s16(-1,3,-1), n);
2226 sector->setNode(p+v3s16(1,3,-1), n);
2228 objects_to_remove.push_back(p);
2230 // Lighting has to be recalculated for this one.
2231 sector->getBlocksInArea(p_min, p_max,
2232 lighting_invalidated_blocks);
2235 else if(d == SECTOR_OBJECT_BUSH_1)
2237 if(sector->isValidArea(p + v3s16(0,0,0),
2238 p + v3s16(0,0,0), &changed_blocks_sector))
2241 n.d = CONTENT_LEAVES;
2242 sector->setNode(p+v3s16(0,0,0), n);
2244 objects_to_remove.push_back(p);
2247 else if(d == SECTOR_OBJECT_RAVINE)
2250 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2251 v3s16 p_max = p + v3s16(6,6,6);
2252 if(sector->isValidArea(p_min, p_max,
2253 &changed_blocks_sector))
2256 n.d = CONTENT_STONE;
2259 s16 depth = maxdepth + (myrand()%10);
2261 s16 minz = -6 - (-2);
2263 for(s16 x=-6; x<=6; x++)
2265 z += -1 + (myrand()%3);
2270 for(s16 y=depth+(myrand()%2); y<=6; y++)
2272 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2275 v3s16 p2 = p + v3s16(x,y,z-2);
2276 if(is_ground_content(sector->getNode(p2).d)
2277 && !is_mineral(sector->getNode(p2).d))
2278 sector->setNode(p2, n);
2281 v3s16 p2 = p + v3s16(x,y,z-1);
2282 if(is_ground_content(sector->getNode(p2).d)
2283 && !is_mineral(sector->getNode(p2).d))
2284 sector->setNode(p2, n2);
2287 v3s16 p2 = p + v3s16(x,y,z+0);
2288 if(is_ground_content(sector->getNode(p2).d)
2289 && !is_mineral(sector->getNode(p2).d))
2290 sector->setNode(p2, n2);
2293 v3s16 p2 = p + v3s16(x,y,z+1);
2294 if(is_ground_content(sector->getNode(p2).d)
2295 && !is_mineral(sector->getNode(p2).d))
2296 sector->setNode(p2, n);
2299 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2300 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2304 objects_to_remove.push_back(p);
2306 // Lighting has to be recalculated for this one.
2307 sector->getBlocksInArea(p_min, p_max,
2308 lighting_invalidated_blocks);
2313 dstream<<"ServerMap::emergeBlock(): "
2314 "Invalid heightmap object"
2319 catch(InvalidPositionException &e)
2321 dstream<<"WARNING: "<<__FUNCTION_NAME
2322 <<": while inserting object "<<(int)d
2323 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2324 <<" InvalidPositionException.what()="
2325 <<e.what()<<std::endl;
2326 // This is not too fatal and seems to happen sometimes.
2331 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2332 i != objects_to_remove.end(); i++)
2334 objects->remove(*i);
2337 for(core::map<s16, MapBlock*>::Iterator
2338 i = changed_blocks_sector.getIterator();
2339 i.atEnd() == false; i++)
2341 MapBlock *block = i.getNode()->getValue();
2343 changed_blocks.insert(block->getPos(), block);
2349 void ServerMap::createDir(std::string path)
2351 if(fs::CreateDir(path) == false)
2353 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2354 <<"\""<<path<<"\""<<std::endl;
2355 throw BaseException("ServerMap failed to create directory");
2359 std::string ServerMap::getSectorSubDir(v2s16 pos)
2362 snprintf(cc, 9, "%.4x%.4x",
2363 (unsigned int)pos.X&0xffff,
2364 (unsigned int)pos.Y&0xffff);
2366 return std::string(cc);
2369 std::string ServerMap::getSectorDir(v2s16 pos)
2371 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2374 v2s16 ServerMap::getSectorPos(std::string dirname)
2376 if(dirname.size() != 8)
2377 throw InvalidFilenameException("Invalid sector directory name");
2379 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2381 throw InvalidFilenameException("Invalid sector directory name");
2382 v2s16 pos((s16)x, (s16)y);
2386 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2388 v2s16 p2d = getSectorPos(sectordir);
2390 if(blockfile.size() != 4){
2391 throw InvalidFilenameException("Invalid block filename");
2394 int r = sscanf(blockfile.c_str(), "%4x", &y);
2396 throw InvalidFilenameException("Invalid block filename");
2397 return v3s16(p2d.X, y, p2d.Y);
2401 #define ENABLE_SECTOR_SAVING 1
2402 #define ENABLE_SECTOR_LOADING 1
2403 #define ENABLE_BLOCK_SAVING 1
2404 #define ENABLE_BLOCK_LOADING 1
2406 void ServerMap::save(bool only_changed)
2408 DSTACK(__FUNCTION_NAME);
2409 if(m_map_saving_enabled == false)
2411 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2415 if(only_changed == false)
2416 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2419 saveMasterHeightmap();
2421 u32 sector_meta_count = 0;
2422 u32 block_count = 0;
2425 JMutexAutoLock lock(m_sector_mutex);
2427 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2428 for(; i.atEnd() == false; i++)
2430 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2431 assert(sector->getId() == MAPSECTOR_SERVER);
2433 if(ENABLE_SECTOR_SAVING)
2435 if(sector->differs_from_disk || only_changed == false)
2437 saveSectorMeta(sector);
2438 sector_meta_count++;
2441 if(ENABLE_BLOCK_SAVING)
2443 core::list<MapBlock*> blocks;
2444 sector->getBlocks(blocks);
2445 core::list<MapBlock*>::Iterator j;
2446 for(j=blocks.begin(); j!=blocks.end(); j++)
2448 MapBlock *block = *j;
2449 if(block->getChangedFlag() || only_changed == false)
2461 Only print if something happened or saved whole map
2463 if(only_changed == false || sector_meta_count != 0
2464 || block_count != 0)
2466 dstream<<DTIME<<"ServerMap: Written: "
2467 <<sector_meta_count<<" sector metadata files, "
2468 <<block_count<<" block files"
2473 void ServerMap::loadAll()
2475 DSTACK(__FUNCTION_NAME);
2476 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2478 loadMasterHeightmap();
2480 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2482 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2484 JMutexAutoLock lock(m_sector_mutex);
2487 s32 printed_counter = -100000;
2488 s32 count = list.size();
2490 std::vector<fs::DirListNode>::iterator i;
2491 for(i=list.begin(); i!=list.end(); i++)
2493 if(counter > printed_counter + 10)
2495 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2496 printed_counter = counter;
2500 MapSector *sector = NULL;
2502 // We want directories
2506 sector = loadSectorMeta(i->name);
2508 catch(InvalidFilenameException &e)
2510 // This catches unknown crap in directory
2513 if(ENABLE_BLOCK_LOADING)
2515 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2516 (m_savedir+"/sectors/"+i->name);
2517 std::vector<fs::DirListNode>::iterator i2;
2518 for(i2=list2.begin(); i2!=list2.end(); i2++)
2524 loadBlock(i->name, i2->name, sector);
2526 catch(InvalidFilenameException &e)
2528 // This catches unknown crap in directory
2533 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2536 void ServerMap::saveMasterHeightmap()
2538 DSTACK(__FUNCTION_NAME);
2539 createDir(m_savedir);
2541 std::string fullpath = m_savedir + "/master_heightmap";
2542 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2543 if(o.good() == false)
2544 throw FileNotGoodException("Cannot open master heightmap");
2546 // Format used for writing
2547 u8 version = SER_FMT_VER_HIGHEST;
2550 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2552 [0] u8 serialization version
2553 [1] X master heightmap
2555 u32 fullsize = 1 + hmdata.getSize();
2556 SharedBuffer<u8> data(fullsize);
2559 memcpy(&data[1], *hmdata, hmdata.getSize());
2561 o.write((const char*)*data, fullsize);
2564 m_heightmap->serialize(o, version);
2567 void ServerMap::loadMasterHeightmap()
2569 DSTACK(__FUNCTION_NAME);
2570 std::string fullpath = m_savedir + "/master_heightmap";
2571 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2572 if(is.good() == false)
2573 throw FileNotGoodException("Cannot open master heightmap");
2575 if(m_heightmap != NULL)
2578 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2581 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2583 DSTACK(__FUNCTION_NAME);
2584 // Format used for writing
2585 u8 version = SER_FMT_VER_HIGHEST;
2587 v2s16 pos = sector->getPos();
2588 createDir(m_savedir);
2589 createDir(m_savedir+"/sectors");
2590 std::string dir = getSectorDir(pos);
2593 std::string fullpath = dir + "/heightmap";
2594 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2595 if(o.good() == false)
2596 throw FileNotGoodException("Cannot open master heightmap");
2598 sector->serialize(o, version);
2600 sector->differs_from_disk = false;
2603 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2605 DSTACK(__FUNCTION_NAME);
2607 v2s16 p2d = getSectorPos(dirname);
2608 std::string dir = m_savedir + "/sectors/" + dirname;
2610 std::string fullpath = dir + "/heightmap";
2611 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2612 if(is.good() == false)
2613 throw FileNotGoodException("Cannot open sector heightmap");
2615 ServerMapSector *sector = ServerMapSector::deSerialize
2616 (is, this, p2d, &m_hwrapper, m_sectors);
2618 sector->differs_from_disk = false;
2623 bool ServerMap::loadSectorFull(v2s16 p2d)
2625 DSTACK(__FUNCTION_NAME);
2626 std::string sectorsubdir = getSectorSubDir(p2d);
2628 MapSector *sector = NULL;
2630 JMutexAutoLock lock(m_sector_mutex);
2633 sector = loadSectorMeta(sectorsubdir);
2635 catch(InvalidFilenameException &e)
2639 catch(FileNotGoodException &e)
2643 catch(std::exception &e)
2648 if(ENABLE_BLOCK_LOADING)
2650 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2651 (m_savedir+"/sectors/"+sectorsubdir);
2652 std::vector<fs::DirListNode>::iterator i2;
2653 for(i2=list2.begin(); i2!=list2.end(); i2++)
2659 loadBlock(sectorsubdir, i2->name, sector);
2661 catch(InvalidFilenameException &e)
2663 // This catches unknown crap in directory
2671 bool ServerMap::deFlushSector(v2s16 p2d)
2673 DSTACK(__FUNCTION_NAME);
2674 // See if it already exists in memory
2676 MapSector *sector = getSectorNoGenerate(p2d);
2679 catch(InvalidPositionException &e)
2682 Try to load the sector from disk.
2684 if(loadSectorFull(p2d) == true)
2693 void ServerMap::saveBlock(MapBlock *block)
2695 DSTACK(__FUNCTION_NAME);
2697 Dummy blocks are not written
2699 if(block->isDummy())
2701 /*v3s16 p = block->getPos();
2702 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2703 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2707 // Format used for writing
2708 u8 version = SER_FMT_VER_HIGHEST;
2710 v3s16 p3d = block->getPos();
2711 v2s16 p2d(p3d.X, p3d.Z);
2712 createDir(m_savedir);
2713 createDir(m_savedir+"/sectors");
2714 std::string dir = getSectorDir(p2d);
2717 // Block file is map/sectors/xxxxxxxx/xxxx
2719 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2720 std::string fullpath = dir + "/" + cc;
2721 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2722 if(o.good() == false)
2723 throw FileNotGoodException("Cannot open block data");
2726 [0] u8 serialization version
2729 o.write((char*)&version, 1);
2731 block->serialize(o, version);
2734 Versions up from 9 have block objects.
2738 block->serializeObjects(o, version);
2741 // We just wrote it to the disk
2742 block->resetChangedFlag();
2745 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2747 DSTACK(__FUNCTION_NAME);
2751 // Block file is map/sectors/xxxxxxxx/xxxx
2752 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2753 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2754 if(is.good() == false)
2755 throw FileNotGoodException("Cannot open block file");
2757 v3s16 p3d = getBlockPos(sectordir, blockfile);
2758 v2s16 p2d(p3d.X, p3d.Z);
2760 assert(sector->getPos() == p2d);
2762 u8 version = SER_FMT_VER_INVALID;
2763 is.read((char*)&version, 1);
2765 /*u32 block_size = MapBlock::serializedLength(version);
2766 SharedBuffer<u8> data(block_size);
2767 is.read((char*)*data, block_size);*/
2769 // This will always return a sector because we're the server
2770 //MapSector *sector = emergeSector(p2d);
2772 MapBlock *block = NULL;
2773 bool created_new = false;
2775 block = sector->getBlockNoCreate(p3d.Y);
2777 catch(InvalidPositionException &e)
2779 block = sector->createBlankBlockNoInsert(p3d.Y);
2783 // deserialize block data
2784 block->deSerialize(is, version);
2787 Versions up from 9 have block objects.
2791 block->updateObjects(is, version, NULL, 0);
2795 sector->insertBlock(block);
2798 Convert old formats to new and save
2801 // Save old format blocks in new format
2802 if(version < SER_FMT_VER_HIGHEST)
2807 // We just loaded it from the disk, so it's up-to-date.
2808 block->resetChangedFlag();
2811 catch(SerializationError &e)
2813 dstream<<"WARNING: Invalid block data on disk "
2814 "(SerializationError). Ignoring."
2819 // Gets from master heightmap
2820 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2822 assert(m_heightmap != NULL);
2830 corners[0] = m_heightmap->getGroundHeight
2831 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2832 corners[1] = m_heightmap->getGroundHeight
2833 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2834 corners[2] = m_heightmap->getGroundHeight
2835 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2836 corners[3] = m_heightmap->getGroundHeight
2837 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2840 void ServerMap::PrintInfo(std::ostream &out)
2851 ClientMap::ClientMap(
2853 MapDrawControl &control,
2854 scene::ISceneNode* parent,
2855 scene::ISceneManager* mgr,
2859 scene::ISceneNode(parent, mgr, id),
2866 /*m_box = core::aabbox3d<f32>(0,0,0,
2867 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2868 /*m_box = core::aabbox3d<f32>(0,0,0,
2869 map->getSizeNodes().X * BS,
2870 map->getSizeNodes().Y * BS,
2871 map->getSizeNodes().Z * BS);*/
2872 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2873 BS*1000000,BS*1000000,BS*1000000);
2875 //setPosition(v3f(BS,BS,BS));
2878 ClientMap::~ClientMap()
2880 JMutexAutoLock lock(mesh_mutex);
2889 MapSector * ClientMap::emergeSector(v2s16 p2d)
2891 DSTACK(__FUNCTION_NAME);
2892 // Check that it doesn't exist already
2894 return getSectorNoGenerate(p2d);
2896 catch(InvalidPositionException &e)
2900 // Create a sector with no heightmaps
2901 ClientMapSector *sector = new ClientMapSector(this, p2d);
2904 JMutexAutoLock lock(m_sector_mutex);
2905 m_sectors.insert(p2d, sector);
2911 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2913 DSTACK(__FUNCTION_NAME);
2914 ClientMapSector *sector = NULL;
2916 JMutexAutoLock lock(m_sector_mutex);
2918 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2922 sector = (ClientMapSector*)n->getValue();
2923 assert(sector->getId() == MAPSECTOR_CLIENT);
2927 sector = new ClientMapSector(this, p2d);
2929 JMutexAutoLock lock(m_sector_mutex);
2930 m_sectors.insert(p2d, sector);
2934 sector->deSerialize(is);
2937 void ClientMap::OnRegisterSceneNode()
2941 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2942 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2945 ISceneNode::OnRegisterSceneNode();
2948 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2950 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2951 DSTACK(__FUNCTION_NAME);
2953 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2956 Get time for measuring timeout.
2958 Measuring time is very useful for long delays when the
2959 machine is swapping a lot.
2961 int time1 = time(0);
2963 u32 daynight_ratio = m_client->getDayNightRatio();
2965 m_camera_mutex.Lock();
2966 v3f camera_position = m_camera_position;
2967 v3f camera_direction = m_camera_direction;
2968 m_camera_mutex.Unlock();
2971 Get all blocks and draw all visible ones
2974 v3s16 cam_pos_nodes(
2975 camera_position.X / BS,
2976 camera_position.Y / BS,
2977 camera_position.Z / BS);
2979 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
2981 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2982 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2984 // Take a fair amount as we will be dropping more out later
2986 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2987 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2988 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2990 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2991 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2992 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2994 u32 vertex_count = 0;
2996 // For limiting number of mesh updates per frame
2997 u32 mesh_update_count = 0;
2999 u32 blocks_would_have_drawn = 0;
3000 u32 blocks_drawn = 0;
3002 //NOTE: The sectors map should be locked but we're not doing it
3003 // because it'd cause too much delays
3005 int timecheck_counter = 0;
3006 core::map<v2s16, MapSector*>::Iterator si;
3007 si = m_sectors.getIterator();
3008 for(; si.atEnd() == false; si++)
3011 timecheck_counter++;
3012 if(timecheck_counter > 50)
3014 int time2 = time(0);
3015 if(time2 > time1 + 4)
3017 dstream<<"ClientMap::renderMap(): "
3018 "Rendering takes ages, returning."
3025 MapSector *sector = si.getNode()->getValue();
3026 v2s16 sp = sector->getPos();
3028 if(m_control.range_all == false)
3030 if(sp.X < p_blocks_min.X
3031 || sp.X > p_blocks_max.X
3032 || sp.Y < p_blocks_min.Z
3033 || sp.Y > p_blocks_max.Z)
3037 core::list< MapBlock * > sectorblocks;
3038 sector->getBlocks(sectorblocks);
3044 core::list< MapBlock * >::Iterator i;
3045 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3047 MapBlock *block = *i;
3050 Compare block position to camera position, skip
3051 if not seen on display
3054 v3s16 blockpos_nodes = block->getPosRelative();
3056 // Block center position
3058 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3059 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3060 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3063 // Block position relative to camera
3064 v3f blockpos_relative = blockpos - camera_position;
3066 // Distance in camera direction (+=front, -=back)
3067 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3070 f32 d = blockpos_relative.getLength();
3072 if(m_control.range_all == false)
3074 // If block is far away, don't draw it
3075 if(d > m_control.wanted_range * BS)
3076 // This is nicer when fog is used
3077 //if((dforward+d)/2 > m_control.wanted_range * BS)
3081 // Maximum radius of a block
3082 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3084 // If block is (nearly) touching the camera, don't
3085 // bother validating further (that is, render it anyway)
3086 if(d > block_max_radius * 1.5)
3088 // Cosine of the angle between the camera direction
3089 // and the block direction (camera_direction is an unit vector)
3090 f32 cosangle = dforward / d;
3092 // Compensate for the size of the block
3093 // (as the block has to be shown even if it's a bit off FOV)
3094 // This is an estimate.
3095 cosangle += block_max_radius / dforward;
3097 // If block is not in the field of view, skip it
3098 //if(cosangle < cos(FOV_ANGLE/2))
3099 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3104 Draw the faces of the block
3107 bool mesh_expired = false;
3110 JMutexAutoLock lock(block->mesh_mutex);
3112 mesh_expired = block->getMeshExpired();
3114 // Mesh has not been expired and there is no mesh:
3115 // block has no content
3116 if(block->mesh == NULL && mesh_expired == false)
3120 f32 faraway = BS*50;
3121 //f32 faraway = m_control.wanted_range * BS;
3124 This has to be done with the mesh_mutex unlocked
3126 if(mesh_expired && mesh_update_count < 6
3127 && (d < faraway || mesh_update_count < 3))
3128 //if(mesh_expired && mesh_update_count < 4)
3130 mesh_update_count++;
3132 // Mesh has been expired: generate new mesh
3133 //block->updateMeshes(daynight_i);
3134 block->updateMesh(daynight_ratio);
3136 mesh_expired = false;
3140 Don't draw an expired mesh that is far away
3142 /*if(mesh_expired && d >= faraway)
3145 // Instead, delete it
3146 JMutexAutoLock lock(block->mesh_mutex);
3149 block->mesh->drop();
3152 // And continue to next block
3157 JMutexAutoLock lock(block->mesh_mutex);
3159 scene::SMesh *mesh = block->mesh;
3164 blocks_would_have_drawn++;
3165 if(blocks_drawn >= m_control.wanted_max_blocks
3166 && m_control.range_all == false
3167 && d > m_control.wanted_min_range * BS)
3171 u32 c = mesh->getMeshBufferCount();
3173 for(u32 i=0; i<c; i++)
3175 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3176 const video::SMaterial& material = buf->getMaterial();
3177 video::IMaterialRenderer* rnd =
3178 driver->getMaterialRenderer(material.MaterialType);
3179 bool transparent = (rnd && rnd->isTransparent());
3180 // Render transparent on transparent pass and likewise.
3181 if(transparent == is_transparent_pass)
3183 driver->setMaterial(buf->getMaterial());
3184 driver->drawMeshBuffer(buf);
3185 vertex_count += buf->getVertexCount();
3189 } // foreach sectorblocks
3192 m_control.blocks_drawn = blocks_drawn;
3193 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3195 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3196 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3199 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3202 Add it to all blocks touching it
3205 v3s16(0,0,0), // this
3206 v3s16(0,0,1), // back
3207 v3s16(0,1,0), // top
3208 v3s16(1,0,0), // right
3209 v3s16(0,0,-1), // front
3210 v3s16(0,-1,0), // bottom
3211 v3s16(-1,0,0), // left
3213 for(u16 i=0; i<7; i++)
3215 v3s16 p2 = p + dirs[i];
3216 // Block position of neighbor (or requested) node
3217 v3s16 blockpos = getNodeBlockPos(p2);
3218 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3219 if(blockref == NULL)
3221 // Relative position of requested node
3222 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3223 blockref->setTempMod(relpos, mod);
3225 return getNodeBlockPos(p);
3227 v3s16 ClientMap::clearTempMod(v3s16 p)
3230 v3s16(0,0,0), // this
3231 v3s16(0,0,1), // back
3232 v3s16(0,1,0), // top
3233 v3s16(1,0,0), // right
3234 v3s16(0,0,-1), // front
3235 v3s16(0,-1,0), // bottom
3236 v3s16(-1,0,0), // left
3238 for(u16 i=0; i<7; i++)
3240 v3s16 p2 = p + dirs[i];
3241 // Block position of neighbor (or requested) node
3242 v3s16 blockpos = getNodeBlockPos(p2);
3243 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3244 if(blockref == NULL)
3246 // Relative position of requested node
3247 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3248 blockref->clearTempMod(relpos);
3250 return getNodeBlockPos(p);
3253 void ClientMap::PrintInfo(std::ostream &out)
3264 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3269 MapVoxelManipulator::~MapVoxelManipulator()
3271 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3276 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3278 TimeTaker timer1("emerge", &emerge_time);
3280 // Units of these are MapBlocks
3281 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3282 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3284 VoxelArea block_area_nodes
3285 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3287 addArea(block_area_nodes);
3289 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3290 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3291 for(s32 x=p_min.X; x<=p_max.X; x++)
3294 core::map<v3s16, bool>::Node *n;
3295 n = m_loaded_blocks.find(p);
3299 bool block_data_inexistent = false;
3302 TimeTaker timer1("emerge load", &emerge_load_time);
3304 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3305 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3308 dstream<<std::endl;*/
3310 MapBlock *block = m_map->getBlockNoCreate(p);
3311 if(block->isDummy())
3312 block_data_inexistent = true;
3314 block->copyTo(*this);
3316 catch(InvalidPositionException &e)
3318 block_data_inexistent = true;
3321 if(block_data_inexistent)
3323 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3324 // Fill with VOXELFLAG_INEXISTENT
3325 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3326 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3328 s32 i = m_area.index(a.MinEdge.X,y,z);
3329 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3333 m_loaded_blocks.insert(p, true);
3336 //dstream<<"emerge done"<<std::endl;
3341 void MapVoxelManipulator::emerge(VoxelArea a)
3343 TimeTaker timer1("emerge", &emerge_time);
3345 v3s16 size = a.getExtent();
3347 VoxelArea padded = a;
3348 padded.pad(m_area.getExtent() / 4);
3351 for(s16 z=0; z<size.Z; z++)
3352 for(s16 y=0; y<size.Y; y++)
3353 for(s16 x=0; x<size.X; x++)
3356 s32 i = m_area.index(a.MinEdge + p);
3357 // Don't touch nodes that have already been loaded
3358 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3362 TimeTaker timer1("emerge load", &emerge_load_time);
3363 MapNode n = m_map->getNode(a.MinEdge + p);
3367 catch(InvalidPositionException &e)
3369 m_flags[i] = VOXELFLAG_INEXISTENT;
3377 TODO: Add an option to only update eg. water and air nodes.
3378 This will make it interfere less with important stuff if
3381 void MapVoxelManipulator::blitBack
3382 (core::map<v3s16, MapBlock*> & modified_blocks)
3384 if(m_area.getExtent() == v3s16(0,0,0))
3387 //TimeTaker timer1("blitBack");
3390 Initialize block cache
3392 v3s16 blockpos_last;
3393 MapBlock *block = NULL;
3394 bool block_checked_in_modified = false;
3396 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3397 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3398 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3402 u8 f = m_flags[m_area.index(p)];
3403 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3406 MapNode &n = m_data[m_area.index(p)];
3408 v3s16 blockpos = getNodeBlockPos(p);
3413 if(block == NULL || blockpos != blockpos_last){
3414 block = m_map->getBlockNoCreate(blockpos);
3415 blockpos_last = blockpos;
3416 block_checked_in_modified = false;
3419 // Calculate relative position in block
3420 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3422 // Don't continue if nothing has changed here
3423 if(block->getNode(relpos) == n)
3426 //m_map->setNode(m_area.MinEdge + p, n);
3427 block->setNode(relpos, n);
3430 Make sure block is in modified_blocks
3432 if(block_checked_in_modified == false)
3434 modified_blocks[blockpos] = block;
3435 block_checked_in_modified = true;
3438 catch(InvalidPositionException &e)