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"
30 #define sleep_ms(x) Sleep(x)
33 #define sleep_ms(x) usleep(x*1000)
36 MapBlockPointerCache::MapBlockPointerCache(Map *map)
39 m_map->m_blockcachelock.cacheCreated();
41 m_from_cache_count = 0;
45 MapBlockPointerCache::~MapBlockPointerCache()
47 m_map->m_blockcachelock.cacheRemoved();
49 /*dstream<<"MapBlockPointerCache:"
50 <<" from_cache_count="<<m_from_cache_count
51 <<" from_map_count="<<m_from_map_count
55 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
57 core::map<v3s16, MapBlock*>::Node *n = NULL;
67 // Throws InvalidPositionException if not found
68 MapBlock *b = m_map->getBlockNoCreate(p);
77 Map::Map(std::ostream &dout):
79 m_camera_position(0,0,0),
80 m_camera_direction(0,0,1),
85 m_sector_mutex.Init();
86 m_camera_mutex.Init();
87 assert(m_sector_mutex.IsInitialized());
88 assert(m_camera_mutex.IsInitialized());
90 // Get this so that the player can stay on it at first
91 //getSector(v2s16(0,0));
99 /*updater.setRun(false);
100 while(updater.IsRunning())
106 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
107 for(; i.atEnd() == false; i++)
109 MapSector *sector = i.getNode()->getValue();
114 MapSector * Map::getSectorNoGenerate(v2s16 p)
116 JMutexAutoLock lock(m_sector_mutex);
118 if(m_sector_cache != NULL && p == m_sector_cache_p){
119 MapSector * sector = m_sector_cache;
120 // Reset inactivity timer
121 sector->usage_timer = 0.0;
125 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
126 // If sector doesn't exist, throw an exception
129 throw InvalidPositionException();
132 MapSector *sector = n->getValue();
134 // Cache the last result
135 m_sector_cache_p = p;
136 m_sector_cache = sector;
138 //MapSector * ref(sector);
140 // Reset inactivity timer
141 sector->usage_timer = 0.0;
145 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorNoGenerate(p2d);
150 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
155 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
159 v2s16 p2d(p3d.X, p3d.Z);
160 MapSector * sector = getSectorNoGenerate(p2d);
161 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
164 catch(InvalidPositionException &e)
170 f32 Map::getGroundHeight(v2s16 p, bool generate)
173 v2s16 sectorpos = getNodeSectorPos(p);
174 MapSector * sref = getSectorNoGenerate(sectorpos);
175 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
176 f32 y = sref->getGroundHeight(relpos);
179 catch(InvalidPositionException &e)
181 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
185 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
187 /*m_dout<<DTIME<<"Map::setGroundHeight(("
189 <<"), "<<y<<")"<<std::endl;*/
190 v2s16 sectorpos = getNodeSectorPos(p);
191 MapSector * sref = getSectorNoGenerate(sectorpos);
192 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
193 //sref->mutex.Lock();
194 sref->setGroundHeight(relpos, y);
195 //sref->mutex.Unlock();
198 bool Map::isNodeUnderground(v3s16 p)
200 v3s16 blockpos = getNodeBlockPos(p);
202 MapBlock * block = getBlockNoCreate(blockpos);
203 return block->getIsUnderground();
205 catch(InvalidPositionException &e)
212 Goes recursively through the neighbours of the node.
214 Alters only transparent nodes.
216 If the lighting of the neighbour is lower than the lighting of
217 the node was (before changing it to 0 at the step before), the
218 lighting of the neighbour is set to 0 and then the same stuff
219 repeats for the neighbour.
221 The ending nodes of the routine are stored in light_sources.
222 This is useful when a light is removed. In such case, this
223 routine can be called for the light node and then again for
224 light_sources to re-light the area without the removed light.
226 values of from_nodes are lighting values.
228 void Map::unspreadLight(enum LightBank bank,
229 core::map<v3s16, u8> & from_nodes,
230 core::map<v3s16, bool> & light_sources,
231 core::map<v3s16, MapBlock*> & modified_blocks)
234 v3s16(0,0,1), // back
236 v3s16(1,0,0), // right
237 v3s16(0,0,-1), // front
238 v3s16(0,-1,0), // bottom
239 v3s16(-1,0,0), // left
242 if(from_nodes.size() == 0)
245 u32 blockchangecount = 0;
247 core::map<v3s16, u8> unlighted_nodes;
248 core::map<v3s16, u8>::Iterator j;
249 j = from_nodes.getIterator();
252 Initialize block cache
255 MapBlock *block = NULL;
256 // Cache this a bit, too
257 bool block_checked_in_modified = false;
259 for(; j.atEnd() == false; j++)
261 v3s16 pos = j.getNode()->getKey();
262 v3s16 blockpos = getNodeBlockPos(pos);
264 // Only fetch a new block if the block position has changed
266 if(block == NULL || blockpos != blockpos_last){
267 block = getBlockNoCreate(blockpos);
268 blockpos_last = blockpos;
270 block_checked_in_modified = false;
274 catch(InvalidPositionException &e)
282 // Calculate relative position in block
283 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
285 // Get node straight from the block
286 MapNode n = block->getNode(relpos);
288 u8 oldlight = j.getNode()->getValue();
290 // Loop through 6 neighbors
291 for(u16 i=0; i<6; i++)
293 // Get the position of the neighbor node
294 v3s16 n2pos = pos + dirs[i];
296 // Get the block where the node is located
297 v3s16 blockpos = getNodeBlockPos(n2pos);
301 // Only fetch a new block if the block position has changed
303 if(block == NULL || blockpos != blockpos_last){
304 block = getBlockNoCreate(blockpos);
305 blockpos_last = blockpos;
307 block_checked_in_modified = false;
311 catch(InvalidPositionException &e)
316 // Calculate relative position in block
317 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
318 // Get node straight from the block
319 MapNode n2 = block->getNode(relpos);
321 bool changed = false;
323 //TODO: Optimize output by optimizing light_sources?
326 If the neighbor is dimmer than what was specified
327 as oldlight (the light of the previous node)
329 if(n2.getLight(bank) < oldlight)
332 And the neighbor is transparent and it has some light
334 if(n2.light_propagates() && n2.getLight(bank) != 0)
337 Set light to 0 and add to queue
340 u8 current_light = n2.getLight(bank);
341 n2.setLight(bank, 0);
342 block->setNode(relpos, n2);
344 unlighted_nodes.insert(n2pos, current_light);
348 Remove from light_sources if it is there
349 NOTE: This doesn't happen nearly at all
351 /*if(light_sources.find(n2pos))
353 std::cout<<"Removed from light_sources"<<std::endl;
354 light_sources.remove(n2pos);
359 if(light_sources.find(n2pos) != NULL)
360 light_sources.remove(n2pos);*/
363 light_sources.insert(n2pos, true);
366 // Add to modified_blocks
367 if(changed == true && block_checked_in_modified == false)
369 // If the block is not found in modified_blocks, add.
370 if(modified_blocks.find(blockpos) == NULL)
372 modified_blocks.insert(blockpos, block);
374 block_checked_in_modified = true;
377 catch(InvalidPositionException &e)
384 /*dstream<<"unspreadLight(): Changed block "
385 <<blockchangecount<<" times"
386 <<" for "<<from_nodes.size()<<" nodes"
389 if(unlighted_nodes.size() > 0)
390 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
394 A single-node wrapper of the above
396 void Map::unLightNeighbors(enum LightBank bank,
397 v3s16 pos, u8 lightwas,
398 core::map<v3s16, bool> & light_sources,
399 core::map<v3s16, MapBlock*> & modified_blocks)
401 core::map<v3s16, u8> from_nodes;
402 from_nodes.insert(pos, lightwas);
404 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
408 Lights neighbors of from_nodes, collects all them and then
411 void Map::spreadLight(enum LightBank bank,
412 core::map<v3s16, bool> & from_nodes,
413 core::map<v3s16, MapBlock*> & modified_blocks)
415 const v3s16 dirs[6] = {
416 v3s16(0,0,1), // back
418 v3s16(1,0,0), // right
419 v3s16(0,0,-1), // front
420 v3s16(0,-1,0), // bottom
421 v3s16(-1,0,0), // left
424 if(from_nodes.size() == 0)
427 u32 blockchangecount = 0;
429 core::map<v3s16, bool> lighted_nodes;
430 core::map<v3s16, bool>::Iterator j;
431 j = from_nodes.getIterator();
434 Initialize block cache
437 MapBlock *block = NULL;
438 // Cache this a bit, too
439 bool block_checked_in_modified = false;
441 for(; j.atEnd() == false; j++)
442 //for(; j != from_nodes.end(); j++)
444 v3s16 pos = j.getNode()->getKey();
446 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
447 v3s16 blockpos = getNodeBlockPos(pos);
449 // Only fetch a new block if the block position has changed
451 if(block == NULL || blockpos != blockpos_last){
452 block = getBlockNoCreate(blockpos);
453 blockpos_last = blockpos;
455 block_checked_in_modified = false;
459 catch(InvalidPositionException &e)
467 // Calculate relative position in block
468 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
470 // Get node straight from the block
471 MapNode n = block->getNode(relpos);
473 u8 oldlight = n.getLight(bank);
474 u8 newlight = diminish_light(oldlight);
476 // Loop through 6 neighbors
477 for(u16 i=0; i<6; i++){
478 // Get the position of the neighbor node
479 v3s16 n2pos = pos + dirs[i];
481 // Get the block where the node is located
482 v3s16 blockpos = getNodeBlockPos(n2pos);
486 // Only fetch a new block if the block position has changed
488 if(block == NULL || blockpos != blockpos_last){
489 block = getBlockNoCreate(blockpos);
490 blockpos_last = blockpos;
492 block_checked_in_modified = false;
496 catch(InvalidPositionException &e)
501 // Calculate relative position in block
502 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
503 // Get node straight from the block
504 MapNode n2 = block->getNode(relpos);
506 bool changed = false;
508 If the neighbor is brighter than the current node,
509 add to list (it will light up this node on its turn)
511 if(n2.getLight(bank) > undiminish_light(oldlight))
513 lighted_nodes.insert(n2pos, true);
514 //lighted_nodes.push_back(n2pos);
518 If the neighbor is dimmer than how much light this node
519 would spread on it, add to list
521 if(n2.getLight(bank) < newlight)
523 if(n2.light_propagates())
525 n2.setLight(bank, newlight);
526 block->setNode(relpos, n2);
527 lighted_nodes.insert(n2pos, true);
528 //lighted_nodes.push_back(n2pos);
533 // Add to modified_blocks
534 if(changed == true && block_checked_in_modified == false)
536 // If the block is not found in modified_blocks, add.
537 if(modified_blocks.find(blockpos) == NULL)
539 modified_blocks.insert(blockpos, block);
541 block_checked_in_modified = true;
544 catch(InvalidPositionException &e)
551 /*dstream<<"spreadLight(): Changed block "
552 <<blockchangecount<<" times"
553 <<" for "<<from_nodes.size()<<" nodes"
556 if(lighted_nodes.size() > 0)
557 spreadLight(bank, lighted_nodes, modified_blocks);
561 A single-node source variation of the above.
563 void Map::lightNeighbors(enum LightBank bank,
565 core::map<v3s16, MapBlock*> & modified_blocks)
567 core::map<v3s16, bool> from_nodes;
568 from_nodes.insert(pos, true);
569 spreadLight(bank, from_nodes, modified_blocks);
572 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
575 v3s16(0,0,1), // back
577 v3s16(1,0,0), // right
578 v3s16(0,0,-1), // front
579 v3s16(0,-1,0), // bottom
580 v3s16(-1,0,0), // left
583 u8 brightest_light = 0;
584 v3s16 brightest_pos(0,0,0);
585 bool found_something = false;
587 // Loop through 6 neighbors
588 for(u16 i=0; i<6; i++){
589 // Get the position of the neighbor node
590 v3s16 n2pos = p + dirs[i];
595 catch(InvalidPositionException &e)
599 if(n2.getLight(bank) > brightest_light || found_something == false){
600 brightest_light = n2.getLight(bank);
601 brightest_pos = n2pos;
602 found_something = true;
606 if(found_something == false)
607 throw InvalidPositionException();
609 return brightest_pos;
613 Propagates sunlight down from a node.
614 Starting point gets sunlight.
616 Returns the lowest y value of where the sunlight went.
618 s16 Map::propagateSunlight(v3s16 start,
619 core::map<v3s16, MapBlock*> & modified_blocks)
624 v3s16 pos(start.X, y, start.Z);
626 v3s16 blockpos = getNodeBlockPos(pos);
629 block = getBlockNoCreate(blockpos);
631 catch(InvalidPositionException &e)
636 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
637 MapNode n = block->getNode(relpos);
639 if(n.sunlight_propagates())
641 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
642 block->setNode(relpos, n);
644 modified_blocks.insert(blockpos, block);
653 void Map::updateLighting(enum LightBank bank,
654 core::map<v3s16, MapBlock*> & a_blocks,
655 core::map<v3s16, MapBlock*> & modified_blocks)
657 /*m_dout<<DTIME<<"Map::updateLighting(): "
658 <<a_blocks.getSize()<<" blocks... ";*/
662 u32 count_was = modified_blocks.size();
664 core::map<v3s16, bool> light_sources;
666 core::map<v3s16, u8> unlight_from;
668 core::map<v3s16, MapBlock*>::Iterator i;
669 i = a_blocks.getIterator();
670 for(; i.atEnd() == false; i++)
672 MapBlock *block = i.getNode()->getValue();
676 // Don't bother with dummy blocks.
680 v3s16 pos = block->getPos();
681 modified_blocks.insert(pos, block);
684 Clear all light from block
686 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
687 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
688 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
693 MapNode n = block->getNode(v3s16(x,y,z));
694 u8 oldlight = n.getLight(bank);
696 block->setNode(v3s16(x,y,z), n);
698 // Collect borders for unlighting
699 if(x==0 || x == MAP_BLOCKSIZE-1
700 || y==0 || y == MAP_BLOCKSIZE-1
701 || z==0 || z == MAP_BLOCKSIZE-1)
703 v3s16 p_map = p + v3s16(
706 MAP_BLOCKSIZE*pos.Z);
707 unlight_from.insert(p_map, oldlight);
710 catch(InvalidPositionException &e)
713 This would happen when dealing with a
717 dstream<<"updateLighting(): InvalidPositionException"
722 if(bank == LIGHTBANK_DAY)
724 bool bottom_valid = block->propagateSunlight(light_sources);
726 // If bottom is valid, we're done.
730 else if(bank == LIGHTBANK_NIGHT)
739 /*dstream<<"Bottom for sunlight-propagated block ("
740 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
743 // Else get the block below and loop to it
747 block = getBlockNoCreate(pos);
749 catch(InvalidPositionException &e)
758 //TimeTaker timer("unspreadLight");
759 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
764 u32 diff = modified_blocks.size() - count_was;
765 count_was = modified_blocks.size();
766 dstream<<"unspreadLight modified "<<diff<<std::endl;
769 // TODO: Spread light from propagated sunlight?
770 // Yes, add it to light_sources... somehow.
771 // It has to be added at somewhere above, in the loop.
773 // NOTE: This actually works fine without doing so
774 // - Find out why it works
777 //TimeTaker timer("spreadLight");
778 spreadLight(bank, light_sources, modified_blocks);
783 u32 diff = modified_blocks.size() - count_was;
784 count_was = modified_blocks.size();
785 dstream<<"spreadLight modified "<<diff<<std::endl;
788 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
791 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
792 core::map<v3s16, MapBlock*> & modified_blocks)
794 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
795 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
798 Update information about whether day and night light differ
800 for(core::map<v3s16, MapBlock*>::Iterator
801 i = modified_blocks.getIterator();
802 i.atEnd() == false; i++)
804 MapBlock *block = i.getNode()->getValue();
805 block->updateDayNightDiff();
810 This is called after changing a node from transparent to opaque.
811 The lighting value of the node should be left as-is after changing
812 other values. This sets the lighting value to 0.
814 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
815 core::map<v3s16, MapBlock*> &modified_blocks)*/
816 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
817 core::map<v3s16, MapBlock*> &modified_blocks)
820 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
821 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
824 From this node to nodes underneath:
825 If lighting is sunlight (1.0), unlight neighbours and
830 v3s16 toppos = p + v3s16(0,1,0);
831 v3s16 bottompos = p + v3s16(0,-1,0);
833 bool node_under_sunlight = true;
834 core::map<v3s16, bool> light_sources;
837 If there is a node at top and it doesn't have sunlight,
838 there has not been any sunlight going down.
840 Otherwise there probably is.
843 MapNode topnode = getNode(toppos);
845 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
846 node_under_sunlight = false;
848 catch(InvalidPositionException &e)
852 if(n.d != CONTENT_TORCH)
855 If there is grass below, change it to mud
858 MapNode bottomnode = getNode(bottompos);
860 if(bottomnode.d == CONTENT_GRASS
861 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
863 bottomnode.d = CONTENT_MUD;
864 setNode(bottompos, bottomnode);
867 catch(InvalidPositionException &e)
872 enum LightBank banks[] =
877 for(s32 i=0; i<2; i++)
879 enum LightBank bank = banks[i];
881 u8 lightwas = getNode(p).getLight(bank);
883 // Add the block of the added node to modified_blocks
884 v3s16 blockpos = getNodeBlockPos(p);
885 MapBlock * block = getBlockNoCreate(blockpos);
886 assert(block != NULL);
887 modified_blocks.insert(blockpos, block);
889 if(isValidPosition(p) == false)
892 // Unlight neighbours of node.
893 // This means setting light of all consequent dimmer nodes
895 // This also collects the nodes at the border which will spread
896 // light again into this.
897 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
905 If node is under sunlight, take all sunlighted nodes under
906 it and clear light from them and from where the light has
908 TODO: This could be optimized by mass-unlighting instead
911 if(node_under_sunlight)
915 //m_dout<<DTIME<<"y="<<y<<std::endl;
916 v3s16 n2pos(p.X, y, p.Z);
922 catch(InvalidPositionException &e)
927 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
929 //m_dout<<DTIME<<"doing"<<std::endl;
930 unLightNeighbors(LIGHTBANK_DAY,
931 n2pos, n2.getLight(LIGHTBANK_DAY),
932 light_sources, modified_blocks);
933 n2.setLight(LIGHTBANK_DAY, 0);
941 for(s32 i=0; i<2; i++)
943 enum LightBank bank = banks[i];
946 Spread light from all nodes that might be capable of doing so
947 TODO: Convert to spreadLight
949 spreadLight(bank, light_sources, modified_blocks);
953 Update information about whether day and night light differ
955 for(core::map<v3s16, MapBlock*>::Iterator
956 i = modified_blocks.getIterator();
957 i.atEnd() == false; i++)
959 MapBlock *block = i.getNode()->getValue();
960 block->updateDayNightDiff();
966 void Map::removeNodeAndUpdate(v3s16 p,
967 core::map<v3s16, MapBlock*> &modified_blocks)
970 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
971 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
973 bool node_under_sunlight = true;
975 v3s16 toppos = p + v3s16(0,1,0);
977 // Node will be replaced with this
978 u8 replace_material = CONTENT_AIR;
981 If there is a node at top and it doesn't have sunlight,
982 there will be no sunlight going down.
985 MapNode topnode = getNode(toppos);
987 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
988 node_under_sunlight = false;
990 catch(InvalidPositionException &e)
994 core::map<v3s16, bool> light_sources;
996 enum LightBank banks[] =
1001 for(s32 i=0; i<2; i++)
1003 enum LightBank bank = banks[i];
1006 Unlight neighbors (in case the node is a light source)
1008 unLightNeighbors(bank, p,
1009 getNode(p).getLight(bank),
1010 light_sources, modified_blocks);
1015 This also clears the lighting.
1019 n.d = replace_material;
1022 for(s32 i=0; i<2; i++)
1024 enum LightBank bank = banks[i];
1027 Recalculate lighting
1029 spreadLight(bank, light_sources, modified_blocks);
1032 // Add the block of the removed node to modified_blocks
1033 v3s16 blockpos = getNodeBlockPos(p);
1034 MapBlock * block = getBlockNoCreate(blockpos);
1035 assert(block != NULL);
1036 modified_blocks.insert(blockpos, block);
1039 If the removed node was under sunlight, propagate the
1040 sunlight down from it and then light all neighbors
1041 of the propagated blocks.
1043 if(node_under_sunlight)
1045 s16 ybottom = propagateSunlight(p, modified_blocks);
1046 /*m_dout<<DTIME<<"Node was under sunlight. "
1047 "Propagating sunlight";
1048 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1050 for(; y >= ybottom; y--)
1052 v3s16 p2(p.X, y, p.Z);
1053 /*m_dout<<DTIME<<"lighting neighbors of node ("
1054 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1056 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1061 // Set the lighting of this node to 0
1062 // TODO: Is this needed? Lighting is cleared up there already.
1064 MapNode n = getNode(p);
1065 n.setLight(LIGHTBANK_DAY, 0);
1068 catch(InvalidPositionException &e)
1074 for(s32 i=0; i<2; i++)
1076 enum LightBank bank = banks[i];
1078 // Get the brightest neighbour node and propagate light from it
1079 v3s16 n2p = getBrightestNeighbour(bank, p);
1081 MapNode n2 = getNode(n2p);
1082 lightNeighbors(bank, n2p, modified_blocks);
1084 catch(InvalidPositionException &e)
1090 Update information about whether day and night light differ
1092 for(core::map<v3s16, MapBlock*>::Iterator
1093 i = modified_blocks.getIterator();
1094 i.atEnd() == false; i++)
1096 MapBlock *block = i.getNode()->getValue();
1097 block->updateDayNightDiff();
1102 void Map::expireMeshes(bool only_daynight_diffed)
1104 TimeTaker timer("expireMeshes()");
1106 core::map<v2s16, MapSector*>::Iterator si;
1107 si = m_sectors.getIterator();
1108 for(; si.atEnd() == false; si++)
1110 MapSector *sector = si.getNode()->getValue();
1112 core::list< MapBlock * > sectorblocks;
1113 sector->getBlocks(sectorblocks);
1115 core::list< MapBlock * >::Iterator i;
1116 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1118 MapBlock *block = *i;
1120 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1126 JMutexAutoLock lock(block->mesh_mutex);
1127 if(block->mesh != NULL)
1129 /*block->mesh->drop();
1130 block->mesh = NULL;*/
1131 block->setMeshExpired(true);
1138 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1140 assert(mapType() == MAPTYPE_CLIENT);
1143 v3s16 p = blockpos + v3s16(0,0,0);
1144 MapBlock *b = getBlockNoCreate(p);
1145 b->updateMesh(daynight_ratio);
1147 catch(InvalidPositionException &e){}
1150 v3s16 p = blockpos + v3s16(-1,0,0);
1151 MapBlock *b = getBlockNoCreate(p);
1152 b->updateMesh(daynight_ratio);
1154 catch(InvalidPositionException &e){}
1156 v3s16 p = blockpos + v3s16(0,-1,0);
1157 MapBlock *b = getBlockNoCreate(p);
1158 b->updateMesh(daynight_ratio);
1160 catch(InvalidPositionException &e){}
1162 v3s16 p = blockpos + v3s16(0,0,-1);
1163 MapBlock *b = getBlockNoCreate(p);
1164 b->updateMesh(daynight_ratio);
1166 catch(InvalidPositionException &e){}
1169 v3s16 p = blockpos + v3s16(1,0,0);
1170 MapBlock *b = getBlockNoCreate(p);
1171 b->updateMesh(daynight_ratio);
1173 catch(InvalidPositionException &e){}
1175 v3s16 p = blockpos + v3s16(0,1,0);
1176 MapBlock *b = getBlockNoCreate(p);
1177 b->updateMesh(daynight_ratio);
1179 catch(InvalidPositionException &e){}
1181 v3s16 p = blockpos + v3s16(0,0,1);
1182 MapBlock *b = getBlockNoCreate(p);
1183 b->updateMesh(daynight_ratio);
1185 catch(InvalidPositionException &e){}*/
1190 bool Map::dayNightDiffed(v3s16 blockpos)
1193 v3s16 p = blockpos + v3s16(0,0,0);
1194 MapBlock *b = getBlockNoCreate(p);
1195 if(b->dayNightDiffed())
1198 catch(InvalidPositionException &e){}
1201 v3s16 p = blockpos + v3s16(-1,0,0);
1202 MapBlock *b = getBlockNoCreate(p);
1203 if(b->dayNightDiffed())
1206 catch(InvalidPositionException &e){}
1208 v3s16 p = blockpos + v3s16(0,-1,0);
1209 MapBlock *b = getBlockNoCreate(p);
1210 if(b->dayNightDiffed())
1213 catch(InvalidPositionException &e){}
1215 v3s16 p = blockpos + v3s16(0,0,-1);
1216 MapBlock *b = getBlockNoCreate(p);
1217 if(b->dayNightDiffed())
1220 catch(InvalidPositionException &e){}
1223 v3s16 p = blockpos + v3s16(1,0,0);
1224 MapBlock *b = getBlockNoCreate(p);
1225 if(b->dayNightDiffed())
1228 catch(InvalidPositionException &e){}
1230 v3s16 p = blockpos + v3s16(0,1,0);
1231 MapBlock *b = getBlockNoCreate(p);
1232 if(b->dayNightDiffed())
1235 catch(InvalidPositionException &e){}
1237 v3s16 p = blockpos + v3s16(0,0,1);
1238 MapBlock *b = getBlockNoCreate(p);
1239 if(b->dayNightDiffed())
1242 catch(InvalidPositionException &e){}
1248 Updates usage timers
1250 void Map::timerUpdate(float dtime)
1252 JMutexAutoLock lock(m_sector_mutex);
1254 core::map<v2s16, MapSector*>::Iterator si;
1256 si = m_sectors.getIterator();
1257 for(; si.atEnd() == false; si++)
1259 MapSector *sector = si.getNode()->getValue();
1260 sector->usage_timer += dtime;
1264 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1267 Wait for caches to be removed before continuing.
1269 This disables the existence of caches while locked
1271 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1273 core::list<v2s16>::Iterator j;
1274 for(j=list.begin(); j!=list.end(); j++)
1276 MapSector *sector = m_sectors[*j];
1279 sector->deleteBlocks();
1284 If sector is in sector cache, remove it from there
1286 if(m_sector_cache == sector)
1288 m_sector_cache = NULL;
1291 Remove from map and delete
1293 m_sectors.remove(*j);
1299 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1300 core::list<v3s16> *deleted_blocks)
1302 JMutexAutoLock lock(m_sector_mutex);
1304 core::list<v2s16> sector_deletion_queue;
1305 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1306 for(; i.atEnd() == false; i++)
1308 MapSector *sector = i.getNode()->getValue();
1310 Delete sector from memory if it hasn't been used in a long time
1312 if(sector->usage_timer > timeout)
1314 sector_deletion_queue.push_back(i.getNode()->getKey());
1316 if(deleted_blocks != NULL)
1318 // Collect positions of blocks of sector
1319 MapSector *sector = i.getNode()->getValue();
1320 core::list<MapBlock*> blocks;
1321 sector->getBlocks(blocks);
1322 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1323 i != blocks.end(); i++)
1325 deleted_blocks->push_back((*i)->getPos());
1330 deleteSectors(sector_deletion_queue, only_blocks);
1331 return sector_deletion_queue.getSize();
1334 void Map::PrintInfo(std::ostream &out)
1343 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1347 m_savedir = savedir;
1348 m_map_saving_enabled = false;
1352 // If directory exists, check contents and load if possible
1353 if(fs::PathExists(m_savedir))
1355 // If directory is empty, it is safe to save into it.
1356 if(fs::GetDirListing(m_savedir).size() == 0)
1358 dstream<<DTIME<<"Server: Empty save directory is valid."
1360 m_map_saving_enabled = true;
1364 // Load master heightmap
1365 loadMasterHeightmap();
1367 // Load sector (0,0) and throw and exception on fail
1368 if(loadSectorFull(v2s16(0,0)) == false)
1369 throw LoadError("Failed to load sector (0,0)");
1371 dstream<<DTIME<<"Server: Successfully loaded master "
1372 "heightmap and sector (0,0) from "<<savedir<<
1373 ", assuming valid save directory."
1376 m_map_saving_enabled = true;
1377 // Map loaded, not creating new one
1381 // If directory doesn't exist, it is safe to save to it
1383 m_map_saving_enabled = true;
1386 catch(std::exception &e)
1388 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1389 <<", exception: "<<e.what()<<std::endl;
1390 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1391 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1394 dstream<<DTIME<<"Initializing new map."<<std::endl;
1396 // Create master heightmap
1397 ValueGenerator *maxgen =
1398 ValueGenerator::deSerialize(hmp.randmax);
1399 ValueGenerator *factorgen =
1400 ValueGenerator::deSerialize(hmp.randfactor);
1401 ValueGenerator *basegen =
1402 ValueGenerator::deSerialize(hmp.base);
1403 m_heightmap = new UnlimitedHeightmap
1404 (hmp.blocksize, maxgen, factorgen, basegen);
1406 // Set map parameters
1409 // Create zero sector
1410 emergeSector(v2s16(0,0));
1412 // Initially write whole map
1416 ServerMap::~ServerMap()
1420 if(m_map_saving_enabled)
1423 // Save only changed parts
1425 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1429 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1432 catch(std::exception &e)
1434 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1435 <<", exception: "<<e.what()<<std::endl;
1438 if(m_heightmap != NULL)
1442 MapSector * ServerMap::emergeSector(v2s16 p2d)
1444 DSTACK("%s: p2d=(%d,%d)",
1447 // Check that it doesn't exist already
1449 return getSectorNoGenerate(p2d);
1451 catch(InvalidPositionException &e)
1456 Try to load the sector from disk.
1458 if(loadSectorFull(p2d) == true)
1460 return getSectorNoGenerate(p2d);
1464 If there is no master heightmap, throw.
1466 if(m_heightmap == NULL)
1468 throw InvalidPositionException("emergeSector(): no heightmap");
1472 Do not generate over-limit
1474 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1475 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1476 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1477 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1478 throw InvalidPositionException("emergeSector(): pos. over limit");
1481 Generate sector and heightmaps
1484 // Number of heightmaps in sector in each direction
1485 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1487 // Heightmap side width
1488 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1490 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1492 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1493 " heightmaps and objects"<<std::endl;*/
1495 // Loop through sub-heightmaps
1496 for(s16 y=0; y<hm_split; y++)
1497 for(s16 x=0; x<hm_split; x++)
1499 v2s16 p_in_sector = v2s16(x,y);
1500 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1502 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1503 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1504 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1505 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1508 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1509 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1512 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1514 sector->setHeightmap(p_in_sector, hm);
1516 //TODO: Make these values configurable
1517 //hm->generateContinued(0.0, 0.0, corners);
1518 hm->generateContinued(0.5, 0.2, corners);
1519 //hm->generateContinued(1.0, 0.2, corners);
1520 //hm->generateContinued(2.0, 0.2, corners);
1530 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1531 sector->setObjects(objects);
1533 v2s16 mhm_p = p2d * hm_split;
1535 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1536 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1537 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1538 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1541 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1542 float avgslope = 0.0;
1543 avgslope += fabs(avgheight - corners[0]);
1544 avgslope += fabs(avgheight - corners[1]);
1545 avgslope += fabs(avgheight - corners[2]);
1546 avgslope += fabs(avgheight - corners[3]);
1548 avgslope /= MAP_BLOCKSIZE;
1549 //dstream<<"avgslope="<<avgslope<<std::endl;
1551 float pitness = 0.0;
1553 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1556 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1559 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1562 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1566 pitness /= MAP_BLOCKSIZE;
1567 //dstream<<"pitness="<<pitness<<std::endl;
1570 Plant some trees if there is not much slope
1573 // Avgslope is the derivative of a hill
1574 float t = avgslope * avgslope;
1575 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1578 tree_max = a / (t/0.03);
1581 u32 count = (rand()%(tree_max+1));
1582 //u32 count = tree_max;
1583 for(u32 i=0; i<count; i++)
1585 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1586 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1587 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1590 objects->insert(v3s16(x, y, z),
1591 SECTOR_OBJECT_TREE_1);
1595 Plant some bushes if sector is pit-like
1598 // Pitness usually goes at around -0.5...0.5
1600 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1602 bush_max = (pitness*a*4);
1605 u32 count = (rand()%(bush_max+1));
1606 for(u32 i=0; i<count; i++)
1608 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1609 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1610 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1613 objects->insert(v3s16(x, y, z),
1614 SECTOR_OBJECT_BUSH_1);
1618 Add ravine (randomly)
1620 if(m_params.ravines_amount != 0)
1622 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1625 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1626 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1629 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1630 objects->insert(v3s16(x, y, z),
1631 SECTOR_OBJECT_RAVINE);
1638 JMutexAutoLock lock(m_sector_mutex);
1639 m_sectors.insert(p2d, sector);
1644 MapBlock * ServerMap::emergeBlock(
1646 bool only_from_disk,
1647 core::map<v3s16, MapBlock*> &changed_blocks,
1648 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1651 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1653 p.X, p.Y, p.Z, only_from_disk);
1655 /*dstream<<"ServerMap::emergeBlock(): "
1656 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1657 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1658 v2s16 p2d(p.X, p.Z);
1661 This will create or load a sector if not found in memory.
1662 If block exists on disk, it will be loaded.
1664 NOTE: On old save formats, this will be slow, as it generates
1665 lighting on blocks for them.
1667 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1668 assert(sector->getId() == MAPSECTOR_SERVER);
1670 // Try to get a block from the sector
1671 MapBlock *block = NULL;
1672 bool not_on_disk = false;
1674 block = sector->getBlockNoCreate(block_y);
1675 if(block->isDummy() == true)
1680 catch(InvalidPositionException &e)
1686 If block was not found on disk and not going to generate a
1687 new one, make sure there is a dummy block in place.
1689 if(not_on_disk && only_from_disk)
1693 // Create dummy block
1694 block = new MapBlock(this, p, true);
1696 // Add block to sector
1697 sector->insertBlock(block);
1703 //dstream<<"Not found on disk, generating."<<std::endl;
1704 //TimeTaker("emergeBlock()", g_irrlicht);
1707 Do not generate over-limit
1709 if(blockpos_over_limit(p))
1710 throw InvalidPositionException("emergeBlock(): pos. over limit");
1715 Go on generating the block.
1717 TODO: If a dungeon gets generated so that it's side gets
1718 revealed to the outside air, the lighting should be
1723 If block doesn't exist, create one.
1724 If it exists, it is a dummy. In that case unDummify() it.
1728 block = sector->createBlankBlockNoInsert(block_y);
1732 // Remove the block so that nobody can get a half-generated one.
1733 sector->removeBlock(block);
1734 // Allocate the block to be a proper one.
1738 // Randomize a bit. This makes dungeons.
1739 /*bool low_block_is_empty = false;
1741 low_block_is_empty = true;*/
1744 //const s32 ued = 8;
1745 bool underground_emptiness[ued*ued*ued];
1746 for(s32 i=0; i<ued*ued*ued; i++)
1748 underground_emptiness[i] = ((rand() % 5) == 0);
1753 This is a messy hack to sort the emptiness a bit
1755 for(s32 j=0; j<2; j++)
1756 for(s32 y0=0; y0<ued; y0++)
1757 for(s32 z0=0; z0<ued; z0++)
1758 for(s32 x0=0; x0<ued; x0++)
1761 bool &e0 = underground_emptiness[
1762 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1763 +ued*(y0*ued/MAP_BLOCKSIZE)
1764 +(x0*ued/MAP_BLOCKSIZE)];
1767 v3s16(0,0,1), // back
1768 v3s16(1,0,0), // right
1769 v3s16(0,0,-1), // front
1770 v3s16(-1,0,0), // left
1771 /*v3s16(0,1,0), // top
1772 v3s16(0,-1,0), // bottom*/
1774 for(s32 i=0; i<4; i++)
1776 v3s16 p1 = p0 + dirs[i];
1777 if(isInArea(p1, ued) == false)
1779 bool &e1 = underground_emptiness[
1780 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1781 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1782 +(p1.X*ued/MAP_BLOCKSIZE)];
1787 v3s16(0,1,0), // top
1788 v3s16(0,-1,0), // bottom
1789 /*v3s16(0,0,1), // back
1790 v3s16(1,0,0), // right
1791 v3s16(0,0,-1), // front
1792 v3s16(-1,0,0), // left*/
1794 for(s32 i=0; i<2; i++)
1796 v3s16 p2 = p1 + dirs[i];
1799 if(isInArea(p2, ued) == false)
1801 bool &e2 = underground_emptiness[
1802 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1803 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1804 +(p2.X*ued/MAP_BLOCKSIZE)];
1819 // This is the basic material of what the visible flat ground
1821 u8 material = CONTENT_GRASS;
1823 u8 water_material = CONTENT_WATER;
1824 if(g_settings.getBool("endless_water"))
1825 water_material = CONTENT_OCEAN;
1827 s32 lowest_ground_y = 32767;
1828 s32 highest_ground_y = -32768;
1831 //sector->printHeightmaps();
1833 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1834 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1836 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1838 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1839 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1840 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1842 dstream<<"WARNING: Surface height not found in sector "
1843 "for block that is being emerged"<<std::endl;
1847 s16 surface_y = surface_y_f;
1848 //avg_ground_y += surface_y;
1849 if(surface_y < lowest_ground_y)
1850 lowest_ground_y = surface_y;
1851 if(surface_y > highest_ground_y)
1852 highest_ground_y = surface_y;
1854 s32 surface_depth = 0;
1856 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1858 //float min_slope = 0.45;
1859 //float max_slope = 0.85;
1860 float min_slope = 0.60;
1861 float max_slope = 1.20;
1862 float min_slope_depth = 5.0;
1863 float max_slope_depth = 0;
1864 if(slope < min_slope)
1865 surface_depth = min_slope_depth;
1866 else if(slope > max_slope)
1867 surface_depth = max_slope_depth;
1869 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1871 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1873 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1878 NOTE: If there are some man-made structures above the
1879 newly created block, they won't be taken into account.
1881 if(real_y > surface_y)
1882 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1888 if(real_y <= surface_y - surface_depth)
1891 if(underground_emptiness[
1892 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1893 +ued*(y0*ued/MAP_BLOCKSIZE)
1894 +(x0*ued/MAP_BLOCKSIZE)])
1900 n.d = CONTENT_STONE;
1903 // If node is at or under heightmap y
1904 else if(real_y <= surface_y)
1906 // If under water level, it's mud
1907 if(real_y < WATER_LEVEL)
1909 // Only the topmost node is grass
1910 else if(real_y <= surface_y - 1)
1912 // Else it's the main material
1916 // If node is over heightmap y
1918 // If under water level, it's water
1919 if(real_y < WATER_LEVEL)
1921 n.d = water_material;
1922 n.setLight(LIGHTBANK_DAY,
1923 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1929 block->setNode(v3s16(x0,y0,z0), n);
1934 Calculate is_underground
1936 // Probably underground if the highest part of block is under lowest
1938 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1939 block->setIsUnderground(is_underground);
1942 Force lighting update if some part of block is underground
1943 This is needed because of caves.
1946 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1947 if(some_part_underground)
1948 //if(is_underground)
1950 lighting_invalidated_blocks[block->getPos()] = block;
1957 //if(is_underground)
1958 if(some_part_underground)
1960 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1961 for(s16 i=0; i<underground_level*3; i++)
1966 (rand()%(MAP_BLOCKSIZE-2))+1,
1967 (rand()%(MAP_BLOCKSIZE-2))+1,
1968 (rand()%(MAP_BLOCKSIZE-2))+1
1974 //if(is_ground_content(block->getNode(cp).d))
1975 if(block->getNode(cp).d == CONTENT_STONE)
1977 block->setNode(cp, n);
1979 for(u16 i=0; i<26; i++)
1981 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1982 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1984 block->setNode(cp+g_26dirs[i], n);
1991 Create a few rats in empty blocks underground
1995 //for(u16 i=0; i<2; i++)
1998 (rand()%(MAP_BLOCKSIZE-2))+1,
1999 (rand()%(MAP_BLOCKSIZE-2))+1,
2000 (rand()%(MAP_BLOCKSIZE-2))+1
2003 // Check that the place is empty
2004 //if(!is_ground_content(block->getNode(cp).d))
2007 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2008 block->addObject(obj);
2014 Add block to sector.
2016 sector->insertBlock(block);
2022 // An y-wise container of changed blocks
2023 core::map<s16, MapBlock*> changed_blocks_sector;
2026 Check if any sector's objects can be placed now.
2029 core::map<v3s16, u8> *objects = sector->getObjects();
2030 core::list<v3s16> objects_to_remove;
2031 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2032 i.atEnd() == false; i++)
2034 v3s16 p = i.getNode()->getKey();
2036 u8 d = i.getNode()->getValue();
2038 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2043 if(d == SECTOR_OBJECT_TEST)
2045 if(sector->isValidArea(p + v3s16(0,0,0),
2046 p + v3s16(0,0,0), &changed_blocks_sector))
2049 n.d = CONTENT_TORCH;
2050 sector->setNode(p, n);
2051 objects_to_remove.push_back(p);
2054 else if(d == SECTOR_OBJECT_TREE_1)
2056 v3s16 p_min = p + v3s16(-1,0,-1);
2057 v3s16 p_max = p + v3s16(1,4,1);
2058 if(sector->isValidArea(p_min, p_max,
2059 &changed_blocks_sector))
2063 sector->setNode(p+v3s16(0,0,0), n);
2064 sector->setNode(p+v3s16(0,1,0), n);
2065 sector->setNode(p+v3s16(0,2,0), n);
2066 sector->setNode(p+v3s16(0,3,0), n);
2068 n.d = CONTENT_LEAVES;
2070 sector->setNode(p+v3s16(0,4,0), n);
2072 sector->setNode(p+v3s16(-1,4,0), n);
2073 sector->setNode(p+v3s16(1,4,0), n);
2074 sector->setNode(p+v3s16(0,4,-1), n);
2075 sector->setNode(p+v3s16(0,4,1), n);
2076 sector->setNode(p+v3s16(1,4,1), n);
2077 sector->setNode(p+v3s16(-1,4,1), n);
2078 sector->setNode(p+v3s16(-1,4,-1), n);
2079 sector->setNode(p+v3s16(1,4,-1), n);
2081 sector->setNode(p+v3s16(-1,3,0), n);
2082 sector->setNode(p+v3s16(1,3,0), n);
2083 sector->setNode(p+v3s16(0,3,-1), n);
2084 sector->setNode(p+v3s16(0,3,1), n);
2085 sector->setNode(p+v3s16(1,3,1), n);
2086 sector->setNode(p+v3s16(-1,3,1), n);
2087 sector->setNode(p+v3s16(-1,3,-1), n);
2088 sector->setNode(p+v3s16(1,3,-1), n);
2090 objects_to_remove.push_back(p);
2092 // Lighting has to be recalculated for this one.
2093 sector->getBlocksInArea(p_min, p_max,
2094 lighting_invalidated_blocks);
2097 else if(d == SECTOR_OBJECT_BUSH_1)
2099 if(sector->isValidArea(p + v3s16(0,0,0),
2100 p + v3s16(0,0,0), &changed_blocks_sector))
2103 n.d = CONTENT_LEAVES;
2104 sector->setNode(p+v3s16(0,0,0), n);
2106 objects_to_remove.push_back(p);
2109 else if(d == SECTOR_OBJECT_RAVINE)
2112 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2113 v3s16 p_max = p + v3s16(6,6,6);
2114 if(sector->isValidArea(p_min, p_max,
2115 &changed_blocks_sector))
2118 n.d = CONTENT_STONE;
2121 s16 depth = maxdepth + (rand()%10);
2123 s16 minz = -6 - (-2);
2125 for(s16 x=-6; x<=6; x++)
2127 z += -1 + (rand()%3);
2132 for(s16 y=depth+(rand()%2); y<=6; y++)
2134 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2137 v3s16 p2 = p + v3s16(x,y,z-2);
2138 if(is_ground_content(sector->getNode(p2).d))
2139 sector->setNode(p2, n);
2142 v3s16 p2 = p + v3s16(x,y,z-1);
2143 if(is_ground_content(sector->getNode(p2).d))
2144 sector->setNode(p2, n2);
2147 v3s16 p2 = p + v3s16(x,y,z+0);
2148 if(is_ground_content(sector->getNode(p2).d))
2149 sector->setNode(p2, n2);
2152 v3s16 p2 = p + v3s16(x,y,z+1);
2153 if(is_ground_content(sector->getNode(p2).d))
2154 sector->setNode(p2, n);
2157 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2158 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2162 objects_to_remove.push_back(p);
2164 // Lighting has to be recalculated for this one.
2165 sector->getBlocksInArea(p_min, p_max,
2166 lighting_invalidated_blocks);
2171 dstream<<"ServerMap::emergeBlock(): "
2172 "Invalid heightmap object"
2177 catch(InvalidPositionException &e)
2179 dstream<<"WARNING: "<<__FUNCTION_NAME
2180 <<": while inserting object "<<(int)d
2181 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2182 <<" InvalidPositionException.what()="
2183 <<e.what()<<std::endl;
2184 // This is not too fatal and seems to happen sometimes.
2189 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2190 i != objects_to_remove.end(); i++)
2192 objects->remove(*i);
2195 for(core::map<s16, MapBlock*>::Iterator
2196 i = changed_blocks_sector.getIterator();
2197 i.atEnd() == false; i++)
2199 MapBlock *block = i.getNode()->getValue();
2201 changed_blocks.insert(block->getPos(), block);
2207 void ServerMap::createDir(std::string path)
2209 if(fs::CreateDir(path) == false)
2211 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2212 <<"\""<<path<<"\""<<std::endl;
2213 throw BaseException("ServerMap failed to create directory");
2217 std::string ServerMap::getSectorSubDir(v2s16 pos)
2220 snprintf(cc, 9, "%.4x%.4x",
2221 (unsigned int)pos.X&0xffff,
2222 (unsigned int)pos.Y&0xffff);
2224 return std::string(cc);
2227 std::string ServerMap::getSectorDir(v2s16 pos)
2229 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2232 v2s16 ServerMap::getSectorPos(std::string dirname)
2234 if(dirname.size() != 8)
2235 throw InvalidFilenameException("Invalid sector directory name");
2237 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2239 throw InvalidFilenameException("Invalid sector directory name");
2240 v2s16 pos((s16)x, (s16)y);
2244 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2246 v2s16 p2d = getSectorPos(sectordir);
2248 if(blockfile.size() != 4){
2249 throw InvalidFilenameException("Invalid block filename");
2252 int r = sscanf(blockfile.c_str(), "%4x", &y);
2254 throw InvalidFilenameException("Invalid block filename");
2255 return v3s16(p2d.X, y, p2d.Y);
2259 #define ENABLE_SECTOR_SAVING 1
2260 #define ENABLE_SECTOR_LOADING 1
2261 #define ENABLE_BLOCK_SAVING 1
2262 #define ENABLE_BLOCK_LOADING 1
2264 void ServerMap::save(bool only_changed)
2266 DSTACK(__FUNCTION_NAME);
2267 if(m_map_saving_enabled == false)
2269 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2273 if(only_changed == false)
2274 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2277 saveMasterHeightmap();
2279 u32 sector_meta_count = 0;
2280 u32 block_count = 0;
2283 JMutexAutoLock lock(m_sector_mutex);
2285 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2286 for(; i.atEnd() == false; i++)
2288 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2289 assert(sector->getId() == MAPSECTOR_SERVER);
2291 if(ENABLE_SECTOR_SAVING)
2293 if(sector->differs_from_disk || only_changed == false)
2295 saveSectorMeta(sector);
2296 sector_meta_count++;
2299 if(ENABLE_BLOCK_SAVING)
2301 core::list<MapBlock*> blocks;
2302 sector->getBlocks(blocks);
2303 core::list<MapBlock*>::Iterator j;
2304 for(j=blocks.begin(); j!=blocks.end(); j++)
2306 MapBlock *block = *j;
2307 if(block->getChangedFlag() || only_changed == false)
2319 Only print if something happened or saved whole map
2321 if(only_changed == false || sector_meta_count != 0
2322 || block_count != 0)
2324 dstream<<DTIME<<"ServerMap: Written: "
2325 <<sector_meta_count<<" sector metadata files, "
2326 <<block_count<<" block files"
2331 void ServerMap::loadAll()
2333 DSTACK(__FUNCTION_NAME);
2334 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2336 loadMasterHeightmap();
2338 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2340 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2342 JMutexAutoLock lock(m_sector_mutex);
2345 s32 printed_counter = -100000;
2346 s32 count = list.size();
2348 std::vector<fs::DirListNode>::iterator i;
2349 for(i=list.begin(); i!=list.end(); i++)
2351 if(counter > printed_counter + 10)
2353 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2354 printed_counter = counter;
2358 MapSector *sector = NULL;
2360 // We want directories
2364 sector = loadSectorMeta(i->name);
2366 catch(InvalidFilenameException &e)
2368 // This catches unknown crap in directory
2371 if(ENABLE_BLOCK_LOADING)
2373 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2374 (m_savedir+"/sectors/"+i->name);
2375 std::vector<fs::DirListNode>::iterator i2;
2376 for(i2=list2.begin(); i2!=list2.end(); i2++)
2382 loadBlock(i->name, i2->name, sector);
2384 catch(InvalidFilenameException &e)
2386 // This catches unknown crap in directory
2391 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2394 void ServerMap::saveMasterHeightmap()
2396 DSTACK(__FUNCTION_NAME);
2397 createDir(m_savedir);
2399 std::string fullpath = m_savedir + "/master_heightmap";
2400 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2401 if(o.good() == false)
2402 throw FileNotGoodException("Cannot open master heightmap");
2404 // Format used for writing
2405 u8 version = SER_FMT_VER_HIGHEST;
2408 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2410 [0] u8 serialization version
2411 [1] X master heightmap
2413 u32 fullsize = 1 + hmdata.getSize();
2414 SharedBuffer<u8> data(fullsize);
2417 memcpy(&data[1], *hmdata, hmdata.getSize());
2419 o.write((const char*)*data, fullsize);
2422 m_heightmap->serialize(o, version);
2425 void ServerMap::loadMasterHeightmap()
2427 DSTACK(__FUNCTION_NAME);
2428 std::string fullpath = m_savedir + "/master_heightmap";
2429 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2430 if(is.good() == false)
2431 throw FileNotGoodException("Cannot open master heightmap");
2433 if(m_heightmap != NULL)
2436 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2439 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2441 DSTACK(__FUNCTION_NAME);
2442 // Format used for writing
2443 u8 version = SER_FMT_VER_HIGHEST;
2445 v2s16 pos = sector->getPos();
2446 createDir(m_savedir);
2447 createDir(m_savedir+"/sectors");
2448 std::string dir = getSectorDir(pos);
2451 std::string fullpath = dir + "/heightmap";
2452 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2453 if(o.good() == false)
2454 throw FileNotGoodException("Cannot open master heightmap");
2456 sector->serialize(o, version);
2458 sector->differs_from_disk = false;
2461 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2463 DSTACK(__FUNCTION_NAME);
2465 v2s16 p2d = getSectorPos(dirname);
2466 std::string dir = m_savedir + "/sectors/" + dirname;
2468 std::string fullpath = dir + "/heightmap";
2469 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2470 if(is.good() == false)
2471 throw FileNotGoodException("Cannot open sector heightmap");
2473 ServerMapSector *sector = ServerMapSector::deSerialize
2474 (is, this, p2d, &m_hwrapper, m_sectors);
2476 sector->differs_from_disk = false;
2481 bool ServerMap::loadSectorFull(v2s16 p2d)
2483 DSTACK(__FUNCTION_NAME);
2484 std::string sectorsubdir = getSectorSubDir(p2d);
2486 MapSector *sector = NULL;
2488 JMutexAutoLock lock(m_sector_mutex);
2491 sector = loadSectorMeta(sectorsubdir);
2493 catch(InvalidFilenameException &e)
2497 catch(FileNotGoodException &e)
2501 catch(std::exception &e)
2506 if(ENABLE_BLOCK_LOADING)
2508 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2509 (m_savedir+"/sectors/"+sectorsubdir);
2510 std::vector<fs::DirListNode>::iterator i2;
2511 for(i2=list2.begin(); i2!=list2.end(); i2++)
2517 loadBlock(sectorsubdir, i2->name, sector);
2519 catch(InvalidFilenameException &e)
2521 // This catches unknown crap in directory
2529 bool ServerMap::deFlushSector(v2s16 p2d)
2531 DSTACK(__FUNCTION_NAME);
2532 // See if it already exists in memory
2534 MapSector *sector = getSectorNoGenerate(p2d);
2537 catch(InvalidPositionException &e)
2540 Try to load the sector from disk.
2542 if(loadSectorFull(p2d) == true)
2551 void ServerMap::saveBlock(MapBlock *block)
2553 DSTACK(__FUNCTION_NAME);
2555 Dummy blocks are not written
2557 if(block->isDummy())
2559 /*v3s16 p = block->getPos();
2560 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2561 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2565 // Format used for writing
2566 u8 version = SER_FMT_VER_HIGHEST;
2568 v3s16 p3d = block->getPos();
2569 v2s16 p2d(p3d.X, p3d.Z);
2570 createDir(m_savedir);
2571 createDir(m_savedir+"/sectors");
2572 std::string dir = getSectorDir(p2d);
2575 // Block file is map/sectors/xxxxxxxx/xxxx
2577 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2578 std::string fullpath = dir + "/" + cc;
2579 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2580 if(o.good() == false)
2581 throw FileNotGoodException("Cannot open block data");
2584 [0] u8 serialization version
2587 o.write((char*)&version, 1);
2589 block->serialize(o, version);
2592 Versions up from 9 have block objects.
2596 block->serializeObjects(o, version);
2599 // We just wrote it to the disk
2600 block->resetChangedFlag();
2603 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2605 DSTACK(__FUNCTION_NAME);
2609 // Block file is map/sectors/xxxxxxxx/xxxx
2610 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2611 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2612 if(is.good() == false)
2613 throw FileNotGoodException("Cannot open block file");
2615 v3s16 p3d = getBlockPos(sectordir, blockfile);
2616 v2s16 p2d(p3d.X, p3d.Z);
2618 assert(sector->getPos() == p2d);
2620 u8 version = SER_FMT_VER_INVALID;
2621 is.read((char*)&version, 1);
2623 /*u32 block_size = MapBlock::serializedLength(version);
2624 SharedBuffer<u8> data(block_size);
2625 is.read((char*)*data, block_size);*/
2627 // This will always return a sector because we're the server
2628 //MapSector *sector = emergeSector(p2d);
2630 MapBlock *block = NULL;
2631 bool created_new = false;
2633 block = sector->getBlockNoCreate(p3d.Y);
2635 catch(InvalidPositionException &e)
2637 block = sector->createBlankBlockNoInsert(p3d.Y);
2641 // deserialize block data
2642 block->deSerialize(is, version);
2645 Versions up from 9 have block objects.
2649 block->updateObjects(is, version, NULL, 0);
2653 sector->insertBlock(block);
2656 Convert old formats to new and save
2659 // Save old format blocks in new format
2660 if(version < SER_FMT_VER_HIGHEST)
2665 // We just loaded it from the disk, so it's up-to-date.
2666 block->resetChangedFlag();
2669 catch(SerializationError &e)
2671 dstream<<"WARNING: Invalid block data on disk "
2672 "(SerializationError). Ignoring."
2677 // Gets from master heightmap
2678 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2680 assert(m_heightmap != NULL);
2688 corners[0] = m_heightmap->getGroundHeight
2689 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2690 corners[1] = m_heightmap->getGroundHeight
2691 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2692 corners[2] = m_heightmap->getGroundHeight
2693 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2694 corners[3] = m_heightmap->getGroundHeight
2695 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2698 void ServerMap::PrintInfo(std::ostream &out)
2709 ClientMap::ClientMap(
2711 JMutex &range_mutex,
2712 s16 &viewing_range_nodes,
2713 bool &viewing_range_all,
2714 scene::ISceneNode* parent,
2715 scene::ISceneManager* mgr,
2719 scene::ISceneNode(parent, mgr, id),
2722 m_range_mutex(range_mutex),
2723 m_viewing_range_nodes(viewing_range_nodes),
2724 m_viewing_range_all(viewing_range_all)
2728 /*m_box = core::aabbox3d<f32>(0,0,0,
2729 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2730 /*m_box = core::aabbox3d<f32>(0,0,0,
2731 map->getSizeNodes().X * BS,
2732 map->getSizeNodes().Y * BS,
2733 map->getSizeNodes().Z * BS);*/
2734 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2735 BS*1000000,BS*1000000,BS*1000000);
2737 //setPosition(v3f(BS,BS,BS));
2740 ClientMap::~ClientMap()
2742 JMutexAutoLock lock(mesh_mutex);
2751 MapSector * ClientMap::emergeSector(v2s16 p2d)
2753 DSTACK(__FUNCTION_NAME);
2754 // Check that it doesn't exist already
2756 return getSectorNoGenerate(p2d);
2758 catch(InvalidPositionException &e)
2762 // Create a sector with no heightmaps
2763 ClientMapSector *sector = new ClientMapSector(this, p2d);
2766 JMutexAutoLock lock(m_sector_mutex);
2767 m_sectors.insert(p2d, sector);
2773 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2775 DSTACK(__FUNCTION_NAME);
2776 ClientMapSector *sector = NULL;
2778 JMutexAutoLock lock(m_sector_mutex);
2780 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2784 sector = (ClientMapSector*)n->getValue();
2785 assert(sector->getId() == MAPSECTOR_CLIENT);
2789 sector = new ClientMapSector(this, p2d);
2791 JMutexAutoLock lock(m_sector_mutex);
2792 m_sectors.insert(p2d, sector);
2796 sector->deSerialize(is);
2799 void ClientMap::OnRegisterSceneNode()
2803 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2804 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2807 ISceneNode::OnRegisterSceneNode();
2810 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2812 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2813 DSTACK(__FUNCTION_NAME);
2815 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2818 Get time for measuring timeout.
2820 Measuring time is very useful for long delays when the
2821 machine is swapping a lot.
2823 int time1 = time(0);
2825 //s32 daynight_i = m_client->getDayNightIndex();
2826 u32 daynight_ratio = m_client->getDayNightRatio();
2829 Collect all blocks that are in the view range
2831 Should not optimize more here as we want to auto-update
2832 all changed nodes in viewing range at the next step.
2835 s16 viewing_range_nodes;
2836 bool viewing_range_all;
2838 JMutexAutoLock lock(m_range_mutex);
2839 viewing_range_nodes = m_viewing_range_nodes;
2840 viewing_range_all = m_viewing_range_all;
2843 m_camera_mutex.Lock();
2844 v3f camera_position = m_camera_position;
2845 v3f camera_direction = m_camera_direction;
2846 m_camera_mutex.Unlock();
2849 Get all blocks and draw all visible ones
2852 v3s16 cam_pos_nodes(
2853 camera_position.X / BS,
2854 camera_position.Y / BS,
2855 camera_position.Z / BS);
2857 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2859 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2860 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2862 // Take a fair amount as we will be dropping more out later
2864 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2865 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2866 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2868 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2869 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2870 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2872 u32 vertex_count = 0;
2874 // For limiting number of mesh updates per frame
2875 u32 mesh_update_count = 0;
2877 //NOTE: The sectors map should be locked but we're not doing it
2878 // because it'd cause too much delays
2880 int timecheck_counter = 0;
2882 core::map<v2s16, MapSector*>::Iterator si;
2883 si = m_sectors.getIterator();
2884 for(; si.atEnd() == false; si++)
2887 timecheck_counter++;
2888 if(timecheck_counter > 50)
2890 int time2 = time(0);
2891 if(time2 > time1 + 4)
2893 dstream<<"ClientMap::renderMap(): "
2894 "Rendering takes ages, returning."
2901 MapSector *sector = si.getNode()->getValue();
2902 v2s16 sp = sector->getPos();
2904 if(viewing_range_all == false)
2906 if(sp.X < p_blocks_min.X
2907 || sp.X > p_blocks_max.X
2908 || sp.Y < p_blocks_min.Z
2909 || sp.Y > p_blocks_max.Z)
2913 core::list< MapBlock * > sectorblocks;
2914 sector->getBlocks(sectorblocks);
2920 core::list< MapBlock * >::Iterator i;
2921 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2923 MapBlock *block = *i;
2926 Compare block position to camera position, skip
2927 if not seen on display
2930 v3s16 blockpos_nodes = block->getPosRelative();
2932 // Block center position
2934 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2935 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2936 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2939 // Block position relative to camera
2940 v3f blockpos_relative = blockpos - camera_position;
2942 // Distance in camera direction (+=front, -=back)
2943 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2946 f32 d = blockpos_relative.getLength();
2948 if(viewing_range_all == false)
2950 // If block is far away, don't draw it
2951 if(d > viewing_range_nodes * BS)
2952 // This is nicer when fog is used
2953 //if((dforward+d)/2 > viewing_range_nodes * BS)
2957 // Maximum radius of a block
2958 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2960 // If block is (nearly) touching the camera, don't
2961 // bother validating further (that is, render it anyway)
2962 if(d > block_max_radius * 1.5)
2964 // Cosine of the angle between the camera direction
2965 // and the block direction (camera_direction is an unit vector)
2966 f32 cosangle = dforward / d;
2968 // Compensate for the size of the block
2969 // (as the block has to be shown even if it's a bit off FOV)
2970 // This is an estimate.
2971 cosangle += block_max_radius / dforward;
2973 // If block is not in the field of view, skip it
2974 //if(cosangle < cos(FOV_ANGLE/2))
2975 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2980 Draw the faces of the block
2983 bool mesh_expired = false;
2986 JMutexAutoLock lock(block->mesh_mutex);
2988 mesh_expired = block->getMeshExpired();
2990 // Mesh has not been expired and there is no mesh:
2991 // block has no content
2992 if(block->mesh == NULL && mesh_expired == false)
2996 f32 faraway = BS*50;
2997 //f32 faraway = viewing_range_nodes * BS;
3000 This has to be done with the mesh_mutex unlocked
3002 if(mesh_expired && mesh_update_count < 6
3003 && (d < faraway || mesh_update_count < 3))
3004 //if(mesh_expired && mesh_update_count < 4)
3006 mesh_update_count++;
3008 // Mesh has been expired: generate new mesh
3009 //block->updateMeshes(daynight_i);
3010 block->updateMesh(daynight_ratio);
3012 mesh_expired = false;
3016 Don't draw an expired mesh that is far away
3018 /*if(mesh_expired && d >= faraway)
3021 // Instead, delete it
3022 JMutexAutoLock lock(block->mesh_mutex);
3025 block->mesh->drop();
3028 // And continue to next block
3033 JMutexAutoLock lock(block->mesh_mutex);
3035 scene::SMesh *mesh = block->mesh;
3040 u32 c = mesh->getMeshBufferCount();
3042 for(u32 i=0; i<c; i++)
3044 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3045 const video::SMaterial& material = buf->getMaterial();
3046 video::IMaterialRenderer* rnd =
3047 driver->getMaterialRenderer(material.MaterialType);
3048 bool transparent = (rnd && rnd->isTransparent());
3049 // Render transparent on transparent pass and likewise.
3050 if(transparent == is_transparent_pass)
3052 driver->setMaterial(buf->getMaterial());
3053 driver->drawMeshBuffer(buf);
3054 vertex_count += buf->getVertexCount();
3058 } // foreach sectorblocks
3061 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3062 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3065 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3068 Add it to all blocks touching it
3071 v3s16(0,0,0), // this
3072 v3s16(0,0,1), // back
3073 v3s16(0,1,0), // top
3074 v3s16(1,0,0), // right
3075 v3s16(0,0,-1), // front
3076 v3s16(0,-1,0), // bottom
3077 v3s16(-1,0,0), // left
3079 for(u16 i=0; i<7; i++)
3081 v3s16 p2 = p + dirs[i];
3082 // Block position of neighbor (or requested) node
3083 v3s16 blockpos = getNodeBlockPos(p2);
3084 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3085 if(blockref == NULL)
3087 // Relative position of requested node
3088 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3089 blockref->setTempMod(relpos, mod);
3091 return getNodeBlockPos(p);
3093 v3s16 ClientMap::clearTempMod(v3s16 p)
3096 v3s16(0,0,0), // this
3097 v3s16(0,0,1), // back
3098 v3s16(0,1,0), // top
3099 v3s16(1,0,0), // right
3100 v3s16(0,0,-1), // front
3101 v3s16(0,-1,0), // bottom
3102 v3s16(-1,0,0), // left
3104 for(u16 i=0; i<7; i++)
3106 v3s16 p2 = p + dirs[i];
3107 // Block position of neighbor (or requested) node
3108 v3s16 blockpos = getNodeBlockPos(p2);
3109 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3110 if(blockref == NULL)
3112 // Relative position of requested node
3113 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3114 blockref->clearTempMod(relpos);
3116 return getNodeBlockPos(p);
3119 void ClientMap::PrintInfo(std::ostream &out)
3130 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3135 MapVoxelManipulator::~MapVoxelManipulator()
3137 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3142 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3144 TimeTaker timer1("emerge", &emerge_time);
3146 // Units of these are MapBlocks
3147 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3148 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3150 VoxelArea block_area_nodes
3151 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3153 addArea(block_area_nodes);
3155 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3156 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3157 for(s32 x=p_min.X; x<=p_max.X; x++)
3160 core::map<v3s16, bool>::Node *n;
3161 n = m_loaded_blocks.find(p);
3165 bool block_data_inexistent = false;
3168 TimeTaker timer1("emerge load", &emerge_load_time);
3170 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3171 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3174 dstream<<std::endl;*/
3176 MapBlock *block = m_map->getBlockNoCreate(p);
3177 if(block->isDummy())
3178 block_data_inexistent = true;
3180 block->copyTo(*this);
3182 catch(InvalidPositionException &e)
3184 block_data_inexistent = true;
3187 if(block_data_inexistent)
3189 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3190 // Fill with VOXELFLAG_INEXISTENT
3191 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3192 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3194 s32 i = m_area.index(a.MinEdge.X,y,z);
3195 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3199 m_loaded_blocks.insert(p, true);
3202 //dstream<<"emerge done"<<std::endl;
3207 void MapVoxelManipulator::emerge(VoxelArea a)
3209 TimeTaker timer1("emerge", &emerge_time);
3211 v3s16 size = a.getExtent();
3213 VoxelArea padded = a;
3214 padded.pad(m_area.getExtent() / 4);
3217 for(s16 z=0; z<size.Z; z++)
3218 for(s16 y=0; y<size.Y; y++)
3219 for(s16 x=0; x<size.X; x++)
3222 s32 i = m_area.index(a.MinEdge + p);
3223 // Don't touch nodes that have already been loaded
3224 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3228 TimeTaker timer1("emerge load", &emerge_load_time);
3229 MapNode n = m_map->getNode(a.MinEdge + p);
3233 catch(InvalidPositionException &e)
3235 m_flags[i] = VOXELFLAG_INEXISTENT;
3243 TODO: Add an option to only update eg. water and air nodes.
3244 This will make it interfere less with important stuff if
3247 void MapVoxelManipulator::blitBack
3248 (core::map<v3s16, MapBlock*> & modified_blocks)
3250 if(m_area.getExtent() == v3s16(0,0,0))
3253 //TimeTaker timer1("blitBack");
3256 Initialize block cache
3258 v3s16 blockpos_last;
3259 MapBlock *block = NULL;
3260 bool block_checked_in_modified = false;
3262 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3263 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3264 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3268 u8 f = m_flags[m_area.index(p)];
3269 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3272 MapNode &n = m_data[m_area.index(p)];
3274 v3s16 blockpos = getNodeBlockPos(p);
3279 if(block == NULL || blockpos != blockpos_last){
3280 block = m_map->getBlockNoCreate(blockpos);
3281 blockpos_last = blockpos;
3282 block_checked_in_modified = false;
3285 // Calculate relative position in block
3286 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3288 // Don't continue if nothing has changed here
3289 if(block->getNode(relpos) == n)
3292 //m_map->setNode(m_area.MinEdge + p, n);
3293 block->setNode(relpos, n);
3296 Make sure block is in modified_blocks
3298 if(block_checked_in_modified == false)
3300 modified_blocks[blockpos] = block;
3301 block_checked_in_modified = true;
3304 catch(InvalidPositionException &e)